getreadystack commited on
Commit
83790d9
·
verified ·
1 Parent(s): 939b8b4

Upload index.html with huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +87 -18
index.html CHANGED
@@ -1,19 +1,88 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html><html lang="en"><meta charset="utf-8">
2
+ <meta name="viewport" content="width=device-width,initial-scale=1">
3
+ <title>Security Headers Lint - CSP and Dead Headers</title>
4
+ <meta name="description" content="Reads security headers and CSP line by line in your config file and names the lines that silently do nothing: retired headers, keywords missing their quotes, directives the browser throws away.">
5
+ <script defer data-auto-init src="https://cdn.jsdelivr.net/npm/@polar-sh/checkout@latest/dist/embed.global.js"></script>
6
+ <style>
7
+ :root{color-scheme:light dark}
8
+ body{margin:0;font:16px/1.6 system-ui,-apple-system,"Segoe UI",sans-serif;
9
+ background:#fbfbfa;color:#16161d}
10
+ @media(prefers-color-scheme:dark){body{background:#14141a;color:#e8e8ea}}
11
+ .wrap{max-width:880px;margin:0 auto;padding:28px 20px 64px}
12
+ h1{font-size:1.7rem;line-height:1.25;margin:.2em 0 .3em}
13
+ p.sub{margin:0 0 1.4em;opacity:.8}
14
+ textarea{width:100%;min-height:230px;font:13px/1.5 ui-monospace,Menlo,Consolas,monospace;
15
+ padding:12px;border:1px solid #c9c9d1;border-radius:10px;background:transparent;color:inherit;
16
+ box-sizing:border-box}
17
+ button{font:600 15px/1 inherit;padding:12px 22px;border:0;border-radius:10px;
18
+ background:#1d4ed8;color:#fff;cursor:pointer;margin:12px 0}
19
+ button:hover{background:#1a43bd}
20
+ table{border-collapse:collapse;width:100%;margin-top:14px;font-size:14px}
21
+ th,td{border-bottom:1px solid #d8d8e0;padding:8px 6px;text-align:left;vertical-align:top}
22
+ th{font-weight:600;opacity:.75}
23
+ td.n{width:64px;font:13px ui-monospace,monospace;opacity:.7}
24
+ .ok{padding:14px;border-radius:10px;background:#e8f5ec;color:#14532d;margin-top:14px}
25
+ @media(prefers-color-scheme:dark){.ok{background:#15321f;color:#bbf7d0}}
26
+ .next{margin-top:30px;padding:18px;border:1px solid #d8d8e0;border-radius:12px}
27
+ .next h2{font-size:1.05rem;margin:0 0 .5em}
28
+ .next ul{margin:.3em 0 1em 1.1em;padding:0}
29
+ </style>
30
+ <div class="wrap">
31
+ <h1>Security Headers Lint - CSP and Dead Headers</h1>
32
+ <p class="sub">Reads security headers and CSP line by line in your config file and names the lines that silently do nothing: retired headers, keywords missing their quotes, directives the browser throws away.</p>
33
+ <textarea id="src" placeholder="Paste your _headers, netlify.toml, nginx.conf or the <meta http-equiv> line here"></textarea>
34
+ <button id="go">Check</button>
35
+ <div id="out"></div>
36
+ <div class="next"><h2>In the full version</h2><ul><li>Scan every config file in the workspace</li><li>Export the report as CSV, JSON or HTML</li><li>CI output that fails the build on errors</li><li>Add your own house policy rules</li><li><a href="https://getreadystack.com" data-polar-checkout data-polar-checkout-theme="light">Get the full version</a></li></ul></div>
37
+ </div>
38
+ <script>
39
+ var RULES=[{"pattern": "X-XSS-Protection", "sev": "error", "message": "Dead header. Chrome removed the XSS Auditor in 2019, Edge followed, and Firefox never shipped it, so this line changes nothing in any browser you can install today. The value 1; mode=block was itself exploitable for cross-site info leaks, which is why it was removed. Delete it and let Content-Security-Policy do the work.", "flags": "i", "fix": "# delete this line - Content-Security-Policy replaces it"}, {"pattern": "Public-Key-Pins", "sev": "error", "message": "HTTP Public Key Pinning is gone. Chrome removed it in 2019 and Firefox followed, so no current browser reads this header and it protects nothing. It was removed because a stale or lost pin bricks your own domain for the whole pin lifetime. Certificate Transparency monitoring plus a CAA record is the live replacement.", "flags": "i", "fix": "# delete - use CAA records and CT monitoring"}, {"pattern": "Expect-CT", "sev": "error", "message": "Expect-CT is obsolete and Chrome stopped enforcing it in 2022. Certificate Transparency is now required unconditionally for every publicly trusted certificate, so the header has nothing left to opt into and is ignored wherever it is sent.", "flags": "i", "fix": "# delete - CT is enforced unconditionally now"}, {"pattern": "X-(?:Content-Security-Policy|WebKit-CSP)", "sev": "error", "message": "Vendor-prefixed CSP header from the IE10 and old-WebKit era. Nothing released this decade reads it, so every directive on this line is inert - the page has no policy at all while the config looks like it does. The header name is Content-Security-Policy, with no prefix.", "flags": "i", "fix": "Content-Security-Policy: ..."}, {"pattern": "X-Frame-Options\\s*[:=]\\s*[\"\\']?\\s*ALLOW-FROM", "sev": "error", "message": "ALLOW-FROM was only ever implemented by IE and old Firefox. Chrome and Safari have never supported it and treat the whole header as invalid, which can leave the page framable by anyone at all - the opposite of what this line intends. CSP frame-ancestors is the specified replacement and it takes a source list.", "flags": "i", "fix": "Content-Security-Policy: frame-ancestors 'self' https://partner.example;"}, {"pattern": "Feature-Policy\\s*[:=]", "sev": "error", "message": "Feature-Policy was renamed to Permissions-Policy and Chrome dropped the old name. The syntax changed with it: Feature-Policy wrote geolocation 'none', Permissions-Policy writes geolocation=(). Copying the old header across keeps none of its restrictions, so every feature stays at its permissive default.", "flags": "i", "fix": "Permissions-Policy: geolocation=(), camera=(), microphone=()"}, {"pattern": "(Permissions-Policy[^\\n]*?\\b(?:geolocation|camera|microphone|payment|fullscreen|autoplay|usb|midi|display-capture|screen-wake-lock|accelerometer|gyroscope))\\s+[\"\\'](?:none|self)[\"\\']", "sev": "error", "message": "Permissions-Policy is a structured field: you write geolocation=() for nobody and geolocation=(self) for your own origin. The quoted Feature-Policy spelling is a parse error, and a browser that cannot parse a structured header discards the entire header - so every permission listed on this line, not just this one, goes back to its default.", "flags": "i", "fix": "Permissions-Policy: geolocation=(), camera=(self)"}, {"pattern": "((?:[a-z]+-src|Content-Security-Policy|frame-ancestors|form-action|base-uri)[^\\n]*?[^\\'\"a-zA-Z0-9_-])(unsafe-inline|unsafe-eval|unsafe-hashes|strict-dynamic|self|none)([^\\'\"a-zA-Z0-9_-]|$)", "sev": "error", "message": "CSP keywords are only keywords inside single quotes. Written bare, self, none, unsafe-inline and strict-dynamic are parsed as host names, so the browser goes looking for a server literally called self and the restriction you meant to set is simply absent. Nothing is logged, the policy still applies, and it applies wrongly.", "fix": "script-src 'self'", "replace": "$1'$2'$3"}, {"pattern": "'nonce-[A-Za-z0-9+/=_-]{6,}'", "sev": "error", "message": "A hard-coded nonce is exactly as weak as 'unsafe-inline'. The value must be unpredictable and regenerated for every response; one that sits in a config file or a checked-in page is known to anyone who reads it, so an injected script simply carries the same nonce. Generate it per request, or move to hashes.", "fix": "'nonce-{{ generated per request }}'"}, {"pattern": "<meta[^>]*http-equiv=[\"\\']?Content-Security-Policy[^>]*\\b(?:frame-ancestors|sandbox)\\b", "sev": "error", "message": "frame-ancestors and sandbox are discarded when the policy arrives in a meta tag - the specification requires browsers to drop them, because both must apply before the document starts parsing. The rest of the policy still works, so the page looks protected while it stays framable. Send these two as a real HTTP response header.", "flags": "i", "fix": "Content-Security-Policy: frame-ancestors 'none' (HTTP header, not meta)"}, {"pattern": "<meta[^>]*http-equiv=[\"\\']?X-Frame-Options", "sev": "error", "message": "X-Frame-Options is only honoured as an HTTP response header; in a meta tag browsers ignore it outright, so this page is framable. Use a real header, and prefer CSP frame-ancestors, which is the specified successor and the one that covers nested frames.", "flags": "i", "fix": "Content-Security-Policy: frame-ancestors 'none' (HTTP header)"}, {"pattern": "script-src[^;\\n]*[\\s\"\\']data:", "sev": "error", "message": "data: inside script-src is a documented full bypass, not a convenience. An injected script tag pointing at a data: URL inherits the permission and runs, so the policy stops nothing it was written to stop. If one library needs it, isolate that page rather than opening the whole policy.", "flags": "i"}, {"pattern": "Strict-Transport-Security[^\\n]*max-age\\s*=\\s*0\\b", "sev": "error", "message": "max-age=0 switches HSTS off and instructs browsers to forget this domain immediately. If it was left in as a placeholder from testing, every returning visitor silently loses HTTPS enforcement and becomes downgradeable again.", "flags": "i", "fix": "Strict-Transport-Security: max-age=31536000; includeSubDomains"}, {"pattern": "Strict-Transport-Security(?![^\\n]*includeSubDomains)[^\\n]*preload", "sev": "error", "message": "The preload list requires all three of max-age at least 31536000, includeSubDomains, and preload. Without includeSubDomains the submission is refused, or an existing entry is removed at the next check - and you keep shipping the token believing you are preloaded.", "flags": "i", "fix": "Strict-Transport-Security: max-age=31536000; includeSubDomains; preload"}, {"pattern": "Strict-Transport-Security(?=[^\\n]*preload)[^\\n]*max-age\\s*=\\s*\\d{1,7}(?!\\d)", "sev": "error", "message": "A preload submission needs max-age of at least 31536000 seconds, one year. This value is below that, so hstspreload.org refuses the domain or drops it at the next check while the header keeps claiming preload.", "flags": "i", "fix": "max-age=31536000"}, {"pattern": "(X-Content-Type-Options\\s*[:=]\\s*[\"\\']?)(?!nosniff)([A-Za-z][\\w-]*)", "sev": "error", "message": "nosniff is the only value this header defines; anything else leaves MIME sniffing switched on. That matters most where users upload files: a document served with the wrong Content-Type can still be sniffed into script and executed on your origin.", "flags": "i", "fix": "X-Content-Type-Options: nosniff", "replace": "$1nosniff"}, {"pattern": "'strict-dynamic'", "sev": "warn", "message": "With 'strict-dynamic' a supporting browser ignores every host allowlist in this same directive, and 'self' and 'unsafe-inline' with them - trust comes only from the nonce or hash. So any domain list kept here is dead weight for modern browsers and survives only as a fallback for CSP1-era ones. Confirm a nonce or hash is actually present, or nothing loads."}, {"pattern": "(?:'(?:nonce-|sha(?:256|384|512)-)[^;\\n]*'unsafe-inline'|'unsafe-inline'[^;\\n]*'(?:nonce-|sha(?:256|384|512)-))", "sev": "warn", "message": "'unsafe-inline' is ignored by every CSP2 and later browser when a nonce or hash appears in the same directive; it only takes effect where neither is understood. Keeping it as a deliberate fallback is fine, but if it was added to make something work today then the real cause is elsewhere and this policy is weaker than it reads."}, {"pattern": "script-src[^;\\n]*[\\s\"\\'](?:\\*|https:)(?=[\\s;\"\\']|$)", "sev": "warn", "message": "script-src * or https: permits script from any host on the web, which is not a meaningful restriction - one uploadable or open-redirect host anywhere is enough to defeat it. Host allowlists were found bypassable on the large majority of real policies; nonce or hash based policies with 'strict-dynamic' are the recommended shape.", "flags": "i"}, {"pattern": "block-all-mixed-content", "sev": "warn", "message": "Obsolete directive, removed from the specification. Browsers now upgrade or block mixed content unconditionally, so this line does nothing. It is harmless in itself, but it hides the fact that nothing here is actually controlling mixed content.", "flags": "i"}, {"pattern": "prefetch-src", "sev": "warn", "message": "prefetch-src was removed from CSP and dropped by Chrome; no shipping browser enforces it. Prefetches fall under default-src instead, so if you meant to restrict them, set default-src.", "flags": "i"}, {"pattern": "plugin-types", "sev": "warn", "message": "plugin-types was removed from CSP Level 3, and from Chrome along with plugin support itself. It restricts nothing today. object-src 'none' is the directive that still matters for embedded content.", "flags": "i"}, {"pattern": "(?:Content-Security-Policy|-src)[^\\n]*[;\\s]referrer\\s+(?:no-referrer|origin|unsafe-url|none|default|same-origin)", "sev": "warn", "message": "referrer was an experimental CSP1-era directive and was removed; it is not part of CSP and browsers ignore it inside this header. Referrer control has its own header, Referrer-Policy, and the values are not spelled the same.", "flags": "i", "fix": "Referrer-Policy: strict-origin-when-cross-origin"}, {"pattern": "report-uri", "sev": "warn", "message": "report-uri is deprecated, and a browser that supports report-to ignores report-uri entirely - so violation reports quietly stop arriving while the dashboard still looks alive. During a migration keep both: report-uri for older browsers, report-to plus a Reporting-Endpoints header for current ones.", "flags": "i", "fix": "report-to csp-endpoint"}, {"pattern": "[;\\s]report-to\\s+[A-Za-z]", "sev": "info", "message": "report-to names a reporting group, not a URL. That group has to be defined by a Reporting-Endpoints header on the same response; without it nothing is ever sent, and the policy looks monitored when it is not. Check that Reporting-Endpoints is set alongside this header.", "flags": "i", "fix": "Reporting-Endpoints: csp-endpoint=\"https://example.com/csp-reports\""}, {"pattern": "Content-Security-Policy-Report-Only", "sev": "info", "message": "Report-Only enforces nothing - it records violations and lets every one of them through. That is the right way to trial a policy, but it is not protection, and a site that only ever ships this header has no CSP in force.", "flags": "i"}, {"pattern": "^(?:(?![^\\n]*base-uri)|(?![^\\n]*form-action))[^\\n]*default-src", "sev": "info", "message": "default-src does not cover base-uri, form-action, frame-ancestors, sandbox or the reporting directives - those fall back to nothing, not to default-src. This line sets default-src but not both base-uri and form-action, so unless they appear elsewhere in the policy, an injected base tag or a rewritten form action still exfiltrates under an otherwise strict default-src.", "flags": "i", "fix": "base-uri 'self'; form-action 'self';"}, {"pattern": "'unsafe-eval'", "sev": "info", "message": "'unsafe-eval' re-enables eval, new Function and string timers, which is a large part of what CSP exists to stop. It is usually dragged in by a single dependency rather than your own code, so it is worth finding which one - most current build setups no longer need it."}];
40
+ function scan(t){var L=String(t).split(/\r?\n/),h=[],i,r,re;
41
+ for(i=0;i<L.length;i++){for(var k=0;k<RULES.length;k++){r=RULES[k];
42
+ try{re=new RegExp(r.pattern,r.flags||'')}catch(e){continue}
43
+ if(re.test(L[i]))h.push({line:i+1,msg:r.message})}}
44
+ return h}
45
+ document.getElementById('go').onclick=function(){
46
+ var t=document.getElementById('src').value,o=document.getElementById('out');
47
+ if(!t.trim()){o.innerHTML='';return}
48
+ var h=scan(t);
49
+ if(!h.length){o.innerHTML='<div class="ok">No dead or silently-ignored security header lines found in this file.</div>';return}
50
+ var s='<table><tr><th>Line</th><th>Finding</th></tr>';
51
+ for(var i=0;i<h.length;i++){s+='<tr><td class="n">'+h[i].line+'</td><td>'+
52
+ String(h[i].msg).replace(/&/g,'&amp;').replace(/</g,'&lt;')+'</td></tr>'}
53
+ o.innerHTML=s+'</table>'};
54
+ </script>
55
  </html>
56
+
57
+ <section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get one email when this rule changes</b><div style="opacity:.78;margin:.35rem 0 .8rem">We watch the regulation and vendor sources behind Security Headers Lint - CSP and Dead Headers every day. When a rule changes, you get a single email with what changed and the updated check. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Notify me</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">You're on the list. One email when it changes.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"security-headers-csp-lint",lang:"en",source:"rule_alert"})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script></section>
58
+
59
+
60
+ <section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://marketplace.visualstudio.com/items?itemName=ReadyStack.security-headers-csp-lint" rel="noopener" data-yj-install-door="mp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'security-headers-csp-lint',src:'mp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">VS Code Marketplace</a><a href="https://open-vsx.org/extension/readystack/security-headers-csp-lint" rel="noopener" data-yj-install-door="ovsx" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'security-headers-csp-lint',src:'ovsx',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Open VSX (Cursor, VSCodium)</a><a href="https://www.npmjs.com/package/@readystack/security-headers-csp-lint" rel="noopener" data-yj-install-door="npm" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'security-headers-csp-lint',src:'npm',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">npm</a><a href="https://www.npmjs.com/package/@readystack/security-headers-csp-lint" rel="noopener" data-yj-install-door="mcp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'security-headers-csp-lint',src:'mcp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">MCP server (npx)</a><a href="https://hub.docker.com/r/getreadystack/security-headers-csp-lint" rel="noopener" data-yj-install-door="docker" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'security-headers-csp-lint',src:'docker',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Docker Hub</a><pre style="margin:.5rem 0 0;padding:.6rem .8rem;background:#f3f5f8;border-radius:8px;font-size:.9rem;overflow:auto">ext install ReadyStack.security-headers-csp-lint
61
+ npx @readystack/security-headers-csp-lint &lt;file&gt;
62
+ npx @readystack/security-headers-csp-lint --mcp</pre><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://buy.polar.sh/polar_cl_l6iN1uWt0FwWu7tBsczD0jWpP2vxFM54Wdwqb3KPi1G" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section>
63
+ <section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the complete version <span class="yj-price">$29</span></h2><p style="margin:.25rem 0 1rem;color:#41505f">This page is the working piece. The full pack has everything below.</p><p style="margin:0 0 1rem;color:#41505f">Reads security headers and CSP line by line in your config file and names the lines that silently do nothing: retired headers, keywords missing their quotes, directives the browser throws aw</p><p style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">Application security consultants doing secure code review bill roughly $120 to $275 an hour, and a security-header and CSP review is a one-to-two hour job per site.</p><a href="https://buy.polar.sh/polar_cl_DxnwX3BvMThBo4369WHczw3OVdSlhjnkHsoJb0ZEGWd" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section>
64
+ <!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script><div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div><section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><p style="font:400 13px/1.5 system-ui,sans-serif;color:#6b7680;margin:0 0 14px"><time datetime="2026-09-08">Updated 2026-09-08</time> · ReadyStack</p><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","dateModified":"2026-09-08","author":{"@type":"Organization","name":"ReadyStack"}}</script><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Worked example</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 16px;color:#5b6570">Real numbers from this tool, line by line.</p><div class="yj-article"><p><img src="https://getreadystack.com/img/promo/sku13805_result_card.jpg" alt="Your security headers can return 200 and protect nothing" loading="lazy" style="max-width:100%;border-radius:6px"></p>
65
+ <p>Four header lines, four errors - for web and platform engineers who keep security headers in the repo and have to show a working policy at a PCI DSS assessment.</p>
66
+ <pre><code>_headers
67
+ 3 ERROR Dead header. Chrome removed the XSS Auditor in 2019, Edge followed...
68
+ 4 ERROR ALLOW-FROM was only ever implemented by IE and old Firefox...
69
+ 5 ERROR HTTP Public Key Pinning is gone. Chrome removed it in 2019...
70
+ 6 ERROR CSP keywords are only keywords inside single quotes. Written bare...
71
+ 6 WARN script-src * or https: permits script from any host on the web...
72
+ 6 INFO default-src does not cover base-uri, form-action, frame-ancestors...</code></pre>
73
+ <p>That is a real run over a <code>_headers</code> file copied from a 2019 blog post. Six findings, four of them errors - and none of them would fail a deploy, a curl, or a browser console. A wrong header does not error: it is served, parsed, and the wrong part is quietly dropped.</p>
74
+ <p>Line 6 is the one that catches almost everyone.</p>
75
+ <pre><code>Content-Security-Policy: default-src self</code></pre>
76
+ <p>CSP keywords are only keywords inside single quotes. Written bare, <code>self</code> is parsed as a <strong>host name</strong> - the browser goes looking for a server literally called <code>self</code>. No console error, no failed request. The same trap applies to <code>none</code>, <code>unsafe-inline</code> and <code>strict-dynamic</code>.</p>
77
+ <p>Lines 3, 4 and 5 are a different layer: headers that are simply dead. <code>X-XSS-Protection</code> has done nothing since Chrome removed the XSS Auditor in 2019. <code>Public-Key-Pins</code> was removed in 2019 too, and <code>Expect-CT</code> stopped being enforced in 2022 because Certificate Transparency is now unconditional. <code>X-Frame-Options: ALLOW-FROM</code> was never implemented in Chrome or Safari, so an invalid X-Frame-Options can leave the page framable by anyone.</p>
78
+ <p>Then there is the layer the browser throws away on purpose. <code>frame-ancestors</code>, <code>sandbox</code> and <code>report-uri</code> are <strong>discarded</strong> when a policy arrives in a <code>&lt;meta http-equiv&gt;</code> tag, because those three must apply before the document starts parsing. The rest of the policy still works, so the page looks protected and stays framable.</p>
79
+ <p>Two more cost real money. <code>Feature-Policy</code> was renamed to <code>Permissions-Policy</code>, and <code>geolocation &#x27;none&#x27;</code> became <code>geolocation=()</code>. Permissions-Policy is a structured field, so the old quoted spelling is a parse error - and a browser that cannot parse a structured header discards <strong>the entire header</strong>, sending every permission on the line back to its default. And <code>Strict-Transport-Security: max-age=600; preload</code> is refused by the preload list, which requires at least 31536000 seconds plus <code>includeSubDomains</code>: you ship the token for years and were never on the list.</p>
80
+ <p>Why does this keep happening? CSP Evaluator and Observatory grade a URL that is already deployed, so every fix costs a deploy cycle and neither sees the config file where the mistake lives. And the fastest source of a header is now an assistant: they still emit <code>X-XSS-Protection</code>, <code>report-uri</code> and bare <code>self</code>, because those were correct when the training data was collected.</p>
81
+ <p>So this is a linter, not a scanner. It reads <code>_headers</code>, <code>netlify.toml</code>, <code>vercel.json</code>, nginx or Apache config: 28 rules, one per silent failure, each naming the behaviour you got, not the one you wrote.</p>
82
+ <p>If you take card payments this became an audit finding. PCI DSS v4.0.1 requirements 6.4.3 and 11.6.1 have been mandatory since 31 March 2025, and 11.6.1 covers detecting unauthorised changes to HTTP headers on the payment page. The assessor reads the headers you shipped, not the ones you meant.</p>
83
+ <p>Run it on the config you have open now - free, in VS Code or the browser, all 28 rules, every finding shown, nothing withheld. A licence is $29 once with a 7-day full refund, and it buys a different job, not more of the same: the whole workspace in one command, an exported CSV, JSON or HTML evidence file, and a CI gate. For comparison, an AppSec consultant billing $120-275 an hour spends one to two hours per site.</p></div><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:30px 0 10px;color:#1b1f24">15 seconds — what it actually does</h2><video controls muted playsinline preload="none" poster="/img/promo/sku13805_result_card.jpg" style="width:100%;max-width:360px;border-radius:10px;display:block;margin:0 auto"><source src="/media/sku13805_short.mp4" type="video/mp4"></video><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does Security Headers Lint actually do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads a config file line by line and names the security header lines that do nothing. It flags retired headers like X-XSS-Protection, Public-Key-Pins and Expect-CT, CSP keywords written without their single quotes, and directives such as frame-ancestors that browsers discard when the policy is delivered in a meta tag. Each finding says what the line does instead of what you meant.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is this for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Web and platform engineers, agencies and ecommerce teams who keep security headers in the repository - a _headers file, netlify.toml, vercel.json, nginx or Apache config, or a meta http-equiv tag. It suits anyone who has to show a working Content-Security-Policy at a PCI DSS assessment, and agencies who need one pass across every client site rather than opening each config by hand.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why not use CSP Evaluator or ask a chatbot?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">CSP Evaluator and Observatory grade a URL that is already live, so each correction costs you a deploy cycle, and neither can see the config file where the mistake is. Chatbots are worse here: they still produce X-XSS-Protection, report-uri and bare self, because those were current when their training data was collected. A wrong header returns HTTP 200 and no console error.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what needs a licence?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Checking the open file is free and complete: all 28 rules run, every finding is shown, and nothing is hidden, watermarked or time-limited. A licence adds a different job rather than more of the same one - scanning every config in a workspace, exporting a report for an evidence pack, failing CI, adding your own house rules, re-checking on save, and applying the safe fixes.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What would this cost me otherwise?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Application security consultants doing secure code review bill roughly $120 to $275 an hour, and a security-header and CSP review runs one to two hours per site. The licence is $29 once with a 7-day full refund, and the free tier already finishes the job for a single config file.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does Security Headers Lint actually do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads a config file line by line and names the security header lines that do nothing. It flags retired headers like X-XSS-Protection, Public-Key-Pins and Expect-CT, CSP keywords written without their single quotes, and directives such as frame-ancestors that browsers discard when the policy is delivered in a meta tag. Each finding says what the line does instead of what you meant."}}, {"@type": "Question", "name": "Who is this for?", "acceptedAnswer": {"@type": "Answer", "text": "Web and platform engineers, agencies and ecommerce teams who keep security headers in the repository - a _headers file, netlify.toml, vercel.json, nginx or Apache config, or a meta http-equiv tag. It suits anyone who has to show a working Content-Security-Policy at a PCI DSS assessment, and agencies who need one pass across every client site rather than opening each config by hand."}}, {"@type": "Question", "name": "Why not use CSP Evaluator or ask a chatbot?", "acceptedAnswer": {"@type": "Answer", "text": "CSP Evaluator and Observatory grade a URL that is already live, so each correction costs you a deploy cycle, and neither can see the config file where the mistake is. Chatbots are worse here: they still produce X-XSS-Protection, report-uri and bare self, because those were current when their training data was collected. A wrong header returns HTTP 200 and no console error."}}, {"@type": "Question", "name": "What is free and what needs a licence?", "acceptedAnswer": {"@type": "Answer", "text": "Checking the open file is free and complete: all 28 rules run, every finding is shown, and nothing is hidden, watermarked or time-limited. A licence adds a different job rather than more of the same one - scanning every config in a workspace, exporting a report for an evidence pack, failing CI, adding your own house rules, re-checking on save, and applying the safe fixes."}}, {"@type": "Question", "name": "What would this cost me otherwise?", "acceptedAnswer": {"@type": "Answer", "text": "Application security consultants doing secure code review bill roughly $120 to $275 an hour, and a security-header and CSP review runs one to two hours per site. The licence is $29 once with a 7-day full refund, and the free tier already finishes the job for a single config file."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:13805,slug:"security-headers-csp-lint",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence, 7-day refund.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/payment-page-script-audit"><span class="yj-rel-t">PCI DSS 6.4.3 Payment Page Script Inventory Auditor</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/pci-payment-page-script-lint"><span class="yj-rel-t">PCI DSS 6.4.3 Payment Page Script Lint</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/spdx-license-field-lint"><span class="yj-rel-t">SPDX License Field Lint for package.json, pyproject.toml and Cargo.toml</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/autohotkey-toml-devicetree-snippets"><span class="yj-rel-t">AutoHotkey v2, TOML and DeviceTree checks for VS Code</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/config-dsl-lint-snippet-pack"><span class="yj-rel-t">Config &amp; DSL Lint + Snippets — 9 Sets</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/devicetree-config-language-pack"><span class="yj-rel-t">DeviceTree Config Audit: 23 dtc-Style Rules, 50 Snippets</span><span class="yj-rel-p">$29</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section>
84
+
85
+
86
+ <script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script>
87
+
88
+ <script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script>