Skip to content

SSR inlineCriticalCss can context-switch stylesheet media into executable onload JavaScript #33342

@SkyZeroZx

Description

@SkyZeroZx

Command

build

Description

Angular SSR's inlineCriticalCss post-processing can transform a normal stylesheet media attribute into executable JavaScript when deferring non-critical CSS.

A server-rendered Angular template can emit a standard stylesheet link:

<link rel="stylesheet" href="styles.css" media="<dynamic-media-value>">

When inlineCriticalCss is enabled, Angular rewrites the stylesheet to defer loading, producing an inline handler similar to:

<link rel="stylesheet" href="styles.css" media="print" onload="this.media='<dynamic-media-value>'">

If the original media value is dynamic, a single quote in that value can break out of the generated JavaScript string. This creates a framework-owned context switch:

Angular template attribute binding
→ SSR HTML attribute
→ inlineCriticalCss post-processing
→ generated inline JavaScript handler
→ browser executes attacker-controlled JavaScript

The SSR response is rewritten to include an executable inline handler

This was reported https://issuetracker.google.com/issues/519654514

I believe this should be considered a security issue that may be a common pattern in some CMSs

Minimal Reproduction

See https://issuetracker.google.com/issues/519654514

Your Environment

Angular CLI       : 22.0.0
Angular           : 22.0.0
Node.js           : 24.16.0
Package Manager   : npm 10.9.2
Operating System  : win32 x64

┌───────────────────────────┬───────────────────┬───────────────────┐
│ Package                   │ Installed Version │ Requested Version │
├───────────────────────────┼───────────────────┼───────────────────┤
│ @angular/build            │ 22.0.0            │ ^22.0.0           │
│ @angular/cli              │ 22.0.0            │ ^22.0.0           │
│ @angular/common           │ 22.0.0            │ ^22.0.0           │
│ @angular/compiler         │ 22.0.0            │ ^22.0.0           │
│ @angular/compiler-cli     │ 22.0.0            │ ^22.0.0           │
│ @angular/core             │ 22.0.0            │ ^22.0.0           │
│ @angular/forms            │ 22.0.0            │ ^22.0.0           │
│ @angular/platform-browser │ 22.0.0            │ ^22.0.0           │
│ @angular/platform-server  │ 22.0.0            │ ^22.0.0           │
│ @angular/router           │ 22.0.0            │ ^22.0.0           │
│ @angular/ssr              │ 22.0.0            │ ^22.0.0           │
│ rxjs                      │ 7.8.2             │ ~7.8.0            │
│ typescript                │ 6.0.3             │ ~6.0.3            │
└───────────────────────────┴───────────────────┴───────────────────┘

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions