You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
checkCrossOriginPolicies in src/rules.ts scores by header presence, not by header value. A site can explicitly opt out of cross-origin isolation by setting Cross-Origin-Opener-Policy: unsafe-none, Cross-Origin-Embedder-Policy: unsafe-none, and Cross-Origin-Resource-Policy: cross-origin — the three least restrictive values, two of which are the browser defaults — and still receive the maximum score (5/5, status good) from this analyzer with zero findings or recommendations.
This is the same class of correctness bug that issue #4 fixed for Permissions-Policy: presence ≠ protection. A header analyzer cannot vouch for cross-origin isolation by checking only that the bytes exist on the wire.
Investigation Path
Read every source file: src/rules.ts, src/analyzer.ts, src/fetch.ts, src/cli.ts, src/index.ts, src/types.ts.
Traced checkCrossOriginPolicies (src/rules.ts lines 145–164) line-by-line and confirmed the scoring only checks Boolean(value) — no string comparison against the value at all.
Hand-evaluated the function against the three least-restrictive values (COOP: unsafe-none, COEP: unsafe-none, CORP: cross-origin):
The test asserts score === 2 based on presence; it would pass identically if the value were 'unsafe-none'.
Cross-referenced authoritative documentation for each header's value semantics (MDN, web.dev). Confirmed unsafe-none (COOP/COEP) and cross-origin (CORP) are the opt-out / least restrictive values.
— i.e. for a site that has explicitly disabled cross-origin isolation on every dimension.
Expected behavior: Award credit only for protective values:
Cross-Origin-Opener-Policy: same-origin or same-origin-allow-popups → credit; unsafe-none → no credit + finding.
Cross-Origin-Embedder-Policy: require-corp or credentialless → credit; unsafe-none → no credit + finding.
Cross-Origin-Resource-Policy: same-origin or same-site → credit; cross-origin → no credit + finding (with note that this may be intentional for public CDN assets).
MDN — Cross-Origin-Opener-Policy: "unsafe-none … This is the default value. Allows the document to be added to its opener's browsing context group unless the opener itself has a COOP of same-origin or same-origin-allow-popups."
MDN — Cross-Origin-Embedder-Policy: "unsafe-none … This is the default value. Allows the document to fetch cross-origin resources without giving explicit permission …"
Every developer who relies on this tool's grade as a signal of cross-origin isolation receives a false positive. Two concrete failure modes:
Misleading "good" on a deliberately opted-out site. A team that explicitly sets COOP: unsafe-none to keep cross-origin window opener access for ad/SSO integrations gets a good 5/5 — the tool gives no indication that cross-origin isolation is disabled. Without that signal, the team has no prompt to revisit the trade-off, and SharedArrayBuffer, performance.measureUserAgentSpecificMemory(), high-precision performance.now(), and other isolation-gated APIs silently fail in production with no diagnostic path back to the headers.
Misleading "good" from a misconfigured middleware that emits the default. Several CDN edge frameworks (and a few helmet wrappers) emit Cross-Origin-Opener-Policy: unsafe-none as a placeholder during scaffold. The current analyzer cannot distinguish that scaffold from real protection — it just sees a header present and awards the points.
This is the symmetric counterpart of #4 (Permissions-Policy presence-vs-value). Fixing #4 without fixing this leaves a known correctness gap in the same scoring philosophy.
Severity is bounded by the relatively small maxScore: 5 weight in the overall grade, but the issue is about correctness of the per-header finding, not the overall percentage — the status: 'good' and empty findings/recommendations arrays are what mislead users, regardless of how much the points move the grade.
Suggested Remediation
Mirror the pattern issue #4 / commit 8d29a8c established for checkPermissionsPolicy: score per-header on protective values, accumulate, and emit specific findings for non-protective values.
exportfunctioncheckCrossOriginPolicies(headers: RawHeaders): HeaderFinding{constcoep=getHeader(headers,'cross-origin-embedder-policy');constcoop=getHeader(headers,'cross-origin-opener-policy');constcorp=getHeader(headers,'cross-origin-resource-policy');constnorm=(v: string|undefined)=>v?.toLowerCase().trim();constcoopOk=['same-origin','same-origin-allow-popups'].includes(norm(coop)??'');constcoepOk=['require-corp','credentialless'].includes(norm(coep)??'');constcorpOk=['same-origin','same-site'].includes(norm(corp)??'');letscore=0;if(coopOk)score+=2;if(coepOk)score+=2;if(corpOk)score+=1;score=Math.min(score,5);constfindings: string[]=[];constrecommendations: string[]=[];if(!coop){findings.push('Cross-Origin-Opener-Policy not set');recommendations.push("Add Cross-Origin-Opener-Policy: same-origin");}elseif(!coopOk){findings.push(`Cross-Origin-Opener-Policy: '${coop}' does not enable cross-origin isolation`);recommendations.push("Set Cross-Origin-Opener-Policy to same-origin (or same-origin-allow-popups)");}if(!coep){findings.push('Cross-Origin-Embedder-Policy not set');recommendations.push('Add Cross-Origin-Embedder-Policy: require-corp');}elseif(!coepOk){findings.push(`Cross-Origin-Embedder-Policy: '${coep}' does not enable cross-origin isolation`);recommendations.push('Set Cross-Origin-Embedder-Policy to require-corp (or credentialless)');}if(!corp){findings.push('Cross-Origin-Resource-Policy not set');recommendations.push('Add Cross-Origin-Resource-Policy: same-origin');}elseif(!corpOk){findings.push(`Cross-Origin-Resource-Policy: '${corp}' is permissive (any origin can embed this resource)`);recommendations.push("Set Cross-Origin-Resource-Policy to same-origin or same-site (use cross-origin only for intentionally-public assets)");}return{header: 'Cross-Origin Policies', score,maxScore: 5,status: score>=4 ? 'good' : score>0 ? 'warning' : 'missing',raw: [coep&&`COEP: ${coep}`,coop&&`COOP: ${coop}`,corp&&`CORP: ${corp}`].filter(Boolean).join('; ')||undefined,
findings, recommendations,};}
PR #2's existing cross-origin test cases use protective values (same-origin, require-corp) and continue to pass under this remediation — only tests that asserted credit for *non-*protective values would need updating. Those tests do not currently exist (PR #2 added presence-only assertions), so the change is forward-compatible.
Acceptance Criteria
checkCrossOriginPolicies({ 'cross-origin-opener-policy': 'unsafe-none', 'cross-origin-embedder-policy': 'unsafe-none', 'cross-origin-resource-policy': 'cross-origin' }) returns score: 0 (or whatever the team decides for permissive values) and includes a finding for each header explaining why it is not protective.
checkCrossOriginPolicies({ 'cross-origin-opener-policy': 'same-origin', 'cross-origin-embedder-policy': 'require-corp', 'cross-origin-resource-policy': 'same-origin' }) continues to return score: 5, status: 'good' with no findings.
Cross-Origin-Resource-Policy: same-site is recognized as protective (in addition to same-origin).
Cross-Origin-Embedder-Policy: credentialless is recognized as protective (in addition to require-corp).
Cross-Origin-Opener-Policy: same-origin-allow-popups is recognized as protective (in addition to same-origin).
New test cases in test/analyzer.test.ts cover: (a) all-unsafe-none/cross-origin opt-out path, (b) each individual permissive value, (c) the protective alternatives listed above.
Summary
checkCrossOriginPoliciesinsrc/rules.tsscores by header presence, not by header value. A site can explicitly opt out of cross-origin isolation by settingCross-Origin-Opener-Policy: unsafe-none,Cross-Origin-Embedder-Policy: unsafe-none, andCross-Origin-Resource-Policy: cross-origin— the three least restrictive values, two of which are the browser defaults — and still receive the maximum score (5/5, statusgood) from this analyzer with zero findings or recommendations.This is the same class of correctness bug that issue #4 fixed for
Permissions-Policy: presence ≠ protection. A header analyzer cannot vouch for cross-origin isolation by checking only that the bytes exist on the wire.Investigation Path
src/rules.ts,src/analyzer.ts,src/fetch.ts,src/cli.ts,src/index.ts,src/types.ts.checkCrossOriginPolicies(src/rules.tslines 145–164) line-by-line and confirmed the scoring only checksBoolean(value)— no string comparison against the value at all.COOP: unsafe-none,COEP: unsafe-none,CORP: cross-origin):count = 3→score = min(6, 5) = 5→status: 'good'→findings: []→recommendations: []score === 2based on presence; it would pass identically if the value were'unsafe-none'.unsafe-none(COOP/COEP) andcross-origin(CORP) are the opt-out / least restrictive values.8d29a8c) — exactly the same shape of fix applies here: keep the header check but gate the points on a protective value.Evidence
File:
src/rules.tslines 145–164, commit5083c52b64190381eadbb9f0c13b42e52c358a5aCurrent behavior: Awards 5/5 with
status: 'good'and no findings/recommendations for this header set:— i.e. for a site that has explicitly disabled cross-origin isolation on every dimension.
Expected behavior: Award credit only for protective values:
Cross-Origin-Opener-Policy:same-originorsame-origin-allow-popups→ credit;unsafe-none→ no credit + finding.Cross-Origin-Embedder-Policy:require-corporcredentialless→ credit;unsafe-none→ no credit + finding.Cross-Origin-Resource-Policy:same-originorsame-site→ credit;cross-origin→ no credit + finding (with note that this may be intentional for public CDN assets).Reproducer (works against the current code):
External references:
unsafe-none… This is the default value. Allows the document to be added to its opener's browsing context group unless the opener itself has a COOP ofsame-originorsame-origin-allow-popups."unsafe-none… This is the default value. Allows the document to fetch cross-origin resources without giving explicit permission …"cross-origin… Any website can include this resource. Use with caution …"COOP: same-originandCOEP: require-corp(orcredentialless);unsafe-noneon either disables it entirely.Impact
Every developer who relies on this tool's grade as a signal of cross-origin isolation receives a false positive. Two concrete failure modes:
Misleading "good" on a deliberately opted-out site. A team that explicitly sets
COOP: unsafe-noneto keep cross-origin window opener access for ad/SSO integrations gets agood5/5 — the tool gives no indication that cross-origin isolation is disabled. Without that signal, the team has no prompt to revisit the trade-off, andSharedArrayBuffer,performance.measureUserAgentSpecificMemory(), high-precisionperformance.now(), and other isolation-gated APIs silently fail in production with no diagnostic path back to the headers.Misleading "good" from a misconfigured middleware that emits the default. Several CDN edge frameworks (and a few helmet wrappers) emit
Cross-Origin-Opener-Policy: unsafe-noneas a placeholder during scaffold. The current analyzer cannot distinguish that scaffold from real protection — it just sees a header present and awards the points.This is the symmetric counterpart of #4 (Permissions-Policy presence-vs-value). Fixing #4 without fixing this leaves a known correctness gap in the same scoring philosophy.
Severity is bounded by the relatively small
maxScore: 5weight in the overall grade, but the issue is about correctness of the per-header finding, not the overall percentage — thestatus: 'good'and emptyfindings/recommendationsarrays are what mislead users, regardless of how much the points move the grade.Suggested Remediation
Mirror the pattern issue #4 / commit
8d29a8cestablished forcheckPermissionsPolicy: score per-header on protective values, accumulate, and emit specific findings for non-protective values.PR #2's existing cross-origin test cases use protective values (
same-origin,require-corp) and continue to pass under this remediation — only tests that asserted credit for *non-*protective values would need updating. Those tests do not currently exist (PR #2 added presence-only assertions), so the change is forward-compatible.Acceptance Criteria
checkCrossOriginPolicies({ 'cross-origin-opener-policy': 'unsafe-none', 'cross-origin-embedder-policy': 'unsafe-none', 'cross-origin-resource-policy': 'cross-origin' })returnsscore: 0(or whatever the team decides for permissive values) and includes a finding for each header explaining why it is not protective.checkCrossOriginPolicies({ 'cross-origin-opener-policy': 'same-origin', 'cross-origin-embedder-policy': 'require-corp', 'cross-origin-resource-policy': 'same-origin' })continues to returnscore: 5, status: 'good'with no findings.Cross-Origin-Resource-Policy: same-siteis recognized as protective (in addition tosame-origin).Cross-Origin-Embedder-Policy: credentiallessis recognized as protective (in addition torequire-corp).Cross-Origin-Opener-Policy: same-origin-allow-popupsis recognized as protective (in addition tosame-origin).test/analyzer.test.tscover: (a) all-unsafe-none/cross-originopt-out path, (b) each individual permissive value, (c) the protective alternatives listed above.Claude Code review routine · commit
5083c52b64190381eadbb9f0c13b42e52c358a5a· 2026-05-26T00:55:00Z