Security checklist for Angular/TypeScript frontend. Covers XSS prevention, client-side validation, secure authentication patterns, CSRF handling, content security policy, and sensitive data in bundles. Invoked via /dev-security (unified entry point) — not directly.
Installation
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
This checklist targets Angular 21+ with zoneless change detection (no zone.js). All components
are standalone (no NgModules). Guards and interceptors use functional patterns.
Zoneless mode (provideExperimentalZonelessChangeDetection()) does not change the security model,
but be aware that async operations (timers, fetch, WebSocket) no longer automatically trigger
change detection. If you manually schedule work outside Angular's awareness, ensure sensitive
state is still cleaned up properly — Angular won't "accidentally" flush it via a zone turn.
Angular bundles are public — everything in them is visible to users.
// environment.ts — URLs are OK, secrets are NOTexportconst environment = {
apiUrl: 'https://api.example.com', // OK — just a URL// apiKey: 'sk-xxx' // NEVER — visible in bundle
};
Checklist:
No API keys, tokens, or passwords in environment.ts or any .ts file
No secrets in angular.json or build configurations
Production source maps disabled ("sourceMap": false)
No secrets in git history (git log -p -S "apikey")
2. XSS Prevention
Angular sanitizes by default. Watch for bypasses:
// Angular sanitizes automatically in templates// {{ userInput }} is safe — Angular escapes it// DANGEROUS — bypasses sanitizationthis.domSanitizer.bypassSecurityTrustHtml(userInput); // Only if you MUST render HTML// If you must render user HTML, sanitize server-side first
innerHTML vs interpolation
// SAFE — Angular escapes
<p>{{ userInput }}</p>
// RISKY — Angular sanitizes but be careful<div [innerHTML]="userInput"></div>// DANGEROUS — bypasses all protection<div [innerHTML]="sanitizer.bypassSecurityTrustHtml(userInput)"></div>
Checklist:
Never use bypassSecurityTrust* with user input
Prefer {{ interpolation }} over [innerHTML]
If [innerHTML] is needed, sanitize server-side first
No eval(), new Function(), or dynamic script injection
3. Client-Side Validation
Client-side validation is for UX, not security. The server must always re-validate.
Security note on [(ngModel)]: Two-way binding itself is not a vulnerability, but avoid
binding user input directly into [innerHTML], URL parameters, or script contexts. Always
treat model values as untrusted when passing them beyond the template.
Checklist:
Template-driven forms with appropriate validators on all inputs
Max length on text inputs (prevent payload bloat)
Number ranges validated (min/max)
Form submission disabled when invalid
Remember: this is UX only — server validates again
4. Authentication Patterns
For code examples (cookie sessions, OAuth2 PKCE, functional guards, HTTP interceptors), see auth-patterns.md.
Checklist:
Session cookies use __Host- prefix (Secure, HttpOnly, SameSite=Strict)
OAuth2 flows use PKCE with S256 challenge method
code_verifier in memory or sessionStorage only (not localStorage)
Tokens stay server-side — client receives session cookie only
Functional route guards (CanActivateFn) on all protected pages
Functional HTTP interceptor handles 401 redirect and 429 rate limiting
Clear auth state on logout (including in-memory signal state)
5. CSRF Protection
Angular's HttpClient handles CSRF automatically when cookies are configured:
// Angular reads XSRF-TOKEN cookie and sends X-XSRF-TOKEN header automatically// Server must set the XSRF-TOKEN cookie and validate the header// If you need custom configuration:provideHttpClient(
withXsrfConfiguration({
cookieName: 'XSRF-TOKEN',
headerName: 'X-XSRF-TOKEN'
})
)
Checklist:
XSRF cookie/header configured
Server validates anti-forgery tokens
SameSite=Strict on auth cookies (server-side)
6. Content Security Policy
CSP headers are set server-side but affect the frontend:
External scripts loaded only from whitelisted domains
Fonts and images from trusted CDNs only
7. Signal-Based State Security
Angular signals replace many RxJS patterns for state management. They avoid subscription leak
risks but introduce their own considerations:
@Component({
standalone: true,
template: `
<!-- SAFE — display name is not sensitive -->
<p>Welcome, {{ displayName() }}</p>
<!-- DANGEROUS — never expose tokens or secrets in templates -->
<!-- <p>Token: {{ authToken() }}</p> -->
`
})
exportclassDashboardComponent {
privatereadonly authService = inject(AuthService);
// OK — non-sensitive derived state
displayName = computed(() =>this.authService.user()?.name ?? 'Guest');
// WRONG — sensitive data in a signal that could appear in templates or DevTools// authToken = computed(() => this.authService.session()?.token);// If you need sensitive data, keep it in a private method, not a signalprivategetToken(): string | null {
returnthis.authService.session()?.token ?? null;
}
}
Checklist:
Signals exposed in templates contain no sensitive data (tokens, PII, secrets)
computed() values that derive from auth state only expose safe projections
Sensitive data accessed via private methods, not public signals
Angular DevTools can inspect signals — assume they are visible
Angular Service Worker caches assets for offline use. Misconfigured caching can leak sensitive data or serve stale auth state. For ngsw-config.json data group examples, see auth-patterns.md.
Checklist:
Auth endpoints use freshness strategy with short/zero maxAge
No sensitive data (PII, tokens) in cached data groups
ngsw-config.json reviewed — no accidental wildcard caching of API responses
Service Worker update strategy tested — stale versions don't serve outdated auth
SW cache cleared on logout (call SwUpdate or registration.unregister() if needed)