Skip to content

Commit 2b48db4

Browse files
committed
fix(@angular/build): ensure import map integrity keys are valid URL-like specifiers
In import maps, the keys for the integrity map must be valid URL-like specifiers (either absolute URLs, or relative URLs starting with /, ./, or ../). If a key is a bare filename like chunk-SYbG1sRo.js, the browser ignores it with a warning. We now prepend ./ to relative paths in the integrity map keys if they do not start with a slash, dot-slash, or protocol, resolving browser warning issues when subresource integrity is enabled for dynamically loaded chunks. Fixes #33617
1 parent 0947f81 commit 2b48db4

3 files changed

Lines changed: 15 additions & 5 deletions

File tree

packages/angular/build/src/builders/application/tests/options/subresource-integrity_spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
200200
const importmapFiles = Object.keys(importmap.integrity);
201201
expect(importmapFiles.sort())
202202
.withContext('importmap integrity keys should match emitted lazy JS files')
203-
.toEqual(lazyJsFiles.sort());
203+
.toEqual(lazyJsFiles.map((f) => `./${f}`).sort());
204204

205205
for (const [file, integrity] of Object.entries(importmap.integrity)) {
206206
const expectedSri =

packages/angular/build/src/utils/index-file/augment-index-html.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ import { extname } from 'node:path';
1111
import { htmlRewritingStream } from './html-rewriting-stream';
1212
import { VALID_SELF_CLOSING_TAGS } from './valid-self-closing-tags';
1313

14+
/**
15+
* RegExp to check if a URL is resolvable.
16+
* A URL is resolvable if it is absolute (starting with http/https) or relative (starting with `./`, `../`, or `/`).
17+
*/
18+
const RESOLVABLE_URL_REGEXP = /^(?:\.{0,2}\/|https?:\/\/)/i;
19+
1420
export type LoadOutputFileFunctionType = (file: string) => Promise<string>;
1521

1622
export type CrossOriginValue = 'none' | 'anonymous' | 'use-credentials';
@@ -168,7 +174,9 @@ export async function augmentIndexHtml(
168174
keyA.localeCompare(keyB),
169175
);
170176
for (const [url, integrityHash] of sortedEntries) {
171-
integrity[generateUrl(url, deployUrl)] = integrityHash;
177+
const resolvedUrl = generateUrl(url, deployUrl);
178+
const key = RESOLVABLE_URL_REGEXP.test(resolvedUrl) ? resolvedUrl : `./${resolvedUrl}`;
179+
integrity[key] = integrityHash;
172180
}
173181
const importMapJson = JSON.stringify({ integrity }).replace(/</g, '\\u003c');
174182
subResourceIntegrityTag = `<script type="importmap">${importMapJson}</script>`;
@@ -268,9 +276,11 @@ export async function augmentIndexHtml(
268276
if (isString(baseHref)) {
269277
updateAttribute(tag, 'href', baseHref);
270278
}
279+
271280
if (subResourceIntegrityTag) {
272281
rewriter.emitRaw(subResourceIntegrityTag);
273282
}
283+
274284
break;
275285
case 'link':
276286
if (readAttribute(tag, 'rel') === 'preconnect') {

packages/angular/build/src/utils/index-file/augment-index-html_spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -468,10 +468,10 @@ describe('augment-index-html', () => {
468468

469469
const match = content.match(/<script type="importmap">([^<]+)<\/script>/);
470470
expect(match).withContext('importmap script tag missing').not.toBeNull();
471-
expect(match?.[1]).toContain('lazy\\u003cchunk.js');
472-
expect(match?.[1]).not.toContain('lazy<chunk.js');
471+
expect(match?.[1]).toContain('./lazy\\u003cchunk.js');
472+
expect(match?.[1]).not.toContain('./lazy<chunk.js');
473473
expect(JSON.parse(match?.[1] ?? '{}')).toEqual({
474-
integrity: { 'lazy<chunk.js': 'sha384-abc' },
474+
integrity: { './lazy<chunk.js': 'sha384-abc' },
475475
});
476476
});
477477

0 commit comments

Comments
 (0)