mediumSeverity: mediumRisk ratingHow usable this gap is to an attacker. Severity sets the posture penalty: critical βˆ’40, high βˆ’20, medium βˆ’10, low βˆ’4, info βˆ’1. Higher severity means fix it sooner.Netlify headers (netlify.toml / _headers)Where it livesImplementationThis fix is applied in: Netlify headers (netlify.toml / _headers). That's the surface you'll edit, DNS, response headers, or a static file.~30 minTime to fixEffortRoughly 30 minutes of hands-on work, propagation aside. Most of these are copy-paste.+10 posture+10 postureScore impactPoints this fix recovers in the ApeCyber posture score (0–100). Posture = 100 minus the severity penalty of every open finding; clearing this finding adds these points back.

CSP: remove 'unsafe-inline' from scripts and kill XSS blast radius

A strong Content-Security-Policy turns a script injection into a non-event

The threat

With 'unsafe-inline''unsafe-inline'CSP keywordA Content-Security-Policy value that lets the browser run inline scripts or styles written directly in the page. In script-src it's the danger switch, it re-enables exactly the inline execution that injected XSS needs. Replace it with per-script hashes or a nonce. in your script-srcscript-srcCSP directiveThe CSP directive that controls which scripts a page may load and execute. It is the single most important directive for stopping cross-site scripting (XSS)., any successful script injection, reflected, stored, or DOM-based, executes with full page privileges: read cookies, keylog, make authenticated requests, exfiltrate data. A strong CSPCSPHTTP headerContent-Security-Policy: a response header that whitelists exactly which scripts, styles, images and connections a page may use, so an injected payload simply has no permission to run. is the seatbelt: even if an injection lands, the browser refuses to run unauthorized script. 'unsafe-inline''unsafe-inline'CSP keywordA Content-Security-Policy value that lets the browser run inline scripts or styles written directly in the page. In script-src it's the danger switch, it re-enables exactly the inline execution that injected XSS needs. Replace it with per-script hashes or a nonce. unbuckles it.

The exact fix

This is the one fix that needs care, removing 'unsafe-inline''unsafe-inline'CSP keywordA Content-Security-Policy value that lets the browser run inline scripts or styles written directly in the page. In script-src it's the danger switch, it re-enables exactly the inline execution that injected XSS needs. Replace it with per-script hashes or a nonce. can break inline scripts, so you authorize your scripts explicitly instead.

Option A, hash every inline script (what we did). At build time, compute the sha256sha256 hashCSP techniqueA fingerprint of an inline script's exact contents. Listing the hash in script-src authorizes that one specific script without needing 'unsafe-inline'. of each inline <script> block and add the hashes to script-srcscript-srcCSP directiveThe CSP directive that controls which scripts a page may load and execute. It is the single most important directive for stopping cross-site scripting (XSS).. The browser then runs only those exact scripts and nothing injected. For a real site this is automated in the build so every page’s inline scripts are covered.

Option B, externalize. Move inline scripts into .js files in your bundle; then script-src 'self' (plus any needed external host) is all you need, no hashes to maintain.

Here is the hardened policy we shipped (Netlify), inline-script hashes included:

# netlify.toml
[[headers]]
  for = "/*"
  [headers.values]
    Content-Security-Policy = "default-src 'self'; script-src 'self' 'sha256-…' 'sha256-…' https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://api.yourdomain.com wss://api.yourdomain.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"
    X-XSS-Protection = "0"

Or the Netlify _headers form:

/*
  Content-Security-Policy: default-src 'self'; script-src 'self' 'sha256-…' https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://api.yourdomain.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'

What this policy already gets right: frame-ancestors 'none' (clickjacking-proof), base-uri 'self', form-action 'self'. The only change needed was the script context.

style-src 'unsafe-inline' is fine to keep. Inline style attributes (style="…") can’t be CSP-hashed and are low-risk. The XSSXSSAttackCross-Site Scripting: an attack that injects attacker-controlled JavaScript into your page to steal sessions, keylog, or act as the user. A strong CSP is the primary defense. danger lives in script-src, that’s the one to lock. (We even corrected our own scanner to stop penalizing style-src for this reason.)

Verify it

curl -sI https://yourdomain.com | grep -i content-security

Confirm script-srcscript-srcCSP directiveThe CSP directive that controls which scripts a page may load and execute. It is the single most important directive for stopping cross-site scripting (XSS). contains no 'unsafe-inline''unsafe-inline'CSP keywordA Content-Security-Policy value that lets the browser run inline scripts or styles written directly in the page. In script-src it's the danger switch, it re-enables exactly the inline execution that injected XSS needs. Replace it with per-script hashes or a nonce. / 'unsafe-eval''unsafe-eval'CSP keywordA CSP value permitting eval() and other string-to-code execution. It lets an attacker turn plain data into running code, keep it out of script-src.. Then load the site with DevTools open, zero CSP violation errors means every legitimate script is authorized.

Proof

dev3lop.com hashed inline scripts across 1,341 pages at build time, removing 'unsafe-inline''unsafe-inline'CSP keywordA Content-Security-Policy value that lets the browser run inline scripts or styles written directly in the page. In script-src it's the danger switch, it re-enables exactly the inline execution that injected XSS needs. Replace it with per-script hashes or a nonce. from scripts entirely while keeping the site fully functional, a clean βˆ’10 on the road to A / 100.