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 Blink
DescriptionInsufficient validation of untrusted input in Blink
ComponentBlink
Bug ClassLogic Error
Tracker513609249
Fix commit5ada8fd8d16f (chromium/src) +16/-26
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Changed Functions

FunctionChangeNotes
if
third_party/blink/renderer/core/events/wheel_event.cc
modified

Files Changed

  • third_party/blink/renderer/core/events/touch_event.cc
  • third_party/blink/renderer/core/events/touch_event_test.cc
  • third_party/blink/renderer/core/events/wheel_event.cc
  • third_party/blink/web_tests/wpt_internal/reporting/resources/intervention.js
From 5ada8fd8d16fc8a1fd168f9c73d3c6d26b45d320 Mon Sep 17 00:00:00 2001
From: David Baron <[email protected]>
Date: Wed, 20 May 2026 20:49:56 -0700
Subject: [PATCH] Limit TouchEvent/WheelEvent messages about preventDefault to trusted events.

Fixed: 513609249
Change-Id: I2c7e37260de6943b41ea5f56c16f5207e21dded0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7853179
Reviewed-by: Robert Flack <[email protected]>
Commit-Queue: David Baron <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1634023}
---

diff --git a/third_party/blink/renderer/core/events/touch_event.cc b/third_party/blink/renderer/core/events/touch_event.cc
index cf8dd01..6fa3306 100644
--- a/third_party/blink/renderer/core/events/touch_event.cc
+++ b/third_party/blink/renderer/core/events/touch_event.cc
@@ -110,6 +110,12 @@
 void TouchEvent::preventDefault() {
   UIEventWithKeyState::preventDefault();
 
+  if (!IsFullyTrusted()) {
+    // The messages below should only be sent for implementation-created
+    // events, not for script-created ones.
+    return;
+  }
+
   // A common developer error is to wait too long before attempting to stop
   // scrolling by consuming a touchmove event. Generate an error if this
   // event is uncancelable.
diff --git a/third_party/blink/renderer/core/events/touch_event_test.cc b/third_party/blink/renderer/core/events/touch_event_test.cc
index 7e81bea..ea353a88 100644
--- a/third_party/blink/renderer/core/events/touch_event_test.cc
+++ b/third_party/blink/renderer/core/events/touch_event_test.cc
@@ -84,6 +84,7 @@
   TouchEvent* event = EventWithDispatchType(
       WebInputEvent::DispatchType::kListenersNonBlockingPassive);
   event->SetHandlingPassive(Event::PassiveMode::kPassiveForcedDocumentLevel);
+  event->SetTrusted(true);
 
   EXPECT_THAT(Messages(), ElementsAre());
   event->preventDefault();
diff --git a/third_party/blink/renderer/core/events/wheel_event.cc b/third_party/blink/renderer/core/events/wheel_event.cc
index 61cf62d4..b7d16af3 100644
--- a/third_party/blink/renderer/core/events/wheel_event.cc
+++ b/third_party/blink/renderer/core/events/wheel_event.cc
@@ -146,6 +146,12 @@
 void WheelEvent::preventDefault() {
   MouseEvent::preventDefault();
 
+  if (!IsFullyTrusted()) {
+    // The messages below should only be sent for implementation-created
+    // events, not for script-created ones.
+    return;
+  }
+
   PassiveMode passive_mode = HandlingPassive();
   if (passive_mode == PassiveMode::kPassiveForcedDocumentLevel) {
     String id = "PreventDefaultPassive";
diff --git a/third_party/blink/web_tests/wpt_internal/reporting/resources/intervention.js b/third_party/blink/web_tests/wpt_internal/reporting/resources/intervention.js
index dc883cbb..4b8e187 100644
--- a/third_party/blink/web_tests/wpt_internal/reporting/resources/intervention.js
+++ b/third_party/blink/web_tests/wpt_internal/reporting/resources/intervention.js
@@ -1,28 +1,5 @@
 function causeIntervention() {
-  var target = document.getElementById('target');
-  var rect = target.getBoundingClientRect();
-  var targetX = rect.left + rect.width / 2;
-  var targetY = rect.top + rect.height / 2;
-
-  var pd = function(e) {
-    e.preventDefault();
-    document.body.removeEventListener('touchstart', pd);
-  };
-
-  document.body.addEventListener('touchstart', pd);
-
-  var touches = [new Touch({identifier: 1, clientX: targetX, clientY: targetY, target: target})];
-  var touchEventInit = {
-    cancelable: false,
-    touches: touches,
-    targetTouches: touches,
-    changedTouches: touches,
-    view: window
-  };
-  var event = new TouchEvent('touchstart', touchEventInit);
-
-  var deadline = performance.now() + 1;
-  while (performance.now() < deadline) {};
-
-  document.body.dispatchEvent(event);
+  // Calling navigator.vibrate() in a frame that has never had user activation
+  // generates an intervention report.
+  navigator.vibrate(100);
 }
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/renderer/core/events/touch_event_test.cc b/third_party/blink/renderer/core/events/touch_event_test.cc
index 7e81bea..ea353a88 100644
--- a/third_party/blink/renderer/core/events/touch_event_test.cc
+++ b/third_party/blink/renderer/core/events/touch_event_test.cc
@@ -84,6 +84,7 @@
   TouchEvent* event = EventWithDispatchType(
       WebInputEvent::DispatchType::kListenersNonBlockingPassive);
   event->SetHandlingPassive(Event::PassiveMode::kPassiveForcedDocumentLevel);
+  event->SetTrusted(true);
 
   EXPECT_THAT(Messages(), ElementsAre());
   event->preventDefault();
diff --git a/third_party/blink/web_tests/wpt_internal/reporting/resources/intervention.js b/third_party/blink/web_tests/wpt_internal/reporting/resources/intervention.js
index dc883cbb..4b8e187 100644
--- a/third_party/blink/web_tests/wpt_internal/reporting/resources/intervention.js
+++ b/third_party/blink/web_tests/wpt_internal/reporting/resources/intervention.js
@@ -1,28 +1,5 @@
 function causeIntervention() {
-  var target = document.getElementById('target');
-  var rect = target.getBoundingClientRect();
-  var targetX = rect.left + rect.width / 2;
-  var targetY = rect.top + rect.height / 2;
-
-  var pd = function(e) {
-    e.preventDefault();
-    document.body.removeEventListener('touchstart', pd);
-  };
-
-  document.body.addEventListener('touchstart', pd);
-
-  var touches = [new Touch({identifier: 1, clientX: targetX, clientY: targetY, target: target})];
-  var touchEventInit = {
-    cancelable: false,
-    touches: touches,
-    targetTouches: touches,
-    changedTouches: touches,
-    view: window
-  };
-  var event = new TouchEvent('touchstart', touchEventInit);
-
-  var deadline = performance.now() + 1;
-  while (performance.now() < deadline) {};
-
-  document.body.dispatchEvent(event);
+  // Calling navigator.vibrate() in a frame that has never had user activation
+  // generates an intervention report.
+  navigator.vibrate(100);
 }
Loading diff…

Original Bug Report

reported by [email protected]

Cross-origin Intervention report injection via TouchEvent and WheelEvent preventDefault

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: A logic flaw in Blink’s event handling allows an attacker to inject unauthorized intervention reports and console messages into a same-process cross-origin frame. This occurs because intervention attribution relies on the event’s ‘view’ property, which can be programmatically set to a victim window.

Affected files:

  • third_party/blink/renderer/core/events/touch_event.cc
  • third_party/blink/renderer/core/events/wheel_event.cc

Estimated timestamp from git blame: 2017-09-21

Description

A potential vulnerability in Blink allows an attacker-controlled frame to perform unauthorized writes to a victim’s console and telemetry systems. This is possible because TouchEvent::preventDefault() and WheelEvent::preventDefault() route intervention information to the frame specified in the event’s view property without verifying if the event is trusted or if the caller has security access to that frame.

When a TouchEvent or WheelEvent is created via its JavaScript constructor, the view property can be set to any Window object the caller has a reference to, including cross-origin windows in the same renderer process (e.g., same-site frames or on platforms like Android where Site Isolation is not universal). Because the bindings for the initializer dictionary (UIEventInit) do not perform a security check, a pointer to the victim’s LocalDOMWindow is stored in the event.

If the attacker calls preventDefault() on this script-generated event (for instance, by setting cancelable: false), Blink attempts to generate an intervention report. It uses the stored view pointer to identify the target frame but captures the current JavaScript execution stack—which belongs to the attacker—to populate the report’s metadata (e.g., sourceFile).

Potential Impact

  1. Reporting API Poisoning: An attacker can trigger POST requests to the victim’s Reporting-Endpoints. By using script directives like //# sourceURL, the attacker can control the sourceFile field in the report, injecting arbitrary data into the victim’s server-side telemetry.
  2. Console Injection: High-severity intervention messages are injected into the victim’s DevTools console.
  3. ReportingObserver Injection: The victim’s ReportingObserver callbacks are triggered with attacker-controlled data.
  4. Metrics Pollution: Blink’s UseCounter metrics (e.g., kTouchEventPreventedNoTouchAction) are incorrectly attributed to the victim frame.

This constitutes a write-only Same-Origin Policy (SOP) bypass.

Suggested Attack Steps

  1. The attacker embeds a same-site cross-origin victim (e.g., attacker at a.example.com and victim at b.example.com).
  2. The attacker executes a script with a specific sourceURL payload: //# sourceURL=https://attacker.com/payload.
  3. The attacker constructs a TouchEvent targeting the victim window: new TouchEvent('touchstart', { view: victimWindow, cancelable: false }).
  4. The attacker calls preventDefault() on this event object.
  5. Observe the intervention report and console error appearing in the victim frame’s context.

Suggested Fix

In third_party/blink/renderer/core/events/touch_event.cc and third_party/blink/renderer/core/events/wheel_event.cc, Blink should verify that the event isTrusted() before generating an intervention report in preventDefault(). Alternatively, a security check should be performed to ensure the current execution context is same-origin with the view() frame before routing the report.

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
Links in the report