Low watch CVSS 5.3 chrome Info Disclosure 🔧 Commit mapped

Overview

Low
Severity
5.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInformation leak in DataTransfer
DescriptionInformation leak in DataTransfer
ComponentDataTransfer
Sub-componentContent
Bug ClassInfo Disclosure
Action tierwatch — information disclosure — security-relevant, below the RCE-class bar
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker514524620
Fix commit7c92200b7e5c (chromium/src) +73/-2
CWECWE-200 (Information exposure)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-05-19 (per vendor advisory)
Fix landed2026-08-24 (public git)
Disclosed2026-09-22
Patch gap29 days — fix public in source before the advisory

Background

`DropData`
the browser-side struct describing a drag payload, whose filenames field holds ui::FileInfo entries each pairing an absolute path with an optional display_name.
`DropData::Metadata`
a lightweight per-item descriptor sent to the renderer during drag-enter/drag-over that mirrors the drag contents without (ideally) exposing sensitive host details.
Drag-enter/drag-over
the pre-drop phase in which the browser forwards drag metadata to the renderer’s blink::mojom::FrameWidget (via DragTargetDragEnter) so the page can decide whether to accept the drop.
`base::FilePath::BaseName`
a method returning only the final path component (the file name) with all leading directory components stripped.

Root Cause Analysis

When a drag operation entered a renderer’s viewport, the browser built a DropData::Metadata list for each dragged file and, on Aura, populated it with the file’s full absolute path via DropData::Metadata::CreateForFilePath(file_info.path, file_info.display_name). This metadata was delivered to the renderer during DragTargetDragEnter before the user committed to a drop, so a compromised or malicious renderer could read the complete host filesystem path of every hovered file (for example /home/user/secret/file.txt) without any user consent to share it. The invariant violated is that the pre-drop phase should expose only what is necessary to negotiate the drop, not sensitive host paths that are only meant to be transferred at drop time.

The fix replaces file_info.path with file_info.path.BaseName() when constructing the drag-enter metadata, so only the bare file name reaches the renderer; the untruncated absolute paths are still preserved and sent when the real drop event fires.

Key insight
The core mistake was sending the full absolute file path in drag-enter/drag-over metadata that a renderer can observe before any drop is committed; the fix truncates that field to its BaseName so the directory structure never crosses the process boundary early.

Attack Path

  1. Compromise the renderer An attacker gains code execution in a sandboxed renderer process (e.g., via a separate bug) so it can observe the raw Mojo drag messages. (inferred)
  2. Induce a drag The user drags one or more local files over content controlled by the attacker’s page, triggering the browser to build drag metadata.
  3. Leak absolute paths on drag-enter The browser calls DragTargetDragEnter, and the unsanitized DropData::Metadata carrying each file_info.path (e.g. /absolute/path/to/file1.txt) is delivered to the renderer before the user drops.
  4. Harvest host filesystem information The compromised renderer reads the full paths from the received blink::mojom::DragData, disclosing usernames, directory layout, and file locations that aid further exploitation. (inferred)

Impact Assessment

An attacker with a compromised renderer gains an information leak of absolute host filesystem paths for dragged files, obtained in the renderer process during the pre-drop drag-enter/drag-over phase with no drop yet committed. The precondition is that the user hovers a file drag over attacker-influenced content while the renderer is already able to inspect the drag metadata. The advisory classifies this as an information leak in DataTransfer and does not state exploitation in the wild; any downstream use of the leaked paths to strengthen another exploit is (inferred).

Changed Functions

FunctionChangeNotes
for
content/browser/renderer_host/render_widget_host_impl.cc
modified
DragCaptureFrameWidget
content/browser/renderer_host/render_widget_host_unittest.cc
modified
RenderWidgetHostDragTest
content/browser/renderer_host/render_widget_host_unittest.cc
modified
RenderWidgetHostDragTest
content/browser/renderer_host/render_widget_host_unittest.cc
modified
TEST_F
content/browser/renderer_host/render_widget_host_unittest.cc
modified

Files Changed

  • content/browser/renderer_host/render_widget_host_unittest.cc
  • content/browser/renderer_host/render_widget_host_impl.cc

