CVE-2026-43660

Overview

Advisory: Apple Security Advisory

Impact:

Description: Processing maliciously crafted web content may prevent Content Security Policy from being enforced

Researchers: Cantina

Attribute Value
CVE CVE-2026-43660
Bugzilla 308906
Component WebCore
Bug Class CSP Bypass
Severity medium
Commit f8ed382fb244cc24…
Advisory Apple Advisory

Root Cause Analysis

CSP blob URL iframe was only inheriting the last CSP header when a document had multiple enforced CSP headers. ContentSecurityPolicyResponseHeaders::addPolicyHeadersTo used setHTTPHeaderField which overwrote instead of appended.

Attack Path

1. Step 1

Attacker crafts page with multiple CSP headers via meta tags or server config.

2. Step 2

Page creates a blob: URL iframe via JS.

3. Step 3

Only the last CSP policy is inherited by the blob iframe.

4. Step 4

Earlier restrictive policies (e.g. script-src none) are dropped.

5. Step 5

Attacker executes inline scripts inside blob iframe, bypassing CSP.

Files Changed

Source Files

  • Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.cpp

Test Files

  • LayoutTests/http/tests/security/contentSecurityPolicy/blob-url-inherits-multiple-csp-policies-expected.txt
  • LayoutTests/http/tests/security/contentSecurityPolicy/blob-url-inherits-multiple-csp-policies.html
  • LayoutTests/http/tests/security/contentSecurityPolicy/resources/create-blob-iframe.js
  • LayoutTests/http/tests/security/contentSecurityPolicy/resources/echo-multiple-csp-blob-iframe.py
  • LayoutTests/imported/w3c/web-platform-tests/trusted-types/inheriting-csp-for-local-schemes-expected.txt

Patch Preview

diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/blob-url-inherits-multiple-csp-policies-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/blob-url-inherits-multiple-csp-policies-expected.txt
new file mode 100644
index 000000000000..228b9da58211
--- /dev/null
+++ b/LayoutTests/http/tests/security/contentSecurityPolicy/blob-url-inherits-multiple-csp-policies-expected.txt
@@ -0,0 +1,14 @@
+CONSOLE MESSAGE: Refused to execute a script because its hash, its nonce, or 'unsafe-inline' does not appear in the script-src directive of the Content Security Policy.
+Tests that a blob: URL iframe inherits ALL CSP policies from its creator document, not just the last one. When the creator has two enforced CSP headers where one blocks inline scripts, inline scripts inside the blob document should be blocked.
+
+
+
+--------
+Frame: '<!--frame1-->'
+--------
+
+
+--------
+Frame: '<!--frame2-->'
+--------
+PASS: Inline script was blocked by CSP.
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/blob-url-inherits-multiple-csp-policies.html b/LayoutTests/http/tests/security/contentSecurityPolicy/blob-url-inherits-multiple-csp-policies.html
new file mode 100644
index 000000000000..3b21b13e864c
--- /dev/null
+++ b/LayoutTests/http/tests/security/contentSecurityPolicy/blob-url-inherits-multiple-csp-policies.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.dumpChildFramesAsText();
+    testRunner.waitUntilDone();
+}
+setTimeout(function() {
+    if (window.testRunner)
+        testRunner.notifyDone();
+}, 1000);
+</script>
+</head>
+<body>
+<p>Tests that a blob: URL iframe inherits ALL CSP policies from its creator document,
+not just the last one. When the creator has two enforced CSP headers where one blocks
+inline scripts, inline scripts inside the blob document should be blocked.</p>
+<iframe src="http://127.0.0.1:8000/security/contentSecurityPolicy/resources/echo-multiple-csp-blob-iframe.py"></iframe>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/resources/create-blob-iframe.js b/LayoutTests/http/tests/security/contentSecurityPolicy/resources/create-blob-iframe.js
new file mode 100644
index 000000000000..3d28c62bbbdf
--- /dev/null
+++ b/LayoutTests/http/tests/security/contentSecurityPolicy/resources/create-blob-iframe.js
@@ -0,0 +1,12 @@
+var iframe = document.getElementById("blob-frame");
+var html = [
+    "<!DOCTYPE html>",
+    "<html><body>",
+    "<p id='result'>PASS: Inline script was blocked by CSP.</p>",
+    "<script>",
+    "document.getElementById('result').textContent = 'FAIL: Inline script executed (CSP policy was dropped).';",
+    "</" + "script>",
+    "</body></html>"
+].join("\n");
+var blob = new Blob([html], { type: "text/html" });
+iframe.src = URL.createObjectURL(blob);
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/resources/echo-multiple-csp-blob-iframe.py b/LayoutTests/http/tests/security/contentSecurityPolicy/resources/echo-multiple-csp-blob-iframe.py
new file mode 100755
index 000000000000..923aa9170663
--- /dev/null
+++ b/LayoutTests/http/tests/security/contentSecurityPolicy/resources/echo-multiple-csp-blob-iframe.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+
+import sys
+
+sys.stdout.write(
+    'Content-Type: text/html; charset=UTF-8\r\n'
+    "Content-Security-Policy: script-src 'self'; frame-src blob:; default-src 'self'\r\n"
+    "Content-Security-Policy: script-src 'self' 'unsafe-inline'; frame-src blob:; default-src 'self'\r\n"
+    '\r\n'
+    '<!DOCTYPE html>\n'
+    '<html>\n'
+    '<body>\n'
+    '<iframe id="blob-frame"></iframe>\n'
+    '<script src="/security/contentSecurityPolicy/resources/create-blob-iframe.js"></script>\n'
+    '</body>\n'
+    '</html>\n'
+)
diff --git a/LayoutTests/imported/w3c/web-platform-tests/trusted-types/inheriting-csp-for-local-schemes-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/trusted-types/inheriting-csp-for-local-schemes-expected.txt
index 47396d14dd48..1a48f1b35af2 100644
--- a/LayoutTests/imported/w3c/web-platform-tests/trusted-types/inheriting-csp-for-local-schemes-expected.txt
+++ b/LayoutTests/imported/w3c/web-platform-tests/trusted-types/inheriting-csp-for-local-schemes-expected.txt
@@ -4,7 +4,7 @@ PASS trusted-types directive should be inherited in local srcdoc frames
 PASS require-trusted-types-for directive should be inherited in local data frames
 PASS trusted-types directive should be inherited in local data frames
 PASS require-trusted-types-for directive should be inherited in local blob frames
-FAIL trusted-types directive should be inherited in local blob frames assert_not_equals: got disallowed value null
+PASS trusted-types directive should be inherited in local blob frames
 PASS require-trusted-types-for directive should be inherited in local about:blank frames