Low chrome Authorization Error 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMissing authorization in Browser
DescriptionMissing authorization in Browser
ComponentBrowser
Sub-componentWebUI Toolbar
Bug ClassAuthorization Error
Tracker553274076
Fix commit7fc5516d7af3 (chromium/src) +153/-19
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-06

Changed Functions

FunctionChangeNotes
WebUIAppMenuBrowserTest
chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc
modified
IN_PROC_BROWSER_TEST_F
chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc
modified

Files Changed

  • chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc
  • chrome/browser/ui/views/toolbar/webui_toolbar_web_view.cc
From 7fc5516d7af3c3f55a6d131295aeed4a22b5e340 Mon Sep 17 00:00:00 2001
From: Tianyang Xu <xtlsheep@google.com>
Date: Wed, 09 Sep 2026 14:02:28 -0700
Subject: [PATCH] [WebUI Toolbar] Block renderer-originated file drops and reset stale drag state

WebUI Toolbar previously cached dragged file paths in
PreHandleDragUpdate without verifying whether the drag originated from
an untrusted renderer (!did_originate_from_renderer). This allowed a
malicious page to initiate a drag operation that tricked the toolbar
into caching a sensitive local file path, which would then be navigated
to upon drop.

Additionally, cached file paths were not cleared when the drag exited
the toolbar or when the drag session ended, opening a two-stage attack
vector where an untrusted renderer drag could consume a previously
cached file path. Furthermore, dropping on the Home button did not
validate drag origin.

This CL resolves these issues by:
- Requiring !did_originate_from_renderer before caching dragged file paths or positions in PreHandleDragUpdate, treating ChromeOS conservatively as renderer-originated.
- Proactively clearing cached file path and position when a renderer-originated drag is detected in PreHandleDragUpdate or CanDragEnter.
- Implementing PreHandleDragExit() and HandleDragEnded() overrides to clear cached drag state whenever the drag exits the view or terminates.
- Adding origin validation in ConsumeDroppedUrl() as defense-in-depth, and resetting the origin state flag after consumption to prevent state leakage.
- Checking drag_originated_from_renderer in OnHomeButtonDropUrl() to block renderer drags while preserving the ability for users to set local files as homepage via external file manager drag-and-drop.

Fixed: 553274076
Change-Id: Ibec8e5d074a42533e1257ac2667fa4f49d7de7d4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8338491
Commit-Queue: Tianyang Xu <xtlsheep@google.com>
Reviewed-by: Paul Jensen <pauljensen@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1694855}
---
diff --git a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.cc b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.cc
index 30420f98..b477096 100644
--- a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.cc
+++ b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.cc
@@ -226,26 +226,31 @@
   // views::WebView:
   void PreHandleDragUpdate(const content::DropData& drop_data,
                            const gfx::PointF& client_pt) override {
-    if (!drop_data.filenames.empty()) {
+    bool did_originate_from_renderer = drop_data.did_originate_from_renderer;
+#if BUILDFLAG(IS_CHROMEOS)
+    // On ChromeOS, drag origin provenance cannot be reliably distinguished
+    // between OS-local and renderer sources (b/256022714). To ensure security
+    // by default, all drags are conservatively treated as renderer-originated.
+    did_originate_from_renderer = true;
+#endif
+    if (!did_originate_from_renderer && !drop_data.filenames.empty()) {
       cached_dragged_file_path_ = drop_data.filenames.front().path;
       cached_dragged_file_position_ = client_pt;
+    } else if (did_originate_from_renderer) {
+      ClearCachedDraggedFile();
     }
     webui_toolbar::WebUIToolbarDragState::GetOrCreateForWebContents(
         web_contents())
-        ->set_drag_originated_from_renderer(
-            drop_data.did_originate_from_renderer);
+        ->set_drag_originated_from_renderer(did_originate_from_renderer);
   }
 
+  void PreHandleDragExit() override { ClearCachedDraggedFile(); }
+
+  void HandleDragEnded() override { ClearCachedDraggedFile(); }
+
   bool CanDragEnter(content::WebContents* source,
                     const content::DropData& data,
                     blink::DragOperationsMask operations_allowed) override {
-    // Cache the drag origin on the WebContents. This is needed because the
-    // subsequent Mojo navigation calls (Navigate/NavigateText) do not receive
-    // did_originate_from_renderer information from the drop event directly.
-    webui_toolbar::WebUIToolbarDragState::GetOrCreateForWebContents(
-        web_contents())
-        ->set_drag_originated_from_renderer(data.did_originate_from_renderer);
-
     // TODO(xtlsheep): We ideally want to block `javascript:` text drags over
     // the general toolbar area (showing a forbidden cursor) to prevent
     // self-XSS, while still allowing them to be dropped specifically into the
@@ -327,12 +332,16 @@
         url = net::FilePathToFileURL(*cached_dragged_file_path_);
       }
     }
