Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in DataTransfer
DescriptionInsufficient validation of untrusted input in DataTransfer
ComponentDataTransfer
Bug ClassLogic Error
Tracker514058439
Fix commitddbfbe0032eb (chromium/src) +72/-4
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Changed Functions

FunctionChangeNotes
if
content/browser/web_contents/web_contents_view_aura.cc
modified
TEST_F
content/browser/web_contents/web_contents_view_aura_unittest.cc
modified
if
ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc
modified

Files Changed

  • content/browser/web_contents/web_contents_view_aura.cc
  • content/browser/web_contents/web_contents_view_aura.h
  • content/browser/web_contents/web_contents_view_aura_unittest.cc
  • ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc
From ddbfbe0032ebbf46b5f611372ee19e4b55c226bd Mon Sep 17 00:00:00 2001
From: Rohan Raja <roraja@microsoft.com>
Date: Wed, 27 May 2026 10:18:52 -0700
Subject: [PATCH] Clamp touch-drag screen location to browser-observed touch point

Security fix for potential synthetic click on permission prompts via
touch-drag on Aura/Windows. The renderer-supplied event_info.location
for touch-initiated drags was passed through to platform
DragDropClients; on Windows it would reach ::SendInput via
DesktopWindowTreeHostWin::StartTouchDrag, where the OS routes the
synthesized click to the topmost HWND at those coordinates (e.g. an
overlapping permission bubble WS_POPUP).

WebContentsViewAura::StartDragging now, for kTouch drags only:
  - Rejects the drag when no touch is in flight
    (aura::Env::is_touch_down() == false).
  - Substitutes the renderer-supplied screen location with the trusted
    browser-observed last touch point from
    aura::Env::GetLastPointerPoint(), with the renderer-supplied value
    as fallback. The trusted point is used for both the visibility/
    bounds check and the StartDragAndDrop call.

Also drops a dead-code ConvertDIPToPixels call on a default-constructed
gfx::Point in DesktopDragDropClientWin::StartDragAndDrop that was
overwritten before being used.

Bug: 514058439
Change-Id: Ibc1372f79609f132646485e29bfd90a518b54e94
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7862906
Reviewed-by: David Bienvenu <davidbienvenu@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Elly <ellyjones@chromium.org>
Commit-Queue: Rohan Raja <roraja@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1637050}
---

diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index 7f40cf79..5715f972f 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -1267,6 +1267,23 @@
   DragOperation result_op;
   {
     gfx::NativeView content_native_view = GetContentNativeView();
+    // For a touch-initiated drag the renderer-supplied `event_info.location`
+    // is untrusted: on Windows it would reach `::SendInput` via
+    // DesktopWindowTreeHostWin::StartTouchDrag and could redirect the
+    // synthesized click to an overlapping HWND (e.g. a permission bubble).
+    // Require an in-flight touch and substitute the browser-observed last
+    // touch point known to aura::Env.
+    gfx::Point trusted_location = event_info.location;
+    if (event_info.source == ui::mojom::DragEventSource::kTouch) {
+      aura::Env* env = aura::Env::GetInstance();
+      if (!env->is_touch_down()) {
+        web_contents_->SystemDragEnded(source_rwh);
+        return;
+      }
+      trusted_location =
+          env->GetLastPointerPoint(event_info.source, content_native_view,
+                                   /*fallback=*/event_info.location);
+    }
     // Make sure event is within the web contents, and the web contents are
     // visible.
     if (
@@ -1274,8 +1291,7 @@
         // TODO(https://crbug.com/454552204): Remove #if when either ChromeOS
         // fixes split screen mode web ui tab strip drag, or web ui tab strip is
         // fully deprecated.
-        !content_native_view->GetBoundsInScreen().Contains(
-            event_info.location) ||
+        !content_native_view->GetBoundsInScreen().Contains(trusted_location) ||
 #endif  // !BUILDFLAG(IS_CHROMEOS)
         !content_native_view->IsVisible()) {
       web_contents_->SystemDragEnded(source_rwh);
@@ -1285,7 +1301,7 @@
     result_op =
         aura::client::GetDragDropClient(root_window)
             ->StartDragAndDrop(std::move(data), root_window,
-                               content_native_view, event_info.location,
+                               content_native_view, trusted_location,
                                ConvertFromDragOperationsMask(operations),
                                event_info.source);
   }
diff --git a/content/browser/web_contents/web_contents_view_aura.h b/content/browser/web_contents/web_contents_view_aura.h
index fe50f38..6bcab47 100644
--- a/content/browser/web_contents/web_contents_view_aura.h
+++ b/content/browser/web_contents/web_contents_view_aura.h
@@ -169,6 +169,8 @@
                            RejectDragFromHiddenWebContents);
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, RejectDragFromOutsideView);
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest,
+                           ClampTouchLocationToBrowserObservedPoint);
+  FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest,
                            UrlInDropDataReturnsUrlInOSExchangeDataGetString);
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest,
                            IgnoreInputs_OngoingDropGetsCleared);
