Block inline scripts where possible
script-src avoids unsafe-inline or uses nonces/hashes, GTM strategy documented if used
Question to ask
"unsafe-inline — do you know what that actually allows?"
Verification guide
Severity: Recommended
Inline scripts (<script>alert('xss')</script>) are the primary XSS attack vector. Blocking them via CSP forces all JavaScript to come from trusted files, making XSS much harder to exploit.
Check automatically:
- Check script-src directive for unsafe-inline:
# Look for unsafe-inline in CSP (bad)
grep -rE "script-src[^;]*unsafe-inline" src/ app/ lib/ nginx/ *.conf 2>/dev/null
# Look for strict script-src (good)
grep -rE "script-src[^;]*'self'" src/ app/ lib/ 2>/dev/null
- Check for nonce-based CSP (allows specific inline scripts):
# Nonce generation
grep -rE "nonce|cspNonce|generateNonce" src/ app/ lib/ --include="*.ts" --include="*.js" 2>/dev/null
# Nonce in script tags
grep -rE "<script[^>]*nonce=" src/ app/ components/ pages/ --include="*.html" --include="*.tsx" --include="*.jsx" 2>/dev/null
# Next.js nonce support
grep -rE "nonce" next.config.* 2>/dev/null
- Check for hash-based CSP (allows specific inline script content):
# SHA hashes in CSP
grep -rE "'sha256-|'sha384-|'sha512-" src/ app/ lib/ nginx/ 2>/dev/null
- Check for inline scripts in HTML:
# Inline script tags (potential issues if CSP blocks inline)
grep -rE "<script>[^<]+" src/ app/ pages/ components/ --include="*.html" --include="*.tsx" --include="*.jsx" 2>/dev/null
# Inline event handlers (also blocked by strict CSP)
grep -rE "onclick=|onload=|onerror=|onmouseover=" src/ app/ components/ --include="*.html" --include="*.tsx" --include="*.jsx" 2>/dev/null
- Check for strict-dynamic (modern approach):
# strict-dynamic allows nonce-loaded scripts to load other scripts
grep -rE "'strict-dynamic'" src/ app/ lib/ nginx/ 2>/dev/null
- Check style-src for unsafe-inline:
# style-src with unsafe-inline (common for CSS-in-JS)
grep -rE "style-src[^;]*unsafe-inline" src/ app/ lib/ nginx/ 2>/dev/null
- Check for Google Tag Manager:
# GTM script tags
grep -rE "googletagmanager|GTM-" src/ app/ pages/ components/ 2>/dev/null
# GTM in CSP whitelist
grep -rE "googletagmanager\.com|tagmanager\.google\.com" src/ app/ lib/ nginx/ 2>/dev/null
Ask user:
- "Do you use inline scripts or event handlers in your HTML?"
- "Is your build pipeline configured to add nonces to script tags?"
- "Do you use Google Tag Manager? What scripts does it load?"
- "Do you use CSS-in-JS (styled-components, emotion, Tailwind)?"
Cross-reference with:
- CSP-001 (full CSP headers - script-src is a key directive)
- CSP-002 (report-only mode - shows what inline scripts would break)
- API-005 (XSS prevention - this is the CSP enforcement layer)
Pass criteria:
script-srcdoes NOT include'unsafe-inline', OR'unsafe-inline'is present but with nonce/hash (nonce takes precedence)- Inline scripts use nonces or are moved to external files
- No inline event handlers (
onclick,onerror, etc.) - GTM strategy documented if GTM is used
Fail criteria:
script-src 'unsafe-inline'without nonce/hash fallback- Many inline scripts with no plan to externalize or add nonces
- Inline event handlers throughout codebase
- GTM in use with no CSP strategy
Notes on style-src 'unsafe-inline':
Often necessary for CSS-in-JS libraries (styled-components, emotion, MUI). Less dangerous than script-src unsafe-inline since CSS can't execute arbitrary code. Acceptable if:
- Using CSS-in-JS that requires it
- Consider nonce-based styles for stricter security
Google Tag Manager strategy:
GTM dynamically injects scripts, which conflicts with strict CSP. Options:
- Nonce propagation: Pass nonce to GTM, use Custom Templates that support nonces
- Server-side GTM: Run GTM server-side to control what scripts load
- Whitelist approach: Add all GTM-loaded domains to script-src (less secure, more brittle)
- Hash known scripts: If GTM scripts are static, hash them
Recommend: Start with report-only to see what GTM loads, then whitelist specific domains or move to server-side GTM for tighter control.
Evidence to capture:
- Current
script-srcdirective value - Current
style-srcdirective value - Whether nonces or hashes are used
- Count of inline scripts/event handlers found
- GTM usage and current strategy
- CSS-in-JS library in use (if any)