-    cached_dragged_file_path_.reset();
-    cached_dragged_file_position_.reset();
+    ClearCachedDraggedFile();
     return url;
   }
 
  private:
+  void ClearCachedDraggedFile() {
+    cached_dragged_file_path_.reset();
+    cached_dragged_file_position_.reset();
+  }
+
 #if BUILDFLAG(IS_MAC)
   std::unique_ptr<WebUIToolbarEventForwarder> forwarder_;
 #endif
diff --git a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc
index 9a564013..4ea5f9a 100644
--- a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc
+++ b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc
@@ -2499,8 +2499,6 @@
           .ExtractBool();
     });
   }
-
-
 };
 
 class WebUIAppMenuBrowserTest : public WebUIToolbarWebViewBrowserTest {
@@ -4022,7 +4020,16 @@
       committed_url.query().find("q=hello%20world") != std::string::npos);
 }
 
-IN_PROC_BROWSER_TEST_F(WebUIToolbarWebViewBrowserTest, DropFileOnToolbar) {
+// On ChromeOS, drag origin provenance cannot be distinguished between OS-local
+// and renderer sources (b/256022714), so all drags are conservatively treated
+// as renderer-originated and local file navigation via drag is blocked.
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_DropFileOnToolbar DISABLED_DropFileOnToolbar
+#else
+#define MAYBE_DropFileOnToolbar DropFileOnToolbar
+#endif
+IN_PROC_BROWSER_TEST_F(WebUIToolbarWebViewBrowserTest,
+                       MAYBE_DropFileOnToolbar) {
   ui::TrackedElement* element = nullptr;
   WebUIToolbarWebView* webui_toolbar_view = nullptr;
   views::WebView* web_view = nullptr;
@@ -4072,6 +4079,109 @@
                           ->GetLastCommittedURL());
 }
 