diff --git a/content/browser/web_contents/web_contents_view_aura_unittest.cc b/content/browser/web_contents/web_contents_view_aura_unittest.cc
index 5e1c407f1..4abd78ab 100644
--- a/content/browser/web_contents/web_contents_view_aura_unittest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_unittest.cc
@@ -23,6 +23,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/drag_drop_client.h"
+#include "ui/aura/env.h"
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/test/window_test_api.h"
 #include "ui/aura/window.h"
@@ -96,6 +97,8 @@
                                  ui::mojom::DragEventSource source) override {
     drag_in_progress_ = true;
     drag_drop_data_ = std::move(data);
+    last_screen_location_ = screen_location;
+    last_source_ = source;
     return DragOperation::kCopy;
   }
 #if BUILDFLAG(IS_LINUX)
@@ -109,10 +112,18 @@
   }
 
   ui::OSExchangeData* GetDragDropData() { return drag_drop_data_.get(); }
+  const gfx::Point& last_screen_location() const {
+    return last_screen_location_;
+  }
+  std::optional<ui::mojom::DragEventSource> last_source() const {
+    return last_source_;
+  }
 
  private:
   bool drag_in_progress_ = false;
   std::unique_ptr<ui::OSExchangeData> drag_drop_data_;
+  gfx::Point last_screen_location_;
+  std::optional<ui::mojom::DragEventSource> last_source_;
 };
 
 }  // namespace
@@ -922,6 +933,46 @@
 #endif  //  BUILDFLAG(IS_CHROMEOS)
 }
 