Audit Directions

  • Pre-commit metadata exposure
    Audit every browser-to-renderer message sent during hover/enter/preview phases (drag, paste, file pickers) for full paths or other host secrets that should only travel at the final commit/drop step.
  • Path fields crossing process boundaries
    Search for base::FilePath or path members serialized into Mojo structs bound for a renderer and confirm each is reduced to BaseName or an opaque token unless the full path is genuinely required.
  • Platform-specific branches
    Review the Aura/Windows/macOS drag code paths (and analogous #if branches elsewhere) since sanitization applied on one platform may be missing on others that populate the same DropData::Metadata.
From 7c92200b7e5c8d2b233a9be68ebd7b54f41c5a4c Mon Sep 17 00:00:00 2001
From: Tzarial <zork@google.com>
Date: Mon, 24 Aug 2026 08:56:20 -0700
Subject: [PATCH] [agy][content] Sanitize drag-enter file paths

During drag-enter and drag-over events, the browser sends drag metadata
to the renderer. Previously, this metadata included the absolute path of
dragged files, which could be leaked to a compromised renderer process
before the user performs a drop.

This CL sanitizes the file paths in the metadata by replacing them with
their BaseName. The full paths are still preserved and sent when the
actual drop event occurs.

Fixed: 514524620
Test: RenderWidgetHostDragTest.DragEnterDoesNotLeakPaths
Change-Id: Ibd7b2d16a63de4028c4fa8d995a8a476c5374b92
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8257413
Reviewed-by: Alex Moshchuk <alexmos@chromium.org>
Commit-Queue: Tzarial <zork@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1684792}
---
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index c2e0532..953f9419 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -264,11 +264,13 @@
         DropData::Kind::STRING, ui::kMimeTypeHtml16));
   }
 
-  // On Aura, filenames are available before drop.
+  // On Aura, filenames are available before drop, but we sanitize the
+  // paths to their BaseName to prevent leaking absolute paths
+  // (https://crbug.com/514524620).
   for (const auto& file_info : drop_data.filenames) {
     if (!file_info.path.empty()) {
       metadata.push_back(DropData::Metadata::CreateForFilePath(
-          file_info.path, file_info.display_name));
+          file_info.path.BaseName(), file_info.display_name));
     }
   }
 
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index 94ff324..5337b09e 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -51,6 +51,8 @@
 #include "content/public/common/drop_data.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_task_environment.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/fake_frame_widget.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/test/mock_render_input_router.h"
@@ -2287,6 +2289,28 @@
   }
 };
 
+class DragCaptureFrameWidget : public FakeFrameWidget {
+ public:
+  explicit DragCaptureFrameWidget(
+      mojo::PendingAssociatedReceiver<blink::mojom::FrameWidget> receiver)
+      : FakeFrameWidget(std::move(receiver)) {}
+
+  void DragTargetDragEnter(blink::mojom::DragDataPtr drag_data,
+                           const gfx::PointF& point_in_viewport,
+                           const gfx::PointF& screen_point,
+                           blink::DragOperationsMask operations_allowed,
+                           uint32_t key_modifiers,
+                           DragTargetDragEnterCallback callback) override {
+    drag_data_ = std::move(drag_data);
+    std::move(callback).Run(ui::mojom::DragOperation::kCopy, true);
+  }
+
+  const blink::mojom::DragDataPtr& drag_data() const { return drag_data_; }
+
+ private:
+  blink::mojom::DragDataPtr drag_data_;
+};
+
 class RenderWidgetHostDragTest : public RenderViewHostImplTestHarness {
  public:
   RenderWidgetHostDragTest() {
@@ -2435,6 +2459,51 @@
             FILE_PATH_LITERAL("payload.so"));
 }
 
+TEST_F(RenderWidgetHostDragTest, DragEnterDoesNotLeakPaths) {
+  // Bind our mock frame widget.
+  mojo::PendingAssociatedReceiver<blink::mojom::FrameWidget> receiver =
+      BindFakeFrameWidgetInterfaces(main_test_rfh());
+  DragCaptureFrameWidget mock_frame_widget(std::move(receiver));
+
+  // Prepare drag data with files: one with display_name and one without.
+  DropData drop_data;
+  drop_data.filenames.emplace_back(
+      base::FilePath(FILE_PATH_LITERAL("/absolute/path/to/file1.txt")),
+      base::FilePath(FILE_PATH_LITERAL("display_name.txt")));
+  drop_data.filenames.emplace_back(
+      base::FilePath(FILE_PATH_LITERAL("/another/absolute/path/to/file2.txt")),
+      base::FilePath());
+
+  // Call DragTargetDragEnter.
+  base::RunLoop run_loop;
+  GetRenderWidgetHost()->DragTargetDragEnter(
+      drop_data, gfx::PointF(), gfx::PointF(),
+      blink::DragOperationsMask::kDragOperationEvery, 0,
+      base::BindOnce(
+          [](base::OnceClosure quit_closure, ui::mojom::DragOperation operation,
+             bool document_is_handling_drag) { std::move(quit_closure).Run(); },
+          run_loop.QuitClosure()));
+  run_loop.Run();
+
+  // Verify that the paths sent to renderer are sanitized to BaseName.
+  const auto& captured_drag_data = mock_frame_widget.drag_data();
+  ASSERT_TRUE(captured_drag_data);
+  ASSERT_EQ(captured_drag_data->items.size(), 2u);
+
+  const auto& item1 = captured_drag_data->items[0];
+  ASSERT_TRUE(item1->is_file());
+  EXPECT_EQ(item1->get_file()->path,
+            base::FilePath(FILE_PATH_LITERAL("file1.txt")));
+  EXPECT_EQ(item1->get_file()->display_name,
+            base::FilePath(FILE_PATH_LITERAL("display_name.txt")));
+
+  const auto& item2 = captured_drag_data->items[1];
+  ASSERT_TRUE(item2->is_file());
+  EXPECT_EQ(item2->get_file()->path,
+            base::FilePath(FILE_PATH_LITERAL("file2.txt")));
+  EXPECT_TRUE(item2->get_file()->display_name.empty());
+}
+
 // A plain <img> drag on macOS populates `file_contents` but supplies neither a
 // source URL nor a Content-Disposition, so it must not surface as a File in the
 // renderer's DataTransfer.files. See crbug.com/522179938.
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index 94ff324..5337b09e 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -51,6 +51,8 @@
 #include "content/public/common/drop_data.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_task_environment.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/fake_frame_widget.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/test/mock_render_input_router.h"