+IN_PROC_BROWSER_TEST_F(WebUIToolbarWebViewBrowserTest,
+                       DropFileOnToolbar_BlockedRendererOriginated) {
+  ui::TrackedElement* element = nullptr;
+  WebUIToolbarWebView* webui_toolbar_view = nullptr;
+  views::WebView* web_view = nullptr;
+  ASSERT_NO_FATAL_FAILURE(SetUpWebUI(kWebUIToolbarElementIdentifier, &element,
+                                     &webui_toolbar_view, &web_view,
+                                     browser()));
+
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath file_path = temp_dir.GetPath().AppendASCII("secret.html");
+  ASSERT_TRUE(base::WriteFile(file_path, "<html><body>secret</body></html>"));
+
+  gfx::Point click_point(10, 10);
+
+  content::DropData drop_data;
+  drop_data.did_originate_from_renderer = true;
+  drop_data.filenames.emplace_back(file_path, base::FilePath());
+
+  web_view->GetWebContents()->GetDelegate()->PreHandleDragUpdate(
+      drop_data, gfx::PointF(click_point));
+
+  content::WebContents* active_contents =
+      browser()->GetTabStripModel()->GetActiveWebContents();
+  GURL initial_url = active_contents->GetLastCommittedURL();
+  NavigationCounter counter(active_contents);
+
+  EXPECT_TRUE(
+      content::ExecJs(web_view->GetWebContents(),
+                      base::StringPrintf(R"(
+    const toolbarApp = document.querySelector('toolbar-app');
+    const dataTransfer = new DataTransfer();
+    Object.defineProperty(dataTransfer, 'types', {value: ['Files']});
+    const dropEvent = new DragEvent('drop', {
+      bubbles: true,
+      cancelable: true,
+      dataTransfer: dataTransfer,
+      clientX: %d,
+      clientY: %d
+    });
+    toolbarApp.dispatchEvent(dropEvent);
+  )",
+                                         click_point.x(), click_point.y())));
+
+  counter.WaitForNoNavigations();
+  EXPECT_EQ(initial_url, active_contents->GetLastCommittedURL());
+}
+
+IN_PROC_BROWSER_TEST_F(WebUIToolbarWebViewBrowserTest,
+                       DropFileOnToolbar_BlockedAfterDragExit) {
+  ui::TrackedElement* element = nullptr;
+  WebUIToolbarWebView* webui_toolbar_view = nullptr;
+  views::WebView* web_view = nullptr;
+  ASSERT_NO_FATAL_FAILURE(SetUpWebUI(kWebUIToolbarElementIdentifier, &element,
+                                     &webui_toolbar_view, &web_view,
+                                     browser()));
+
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath file_path = temp_dir.GetPath().AppendASCII("test.html");
+  ASSERT_TRUE(base::WriteFile(file_path, "<html><body>test</body></html>"));
+
+  gfx::Point click_point(10, 10);
+
+  content::DropData drop_data;
+  drop_data.did_originate_from_renderer = false;
+  drop_data.filenames.emplace_back(file_path, base::FilePath());
+
+  auto* delegate = web_view->GetWebContents()->GetDelegate();
+  delegate->PreHandleDragUpdate(drop_data, gfx::PointF(click_point));
+
+  // Drag exits the toolbar area before dropping.
+  delegate->PreHandleDragExit();
+
+  content::WebContents* active_contents =
+      browser()->GetTabStripModel()->GetActiveWebContents();
+  GURL initial_url = active_contents->GetLastCommittedURL();
+  NavigationCounter counter(active_contents);
+
+  EXPECT_TRUE(
+      content::ExecJs(web_view->GetWebContents(),
+                      base::StringPrintf(R"(
+    const toolbarApp = document.querySelector('toolbar-app');
+    const dataTransfer = new DataTransfer();
+    Object.defineProperty(dataTransfer, 'types', {value: ['Files']});
+    const dropEvent = new DragEvent('drop', {
+      bubbles: true,
+      cancelable: true,
+      dataTransfer: dataTransfer,
+      clientX: %d,
+      clientY: %d
+    });
+    toolbarApp.dispatchEvent(dropEvent);
+  )",
+                                         click_point.x(), click_point.y())));
+
+  counter.WaitForNoNavigations();
+  EXPECT_EQ(initial_url, active_contents->GetLastCommittedURL());
+}
+
 IN_PROC_BROWSER_TEST_F(WebUIToolbarWebViewBrowserTest, LoadExtension) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -4667,7 +4777,16 @@
             GetOmniboxTextEventually(web_contents));
 }
 
-IN_PROC_BROWSER_TEST_F(WebUIReadOnlyOmniboxDragDropBrowserTest, DropFilePath) {
+// On ChromeOS, drag origin provenance cannot be distinguished between OS-local
+// and renderer sources (b/256022714), so all drags are conservatively treated
+// as renderer-originated and local file drops on Omnibox are blocked.
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_DropFilePath DISABLED_DropFilePath
+#else
+#define MAYBE_DropFilePath DropFilePath
+#endif
+IN_PROC_BROWSER_TEST_F(WebUIReadOnlyOmniboxDragDropBrowserTest,
+                       MAYBE_DropFilePath) {
   ui::TrackedElement* element = nullptr;
   WebUIToolbarWebView* webui_toolbar_view = nullptr;
   views::WebView* web_view = nullptr;
@@ -4998,10 +5117,16 @@
   EXPECT_TRUE(prefs->GetBoolean(prefs::kHomePageIsNewTabPage));
 }
 
-// Verify that dropping a file on the home button sets it as the home page,
-// and the action can be undone.
+// On ChromeOS, drag origin provenance cannot be distinguished between OS-local
+// and renderer sources (b/256022714), so all drags are conservatively treated
+// as renderer-originated and home button file drop is blocked.
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_DropFileOnHomeButtonAndUndo DISABLED_DropFileOnHomeButtonAndUndo
+#else
+#define MAYBE_DropFileOnHomeButtonAndUndo DropFileOnHomeButtonAndUndo
+#endif
 IN_PROC_BROWSER_TEST_F(WebUIToolbarWebViewHomeButtonBrowserTest,
-                       DropFileOnHomeButtonAndUndo) {
+                       MAYBE_DropFileOnHomeButtonAndUndo) {
   WebUIToolbarWebView* webui_toolbar_view = SetUpAndPinHomeButton(browser());
   content::WebContents* web_contents =
       webui_toolbar_view->GetWebViewForTesting()->GetWebContents();
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc
index 9a564013..4ea5f9a 100644
--- a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc
+++ b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc
@@ -2499,8 +2499,6 @@
           .ExtractBool();
     });
   }
