Chrome · DevTools › DevTools
CVE-2026-113125
Memory Corruption in DevTools (DevTools)
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Files Changed
content/browser/devtools/protocol/page_handler.cccontent/browser/devtools/protocol/devtools_protocol_browsertest.cc
Patch
From 9ae39759da3c0ee89b53ef45989ffbf52488c48a Mon Sep 17 00:00:00 2001
From: Alex Rudenko <alexrudenko@chromium.org>
Date: Wed, 23 Sep 2026 07:56:43 -0700
Subject: [PATCH] [DevTools] Redact inaccessible URLs in Page.frameStartedNavigating
A client that is not allowed to navigate the page to a URL must not
learn it via Page.frameStartedNavigating either. Check
CheckNavigationAllowed() in both PageHandler::Enable() and
PageHandler::DidStartNavigating() and redact the URL to an empty string
when disallowed, while still emitting the event so client navigation
bookkeeping continues to work.
Fixed: 563819290
Change-Id: I9a799b866c71d116006a28cf930a6da7eaddb76e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8439039
Auto-Submit: Alex Rudenko <alexrudenko@chromium.org>
Commit-Queue: Maksim Sadym <sadym@chromium.org>
Reviewed-by: Maksim Sadym <sadym@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1703435}
---
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index 078c16a..c750e9a 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -683,11 +683,17 @@
FrameTreeNode* frame_tree_node = host_->frame_tree_node();
NavigationRequest* navigation_request =
host_->frame_tree_node()->navigation_request();
+ // A client that is not allowed to navigate the page to a URL must not
+ // learn it either (see GetNavigationHistory()). Redact the URL rather
+ // than omitting the event, so that navigation bookkeeping in clients
+ // keeps working.
+ const GURL& pending_url = navigation_request->common_params().url;
+ const bool may_disclose = !CheckNavigationAllowed(pending_url).IsError();
frontend_->FrameStartedNavigating(
frame_tree_node->current_frame_host()
->devtools_frame_token()
.ToString(),
- navigation_request->common_params().url.spec(),
+ may_disclose ? pending_url.spec() : std::string(),
navigation_request->devtools_navigation_token().ToString(),
GetFrameStartedNavigatingNavigationTypeString(
navigation_request->common_params().navigation_type));
@@ -1188,9 +1194,16 @@
return;
}
+ // A client that is not allowed to navigate the page to a URL must not
+ // learn it either (see GetNavigationHistory()). Redact the URL rather than
+ // omitting the event, so that navigation bookkeeping in clients keeps
+ // working. This event fires at FrameTreeNode::TakeNavigationRequest() time,
+ // BEFORE NavigationRequest::OnStartChecksComplete() force-detaches sessions
+ // whose client fails MayAttachToURL(), so it must gate disclosure itself.
+ const bool may_disclose = host_ && !CheckNavigationAllowed(url).IsError();
frontend_->FrameStartedNavigating(
- ftn.current_frame_host()->devtools_frame_token().ToString(), url.spec(),
- loader_id.ToString(),
+ ftn.current_frame_host()->devtools_frame_token().ToString(),
+ may_disclose ? url.spec() : std::string(), loader_id.ToString(),
GetFrameStartedNavigatingNavigationTypeString(navigation_type));
}
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 384ce53..c717343 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -2116,6 +2116,59 @@
}
IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest,
+ FrameStartedNavigatingRedactsInaccessibleUrls) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ const GURL file_url = GetTestUrl("devtools", "navigation.html");
+ const GURL http_url = embedded_test_server()->GetURL("/title1.html");
+ const GURL allowed_url = embedded_test_server()->GetURL("/title2.html");
+ const GURL disallowed_url =
+ embedded_test_server()->GetURL("disallowed.test", "/title1.html");
+
+ NavigateToURLBlockUntilNavigationsComplete(shell(), http_url, 1);
+
+ set_agent_host_can_close();
+ SetMayReadLocalFiles(false);
+ SetNotAttachableHosts({"disallowed.test"});
+ Attach();
+ SendCommandSync("Page.enable");
+
+ // Accessible URLs are disclosed in Page.frameStartedNavigating.
+ NavigateToURLBlockUntilNavigationsComplete(shell(), allowed_url, 1);
+ base::DictValue notification =
+ WaitForNotification("Page.frameStartedNavigating", true);
+ EXPECT_EQ(allowed_url.spec(), *notification.FindString("url"));
+
+ // Navigating to a file:// URL without local file access still emits
+ // Page.frameStartedNavigating, but redacts the URL.
+ ClearNotifications();
+ NavigateToURLBlockUntilNavigationsComplete(shell(), file_url, 1);
+ notification = WaitForNotification("Page.frameStartedNavigating", true);
+ EXPECT_EQ("", *notification.FindString("url"));
+ EXPECT_FALSE(notification.FindString("loaderId")->empty());
+
+ // Calling Page.enable while a navigation to an inaccessible URL is already
+ // pending also redacts the URL in the catch-up FrameStartedNavigating event.
+ Detach();
+ NavigateToURLBlockUntilNavigationsComplete(shell(), http_url, 1);
+ TestNavigationManager pending_file_nav(shell()->web_contents(), file_url);
+ shell()->LoadURL(file_url);
+ ASSERT_TRUE(pending_file_nav.WaitForRequestStart());
+
+ Attach();
+ SendCommandAsync("Page.enable");
+ notification = WaitForNotification("Page.frameStartedNavigating", true);
+ EXPECT_EQ("", *notification.FindString("url"));
+ ASSERT_TRUE(pending_file_nav.WaitForNavigationFinished());
+
+ // Navigating to a host that fails MayAttachToURL() fires DidStartNavigating()
+ // before the session is force-detached; the URL must be redacted there too.
+ ClearNotifications();
+ shell()->LoadURL(disallowed_url);
+ notification = WaitForNotification("Page.frameStartedNavigating", true);
+ EXPECT_EQ("", *notification.FindString("url"));
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest,
NavigateToHistoryEntryWithFileUrlRequiresFileAccess) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL file_url = GetTestUrl("devtools", "navigation.html");
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 384ce53..c717343 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -2116,6 +2116,59 @@
}
IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest,
+ FrameStartedNavigatingRedactsInaccessibleUrls) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ const GURL file_url = GetTestUrl("devtools", "navigation.html");
+ const GURL http_url = embedded_test_server()->GetURL("/title1.html");
+ const GURL allowed_url = embedded_test_server()->GetURL("/title2.html");
+ const GURL disallowed_url =
+ embedded_test_server()->GetURL("disallowed.test", "/title1.html");
+
+ NavigateToURLBlockUntilNavigationsComplete(shell(), http_url, 1);
+
+ set_agent_host_can_close();
+ SetMayReadLocalFiles(false);
+ SetNotAttachableHosts({"disallowed.test"});
+ Attach();
+ SendCommandSync("Page.enable");
+
+ // Accessible URLs are disclosed in Page.frameStartedNavigating.
+ NavigateToURLBlockUntilNavigationsComplete(shell(), allowed_url, 1);
+ base::DictValue notification =
+ WaitForNotification("Page.frameStartedNavigating", true);
+ EXPECT_EQ(allowed_url.spec(), *notification.FindString("url"));
+
+ // Navigating to a file:// URL without local file access still emits
+ // Page.frameStartedNavigating, but redacts the URL.
+ ClearNotifications();
+ NavigateToURLBlockUntilNavigationsComplete(shell(), file_url, 1);
+ notification = WaitForNotification("Page.frameStartedNavigating", true);
+ EXPECT_EQ("", *notification.FindString("url"));
+ EXPECT_FALSE(notification.FindString("loaderId")->empty());
+
+ // Calling Page.enable while a navigation to an inaccessible URL is already
+ // pending also redacts the URL in the catch-up FrameStartedNavigating event.
+ Detach();
+ NavigateToURLBlockUntilNavigationsComplete(shell(), http_url, 1);
+ TestNavigationManager pending_file_nav(shell()->web_contents(), file_url);
+ shell()->LoadURL(file_url);
+ ASSERT_TRUE(pending_file_nav.WaitForRequestStart());
+
+ Attach();
+ SendCommandAsync("Page.enable");
+ notification = WaitForNotification("Page.frameStartedNavigating", true);
+ EXPECT_EQ("", *notification.FindString("url"));
+ ASSERT_TRUE(pending_file_nav.WaitForNavigationFinished());
+
+ // Navigating to a host that fails MayAttachToURL() fires DidStartNavigating()
+ // before the session is force-detached; the URL must be redacted there too.
+ ClearNotifications();
+ shell()->LoadURL(disallowed_url);
+ notification = WaitForNotification("Page.frameStartedNavigating", true);
+ EXPECT_EQ("", *notification.FindString("url"));
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest,
NavigateToHistoryEntryWithFileUrlRequiresFileAccess) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL file_url = GetTestUrl("devtools", "navigation.html");
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page