[2] CSP object-src empty-source-list bypass for no-URL plugin elements
Severity: Medium | Component: WebCore Content Security Policy enforcement | c6228ab
Rated Medium because the diff fixes a CSP object-src policy bypass where an empty source list was treated as permissive instead of deny-all for no-URL <object>/<embed> instantiations. The bug expands plugin attack surface against pages relying on the deployed policy but produces no direct memory primitive; exploitation requires an existing HTML injection foothold, and the impact is gated to CSPs that use the empty-source-list form rather than 'none'.
When an <object> or <embed> element has no data/src attribute, WebKit previously passed an empty URL into the CSP check with special-case logic that only blocked for the literal 'none' keyword. An empty source list (object-src;) was incorrectly allowed despite being equivalent to 'none' per CSP Level 3 §6.7.2.7. The fix removes the §6.1.9 special case entirely and substitutes the document's own URL as a fallback for source-list matching when the element has no associated URL — the document URL naturally fails to match empty source lists and 'none' (blocked), but matches 'self' or wildcards (allowed). Three new WPT tests assert that <object>/<embed> without a URL are blocked under both object-src 'none' and an empty source list.
Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp
-bool ContentSecurityPolicySourceListDirective::allows(const URL& url, bool didReceiveRedirectResponse, ShouldAllowEmptyURLIfSourceListIsNotNone shouldAllowEmptyURLIfSourceListEmpty)
+bool ContentSecurityPolicySourceListDirective::allows(const URL& url, bool didReceiveRedirectResponse)
{
if (url.isEmpty())
- return shouldAllowEmptyURLIfSourceListEmpty == ShouldAllowEmptyURLIfSourceListIsNotNone::Yes && !m_sourceList.isNone();
+ return false;
return m_sourceList.matches(url, didReceiveRedirectResponse);
}
Source/WebCore/page/csp/ContentSecurityPolicy.cpp
- if (m_policies.isEmpty() || LegacySchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
+ const auto& urlToCheck = url.isEmpty() ? m_protectedURL : url;
+ if (m_policies.isEmpty() || LegacySchemeRegistry::schemeShouldBypassContentSecurityPolicy(urlToCheck.protocol()))
return true;
- // ... 'MUST be blocked if object-src's value is 'none', but will otherwise be allowed' ...
String sourceURL;
- const auto& blockedURL = !preRedirectURL.isNull() ? preRedirectURL : url;
+ const auto& blockedURL = !preRedirectURL.isNull() ? preRedirectURL : urlToCheck;
...
- return allPoliciesAllow(handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForObjectSource, url, redirectResponseReceived == RedirectResponseReceived::Yes, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone::Yes);
+ return allPoliciesAllow(handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForObjectSource, urlToCheck, redirectResponseReceived == RedirectResponseReceived::Yes);
LayoutTests/imported/w3c/web-platform-tests/content-security-policy/object-src/object-src-no-url-empty-source-list-blocked.html
+<meta http-equiv="Content-Security-Policy" content="object-src; script-src 'self' 'unsafe-inline';">
+<object type="text/html"></object>
Patch Details
The patch removes the ShouldAllowEmptyURLIfSourceListIsNotNone parameter from ContentSecurityPolicySourceListDirective::allows, which now returns false unconditionally for empty URLs. In ContentSecurityPolicy::allowObjectFromSource, when the incoming url is empty (an <object>/<embed> with no data/src), the code substitutes m_protectedURL (the document's own URL) before invoking the policy match. The parameter is also removed from checkSource(), violatedDirectiveForObjectSource(), and the related allows() overload signature. Both checkFrameAncestors() overloads drop the now-removed third argument.
Conflation of two CSP states ('none' keyword vs. an empty source list) in the empty-URL fast path of source-list matching, causing an empty source list to behave permissively rather than as a deny-all.
Background
Content Security Policy is an HTTP/meta-delivered policy that lets a document restrict which sources may be loaded for various resource types. The object-src directive controls <object>/<embed> plugin loads. A source list is the right-hand side of a directive: it can hold tokens like 'self', 'none', scheme/host expressions, or be empty. The CSP Level 3 spec specifies that an empty source list matches no URL, behaving identically to 'none'.
WebKit models a source list with ContentSecurityPolicySourceList; isNone() returns true only for the literal 'none' token, while an empty list returns false. <object>/<embed> may instantiate a plugin with no associated URL — when the element has neither data nor src, only the type attribute selects the plugin — which historically prompted a special clause in early CSP drafts that allowed such no-URL plugins unless 'none' was specified.
Analysis
The pre-fix predicate inside allows() was shouldAllowEmptyURLIfSourceListEmpty == Yes && !m_sourceList.isNone(). The intent (cited in the now-removed comment) was the §6.1.9 special case: a plugin with no URL must be blocked only by 'none', otherwise allowed. But the predicate conflates two distinct CSP states: (a) the explicit 'none' keyword and (b) an empty source list (object-src;). Per CSP Level 3 §6.7.2.7 these are semantically equivalent — both match no URL — yet m_sourceList.isNone() is true only for the literal 'none' token. So a policy of object-src; produced Yes && !false = true, allowing a no-URL <object>/<embed> to load its default plugin even though the deployed policy was meant to forbid all object sources.
Aa a Aaaa Aaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaa Aa Aaaaaaaa Aaaa Aa Aaaa Aaaaaaaaa Aaaa Aaaaa Aaaaaa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaa Aaaaaaaaaaaa Aaaa Aa Aaaaaaaaaaaaa Aaaaaaaa Aaa Aaaaaaaaa Aaaa Aaaaaaaa Aaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaa Aaaaaa Aaaaaaaaaaaaaa Aaa Aaaaaa Aaaaaaaa Aa Aaa Aaaaaa Aaaaaaaaa Aaaaa Aaaa Aaa Aaaaaaa Aaa Aaaaaaaa Aaaaaaaa Aaaaaaa
Aaa Aaaaa Aaaaaaaa a Aaaaaaaaaa Aaa Aaaaaaaaaa Aaa Aaa Aaaa Aaa Aaaaaaaa Aaa Aa Aaaaa a Aa Aaa Aaaaaaa Aaaaaaaa Aa Aaaa Aaa Aaaa Aaaaaaa Aaaaaa Aaaaaa Aaaaa Aaaaaaa Aaaaaaa Aaaaaa Aaaaaaa Aaa Aaaaaaaa Aaa Aaaaaaaaa Aaaaa Aaaaaaaa Aaa Aaaaa Aaaaa Aaaaa Aaaaaaaaaa Aaaaaaaaaaaaaaaaaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaa Aaa Aaa Aaaaaaaaaaaa Aaaaaaaaaaaaaaaa Aaaaaaaaa Aaa Aaaaaaaa Aaaa Aaaaaaa Aaaa Aaaaaaaaaaaaa Aa Aaaaaaaaaa Aa Aaaaaaaaaaa Aaaaaaa a Aaaa Aaaaaa Aaaaaa Aaa Aaaaaa Aaaaaaaaaaaaaa Aaaaaa Aaa Aaa Aaaa Aaaaaaaaa Aaa Aaaaaaaa Aaa Aaa Aaaaaa Aaaaaa Aaaaa Aa a Aaaa Aaaa Aaaaaaaa Aaaaaaa Aaaaaaaaaaaa Aa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaa Aaaa a Aaaaaaaa Aaaaaaaaa Aa Aaaaaaaaaaaaa Aaaaa Aaaaa Aaaaa Aaa Aaaaaaa Aa Aaaaaaaaaaa Aaa Aaaaaaa Aaaaaa Aaa Aaa Aaaaa Aaaaaaaaaaaa a Aaaaaa Aaa Aaaaaa Aaaaaaaa Aa Aaaaaaaaa Aa Aaaaaaaa Aaa Aaaaaaa Aaa Aa Aaaa Aaaaaaaaa Aaaaaaaa Aaaaa Aaaaaaa Aaaaaa Aaaaaa Aaaaaaa Aaaaaaa Aaa Aaaa Aaaaaaa Aaa Aaaaaaaa Aaa a Aaaaaaaaaaa a Aaaaaaaaaa Aaaaa Aaaaaaaa Aaaaaaaaaaaa Aa Aaaaaa Aaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaa
🔒The policy-enforcement implications of conflating two CSP states in a single matcher predicate, and the boundary this weakens when the policy is deployed as a deny-all.
Subscribe to read more
Audit directions
a Aaaaaaaaaaaaaaaaaaaaa Aaaaaaaa Aaaaaaaa Aaaaaa Aaaa Aaaaaaa Aaaaa Aaa Aaa Aaaaaaaaaa Aaaa Aa Aaaaaaa Aaaaaaa Aaaaaaaaa Aaaaa Aaaaa Aaaaaaaaaaa Aaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaa Aaaaaaaa Aaaaa Aa Aaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaa Aaaa Aaa Aaaaaaa Aaaaaaa Aa Aaaaaaaa Aaaaaaaa Aaaaa Aaa Aa Aaaaa Aaaaaa Aaaaa Aaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaa Aaa Aaaaaaaaaa Aaaaaaaa
a Aaaaaaaaaaaa Aa Aaa Aaaaa Aaaaa Aa Aaa Aa Aaaaaaaaaaaaa Aaaa Aaaaaaaa Aaaaaa a Aa Aaaa Aaaa Aaaaaa a Aa Aaaaaaaaaaaaaaa Aaaaaa Aaa Aaa Aaaaa Aaaaaa Aaaa Aaa Aaaaaaa Aaaaaaaaaaaaa Aaaaa Aaaaa Aaa Aaaaaaaa Aaaaaaaa Aa Aa Aaaaaaaaa Aaaaa Aaaaaaa Aaaaa Aaaaaa Aaaaaaaa Aaaaaaaaaaaa Aaaaaaaa Aaaaa Aaaaaa Aaaaaaaa Aaaaaaa Aaa Aaaaa Aaaaa Aaaaaaa Aaa Aaaaaaa Aaaa Aaaaaaa Aaa Aaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaa Aaaaaa Aaaaaaa Aaaaaaaaaa Aaaaaaaa Aaaaaaaa Aaaaaaaaaa
a Aaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaa Aaaaaaaaaaaaaaaaaa Aaaaa Aaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaa Aaaaaa Aaaa Aaaaaaaaaaa Aa Aaaaa Aaaaa Aa Aaaa Aaa Aaaaaaaaaa Aaaaaaaaa Aaa Aaaaaaaa a Aaaaaaaa Aaaaaaaa Aaaaa Aaa Aaaaaaaaaaaa Aaaaaaaa Aaa Aaaa Aa Aaaaa Aaa Aa Aaaaaaaa Aaaaaaaaaa Aaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
a Aaaaaaaaaa Aaaaaaa Aaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaa Aaaaaaa Aaaaaa Aaa Aaa Aaa Aaaaa Aaaaa Aaaa Aaaaaaaa Aaaaaa Aaa Aaaaa Aaaa Aaaaaaaaaaaa Aaaaaaaaaaaa Aaa Aaaaaaaaaaaaa Aaaaaaa Aaaaaaaaaa Aaaaa Aaaaaaaaa Aaa Aaaaaaaa Aaa Aaaaa Aaaaaa Aaaaa Aaaa Aaa Aaaaaaa Aaa Aa Aaaaaaa Aa Aaaaaaaaaaaa
a Aaaaaaaaa a Aaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaaa Aaaa Aaa Aaa Aaaaaaa Aaaaaaaa Aaaaaa Aaa Aaaaaaaa Aaaa Aaaaa Aaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaa Aaaaaaaa Aaaa Aaaaaaaaa Aaa Aaaaaaaa Aaaa Aaa Aaaaa Aaaaaaa Aaa Aaaaaaaaaaa Aaaa Aaaaaa Aaaa Aaaaaaa Aa Aaa Aaaaa Aaa Aaaaaa Aaaaaaaaa a Aaaaa Aaaaaa Aaaa Aaaaaaaa Aaa Aaaaaaaaaa Aaaaaa a Aa Aaaaaaaa Aaaaaaaaaaaa Aa Aaa Aaaa Aaaaa Aaa Aaaaa Aaaaa
🔒Multiple reusable audit patterns identified across CSP source-list matching and similar two-condition policy gates, with concrete starting points for variant discovery.
Subscribe to read more