-
-
 };
 
 class WebUIAppMenuBrowserTest : public WebUIToolbarWebViewBrowserTest {
@@ -4022,7 +4020,16 @@
       committed_url.query().find("q=hello%20world") != std::string::npos);
 }
 
-IN_PROC_BROWSER_TEST_F(WebUIToolbarWebViewBrowserTest, DropFileOnToolbar) {
+// On ChromeOS, drag origin provenance cannot be distinguished between OS-local
+// and renderer sources (b/256022714), so all drags are conservatively treated
+// as renderer-originated and local file navigation via drag is blocked.
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_DropFileOnToolbar DISABLED_DropFileOnToolbar
+#else
+#define MAYBE_DropFileOnToolbar DropFileOnToolbar
+#endif
+IN_PROC_BROWSER_TEST_F(WebUIToolbarWebViewBrowserTest,
+                       MAYBE_DropFileOnToolbar) {
   ui::TrackedElement* element = nullptr;
   WebUIToolbarWebView* webui_toolbar_view = nullptr;
   views::WebView* web_view = nullptr;
@@ -4072,6 +4079,109 @@
                           ->GetLastCommittedURL());
 }
 
+IN_PROC_BROWSER_TEST_F(WebUIToolbarWebViewBrowserTest,
+                       DropFileOnToolbar_BlockedRendererOriginated) {
+  ui::TrackedElement* element = nullptr;
+  WebUIToolbarWebView* webui_toolbar_view = nullptr;
+  views::WebView* web_view = nullptr;
+  ASSERT_NO_FATAL_FAILURE(SetUpWebUI(kWebUIToolbarElementIdentifier, &element,
+                                     &webui_toolbar_view, &web_view,
+                                     browser()));
+
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath file_path = temp_dir.GetPath().AppendASCII("secret.html");
+  ASSERT_TRUE(base::WriteFile(file_path, "<html><body>secret</body></html>"));
+
+  gfx::Point click_point(10, 10);
+
+  content::DropData drop_data;
+  drop_data.did_originate_from_renderer = true;
+  drop_data.filenames.emplace_back(file_path, base::FilePath());
+
+  web_view->GetWebContents()->GetDelegate()->PreHandleDragUpdate(
+      drop_data, gfx::PointF(click_point));
+
+  content::WebContents* active_contents =
+      browser()->GetTabStripModel()->GetActiveWebContents();
+  GURL initial_url = active_contents->GetLastCommittedURL();
+  NavigationCounter counter(active_contents);
+
+  EXPECT_TRUE(
+      content::ExecJs(web_view->GetWebContents(),
+                      base::StringPrintf(R"(
+    const toolbarApp = document.querySelector('toolbar-app');
+    const dataTransfer = new DataTransfer();
+    Object.defineProperty(dataTransfer, 'types', {value: ['Files']});
+    const dropEvent = new DragEvent('drop', {
+      bubbles: true,
+      cancelable: true,
+      dataTransfer: dataTransfer,
+      clientX: %d,
+      clientY: %d
+    });
+    toolbarApp.dispatchEvent(dropEvent);
+  )",
+                                         click_point.x(), click_point.y())));
+
+  counter.WaitForNoNavigations();
+  EXPECT_EQ(initial_url, active_contents->GetLastCommittedURL());
+}
+
+IN_PROC_BROWSER_TEST_F(WebUIToolbarWebViewBrowserTest,
+                       DropFileOnToolbar_BlockedAfterDragExit) {
+  ui::TrackedElement* element = nullptr;
+  WebUIToolbarWebView* webui_toolbar_view = nullptr;
+  views::WebView* web_view = nullptr;
+  ASSERT_NO_FATAL_FAILURE(SetUpWebUI(kWebUIToolbarElementIdentifier, &element,
+                                     &webui_toolbar_view, &web_view,
+                                     browser()));
+
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath file_path = temp_dir.GetPath().AppendASCII("test.html");
+  ASSERT_TRUE(base::WriteFile(file_path, "<html><body>test</body></html>"));
+
+  gfx::Point click_point(10, 10);
+
+  content::DropData drop_data;
+  drop_data.did_originate_from_renderer = false;
+  drop_data.filenames.emplace_back(file_path, base::FilePath());
+
+  auto* delegate = web_view->GetWebContents()->GetDelegate();
+  delegate->PreHandleDragUpdate(drop_data, gfx::PointF(click_point));
+
+  // Drag exits the toolbar area before dropping.
+  delegate->PreHandleDragExit();
+
+  content::WebContents* active_contents =
+      browser()->GetTabStripModel()->GetActiveWebContents();
+  GURL initial_url = active_contents->GetLastCommittedURL();
+  NavigationCounter counter(active_contents);
+
+  EXPECT_TRUE(
+      content::ExecJs(web_view->GetWebContents(),
+                      base::StringPrintf(R"(
+    const toolbarApp = document.querySelector('toolbar-app');
+    const dataTransfer = new DataTransfer();
+    Object.defineProperty(dataTransfer, 'types', {value: ['Files']});
+    const dropEvent = new DragEvent('drop', {
+      bubbles: true,
+      cancelable: true,
+      dataTransfer: dataTransfer,
+      clientX: %d,
+      clientY: %d
+    });
+    toolbarApp.dispatchEvent(dropEvent);
+  )",
+                                         click_point.x(), click_point.y())));
+
+  counter.WaitForNoNavigations();
+  EXPECT_EQ(initial_url, active_contents->GetLastCommittedURL());
+}
+
 IN_PROC_BROWSER_TEST_F(WebUIToolbarWebViewBrowserTest, LoadExtension) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -4667,7 +4777,16 @@
             GetOmniboxTextEventually(web_contents));
 }
 
