0 day streak
javascript / expert
Snippet

Sanitized Dynamic Class Binding Against Style Injection

When a class name is derived from user-controlled or backend-driven data (e.g. a CMS field or query parameter forwarded into a `theme` input), binding it directly via `[class]="userValue"` lets an attacker inject arbitrary class names that may collide with unrelated selectors elsewhere in the app, or — combined with a loose global stylesheet — be leveraged for CSS-based data exfiltration (attribute selectors that trigger background-image requests). Angular does not sanitize class bindings the way it sanitizes `innerHTML` or `href`, because a class name isn't inherently active content, so the allowlist check here is the actual security boundary, not a redundant one.

snippet.js
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const ALLOWED_THEME_CLASSES = new Set(['theme-default', 'theme-compact', 'theme-highcontrast']);
@Component({
selector: 'app-themed-card',
standalone: true,
template: `<div [class]="resolvedClass()"><ng-content /></div>`,
})
export class ThemedCardComponent {
readonly themeInput = input<string>('theme-default', { alias: 'theme' });
readonly resolvedClass = computed(() => {
const requested = this.themeInput().trim();
if (!ALLOWED_THEME_CLASSES.has(requested)) {
console.warn(`Rejected unrecognized theme class "${requested}"; falling back to default.`);
return 'theme-default';
}
return requested;
});
}
angular
Breakdown
1
const ALLOWED_THEME_CLASSES = new Set(['theme-default', 'theme-compact', 'theme-highcontrast']);
An explicit allowlist defined at module scope — the only classes this component will ever render, regardless of what's passed in.
2
if (!ALLOWED_THEME_CLASSES.has(requested)) { ... return 'theme-default'; }
Fails closed to a safe default rather than passing unrecognized input through, so an unexpected value degrades gracefully instead of becoming an injection vector.
3
console.warn(`Rejected unrecognized theme class "${requested}"; falling back to default.`)
Logs the rejection so a legitimate new theme that was never added to the allowlist is discoverable in production logs rather than silently misrendering.