1"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[87200],{87200:(e,t,s)=>{s.r(t),s.d(t,{DIRECTIVES:()=>m,EMPTY_VALUES:()=>g,META_IGNORES:()=>H,NO_FALLBACK:()=>P,PRESETS:()=>V,auditPolicy:()=>_,buildOutput:()=>K,compile:()=>L,default:()=>en,evaluateResource:()=>z,headerValue:()=>M,mk:()=>v,parseSources:()=>I,resolveDirective:()=>E,strictDynamicIgnores:()=>G});var n=s(95155),r=s(12115),o=s(97003),a=s(33789),i=s(90235),l=s(67001),c=s(33534),d=s(69869),u=s(25016),p=s(14827),h=s(16110);let m=[{name:"default-src",plain:"The catch all. Any content type you do not name separately falls back to this one.",chain:[],group:"catchall",suggest:"'self'"},{name:"script-src",plain:"JavaScript files and inline script blocks. The directive that actually stops cross site scripting.",chain:["default-src"],group:"content",suggest:"'self'"},{name:"style-src",plain:"Stylesheets, inline style blocks, and style attributes.",chain:["default-src"],group:"content",suggest:"'self'"},{name:"img-src",plain:"Images, including favicons and anything a stylesheet pulls in as a background.",chain:["default-src"],group:"content",suggest:"'self' data:"},{name:"font-src",plain:"Web fonts loaded through @font-face.",chain:["default-src"],group:"content",suggest:"'self'"},{name:"connect-src",plain:"Everywhere your JavaScript talks to: fetch, XHR, websockets, EventSource, sendBeacon.",chain:["default-src"],group:"content",suggest:"'self'"},{name:"media-src",plain:"Audio and video files.",chain:["default-src"],group:"content",suggest:"'self'"},{name:"object-src",plain:"Plugin content in object, embed, and applet tags. Almost every site should set this to none.",chain:["default-src"],group:"content",suggest:"'none'"},{name:"manifest-src",plain:"The web app manifest file.",chain:["default-src"],group:"content",suggest:"'self'"},{name:"child-src",plain:"The older directive covering frames and workers together. Both fall back to it.",chain:["default-src"],group:"content",suggest:"'self'"},{name:"frame-src",plain:"Pages you put inside an iframe, like a YouTube embed, a Stripe field, or a map.",chain:["child-src","default-src"],group:"content",suggest:"'self'"},{name:"worker-src",plain:"Web workers, shared workers, and service workers.",chain:["child-src","script-src","default-src"],group:"content",suggest:"'self'"},{name:"base-uri",plain:"What a base tag is allowed to set. Leave it open and injected markup can point every relative script URL at another server.",chain:[],group:"navigation",suggest:"'self'"},{name:"form-action",plain:"Where a form is allowed to post. Leave it open and a rewritten action can send your login form anywhere.",chain:[],group:"navigation",suggest:"'self'"},{name:"frame-ancestors",plain:"Who may put YOUR page inside their iframe. This is the clickjacking defense that replaced X-Frame-Options.",chain:[],group:"navigation",suggest:"'self'"}],f=m.reduce((e,t)=>(e[t.name]=t,e),{}),g=m.reduce((e,t)=>(e[t.name]="",e),{}),x="'nonce-{{NONCE}}'",b=[{v:"script",label:"Script file",dir:"script-src",inline:!1,noncable:!0,allowIsGood:!0,refusal:e=>`load the script '${e}'`},{v:"inline-script",label:"Inline script block",dir:"script-src",inline:!0,noncable:!0,allowIsGood:!0,refusal:()=>"execute inline script"},{v:"style",label:"Stylesheet",dir:"style-src",inline:!1,noncable:!0,allowIsGood:!0,refusal:e=>`load the stylesheet '${e}'`},{v:"inline-style",label:"Inline style or style attribute",dir:"style-src",inline:!0,noncable:!0,allowIsGood:!0,refusal:()=>"apply inline style"},{v:"img",label:"Image",dir:"img-src",inline:!1,noncable:!1,allowIsGood:!0,refusal:e=>`load the image '${e}'`},{v:"font",label:"Web font",dir:"font-src",inline:!1,noncable:!1,allowIsGood:!0,refusal:e=>`load the font '${e}'`},{v:"connect",label:"fetch, XHR, or websocket",dir:"connect-src",inline:!1,noncable:!1,allowIsGood:!0,refusal:e=>`connect to '${e}'`},{v:"media",label:"Audio or video",dir:"media-src",inline:!1,noncable:!1,allowIsGood:!0,refusal:e=>`load media from '${e}'`},{v:"frame",label:"iframe your page embeds",dir:"frame-src",inline:!1,noncable:!1,allowIsGood:!0,refusal:e=>`frame '${e}'`},{v:"worker",label:"Web or service worker",dir:"worker-src",inline:!1,noncable:!1,allowIsGood:!0,refusal:e=>`create a worker from '${e}'`},{v:"manifest",label:"Web app manifest",dir:"manifest-src",inline:!1,noncable:!1,allowIsGood:!0,refusal:e=>`load manifest from '${e}'`}
1,{v:"object",label:"Plugin object or embed",dir:"object-src",inline:!1,noncable:!1,allowIsGood:!0,refusal:e=>`load plugin data from '${e}'`},{v:"form",label:"Form posts to",dir:"form-action",inline:!1,noncable:!1,allowIsGood:!0,refusal:e=>`send form data to '${e}'`},{v:"ancestor",label:"Another site framing YOUR page",dir:"frame-ancestors",inline:!1,noncable:!1,allowIsGood:!1,refusal:e=>`frame '${e}'`}],y=b.reduce((e,t)=>(e[t.v]=t,e),{}),w=0,v=(e,t,s=!1)=>(w+=1,{id:`r${w}`,kind:e,url:t,nonced:s}),k={http:80,https:443,ws:80,wss:443,ftp:21},j=new Set(["http","https","ws","wss","ftp"]);function $(e){let t=e.trim();if(!t)return null;let s=/^([a-z][a-z0-9+.-]*):/i.exec(t);if(!s||!s[1])return/^[a-z0-9*]/i.test(t)?$(`https://${t}`):null;let n=s[1].toLowerCase(),r=t.slice(s[0].length);if(!r.startsWith("//"))return{scheme:n,host:"",port:null,path:"",opaque:!0};let o=r.slice(2),a=o.search(/[/?#]/),i=a<0?o:o.slice(0,a),l=(a<0?"/":o.slice(a)).replace(/[?#].*$/,"")||"/",c=i.lastIndexOf("@"),d=c>=0?i.slice(c+1):i,u=/^(\[[^\]]*\]|[^:]*)(?::(\d+))?$/.exec(d);if(!u||!u[1])return null;let p=u[1].toLowerCase(),h=u[2]?parseInt(u[2],10):k[n]??null;return{scheme:n,host:p,port:h,path:l,opaque:!1}}let N=e=>{let t=k[e.scheme],s=null!==e.port&&e.port!==t;return`${e.scheme}://${e.host}${s?`:${e.port}`:""}`},C=new Set(["'self'","'none'","'unsafe-inline'","'unsafe-eval'","'strict-dynamic'","'unsafe-hashes'","'wasm-unsafe-eval'","'report-sample'","'inline-speculation-rules'"]),T=new Set(["self","none","unsafe-inline","unsafe-eval","strict-dynamic","unsafe-hashes","wasm-unsafe-eval"]),R=/^(?:([a-z][a-z0-9+.-]*):\/\/)?(\*\.[a-z0-9.-]+|\*|[a-z0-9.-]+|\[[^\]]*\])(?::(\d+|\*))?(\/[^\s]*)?$/i;function O(e){let t=e.trim(),s=t.toLowerCase();if(s.startsWith("'nonce-"))return{raw:t,kind:"nonce"};if(/^'(sha256|sha384|sha512)-/i.test(t))return{raw:t,kind:"hash"};if(t.startsWith("'"))return C.has(s)?{raw:t,kind:"keyword",keyword:s}:{raw:t,kind:"unknown"};if("*"===t)return{raw:t,kind:"star"};if(T.has(s))return{raw:t,kind:"bare-keyword"};if(/^[a-z][a-z0-9+.-]*:$/i.test(t))return{raw:t,kind:"scheme",scheme:s.slice(0,-1)};let n=R.exec(t);if(n&&n[2]){let e={raw:t,kind:"host",host:n[2].toLowerCase()};return n[1]&&(e.scheme=n[1].toLowerCase()),n[3]&&(e.port=n[3]),n[4]&&(e.path=n[4]),e}return{raw:t,kind:"unknown"}}let S=e=>e.replace(/;/g," ").split(/\s+/).map(e=>e.trim()).filter(Boolean),I=e=>S(e).map(O);function A(e,t){return e===t||"http"===e&&"https"===t||"ws"===e&&("wss"===t||"http"===t||"https"===t)||"wss"===e&&"https"===t}function L(e){let t={...e.values};if(e.nonce)for(let e of["script-src","style-src"]){let s=t[e].trim();s&&!s.includes("nonce-")&&(t[e]=`${s} ${x}`)}return t}function E(e,t){let s=f[t];for(let n of[t,...s.chain]){let s=e[n].trim();if(s)return{used:n,sources:I(s),raw:s,viaFallback:n!==t}}return{used:null,sources:[],raw:"",viaFallback:!1}}let U=(e,t)=>e.some(e=>"keyword"===e.kind&&e.keyword===t),q=e=>e.some(e=>"nonce"===e.kind||"hash"===e.kind),W=e=>1===e.length&&e[0]?.kind==="keyword"&&e[0]?.keyword==="'none'";function G(e){return e.filter(e=>"host"===e.kind||"star"===e.kind||"scheme"===e.kind||"keyword"===e.kind&&"'self'"===e.keyword)}function F(e,t,s,n,r){let o="ancestor"===e.v?`frame '${t}' because an ancestor violates`:`${e.refusal(t)} because it violates`;return[`${n?"[Report Only] ":""}Refused to ${o} the following Content Security Policy directive: "${s.used} ${s.raw}".`,...r].join(" ")}function z(e,t,s){let n=y[s.kind],r=$(t.site)??{scheme:"https",host:"example.com",port:443,path:"/",opaque:!1},o=E(e,n.dir);if(!o.used){let e=f[n.dir],t=0===e.chain.length?`${n.dir} is not set, and nothing falls back to it. Every request of this kind is allowed.`:`Neither ${n.dir} nor ${e.chain.join(" nor ")} is set, so nothing restricts this.`;return{outcome:"unrestricted",askedFor:n.dir,used:null,reason:t,consoleText:null,fix:{label:`Set ${n.dir}`,dir:n.dir,replace:e.suggest}}}let{sources:a}=o,i=o.viaFallback?`Note that '${n.dir}' was not explicitly set, so '${o.used}' is used as a fallback.`:"";if(n.inline){if(W(a))return{outcome:"blocked",askedFor:n.dir,used:o.used,reason:`${o.used} is set to 'none', which blocks everything of this kind.`,consoleText:F(n,"",o,t.reportOnly,[i].filter(
1Boolean)),fix:null};let e=a.some(e=>"nonce"===e.kind);if(s.nonced&&e)return{outcome:"allowed",askedFor:n.dir,used:o.used,reason:`Allowed by the nonce on ${o.used}. The server has to print a fresh random value into both the header and the tag on every response.`,consoleText:null,fix:null};let r=U(a,"'unsafe-inline'");if(r&&!q(a))return{outcome:"allowed",askedFor:n.dir,used:o.used,reason:`Allowed by 'unsafe-inline' on ${o.used}.`,consoleText:null,fix:null};let l=["Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution."];i&&l.push(i);let c=!r&&!s.nonced&&q(a),d=r&&q(a)?`${o.used} carries both 'unsafe-inline' and a nonce. Once a nonce is present every modern browser ignores 'unsafe-inline' completely, so this is blocked even though the keyword is sitting right there.`:s.nonced?`This tag carries a nonce but ${o.used} has no nonce source, so the browser has nothing to match it against.`:c?`${o.used} carries a hash but no 'unsafe-inline' and no nonce, so only a block whose contents hash to exactly that value runs. Any other inline block is refused.`:`${o.used} has no 'unsafe-inline', no nonce, and no hash.`;return{outcome:"blocked",askedFor:n.dir,used:o.used,reason:d,consoleText:F(n,"",o,t.reportOnly,l),fix:s.nonced?{label:`Add a nonce source to ${o.used}`,dir:o.used,add:x}:{label:`Add 'unsafe-inline' to ${o.used}`,dir:o.used,add:"'unsafe-inline'"}}}let l=s.url.trim();if(!l)return{outcome:"invalid",askedFor:n.dir,used:o.used,reason:"Add a URL and this row gets a verdict.",consoleText:null,fix:null};let c=$(l);if(!c)return{outcome:"invalid",askedFor:n.dir,used:o.used,reason:"That is not a URL the browser could request.",consoleText:null,fix:null};let d="localhost"===c.host||"127.0.0.1"===c.host||"[::1]"===c.host,u=!1;t.upgrade&&"http"===c.scheme&&!d&&(c={...c,scheme:"https",port:80===c.port?443:c.port},u=!0);let p=l.match(/[?#].*$/)?.[0]??"",h=u?`${N(c)}${"/"===c.path?"/":c.path}${p}`:l,m=u?"upgrade-insecure-requests rewrote this to https before the policy was checked.":"";if(W(a))return{outcome:"blocked",askedFor:n.dir,used:o.used,reason:`${o.used} is set to 'none', which blocks everything of this kind.`,consoleText:F(n,h,o,t.reportOnly,[i].filter(Boolean)),fix:{label:`Allow ${N(c)}`,dir:o.used,replace:N(c)}};if(n.noncable&&s.nonced&&a.some(e=>"nonce"===e.kind))return{outcome:"allowed",askedFor:n.dir,used:o.used,reason:`Allowed by the nonce on ${o.used}. A nonce matches the tag itself, so the host does not need to be in the allowlist. The server has to print a fresh random value into both the header and the tag on every response.`,consoleText:null,fix:null};let g="frame-ancestors"===n.dir;if(("script-src"===n.dir||"worker-src"===n.dir)&&U(a,"'strict-dynamic'")){let e=a.some(e=>"nonce"===e.kind);if(s.nonced&&e)return{outcome:"allowed",askedFor:n.dir,used:o.used,reason:"Allowed by its nonce. With 'strict-dynamic' the allowlist is not consulted at all, so this loads purely because the tag carries the nonce.",consoleText:null,fix:null};let r=G(a),l=["Note that 'strict-dynamic' is present, so host-based allowlisting is disabled.",i].filter(Boolean);return{outcome:"blocked",askedFor:n.dir,used:o.used,reason:r.length>0?`'strict-dynamic' turns off host matching and 'self' along with it, so ${r.map(e=>e.raw).join(", ")} does nothing here. A script only loads if it carries the nonce, or if a script that did carry it created this one.`:"'strict-dynamic' means a script only loads if it carries the nonce, or if a script that did carry it created this one.",consoleText:F(n,h,o,t.reportOnly,l),fix:{label:"Mark this tag as carrying the nonce",dir:o.used}}}let b=a.find(e=>(function(e,t,s,n){switch(e.kind){case"star":return!t.opaque&&j.has(t.scheme);case"scheme":return!!e.scheme&&A(e.scheme,t.scheme);case"keyword":return"'self'"===e.keyword&&function(e,t){if(t.opaque)return!1;if(t.scheme===e.scheme&&t.host===e.host&&t.port===e.port)return!0;if(t.host!==e.host||("http"!==e.scheme||"https"!==t.scheme&&"ws"!==t.scheme&&"wss"!==t.scheme)&&("https"!==e.scheme||"wss"!==t.scheme))return!1;let s=k[e.scheme]??null,n=k[t.scheme]??null;return e.port===s&&t.port===n||e.port===t.port}(s,t);case"host":if(t.opaque||!e.host)return!1;if(e.scheme){if(!A(e.scheme,t.scheme))return!1}else if("http"===s.scheme){if("http"!==t.scheme&&"https"!==t.scheme)return!1}else if(t.scheme!==s.scheme)return!1;return function(e,t){if("*"===e)return!0;
1if(e.startsWith("*.")){let s=e.slice(1);return t.length>s.length&&t.endsWith(s)}return e===t}(e.host,t.host)&&function(e,t){if("*"===e.port)return!0;if(void 0===e.port){let e=k[t.scheme];return null===t.port||t.port===e}let s=parseInt(e.port,10);return t.port===s||80===s&&443===t.port&&"https"===t.scheme}(e,t)&&function(e,t,s){if(s)return!0;let n=e.path;return!n||"/"===n||(n.endsWith("/")?t.path.startsWith(n):t.path===n)}(e,t,n);default:return!1}})(e,c,r,g));if(b){let e=[`Matched by ${b.raw} on ${o.used}.`];return o.viaFallback&&e.push(`${n.dir} is not set, so ${o.used} decided.`),m&&e.push(m),{outcome:"allowed",askedFor:n.dir,used:o.used,reason:e.join(" "),consoleText:null,fix:null}}let w=("wss"===c.scheme||"ws"===c.scheme)&&a.some(e=>"host"===e.kind&&e.host===c?.host),v=[i].filter(Boolean),C=[`Nothing in ${o.used} matches ${N(c)}.`];w&&C.push(`The host is listed, but not with a websocket scheme. Browsers do not treat an https source as covering wss, so write it out as ${c.scheme}://${c.host}.`),m&&C.push(m),c.opaque&&C.push(`A ${c.scheme}: URL is only allowed by a ${c.scheme}: source. 'self' and a wildcard both skip it.`);let T=c.opaque?`${c.scheme}:`:w?`${c.scheme}://${c.host}`:N(c);return{outcome:"blocked",askedFor:n.dir,used:o.used,reason:C.join(" "),consoleText:F(n,h,o,t.reportOnly,v),fix:{label:`Add ${T} to ${o.used}`,dir:o.used,add:T}}}let P=["base-uri","form-action","frame-ancestors"],H=["frame-ancestors"];function _(e,t,s){let n=[],r=t=>e[t].trim().length>0;for(let e of(t.reportOnly&&!t.reportUri.trim()&&n.push({level:"error",text:"Report-Only never blocks anything, that is the point of it. With no endpoint it also reports nowhere, so the whole header does nothing but write console lines nobody is watching. Add an endpoint or switch to enforcing."}),P)){if(r(e))continue;let t=f[e],s="frame-ancestors"===e?"any site can load your pages in an iframe and stack a transparent layer over your buttons":"base-uri"===e?"a single injected base tag can point every relative script URL at an attacker":"a rewritten form action can post your login form to another domain";n.push({level:"warn",text:`${e} is not set, and nothing falls back to it. A default-src of 'self' does not cover it, so ${s}.`,fix:{label:`Add ${e} ${t.suggest}`,dir:e,replace:t.suggest}})}if(!r("default-src")){let e=m.filter(e=>"content"===e.group&&e.chain.includes("default-src")&&!r(e.name)&&"child-src"!==e.name).map(e=>e.name);e.length>0&&n.push({level:"warn",text:`default-src is not set, so ${e.join(", ")} restrict nothing at all. Those are not defaults, they are absences.`,fix:{label:"Add default-src 'self'",dir:"default-src",replace:"'self'"}})}for(let t of m){let s=e[t.name].trim();if(!s)continue;let r=I(s);for(let e of(U(r,"'unsafe-inline'")&&q(r)&&n.push({level:"note",text:`${t.name} has 'unsafe-inline' next to a nonce. Every browser that understands nonces ignores the keyword, which is deliberate. It is only doing something for browsers old enough to predate CSP2.`}),U(r,"'none'")&&r.length>1&&n.push({level:"warn",text:`${t.name} lists 'none' alongside other sources. 'none' only counts when it is the only value, so here the browser throws it away and uses the rest.`,fix:{label:`Remove 'none' from ${t.name}`,dir:t.name,remove:"'none'"}}),r))"bare-keyword"===e.kind?n.push({level:"error",text:`${t.name} contains ${e.raw} without quotes, so the browser reads it as a hostname called "${e.raw}" and it matches nothing. Keywords need the single quotes.`,fix:{label:`Quote it as '${e.raw}'`,dir:t.name,remove:e.raw,add:`'${e.raw}'`}}):"unknown"===e.kind?n.push({level:"warn",text:`${t.name} contains ${e.raw}, which is not a source the browser recognizes. Unknown sources are skipped in silence.`}):"host"===e.kind&&e.path&&"frame-ancestors"===t.name&&n.push({level:"note",text:`frame-ancestors compares origins only, so the path on ${e.raw} is ignored.`})}let o=e["script-src"].trim();if(o){let e=I(o);if(U(e,"'strict-dynamic'")){let t=G(e);t.length>0&&n.push({level:"warn",text:`'strict-dynamic' switches off host matching in script-src, and 'self' is switched off with it, so ${t.map(e=>e.raw).join(", ")} does nothing. Keep the entries only if you also support browsers that ignore 'strict-dynamic'.`}),q(e)||n.push({level:"error",text:"'strict-dynamic' with no nonce and no hash blocks every script on the page, including your own. Turn the nonce on or add hashes."})}U(e,"'unsafe-inline'")&&!q(e)&&n.push({level:"error",text:"script-src allows 'unsafe-inline'. Injected markup is inline by definition, so this is the setting that quietly turns a Content Security Policy into decoration. A nonce is the way out."}),e.some(e=>"scheme"===e.kind&&"data"===e.scheme)&&n.push({level:"error",text:"script-src allows data: URLs. That is a documented way straight around a policy, because an attacker can put the whole script in the URL.",fix:{label:"Remove data: from script-src",dir:"script-src",remove:"data:"}});let t=e.find(e=>"star"===e.kind||"scheme"===e.kind&&"https"===e.scheme);t&&n.push({level:"warn",text:`script-src contains ${t.raw}, which allows script from any host that answers. It parses, it deploys, and it stops nothing.`})}
1if(E(e,"object-src").used||n.push({level:"warn",text:"Nothing restricts object-src. Set it to 'none' unless you genuinely serve plugin content, which almost nobody does now.",fix:{label:"Set object-src 'none'",dir:"object-src",replace:"'none'"}}),t.nonce&&(("meta"===s||"apache"===s)&&n.push({level:"warn",text:"A nonce has to be a fresh random value on every single response. The snippet below is a static file, so whatever you paste there becomes a constant an attacker can read once and reuse forever. Generate it in your app."}),e["script-src"].trim()||n.push({level:"warn",text:"The nonce is attached to script-src and style-src. Neither is set, so it is not being added anywhere.",fix:{label:"Set script-src 'self'",dir:"script-src",replace:"'self'"}})),"meta"===s){let e=H.filter(e=>r(e)),s=[];e.length>0&&s.push(e.join(" and ")),t.reportUri.trim()&&s.push("report-uri and report-to"),s.length>0&&n.push({level:"warn",text:`A meta tag ignores ${s.join(", ")} by specification. They are stripped from the snippet below rather than left there looking like they work. Send the header from your server instead.`}),t.reportOnly&&n.push({level:"error",text:"Report-Only does not exist as a meta tag. A browser reading this will enforce the policy instead of reporting on it, which is the opposite of what you asked for."})}if(t.upgrade){let t=[];for(let s of m)for(let n of I(e[s.name]))"host"===n.kind&&"http"===n.scheme&&t.push(`${n.raw} in ${s.name}`);t.length>0&&n.push({level:"note",text:`upgrade-insecure-requests rewrites requests to https before the policy runs, so ${t.join(", ")} will never be reached the way you wrote it. Localhost is the exception browsers leave alone.`})}return 0===n.length&&n.push({level:"ok",text:"Nothing in this policy is being silently ignored, and the three directives that never fall back are all set."}),n}let B="csp-endpoint";function M(e,t,s){let n=[];for(let t of m){if(s&&H.includes(t.name))continue;let r=e[t.name].trim();r&&n.push(`${t.name} ${r}`)}t.upgrade&&n.push("upgrade-insecure-requests");let r=t.reportUri.trim();return r&&!s&&(n.push(`report-uri ${r}`),n.push(`report-to ${B}`)),n.join("; ")}let D=[{v:"raw",label:"Raw header",file:"csp-header.txt"},{v:"nginx",label:"Nginx",file:"csp.conf"},{v:"apache",label:"Apache",file:"csp.htaccess"},{v:"nextjs",label:"Next.js",file:"csp-next.js"},{v:"express",label:"Express",file:"csp-express.js"},{v:"worker",label:"Cloudflare Worker",file:"csp-worker.js"},{v:"meta",label:"HTML meta tag",file:"csp-meta.html"}],J=e=>e.replace(/\\/g,"\\\\").replace(/"/g,'\\"'),Y=e=>e.replace(/\\/g,"\\\\").replace(/'/g,"\\'");function K(e,t,s){let n=s.reportOnly?"Content-Security-Policy-Report-Only":"Content-Security-Policy",r=M(t,s,"meta"===e),o=s.reportUri.trim()?`Reporting-Endpoints: ${B}="${s.reportUri.trim()}"`:"",a=s.nonce;switch(e){case"raw":{let e=[`${n}: ${r}`];return o&&e.push(o),a&&(e.push(""),e.push("# Replace {{NONCE}} with a fresh random value on every response,"),e.push("# and print the same value into every script tag you want to run.")),e.join("\n")}case"nginx":{let e=a?r.replace(/\{\{NONCE\}\}/g,"$csp_nonce"):r,t=[];if(a&&(t.push("# $request_id is a fresh random value per request, which is what a nonce needs."),t.push("# sub_filter writes the same value into your HTML so the tags match the header."),t.push("set $csp_nonce $request_id;"),t.push("sub_filter_once off;"),t.push("sub_filter '{{NONCE}}' $csp_nonce;"),t.push("")),t.push("# Inside your server or location block."),t.push(`add_header ${n} "${J(e)}" always;`),o){let[,e]=o.split(": ");t.push(`add_header Reporting-Endpoints "${J(e??"")}" always;`)}
1return t.push(""),t.push("# The always flag matters. Without it the header is skipped on error responses,"),t.push("# which is exactly when you need it most."),t.join("\n")}case"apache":{let e=["# .htaccess or your virtual host.",`Header always set ${n} "${J(r)}"`];if(o){let[,t]=o.split(": ");e.push(`Header always set Reporting-Endpoints "${J(t??"")}"`)}return a&&(e.push(""),e.push("# Apache cannot invent a fresh nonce per response on its own."),e.push("# Generate {{NONCE}} in your application and set the header there instead.")),e.join("\n")}case"nextjs":if(a)return["// middleware.ts","import { NextResponse } from 'next/server';","import type { NextRequest } from 'next/server';","","export function middleware(request: NextRequest) {"," const nonce = Buffer.from(crypto.randomUUID()).toString('base64');",` const csp = \`${r.replace(/\{\{NONCE\}\}/g,"${nonce}")}\`;`,""," const headers = new Headers(request.headers);"," headers.set('x-nonce', nonce);",""," const response = NextResponse.next({ request: { headers } });",` response.headers.set('${n}', csp);`,o?` response.headers.set('Reporting-Endpoints', '${Y(o.split(": ")[1]??"")}');`:""," return response;","}","","// Read the nonce in a server component with headers().get('x-nonce')","// and put it on every script tag you control."].filter(e=>""!==e).join("\n");return["// next.config.js",`const csp = "${J(r)}";`,"","module.exports = {"," async headers() {"," return ["," {"," source: '/:path*',"," headers: [",` { key: '${n}', value: csp },`,o?` { key: 'Reporting-Endpoints', value: '${Y(o.split(": ")[1]??"")}' },`:""," ],"," },"," ];"," },","};"].filter(e=>""!==e).join("\n");case"express":if(a)return["const crypto = require('crypto');","","app.use((req, res, next) => {"," const nonce = crypto.randomBytes(16).toString('base64');"," res.locals.nonce = nonce;",` res.setHeader('${n}', \`${r.replace(/\{\{NONCE\}\}/g,"${nonce}")}\`);`,o?` res.setHeader('Reporting-Endpoints', '${Y(o.split(": ")[1]??"")}');`:""," next();","});","",'// In your template, render nonce="<%= nonce %>" on every script tag you control.'].filter(e=>""!==e).join("\n");return["app.use((req, res, next) => {",` res.setHeader('${n}', "${J(r)}");`,o?` res.setHeader('Reporting-Endpoints', '${Y(o.split(": ")[1]??"")}');`:""," next();","});"].filter(e=>""!==e).join("\n");case"worker":return(a?["export default {"," async fetch(request, env, ctx) {",' const nonce = crypto.randomUUID().replace(/-/g, "");'," const response = await fetch(request);"," const headers = new Headers(response.headers);",` headers.set('${n}', \`${r.replace(/\{\{NONCE\}\}/g,"${nonce}")}\`);`,o?` headers.set('Reporting-Endpoints', '${Y(o.split(": ")[1]??"")}');`:""," // Rewrite {{NONCE}} in the HTML body with HTMLRewriter so the tags match."," return new Response(response.body, {"," status: response.status,"," statusText: response.statusText,"," headers,"," });"," },","};"]:["export default {"," async fetch(request, env, ctx) {"," const response = await fetch(request);"," const headers = new Headers(response.headers);",` headers.set('${n}', "${J(r)}");`,o?` headers.set('Reporting-Endpoints', '${Y(o.split(": ")[1]??"")}');`:""," return new Response(response.body, {"," status: response.status,"," statusText: response.statusText,"," headers,"," });"," },","};"]).filter(e=>""!==e).join("\n");case"meta":{let e=["\x3c!-- Put this before any script or stylesheet, or the ones above it load unchecked. --\x3e",`<meta http-equiv="Content-Security-Policy" content="${r.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}">`],s=H.filter(e=>t[e].trim());return s.length>0&&(e.push(""),e.push(`<!-- ${s.join(" and ")} was left out because a meta tag ignores it. -->`),e.push("\x3c!-- Send that one as a real HTTP header. --\x3e")),e.join("\n")}default:return""}}let V=[{key:"static",label:"Marketing site with fonts and analytics",values:{"default-src":"'self'","script-src":"'self' https://www.googletagmanager.com","style-src":"'self' https://fonts.googleapis.com","font-src":"'self' https://fonts.gstatic.
1com","img-src":"'self' data: https://www.google-analytics.com","connect-src":"'self' https://www.google-analytics.com","object-src":"'none'","base-uri":"'self'","form-action":"'self'","frame-ancestors":"'none'"},policy:{upgrade:!0,reportOnly:!1,nonce:!1,reportUri:"",site:"https://example.com"},resources:[["script","https://www.googletagmanager.com/gtag/js?id=G-ABC123"],["inline-script",""],["style","https://fonts.googleapis.com/css2?family=Inter"],["font","https://fonts.gstatic.com/s/inter/v13/font.woff2"],["img","data:image/svg+xml,%3Csvg%3E"],["connect","https://www.google-analytics.com/g/collect"]]},{key:"nextjs",label:"Next.js with a nonce",values:{"default-src":"'self'","script-src":"'self' 'strict-dynamic'","style-src":"'self' 'unsafe-inline'","img-src":"'self' data: blob:","font-src":"'self'","connect-src":"'self'","object-src":"'none'","base-uri":"'self'","form-action":"'self'","frame-ancestors":"'none'"},policy:{upgrade:!0,reportOnly:!1,nonce:!0,reportUri:"",site:"https://example.com"},resources:[["inline-script","",!0],["script","https://cdn.thirdparty.com/widget.js"],["inline-style",""],["img","blob:https://example.com/9f8a"],["connect","https://example.com/api/session"]]},{key:"wordpress",label:"WordPress with plugins",values:{"default-src":"'self'","script-src":"'self' 'unsafe-inline' 'unsafe-eval'","style-src":"'self' 'unsafe-inline' https://fonts.googleapis.com","img-src":"'self' data: https://secure.gravatar.com https://*.wp.com","font-src":"'self' data: https://fonts.gstatic.com","connect-src":"'self'","frame-src":"'self' https://www.youtube-nocookie.com","object-src":"'none'","base-uri":"'self'","form-action":"'self'","frame-ancestors":"'self'"},policy:{upgrade:!0,reportOnly:!1,nonce:!1,reportUri:"",site:"https://example.com"},resources:[["img","https://secure.gravatar.com/avatar/abc123"],["img","https://i0.wp.com/example.com/photo.jpg"],["inline-script",""],["frame","https://www.youtube-nocookie.com/embed/xyz"],["connect","https://example.com/wp-admin/admin-ajax.php"]]},{key:"spa",label:"Single page app with an API",values:{"default-src":"'self'","script-src":"'self'","style-src":"'self' 'unsafe-inline'","img-src":"'self' data: blob:","font-src":"'self'","connect-src":"'self' https://api.example.com","worker-src":"'self' blob:","object-src":"'none'","base-uri":"'self'","form-action":"'self'","frame-ancestors":"'none'"},policy:{upgrade:!0,reportOnly:!1,nonce:!1,reportUri:"",site:"https://app.example.com"},resources:[["connect","https://api.example.com/v1/me"],["connect","wss://api.example.com/live"],["worker","blob:https://app.example.com/4d2c"],["img","data:image/png;base64,iVBORw0KGgo="],["inline-style",""]]},{key:"stripe",label:"Checkout with Stripe",values:{"default-src":"'self'","script-src":"'self' https://js.stripe.com","style-src":"'self' 'unsafe-inline'","img-src":"'self' data:","connect-src":"'self' https://api.stripe.com","frame-src":"https://js.stripe.com https://hooks.stripe.com","object-src":"'none'","base-uri":"'self'","form-action":"'self'","frame-ancestors":"'none'"},policy:{upgrade:!0,reportOnly:!1,nonce:!1,reportUri:"",site:"https://shop.example.com"},resources:[["script","https://js.stripe.com/v3/"],["frame","https://js.stripe.com/v3/elements-inner-card"],["connect","https://api.stripe.com/v1/tokens"],["form","https://shop.example.com/checkout"],["ancestor","https://partner.example.net"]]},{key:"reportonly",label:"First pass in Report-Only",values:{"default-src":"'self'","script-src":"'self'","style-src":"'self'","img-src":"'self' data:","connect-src":"'self'","object-src":"'none'","base-uri":"'self'","form-action":"'self'","frame-ancestors":"'self'"},policy:{upgrade:!1,reportOnly:!0,nonce:!1,reportUri:"https://example.report-uri.com/r/d/csp/reportOnly",site:"https://example.com"},resources:[["script","https://cdn.jsdelivr.net/npm/chart.js"],["inline-script",""],["inline-style",""],["img","https://images.unsplash.com/photo-1"],["font","https://fonts.gstatic.com/s/inter/v13/font.woff2"]]}],X={values:{...g,...V[0]?.values},upgrade:!0,reportOnly:!1,reportUri:"",nonce:!1,site:"https://example.com"},Q=(V[0]?.resources??[]).map(([e,t,s])=>v(e,t,s??!1)),Z=[{label:"Google Fonts",items:[["style","https://fonts.googleapis.com/css2?family=Inter"],["font","https://fonts.gstatic.
1com/s/inter/v13/font.woff2"]]},{label:"Google Analytics",items:[["script","https://www.googletagmanager.com/gtag/js?id=G-ABC123"],["connect","https://www.google-analytics.com/g/collect"]]},{label:"Stripe",items:[["script","https://js.stripe.com/v3/"],["frame","https://js.stripe.com/v3/elements-inner-card"],["connect","https://api.stripe.com/v1/tokens"]]},{label:"YouTube embed",items:[["frame","https://www.youtube-nocookie.com/embed/xyz"]]},{label:"Inline script",items:[["inline-script",""]]},{label:"Inline style",items:[["inline-style",""]]},{label:"A site framing you",items:[["ancestor","https://partner.example.net"]]}];function ee({on:e,onLabel:t,offLabel:s,onClick:r}){return(0,n.jsx)(o.$,{type:"button",size:"sm",variant:e?"default":"outline","aria-pressed":e,onClick:r,children:e?t:s})}let et={error:"!",warn:"!",note:"i",ok:"â"},es=["'self'","'none'","'unsafe-inline'","'unsafe-eval'","'strict-dynamic'","data:","blob:","https:","*"];function en(){let[e,t]=(0,r.useState)({...X,values:{...X.values}}),[s,x]=(0,r.useState)([...Q]),[w,k]=(0,r.useState)("raw"),[j,$]=(0,r.useState)(""),[N,C]=(0,r.useState)(!1),[T,R]=(0,r.useState)(!1),[O,I]=(0,r.useState)(null);(0,r.useEffect)(()=>{let e=(0,d.qA)(window.location.search,window.location.hash),s=[e.get("s"),e.get("o")].filter(e=>!!e);if(0!==s.length)for(let e of s){var n=e;try{let e=JSON.parse(n);if(e&&"object"==typeof e){if(e.p&&"object"==typeof e.p){let s=e.p.values;t(t=>({...t,...e.p,values:s?{...g,...s}:t.values}))}if(Array.isArray(e.r)){let t=e.r.filter(e=>e&&"string"==typeof e.url&&y[e.kind]).map(e=>v(e.kind,e.url,!!e.nonced));t.length>40&&C(!0),x(t.slice(0,40))}e.t&&D.some(t=>t.v===e.t)&&k(e.t)}}catch{}}},[]);let A=(0,r.useMemo)(()=>L(e),[e]),U=(0,r.useMemo)(()=>_(A,e,w),[A,e,w]),q=(0,r.useMemo)(()=>s.map(t=>({res:t,verdict:z(A,e,t)})),[A,e,s]),W=(0,r.useMemo)(()=>K(w,A,e),[w,A,e]),G=D.find(e=>e.v===w)??D[0],F=q.filter(e=>"blocked"===e.verdict.outcome&&y[e.res.kind].allowIsGood).length,P=q.filter(e=>!y[e.res.kind].allowIsGood&&("allowed"===e.verdict.outcome||"unrestricted"===e.verdict.outcome)).length,H=q.filter(e=>"unrestricted"===e.verdict.outcome&&y[e.res.kind].allowIsGood).length,B=e=>t(t=>{if(void 0!==e.replace)return{...t,values:{...t.values,[e.dir]:e.replace}};let s=S(t.values[e.dir]);return e.remove&&(s=s.filter(t=>t.toLowerCase()!==e.remove?.toLowerCase())),e.add&&!s.includes(e.add)&&(s=[...s,e.add]),{...t,values:{...t.values,[e.dir]:s.join(" ")}}}),M=(e,s)=>t(t=>({...t,[e]:s})),J=e=>x(t=>{let s=40-t.length;if(s<=0)return C(!0),t;let n=e.slice(0,s);return n.length<e.length&&C(!0),[...t,...n.map(([e,t,s])=>v(e,t,s??!1))]}),Y=(e,t)=>x(s=>s.map(s=>s.id===e?{...s,...t}:s)),en=(0,r.useMemo)(()=>JSON.stringify({p:{upgrade:e.upgrade,reportOnly:e.reportOnly,nonce:e.nonce},t:w}),[e.upgrade,e.reportOnly,e.nonce,w]),er=(0,r.useMemo)(()=>JSON.stringify({p:{values:e.values,reportUri:e.reportUri,site:e.site},r:s.map(e=>({kind:e.kind,url:e.url,nonced:e.nonced}))}),[e.values,e.reportUri,e.site,s]),eo=en+er,ea=(0,r.useCallback)(()=>(0,d.CZ)([{key:"s",value:en},{key:"o",value:eo.length<=3500?er:null,personal:"your site, the origins it loads from and the address reports go to"}]),[en,er,eo.length]),ei=eo.length>3500,el=s=>{let r=e.values[s.name],o=r.trim().length>0,i=E(A,s.name),l=A[s.name].trim();return(0,n.jsxs)("div",{className:"space-y-1.5 rounded-md border p-3",children:[(0,n.jsxs)("div",{className:"flex flex-wrap items-center justify-between gap-2",children:[(0,n.jsx)("code",{className:"font-mono text-sm font-semibold",children:s.name}),(0,n.jsx)(ee,{on:o,onLabel:"Set",offLabel:"Not set",onClick:()=>{let e;return e=s.name,t(t=>({...t,values:{...t.values,[e]:t.values[e].trim()?"":f[e].suggest}}))}})]}),(0,n.jsx)("p",{className:"text-xs text-muted-foreground",children:s.plain}),o?(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(a.p,{"aria-label":`${s.name} sources`,value:r,onChange:e=>{let n,r;return n=s.name,r=e.target.value,t(e=>({...e,values:{...e.values,[n]:r}}))},placeholder:s.suggest,className:"font-mono text-xs"}),(0,n.jsx)("div",{className:"flex flex-wrap gap-1",children:es.map(e=>(0,n.jsx)("button",{type:"button",onClick:()=>{let n;return n=s.name,t(t=>{let s=S(t.values[n]);return s.includes(e)?t:{...t,values:{...t.values,[n]:[...s,e].join(" ")}}})},className:"rounded border px-1.5 py-0.5 font-mono text-[11px] text-muted-foreground hover:bg-muted",children:e},e))}),e.nonce&&l!==r.trim()&&(0,n.jsxs)("p",{className:"font-mono text-[11px] text-sky-700 dark:text-sky-400",children:["sends as: ",l]})]}):s.chain.length>0?(0,n.jsxs)("p",{className:"text-xs text-muted-foreground",children:["Not set, so the browser falls back to"," ",(0,n.jsx)("code",{className:"font-mono",children:i.used??s.chain[s.chain.length-1]}),i.used?".":", which is not set either. Nothing is restricted."]}):(0,n.jsx)("p",{className:"text-xs font-medium text-amber-700 dark:text-amber-500",children:"Nothing falls back to this one. Leave it off and it is simply not enforced, no matter what default-src says."})]},s.name)};return(0,n.jsxs)("div",{className:"space-y-5",children:[(0,n.jsxs)("div",{className:"space-y-2 rounded-lg border p-3",children:[(0,n.jsx)("div",{className:"text-sm font-medium",children:"Start from a stack"}),(0,n.jsx)("div",{className:"flex flex-wrap gap-2",children:V.map(e=>(0,n.jsx)(o.$,{type:"button",variant:"outline",size:"sm",onClick:()=>{t({...X,...e.policy,values:{...g,...e.values}}),x(e.resources.map(([e,t,s])=>v(e,t,s??!1))),C(!1),I(null)},children:e.label},e.key))}),(0,n.jsx)("p",{className:"text-xs text-muted-foreground",children:"Each one loads a working policy plus the resources that stack actually loads, so the check on the right has something real to chew on. Everything runs in your browser, nothing is uploaded."})]}),(0,n.jsxs)("div",{className:"grid gap-5 lg:grid-cols-2",children:[(0,n.jsxs)("div",{className:"space-y-4",children:[(0,n.jsxs)("div",{className:"space-y-3 rounded-lg border p-4",children:[(0,n.jsx)("div",{className:"text-sm font-medium",children:"Your policy"}),(0,n.jsxs)("div",{className:"grid gap-3 sm:grid-cols-2",children:[(0,n.jsxs)("div",{className:"space-y-1",children:[(0,n.jsx)(i.J,{htmlFor:"csp-site",children:"The site this protects"}),(0,n.jsx)(a.p,{id:"csp-site",value:e.site,onChange:e=>M("site",e.target.value),placeholder:"https://example.com",className:"font-mono text-xs"}),(0,n.jsx)("p",{className:"text-xs text-muted-foreground",children:"Every '
1self' is measured against this."})]}),(0,n.jsxs)("div",{className:"space-y-1",children:[(0,n.jsx)(i.J,{htmlFor:"csp-report",children:"Where violations get reported"}),(0,n.jsx)(a.p,{id:"csp-report",value:e.reportUri,onChange:e=>M("reportUri",e.target.value),placeholder:"https://example.report-uri.com/r/d/csp/enforce",className:"font-mono text-xs"}),(0,n.jsx)("p",{className:"text-xs text-muted-foreground",children:"Leave blank and reports go nowhere but the console."})]})]}),(0,n.jsxs)("div",{className:"flex flex-wrap gap-2",children:[(0,n.jsx)(ee,{on:e.reportOnly,onLabel:"Report-Only",offLabel:"Enforcing",onClick:()=>M("reportOnly",!e.reportOnly)}),(0,n.jsx)(ee,{on:e.nonce,onLabel:"Nonce on",offLabel:"No nonce",onClick:()=>t(e=>{let t=!e.nonce,s=t&&!e.values["script-src"].trim()?{...e.values,"script-src":"'self'"}:e.values;return{...e,nonce:t,values:s}})}),(0,n.jsx)(ee,{on:e.upgrade,onLabel:"Upgrade http to https",offLabel:"No upgrade",onClick:()=>M("upgrade",!e.upgrade)})]})]}),(0,n.jsxs)("div",{className:"space-y-2",children:[(0,n.jsx)("div",{className:"text-sm font-medium",children:"The catch all"}),m.filter(e=>"catchall"===e.group).map(el)]}),(0,n.jsxs)("div",{className:"space-y-2",children:[(0,n.jsx)("div",{className:"text-sm font-medium",children:"Where content may come from"}),m.filter(e=>"content"===e.group).map(el)]}),(0,n.jsxs)("div",{className:"space-y-2",children:[(0,n.jsx)("div",{className:"text-sm font-medium",children:"The three nothing falls back to"}),m.filter(e=>"navigation"===e.group).map(el)]})]}),(0,n.jsxs)("div",{className:"space-y-4",children:[(0,n.jsxs)("div",{className:"space-y-3 rounded-lg border p-4",children:[(0,n.jsx)("div",{className:"text-sm font-medium",children:"What your page actually loads"}),(0,n.jsx)("p",{className:"text-xs text-muted-foreground",children:"List it once and every row gets a verdict, plus the directive that decided and the console line you would have gone hunting for."}),(0,n.jsxs)("div",{className:"space-y-1",children:[(0,n.jsx)(i.J,{htmlFor:"csp-paste",children:"Paste URLs, one per line"}),(0,n.jsx)(l.T,{id:"csp-paste",value:j,onChange:e=>$(e.target.value),placeholder:"https://cdn.example.com/app.js\nhttps://fonts.gstatic.com/s/inter/font.woff2",className:"min-h-[64px] font-mono text-xs"}),(0,n.jsxs)("div",{className:"flex flex-wrap gap-2",children:[(0,n.jsx)(o.$,{type:"button",size:"sm",variant:"outline",onClick:()=>{let e=j.split(/[\n,]/).map(e=>e.trim()).filter(Boolean);0!==e.length&&(J(e.map(e=>[function(e){let t=e.trim().toLowerCase();if(t.startsWith("ws://")||t.startsWith("wss://"))return"connect";if(t.startsWith("data:image"))return"img";if(t.startsWith("data:font"))return"font";if(t.startsWith("blob:"))return"img";let s=(t.split("?")[0]??t).split("#")[0]??t;return/\.(m?js|jsx|ts)$/.test(s)?"script":/\.css$/.test(s)?"style":/\.(png|jpe?g|gif|svg|webp|avif|ico|bmp)$/.test(s)?"img":/\.(woff2?|ttf|otf|eot)$/.test(s)?"font":/\.(mp4|webm|mp3|ogg|wav|mov|m4a)$/.test(s)?"media":/\.webmanifest$/.test(s)||/manifest\.json$/.test(s)?"manifest":/\.(json|xml)$/.test(s)||/\/api\//.test(s)?"connect":"script"}(e),e])),$(""))},disabled:!j.trim(),children:"Add them"}),Z.map(e=>(0,n.jsxs)(o.$,{type:"button",size:"sm",variant:"ghost",onClick:()=>J(e.items),children:["+ ",e.label]},e.label))]}),N&&(0,n.jsxs)("p",{className:"text-xs font-medium text-amber-700 dark:text-amber-500",children:["Stopped at ",40," rows. Remove a few if you need to test more."]})]}),(0,n.jsxs)("div",{className:"flex flex-wrap gap-2 text-xs",children:[(0,n.jsxs)("span",{className:"rounded bg-destructive/15 px-2 py-0.5 font-medium text-destructive",children:[F," blocked"]}),(0,n.jsxs)("span",{className:"rounded bg-amber-500/15 px-2 py-0.5 font-medium text-amber-700 dark:text-amber-500",children:[H," unrestricted"]}),P>0&&(0,n.jsxs)("span",{className:"rounded bg-amber-500/15 px-2 py-0.5 font-medium text-amber-700 dark:text-amber-500",children:[P," can frame you"]})]}),(0,n.jsxs)("ul",{className:"space-y-2",children:[q.map(({res:t,verdict:s})=>{let r=y[t.kind],i=r.allowIsGood?"allowed"===s.outcome:"blocked"===s.outcome,l="invalid"===s.outcome?"Waiting":r.allowIsGood?"allowed"===s.outcome?"Allowed":"blocked"===s.outcome?e.reportOnly?"Would be blocked":"Blocked":"Unrestricted":"blocked"===s.outcome?"Cannot frame you":"Can frame you";return(0,n.jsxs)("li",{className:(0,u.cn)("space-y-2 rounded-md border p-2.5","invali
1d"===s.outcome&&"bg-muted/30","invalid"!==s.outcome&&i&&"border-primary/40 bg-primary/5","invalid"!==s.outcome&&!i&&("unrestricted"!==s.outcome&&r.allowIsGood?"border-destructive/40 bg-destructive/5":"border-amber-500/40 bg-amber-500/5")),children:[(0,n.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[(0,n.jsx)("select",{"aria-label":"Resource type",value:t.kind,onChange:e=>Y(t.id,{kind:e.target.value}),className:(0,u.cn)("h-8 w-full rounded-md border border-input bg-background px-2 text-xs focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring","sm:w-56"),children:b.map(e=>(0,n.jsx)("option",{value:e.v,children:e.label},e.v))}),(0,n.jsx)("span",{className:(0,u.cn)("rounded px-2 py-0.5 text-[11px] font-semibold","invalid"===s.outcome&&"bg-muted text-muted-foreground","invalid"!==s.outcome&&i&&"bg-primary/15 text-primary","invalid"!==s.outcome&&!i&&("unrestricted"!==s.outcome&&r.allowIsGood?"bg-destructive/15 text-destructive":"bg-amber-500/20 text-amber-700 dark:text-amber-500")),children:l}),s.used&&(0,n.jsxs)("code",{className:"font-mono text-[11px] text-muted-foreground",children:["via ",s.used,s.used!==s.askedFor?` (for ${s.askedFor})`:""]}),(0,n.jsx)(o.$,{type:"button",size:"sm",variant:"ghost",className:"ml-auto h-7 px-2 text-xs",onClick:()=>{var e;return e=t.id,void(x(t=>t.filter(t=>t.id!==e)),C(!1),I(t=>t===e?null:t))},"aria-label":"Remove this row",children:"Remove"})]}),!r.inline&&(0,n.jsx)(a.p,{"aria-label":"Resource URL",value:t.url,onChange:e=>Y(t.id,{url:e.target.value}),placeholder:"https://cdn.example.com/app.js",className:"h-8 font-mono text-xs"}),r.noncable&&(0,n.jsx)(ee,{on:t.nonced,onLabel:"Carries your nonce",offLabel:"No nonce on the tag",onClick:()=>Y(t.id,{nonced:!t.nonced})}),(0,n.jsx)("p",{className:"text-xs text-muted-foreground",children:s.reason}),(0,n.jsxs)("div",{className:"flex flex-wrap gap-2",children:[s.consoleText&&(0,n.jsx)(o.$,{type:"button",size:"sm",variant:"outline",className:"h-7 px-2 text-xs",onClick:()=>I(e=>e===t.id?null:t.id),children:O===t.id?"Hide the console line":"Show the console line"}),s.fix&&void 0!==s.fix.replace&&(0,n.jsx)(o.$,{type:"button",size:"sm",variant:"outline",className:"h-7 px-2 text-xs",onClick:()=>B(s.fix),children:s.fix.label}),s.fix&&void 0===s.fix.replace&&s.fix.add&&(0,n.jsx)(o.$,{type:"button",size:"sm",variant:"outline",className:"h-7 px-2 text-xs",onClick:()=>B(s.fix),children:s.fix.label}),s.fix&&!s.fix.add&&void 0===s.fix.replace&&r.noncable&&(0,n.jsx)(o.$,{type:"button",size:"sm",variant:"outline",className:"h-7 px-2 text-xs",onClick:()=>Y(t.id,{nonced:!0}),children:s.fix.label})]}),O===t.id&&s.consoleText&&(0,n.jsx)("pre",{className:"overflow-x-auto whitespace-pre-wrap rounded border bg-background p-2 font-mono text-[11px] leading-relaxed text-destructive",children:s.consoleText})]},t.id)}),0===q.length&&(0,n.jsx)("li",{className:"rounded-md border bg-muted/30 p-3 text-xs text-muted-foreground",children:"Nothing listed yet. Paste a few URLs above, or press one of the shortcuts."})]})]}),(0,n.jsxs)("div",{className:"space-y-2 rounded-lg border p-4",children:[(0,n.jsx)("div",{className:"text-sm font-medium",children:"What this policy is not doing"}),(0,n.jsx)("p",{className:"text-xs text-muted-foreground",children:"Directives that parse cleanly, deploy cleanly, and protect nothing."}),(0,n.jsx)("ul",{className:"space-y-2",children:U.map((e,t)=>(0,n.jsxs)("li",{className:"flex items-start gap-2 text-sm",children:[(0,n.jsx)("span",{"aria-hidden":!0,className:(0,u.cn)("mt-0.5 font-semibold","ok"===e.level&&"text-primary","note"===e.level&&"text-sky-600 dark:text-sky-400","warn"===e.level&&"text-amber-600 dark:text-amber-500","error"===e.level&&"text-destructive"),children:et[e.level]}),(0,n.jsxs)("div",{className:"flex-1 space-y-1.5",children:[(0,n.jsx)("p",{className:(0,u.cn)("error"===e.level||"warn"===e.level?"text-foreground":"text-muted-foreground"),children:e.text}),e.fix&&(0,n.jsx)(o.$,{type:"button",size:"sm",variant:"outline",onClick:()=>B(e.fix),children:e.fix.label})]})]},t))})]})]})]}),(0,n.jsxs)("div",{className:"space-y-2",children:[(0,n.jsxs)("div",{className:"flex flex-wrap items-center justify-between gap-2",children:[(0,n.jsxs)(i.J,{htmlFor:"csp-out",children:["Header for ",G.label]}),(0,n.jsx)("span",{className:"font-mono text-xs text-muted-foreground",children:G.file})]}),(0,n.jsx)("div",{className:"flex flex-wrap gap-1",role:"group","aria-label":"Deploy target",children:D.map(e=>(0,n.jsx)(o.$,{type:"button",size:"sm",variant:w===e.v?"default":"outline","aria-pressed":w===e.v,onClick:()=>k(e.v),children:e.label},e.v))}),(0,n.jsx)(l.T,{id:"csp-out",readOnly:!0,"aria-label":`Generated Content Security Policy for ${G.label}`,value:W,className:"min-h-[240px] font-mono text-xs"}),(0,n.jsxs)("div",{className:"flex flex-wrap gap-2",children:[(0,n.jsx)(h.i,{value:W,label:"Copy",copiedLabel:"Copied!",variant:"outline",size:"sm",disabled:!W}),(0,n.jsx)(o.$,{type:"button",variant:"outline",size:"sm",onClick:()=>
1{if(!W)return;let e=new Blob([W],{type:"text/plain"});(0,p.WN)(e,G.file),R(!0),setTimeout(()=>R(!1),1500)},disabled:!W,children:T?"Downloaded!":`Download ${G.file}`}),(0,n.jsx)(o.$,{type:"button",variant:"ghost",size:"sm",onClick:()=>{t({...X,values:{...X.values}}),x((V[0]?.resources??[]).map(([e,t,s])=>v(e,t,s??!1))),k("raw"),C(!1),I(null)},children:"Reset"})]})]}),ei&&(0,n.jsxs)("p",{className:"text-sm text-muted-foreground",children:["This policy and resource list come to over ",3500," characters, so the share link below cannot carry them and opens on the defaults instead. Copy the config above and send that."]}),(0,n.jsx)(c.ToolActions,{share:ea,title:"Content Security Policy Generator",embedCarriesPersonal:!1})]})}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.