API-005 critical general
XSS prevention
User-generated content is escaped or sanitized before rendering to prevent script injection
Question to ask
"Could a user store a script that runs in another user's browser?"
Verification guide
Severity: Critical
User-generated content must be escaped/sanitized before rendering to prevent script injection.
Check automatically:
- Check framework (many auto-escape by default):
# React/Vue/Angular (auto-escape by default)
grep -E "\"react\"|\"vue\"|\"@angular/core\"|\"svelte\"|\"solid-js\"" package.json 2>/dev/null
# Server-side templating (check escape mode)
grep -E "\"ejs\"|\"pug\"|\"handlebars\"|\"mustache\"|\"nunjucks\"" package.json 2>/dev/null
grep -E "jinja2|django|mako" requirements*.txt 2>/dev/null
- Check for dangerous bypass patterns:
# React dangerouslySetInnerHTML
grep -rE "dangerouslySetInnerHTML" src/ app/ components/ --include="*.tsx" --include="*.jsx" 2>/dev/null
# Vue v-html directive
grep -rE "v-html" src/ app/ components/ --include="*.vue" 2>/dev/null
# Angular innerHTML binding
grep -rE "\[innerHTML\]" src/ app/ --include="*.html" --include="*.ts" 2>/dev/null
# Direct innerHTML assignment
grep -rE "\.innerHTML\s*=" src/ app/ --include="*.ts" --include="*.js" 2>/dev/null
# jQuery html()
grep -rE "\.html\(" src/ app/ --include="*.ts" --include="*.js" 2>/dev/null
- Check for sanitization libraries:
# Sanitization libraries (good sign if dangerous patterns exist)
grep -E "\"dompurify\"|\"sanitize-html\"|\"xss\"|\"isomorphic-dompurify\"|\"js-xss\"" package.json 2>/dev/null
# Python sanitization
grep -E "bleach|html\.escape|markupsafe" requirements*.txt 2>/dev/null
# Sanitization usage
grep -rE "DOMPurify\.sanitize|sanitizeHtml|xss\(|escape\(" src/ app/ --include="*.ts" --include="*.js" 2>/dev/null
- Check for user content rendering:
# User content fields being rendered (manual review needed)
grep -rE "user\.bio|user\.description|comment\.content|post\.body|message\.text" src/ app/ components/ 2>/dev/null | head -10
# Markdown rendering (often allows HTML)
grep -E "\"marked\"|\"markdown-it\"|\"remark\"|\"showdown\"" package.json 2>/dev/null
grep -rE "marked\(|markdownIt|renderMarkdown" src/ app/ 2>/dev/null
- Check Content-Security-Policy (defense in depth):
# CSP headers in code
grep -rE "Content-Security-Policy|contentSecurityPolicy|helmet" src/ app/ lib/ 2>/dev/null
# Next.js CSP config
grep -rE "contentSecurityPolicy" next.config.* 2>/dev/null
Ask user:
- "What frontend framework do you use?"
- "Is there any user-generated content that's rendered as HTML?"
- "Do you use a markdown renderer? Does it allow HTML?"
Cross-reference with:
- Section 32 (Content Security Policy - defense in depth)
- API-003 (input validation)
- CSS-001 (HttpOnly cookies - XSS can't steal them)
Pass criteria:
- Framework with auto-escaping (React, Vue, Angular, Svelte)
- Dangerous bypasses only used with sanitized content (DOMPurify, sanitize-html)
- OR no user-generated content rendered as HTML
Fail criteria:
dangerouslySetInnerHTMLwith unsanitized user contentv-htmlwith unsanitized user content- Direct
.innerHTMLassignment from user input - Markdown rendered without HTML sanitization
Evidence to capture:
- Frontend framework (and whether it auto-escapes)
- Dangerous patterns found and whether sanitized
- Sanitization library used (if any)
- CSP headers present (defense in depth)