150a3a12e5 CSP applies script-src to JSON module imports instead of connect-src
Triage note: Corrects which CSP directive governs JSON imports; a policy-enforcement correctness issue with bypass/over-block implications.
Contents
The bug at a glance
This is a Content Security Policy enforcement-correctness bug: JSON module imports were being checked against script-src instead of connect-src, the directive the spec assigns to them. The impact is policy misapplication in both directions (a policy that allows scripts but restricts connections would wrongly permit a JSON import, and a script-src-only policy could wrongly block one), a bypass/over-block issue rather than memory corruption, so medium severity is appropriate.
In CachedResourceLoader::allowedByContentSecurityPolicy, CachedResource::Type::JSON shared the Script case and was enforced with allowScriptFromSource. The fix gives JSON its own case that enforces allowConnectToSource, aligning WebKit with the spec that JSON module imports are governed by connect-src.
Root cause
allowedByContentSecurityPolicy maps a CachedResource::Type to the CSP directive that should gate the fetch. Before the patch, CachedResource::Type::JSON was grouped in the same switch case as Type::Script (and XSLStyleSheet), so a JSON module import was evaluated with contentSecurityPolicy->allowScriptFromSource(…). Per the HTML/Fetch and CSP integration, JSON module imports (import … with { type: ‘json’ }) are a connect-type fetch and must be controlled by the connect-src directive, not script-src.
Because the wrong directive was consulted, enforcement diverged from policy intent in both directions. A page whose policy permits scripts (script-src) but is restrictive about connections (connect-src) would have its JSON imports wrongly allowed, since only script-src was consulted, a bypass of the connect-src restriction the author expected to apply. Conversely, a policy that specifies script-src without an appropriate connect-src could wrongly block a JSON import that connect-src would have permitted, an over-block. Either way the observable security decision (allow vs report/block, and which directive’s report is emitted) was attributed to the wrong directive.
The patch adds a dedicated case CachedResource::Type::JSON: ahead of the Script/XSLStyleSheet case that calls contentSecurityPolicy->allowConnectToSource(url, document->currentParserSourcePosition(), redirectResponseReceived, preRedirectURL) and returns false on denial, then breaks. JSON is removed from the Script case so it no longer falls through to allowScriptFromSource. The two updated WPT expectation files confirm the corrected behavior: connect-src-json-import-allowed now PASSes (‘import should be allowed’ under a permissive connect-src) and connect-src-json-import-blocked now PASSes (‘connect-src-json-import-blocked’, the import is rejected under a restrictive connect-src), both of which previously FAILed because script-src was the directive actually applied.
Key code
JSON module imports now enforced via connect-src instead of script-src
switch (type) {
case CachedResource::Type::JSON:
if (!contentSecurityPolicy->allowConnectToSource(url, document->currentParserSourcePosition(), redirectResponseReceived, preRedirectURL))
return false;
break;
#if ENABLE(XSLT)
case CachedResource::Type::XSLStyleSheet:
#endif
case CachedResource::Type::Script:
if (!contentSecurityPolicy->allowScriptFromSource(url, document->currentParserSourcePosition(), redirectResponseReceived, preRedirectURL, options.integrity, options.nonce))
return false;
Patch walkthrough
Source/WebCore/loader/cache/CachedResourceLoader.cpp— In allowedByContentSecurityPolicy, removes CachedResource::Type::JSON from the shared Script/XSLStyleSheet case (which used allowScriptFromSource) and adds a dedicated JSON case that calls allowConnectToSource and returns false on denial. This routes JSON module imports through connect-src as the spec requires.LayoutTests/imported/w3c/web-platform-tests/content-security-policy/connect-src/connect-src-json-import-allowed.sub-expected.txt— Updated expectation flips FAIL to PASS (‘import should be allowed’): a JSON import under a permissive connect-src is now correctly allowed.LayoutTests/imported/w3c/web-platform-tests/content-security-policy/connect-src/connect-src-json-import-blocked.sub-expected.txt— Updated expectation flips FAIL to PASS (‘connect-src-json-import-blocked’): a JSON import under a restrictive connect-src is now correctly blocked.
Background
JSON module imports — ECMAScript JSON modules (import data from ‘./x.json’ with { type: ‘json’ }) fetch a JSON resource as a module. The CSP integration classifies this fetch as a connect-type request, so it is governed by connect-src rather than script-src, unlike a classic script import.
connect-src vs script-src — script-src controls where executable script may be loaded from; connect-src controls script-initiated network connections (fetch, XHR, WebSocket, EventSource, and by spec JSON module imports). Applying the wrong directive means an author’s policy is enforced against the wrong category of requests.
CachedResourceLoader::allowedByContentSecurityPolicy — The WebCore chokepoint that, per CachedResource::Type, selects and invokes the correct CSP allow* method (allowScriptFromSource, allowConnectToSource, allowObjectFromSource, etc.) before a subresource load proceeds. A misclassified type here silently applies the wrong policy directive.
CachedResource::Type::JSON — The resource-type tag for JSON module fetches. Its placement in the switch determines which CSP directive gates it; sharing the Script case caused script-src enforcement, the root of this bug.
WPT CSP expectation files — The connect-src-json-import-allowed/blocked web-platform-tests assert the spec-mandated connect-src behavior. Their FAIL->PASS flips are the ground-truth evidence that the directive selection was previously wrong and is now correct.
Vulnerability window
- Misclassification — CachedResource::Type::JSON is grouped with Type::Script in allowedByContentSecurityPolicy and enforced via allowScriptFromSource.
- Wrong-directive enforcement — JSON module imports are gated by script-src, so connect-src (the spec-correct directive) is never consulted for them.
- Divergence — Policies relying on connect-src to restrict JSON imports are bypassed, and script-src-only policies may over-block, with reports attributed to the wrong directive.
- Fix — A dedicated JSON case calls allowConnectToSource; JSON is removed from the Script case.
- Verification — WPT connect-src JSON import allowed/blocked expectations flip from FAIL to PASS.
Triggering
No new test code was added (only WPT expectation .txt files were updated). The behavior is exercised by the existing web-platform-tests: a document with a policy such as connect-src that omits a given origin performs import data from 'https://blocked.example/x.json' with { type: 'json' }; pre-patch the import was decided by script-src (wrong directive) so a permissive script-src let it through and a restrictive connect-src did not block it. Post-patch connect-src governs the import, matching the allowed/blocked WPT expectations.
Exploitation
- Policy-bypass scenario — A site sets a strict connect-src (to limit data exfiltration/loading endpoints) while allowing scripts; pre-patch an injected JSON module import to a connect-src-forbidden origin would load anyway because only script-src was checked, undermining the author’s data-connection policy.
- Over-block scenario — Conversely a site with script-src but a permissive/absent connect-src for JSON could see legitimate JSON imports blocked by script-src, a functional/availability impact rather than a security breach.
- No memory-safety impact — This is a policy-enforcement correctness bug; there is no memory corruption or code-execution primitive. Impact is confined to CSP decisions and violation reporting for JSON module imports.
Detection & hunting
For defenders and SOC / detection engineers:
- CSP violation reports on the wrong directive — Reports attributing JSON module import decisions to script-src (rather than connect-src) indicate the pre-patch behavior; a shift of such reports to connect-src confirms the fix.
- JSON imports loading under strict connect-src — Monitor whether import … with { type: ‘json’ } to origins excluded by connect-src still loads; success under an otherwise-blocking connect-src flags the bypass.
- WPT CSP regression suite — Track the connect-src-json-import-allowed/blocked WPT results as a canary for correct directive selection.
Audit directions
- Directive mapping for all module import types — Review allowedByContentSecurityPolicy for every CachedResource::Type (CSS module, WASM/other module types, XSLStyleSheet) to confirm each is gated by the spec-mandated directive and none share a case that applies the wrong one.
- Fetch destination vs CSP directive parity — Cross-check WebKit’s type-to-directive table against the Fetch request-destination and CSP integration spec, since JSON’s connect classification is easy to miss for module-loaded data types.
- Worker and import-attribute paths — Verify JSON/module imports initiated from workers, import maps, or with other import attributes also reach this corrected connect-src enforcement rather than a separate code path still using script-src.
- Report-only and enforcement parity — Ensure the directive change is reflected consistently in both enforced and report-only CSP, so violation reports name connect-src for JSON imports in all modes.