From 4fd8f61c0fa255a348d2628bc8a8647fa5245f6f Mon Sep 17 00:00:00 2001 From: SkyZeroZx <73321943+SkyZeroZx@users.noreply.github.com> Date: Fri, 10 Jul 2026 17:00:07 -0500 Subject: [PATCH] fix(@angular/build): prevent HTML injection from inlined font CSS Remote font CSS is embedded into a raw-text style element. Escape closing style tags so a compromised allowlisted response cannot terminate the style element and inject nonce-bearing markup. --- .../src/utils/index-file/inline-fonts.ts | 5 ++-- .../src/utils/index-file/inline-fonts_spec.ts | 28 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/angular/build/src/utils/index-file/inline-fonts.ts b/packages/angular/build/src/utils/index-file/inline-fonts.ts index 52181200c885..c4bdc18af7af 100644 --- a/packages/angular/build/src/utils/index-file/inline-fonts.ts +++ b/packages/angular/build/src/utils/index-file/inline-fonts.ts @@ -150,8 +150,9 @@ export class InlineFontsProcessor { attrs.find(({ name, value }) => name === 'href' && hrefsContent.has(value)); if (hrefAttr) { const href = hrefAttr.value; - const cssContent = hrefsContent.get(href); - rewriter.emitRaw(``); + const cssContent = hrefsContent.get(href) ?? ''; + // Prevent the CSS from terminating the generated raw-text style element. + rewriter.emitRaw(``); } else { rewriter.emitStartTag(tag); } diff --git a/packages/angular/build/src/utils/index-file/inline-fonts_spec.ts b/packages/angular/build/src/utils/index-file/inline-fonts_spec.ts index 46511f853ecf..ad1aea3c8830 100644 --- a/packages/angular/build/src/utils/index-file/inline-fonts_spec.ts +++ b/packages/angular/build/src/utils/index-file/inline-fonts_spec.ts @@ -6,7 +6,9 @@ * found in the LICENSE file at https://angular.dev/license */ +import { htmlRewritingStream } from './html-rewriting-stream'; import { InlineFontsProcessor } from './inline-fonts'; +import { addNonce } from './nonce'; describe('InlineFontsProcessor', () => { describe('Google fonts', () => { @@ -100,6 +102,32 @@ describe('InlineFontsProcessor', () => { ``, ); }); + + it('should escape closing style tags in inlined CSS', async () => { + const inlineFontsProcessor = new InlineFontsProcessor({ + minify: true, + }); + spyOn(inlineFontsProcessor, 'processURL').and.resolveTo( + 'body { color: red; }