@@ -2287,6 +2289,28 @@
   }
 };
 
+class DragCaptureFrameWidget : public FakeFrameWidget {
+ public:
+  explicit DragCaptureFrameWidget(
+      mojo::PendingAssociatedReceiver<blink::mojom::FrameWidget> receiver)
+      : FakeFrameWidget(std::move(receiver)) {}
+
+  void DragTargetDragEnter(blink::mojom::DragDataPtr drag_data,
+                           const gfx::PointF& point_in_viewport,
+                           const gfx::PointF& screen_point,
+                           blink::DragOperationsMask operations_allowed,
+                           uint32_t key_modifiers,
+                           DragTargetDragEnterCallback callback) override {
+    drag_data_ = std::move(drag_data);
+    std::move(callback).Run(ui::mojom::DragOperation::kCopy, true);
+  }
+
+  const blink::mojom::DragDataPtr& drag_data() const { return drag_data_; }
+
+ private:
+  blink::mojom::DragDataPtr drag_data_;
+};
+
 class RenderWidgetHostDragTest : public RenderViewHostImplTestHarness {
  public:
   RenderWidgetHostDragTest() {
@@ -2435,6 +2459,51 @@
             FILE_PATH_LITERAL("payload.so"));
 }
 
+TEST_F(RenderWidgetHostDragTest, DragEnterDoesNotLeakPaths) {
+  // Bind our mock frame widget.
+  mojo::PendingAssociatedReceiver<blink::mojom::FrameWidget> receiver =
+      BindFakeFrameWidgetInterfaces(main_test_rfh());
+  DragCaptureFrameWidget mock_frame_widget(std::move(receiver));
+
+  // Prepare drag data with files: one with display_name and one without.
+  DropData drop_data;
+  drop_data.filenames.emplace_back(
+      base::FilePath(FILE_PATH_LITERAL("/absolute/path/to/file1.txt")),
+      base::FilePath(FILE_PATH_LITERAL("display_name.txt")));
+  drop_data.filenames.emplace_back(
+      base::FilePath(FILE_PATH_LITERAL("/another/absolute/path/to/file2.txt")),
+      base::FilePath());
+
+  // Call DragTargetDragEnter.
+  base::RunLoop run_loop;
+  GetRenderWidgetHost()->DragTargetDragEnter(
+      drop_data, gfx::PointF(), gfx::PointF(),
+      blink::DragOperationsMask::kDragOperationEvery, 0,
+      base::BindOnce(
+          [](base::OnceClosure quit_closure, ui::mojom::DragOperation operation,
+             bool document_is_handling_drag) { std::move(quit_closure).Run(); },
+          run_loop.QuitClosure()));
+  run_loop.Run();
+
+  // Verify that the paths sent to renderer are sanitized to BaseName.
+  const auto& captured_drag_data = mock_frame_widget.drag_data();
+  ASSERT_TRUE(captured_drag_data);
+  ASSERT_EQ(captured_drag_data->items.size(), 2u);
+
+  const auto& item1 = captured_drag_data->items[0];
+  ASSERT_TRUE(item1->is_file());
+  EXPECT_EQ(item1->get_file()->path,
+            base::FilePath(FILE_PATH_LITERAL("file1.txt")));
+  EXPECT_EQ(item1->get_file()->display_name,
+            base::FilePath(FILE_PATH_LITERAL("display_name.txt")));
+
+  const auto& item2 = captured_drag_data->items[1];
+  ASSERT_TRUE(item2->is_file());
+  EXPECT_EQ(item2->get_file()->path,
+            base::FilePath(FILE_PATH_LITERAL("file2.txt")));
+  EXPECT_TRUE(item2->get_file()->display_name.empty());
+}
+
 // A plain <img> drag on macOS populates `file_contents` but supplies neither a
 // source URL nor a Content-Disposition, so it must not surface as a File in the
 // renderer's DataTransfer.files. See crbug.com/522179938.
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.