-IN_PROC_BROWSER_TEST_F(WebUIReadOnlyOmniboxDragDropBrowserTest, DropFilePath) {
+// On ChromeOS, drag origin provenance cannot be distinguished between OS-local
+// and renderer sources (b/256022714), so all drags are conservatively treated
+// as renderer-originated and local file drops on Omnibox are blocked.
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_DropFilePath DISABLED_DropFilePath
+#else
+#define MAYBE_DropFilePath DropFilePath
+#endif
+IN_PROC_BROWSER_TEST_F(WebUIReadOnlyOmniboxDragDropBrowserTest,
+                       MAYBE_DropFilePath) {
   ui::TrackedElement* element = nullptr;
   WebUIToolbarWebView* webui_toolbar_view = nullptr;
   views::WebView* web_view = nullptr;
@@ -4998,10 +5117,16 @@
   EXPECT_TRUE(prefs->GetBoolean(prefs::kHomePageIsNewTabPage));
 }
 
-// Verify that dropping a file on the home button sets it as the home page,
-// and the action can be undone.
+// On ChromeOS, drag origin provenance cannot be distinguished between OS-local
+// and renderer sources (b/256022714), so all drags are conservatively treated
+// as renderer-originated and home button file drop is blocked.
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_DropFileOnHomeButtonAndUndo DISABLED_DropFileOnHomeButtonAndUndo
+#else
+#define MAYBE_DropFileOnHomeButtonAndUndo DropFileOnHomeButtonAndUndo
+#endif
 IN_PROC_BROWSER_TEST_F(WebUIToolbarWebViewHomeButtonBrowserTest,
-                       DropFileOnHomeButtonAndUndo) {
+                       MAYBE_DropFileOnHomeButtonAndUndo) {
   WebUIToolbarWebView* webui_toolbar_view = SetUpAndPinHomeButton(browser());
   content::WebContents* web_contents =
       webui_toolbar_view->GetWebViewForTesting()->GetWebContents();
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.