+// For a touch-initiated drag, the renderer-supplied screen location must
+// not flow through to DragDropClient::StartDragAndDrop. Instead the trusted
+// browser-observed last touch location (aura::Env) must be used.
+TEST_F(WebContentsViewAuraTest, ClampTouchLocationToBrowserObservedPoint) {
+  NavigateAndCommit(GURL("https://example.com/"));
+
+  TestDragDropClient drag_drop_client;
+  aura::client::SetDragDropClient(root_window(), &drag_drop_client);
+
+  WebContentsViewAura* view = GetView();
+  view->drag_in_progress_ = true;
+
+  aura::Window* const content = view->GetContentNativeView();
+  const gfx::Rect bounds = content->GetBoundsInScreen();
+  const gfx::Point trusted(bounds.x() + 3, bounds.y() + 4);
+  const gfx::Point spoofed(bounds.right() - 2, bounds.bottom() - 2);
+  ASSERT_NE(trusted, spoofed);
+  ASSERT_TRUE(bounds.Contains(trusted));
+  ASSERT_TRUE(bounds.Contains(spoofed));
+
+  aura::Env* const env = aura::Env::GetInstance();
+  env->SetTouchDown(true);
+  env->SetLastTouchLocation(content, trusted);
+
+  DropData drop_data;
+  view->StartDragging(*main_rfh(), drop_data,
+                      blink::DragOperationsMask::kDragOperationNone,
+                      gfx::ImageSkia(), gfx::Vector2d(), gfx::Rect(),
+                      blink::mojom::DragEventSourceInfo(
+                          spoofed, ui::mojom::DragEventSource::kTouch));
+
+  EXPECT_TRUE(drag_drop_client.GetDragDropData());
+  EXPECT_EQ(ui::mojom::DragEventSource::kTouch, drag_drop_client.last_source());
+  EXPECT_EQ(trusted, drag_drop_client.last_screen_location())
+      << "Renderer-supplied screen location must be clamped to the "
+         "browser-observed last touch point.";
+
+  env->SetTouchDown(false);
+}
+
 // Test that a drag from an event located outside the source view doesn't start.
 TEST_F(WebContentsViewAuraTest, EmptyTextInDropDataIsNonNullInOSExchangeData) {
   const char kGoogleUrl[] = "https://google.com/";
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc
index 6d31bee..19d8cb90 100644
--- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc
@@ -77,7 +77,6 @@
   CHECK(!g_is_dragging);
   gfx::Point touch_screen_point;
   if (source == ui::mojom::DragEventSource::kTouch) {
-    source_window->GetHost()->ConvertDIPToPixels(&touch_screen_point);
     display::Screen* screen = display::Screen::Get();
     CHECK(screen);
     aura::Window* window =
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/web_contents/web_contents_view_aura_unittest.cc b/content/browser/web_contents/web_contents_view_aura_unittest.cc
index 5e1c407f1..4abd78ab 100644
--- a/content/browser/web_contents/web_contents_view_aura_unittest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_unittest.cc
@@ -23,6 +23,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/drag_drop_client.h"
+#include "ui/aura/env.h"
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/test/window_test_api.h"
 #include "ui/aura/window.h"
@@ -96,6 +97,8 @@
                                  ui::mojom::DragEventSource source) override {
     drag_in_progress_ = true;
     drag_drop_data_ = std::move(data);
+    last_screen_location_ = screen_location;
+    last_source_ = source;
     return DragOperation::kCopy;
   }
 #if BUILDFLAG(IS_LINUX)
@@ -109,10 +112,18 @@
   }
 
   ui::OSExchangeData* GetDragDropData() { return drag_drop_data_.get(); }
+  const gfx::Point& last_screen_location() const {
+    return last_screen_location_;
+  }
+  std::optional<ui::mojom::DragEventSource> last_source() const {
+    return last_source_;
+  }
 
  private:
   bool drag_in_progress_ = false;
   std::unique_ptr<ui::OSExchangeData> drag_drop_data_;
+  gfx::Point last_screen_location_;
+  std::optional<ui::mojom::DragEventSource> last_source_;
 };
 
 }  // namespace
@@ -922,6 +933,46 @@
 #endif  //  BUILDFLAG(IS_CHROMEOS)
 }
 
