From 2b48db4aa4ee386b6a0a3c0ec255c2187cb14869 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Tue, 21 Jul 2026 09:38:38 +0000 Subject: [PATCH] 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 --- .../tests/options/subresource-integrity_spec.ts | 2 +- .../build/src/utils/index-file/augment-index-html.ts | 12 +++++++++++- .../src/utils/index-file/augment-index-html_spec.ts | 6 +++--- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/angular/build/src/builders/application/tests/options/subresource-integrity_spec.ts b/packages/angular/build/src/builders/application/tests/options/subresource-integrity_spec.ts index 43700564306c..e846053e64c4 100644 --- a/packages/angular/build/src/builders/application/tests/options/subresource-integrity_spec.ts +++ b/packages/angular/build/src/builders/application/tests/options/subresource-integrity_spec.ts @@ -200,7 +200,7 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { const importmapFiles = Object.keys(importmap.integrity); expect(importmapFiles.sort()) .withContext('importmap integrity keys should match emitted lazy JS files') - .toEqual(lazyJsFiles.sort()); + .toEqual(lazyJsFiles.map((f) => `./${f}`).sort()); for (const [file, integrity] of Object.entries(importmap.integrity)) { const expectedSri = diff --git a/packages/angular/build/src/utils/index-file/augment-index-html.ts b/packages/angular/build/src/utils/index-file/augment-index-html.ts index 5e9d05d1bd56..becbee4be002 100644 --- a/packages/angular/build/src/utils/index-file/augment-index-html.ts +++ b/packages/angular/build/src/utils/index-file/augment-index-html.ts @@ -11,6 +11,12 @@ import { extname } from 'node:path'; import { htmlRewritingStream } from './html-rewriting-stream'; import { VALID_SELF_CLOSING_TAGS } from './valid-self-closing-tags'; +/** + * RegExp to check if a URL is resolvable. + * A URL is resolvable if it is absolute (starting with http/https) or relative (starting with `./`, `../`, or `/`). + */ +const RESOLVABLE_URL_REGEXP = /^(?:\.{0,2}\/|https?:\/\/)/i; + export type LoadOutputFileFunctionType = (file: string) => Promise; export type CrossOriginValue = 'none' | 'anonymous' | 'use-credentials'; @@ -168,7 +174,9 @@ export async function augmentIndexHtml( keyA.localeCompare(keyB), ); for (const [url, integrityHash] of sortedEntries) { - integrity[generateUrl(url, deployUrl)] = integrityHash; + const resolvedUrl = generateUrl(url, deployUrl); + const key = RESOLVABLE_URL_REGEXP.test(resolvedUrl) ? resolvedUrl : `./${resolvedUrl}`; + integrity[key] = integrityHash; } const importMapJson = JSON.stringify({ integrity }).replace(/${importMapJson}`; @@ -268,9 +276,11 @@ export async function augmentIndexHtml( if (isString(baseHref)) { updateAttribute(tag, 'href', baseHref); } + if (subResourceIntegrityTag) { rewriter.emitRaw(subResourceIntegrityTag); } + break; case 'link': if (readAttribute(tag, 'rel') === 'preconnect') { diff --git a/packages/angular/build/src/utils/index-file/augment-index-html_spec.ts b/packages/angular/build/src/utils/index-file/augment-index-html_spec.ts index f2801ab3202a..df292b8771a3 100644 --- a/packages/angular/build/src/utils/index-file/augment-index-html_spec.ts +++ b/packages/angular/build/src/utils/index-file/augment-index-html_spec.ts @@ -468,10 +468,10 @@ describe('augment-index-html', () => { const match = content.match(/