+// For a touch-initiated drag, the renderer-supplied screen location must
+// not flow through to DragDropClient::StartDragAndDrop. Instead the trusted
+// browser-observed last touch location (aura::Env) must be used.
+TEST_F(WebContentsViewAuraTest, ClampTouchLocationToBrowserObservedPoint) {
+  NavigateAndCommit(GURL("https://example.com/"));
+
+  TestDragDropClient drag_drop_client;
+  aura::client::SetDragDropClient(root_window(), &drag_drop_client);
+
+  WebContentsViewAura* view = GetView();
+  view->drag_in_progress_ = true;
+
+  aura::Window* const content = view->GetContentNativeView();
+  const gfx::Rect bounds = content->GetBoundsInScreen();
+  const gfx::Point trusted(bounds.x() + 3, bounds.y() + 4);
+  const gfx::Point spoofed(bounds.right() - 2, bounds.bottom() - 2);
+  ASSERT_NE(trusted, spoofed);
+  ASSERT_TRUE(bounds.Contains(trusted));
+  ASSERT_TRUE(bounds.Contains(spoofed));
+
+  aura::Env* const env = aura::Env::GetInstance();
+  env->SetTouchDown(true);
+  env->SetLastTouchLocation(content, trusted);
+
+  DropData drop_data;
+  view->StartDragging(*main_rfh(), drop_data,
+                      blink::DragOperationsMask::kDragOperationNone,
+                      gfx::ImageSkia(), gfx::Vector2d(), gfx::Rect(),
+                      blink::mojom::DragEventSourceInfo(
+                          spoofed, ui::mojom::DragEventSource::kTouch));
+
+  EXPECT_TRUE(drag_drop_client.GetDragDropData());
+  EXPECT_EQ(ui::mojom::DragEventSource::kTouch, drag_drop_client.last_source());
+  EXPECT_EQ(trusted, drag_drop_client.last_screen_location())
+      << "Renderer-supplied screen location must be clamped to the "
+         "browser-observed last touch point.";
+
+  env->SetTouchDown(false);
+}
+
 // Test that a drag from an event located outside the source view doesn't start.
 TEST_F(WebContentsViewAuraTest, EmptyTextInDropDataIsNonNullInOSExchangeData) {
   const char kGoogleUrl[] = "https://google.com/";
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential synthetic click on permission prompts via touch-drag on Windows

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: Coordinate validation for drag-and-drop on Aura fails to account for window occlusion by top-level HWNDs on Windows. A compromised renderer can potentially exploit this by spoofing touch-drag coordinates to target permission bubbles. This triggers OS-level mouse event injection that is routed to the topmost window, potentially granting sensitive permissions.

Affected files:

  • content/browser/web_contents/web_contents_view_aura.cc
  • ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc
  • ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
  • ui/base/win/event_creation_utils.cc

Estimated timestamp from git blame: Unknown (Google3 checkout)

Summary

A potential vulnerability exists in the coordinate validation logic of WebContentsViewAura::StartDragging that allows a compromised renderer to spoof input events on Windows. The current geometric bounds check does not account for window z-order or occlusion. On Windows, when a drag is initiated via touch, the browser injects synthetic OS-level mouse events. If the targeted coordinates are geometrically within the WebContents bounds but are occluded by a top-level permission bubble, the OS routes the injected events to the bubble, resulting in a synthetic click.

Technical Details

  1. Insufficient Coordinate Validation: In content/browser/web_contents/web_contents_view_aura.cc, StartDragging validates the renderer-supplied location using content_native_view->GetBoundsInScreen().Contains(event_info.location). This is a 2D geometric check that does not verify if the location is actually visible or if it is occluded by another window. Permission prompts in Chrome are separate top-level HWNDs that often overlap the content area.

  2. Windows Input Injection: On Windows, DesktopDragDropClientWin::StartDragAndDrop handles touch-initiated drags by calling DesktopWindowTreeHostWin::StartTouchDrag. This function uses ui::SendMouseEvent, which invokes the Windows API ::SendInput to inject MOUSEEVENTF_LEFTDOWN and MOUSEEVENTF_LEFTUP events at the specified screen coordinates. Because these are injected as system-level mouse events, the Windows OS routes them to the topmost window at that location—the permission bubble.

  3. Protection Bypasses: The primary defense against rapid/unintended interactions is the InputEventActivationProtector. However, this protection is time-based (checking if interaction occurs within ~500ms of the UI showing). A compromised renderer can bypass this by simply waiting for the interval to pass before triggering the malicious IPC.

Potential Reproduction Steps

  1. Use a compromised renderer to request a sensitive permission (e.g., Geolocation).
  2. Wait for more than 500ms to bypass InputEventActivationProtector.
  3. Display a decoy “Tap to continue” element in the web content at a location not covered by the prompt.
  4. When the user touches the decoy element (satisfying the browser’s is_touch_down requirement), the renderer sends a LocalFrameHost.StartDragging IPC.
  5. The IPC contains event_info.source = kTouch and event_info.location set to the screen coordinates of the permission prompt’s “Allow” button.
  6. The browser processes the drag, injecting system-level mouse events that the OS routes to the permission bubble, granting the permission.

Suggested Fix

Coordinate validation in WebContentsViewAura::StartDragging should be made occlusion-aware. Before initiating a drag or injecting events, the browser should verify that the targeted coordinates actually correspond to the WebContents view and are not occluded by a top-level window. On Windows, this could involve using ::WindowFromPoint or similar APIs to ensure the target HWND matches the expected content_native_view.

Evaluated with Chrome root at commit: 1a8d40fc44df2088d5945c0bf53584038aa1614a


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker