Medium chrome Sandbox Escape 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient policy enforcement in IFrame Sandbox
DescriptionInsufficient policy enforcement in IFrame Sandbox
ComponentIFrame Sandbox
Bug ClassSandbox Escape
Tracker40061220
Fix commitd6cf22406be1 (chromium/src) +122/-0
CISA KEVNot listed
CreditedLuan Herrera (@lbherrera_)
Disclosed2026-05-12

Changed Functions

FunctionChangeNotes
test
third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-area-use-counter.html
modified
test
third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-svg-anchor-use-counter.html
modified
test
third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-use-counter.html
modified

Files Changed

  • third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
  • third_party/blink/renderer/core/html/html_anchor_element.cc
  • third_party/blink/renderer/core/svg/svg_a_element.cc
  • third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-area-use-counter.html
  • third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-svg-anchor-use-counter.html
  • third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-use-counter.html
  • tools/metrics/histograms/metadata/blink/enums.xml
From d6cf22406be1252f030a6a75529bc7570fab675b Mon Sep 17 00:00:00 2001
From: Emily Stark <[email protected]>
Date: Tue, 10 Mar 2026 08:16:16 -0700
Subject: [PATCH] Measure click events from synthesized middle-clicks

Chrome will dispatch a synthesized "click" event with {"button": 1} to
an anchor element, but per spec it is unclear whether this should be
allowed. Measure how common this is so that we can hopefully deprecate.

Bug: 40061220,40262435
Change-Id: I7643f0a38e2d43eab57c49e5a024482ac3fd1393
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7643254
Reviewed-by: Mason Freed <[email protected]>
Commit-Queue: Emily Stark <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1597071}
---

diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index cb23d2a..da550c03 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -5133,6 +5133,9 @@
   kHTMLInputElementTypeChangedWhileConnected = 5816,
   kHTMLButtonElementTypeChangedWhileDisconnected = 5817,
   kHTMLInputElementTypeChangedWhileDisconnected = 5818,
+  kSynthesizedMiddleClickAnchor = 5819,
+  kSynthesizedMiddleClickArea = 5820,
+  kSynthesizedMiddleClickSVGAnchor = 5821,
 
   // Add new features immediately above this line. Don't change the existing
   // numbers of any item, and don't reuse removed slots. Also don't add extra
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.cc b/third_party/blink/renderer/core/html/html_anchor_element.cc
index eb9ae05..ad38749 100644
--- a/third_party/blink/renderer/core/html/html_anchor_element.cc
+++ b/third_party/blink/renderer/core/html/html_anchor_element.cc
@@ -550,6 +550,22 @@
 void HTMLAnchorElementBase::HandleClick(MouseEvent& event) {
   event.SetDefaultHandled();
 
+  // It's unclear whether synthesized middle-button "click" events should be
+  // allowed to be dispatched and create a navigation. Measure how common this
+  // is to see if we can disallow it. Per Pointer Events: "The click event
+  // should only be fired for the primary pointer button (i.e., when button
+  // value is 0, buttons value is 1). Secondary buttons (like the middle or
+  // right button on a standard mouse) MUST NOT fire click events."
+  // (https://w3c.github.io/pointerevents/#dfn-click)
+  if (event.type() == event_type_names::kClick && !event.isTrusted() &&
+      event.button() ==
+          static_cast<int16_t>(WebPointerProperties::Button::kMiddle)) {
+    UseCounter::Count(GetDocument(),
+                      IsA<HTMLAreaElement>(this)
+                          ? WebFeature::kSynthesizedMiddleClickArea
+                          : WebFeature::kSynthesizedMiddleClickAnchor);
+  }
+
   LocalDOMWindow* window = GetDocument().domWindow();
   if (!window)
     return;
diff --git a/third_party/blink/renderer/core/svg/svg_a_element.cc b/third_party/blink/renderer/core/svg/svg_a_element.cc
index 26992db..1a4e2e8 100644
--- a/third_party/blink/renderer/core/svg/svg_a_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_a_element.cc
@@ -48,6 +48,7 @@
 #include "third_party/blink/renderer/core/svg_names.h"
 #include "third_party/blink/renderer/core/xlink_names.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
 
@@ -124,6 +125,20 @@
     }
 
     if (IsLinkClick(event)) {
+      // It's unclear whether synthesized middle-button "click" events should be
+      // allowed to be dispatched and create a navigation. Measure how common
+      // this is to see if we can disallow it. Per Pointer Events: "The click
+      // event should only be fired for the primary pointer button (i.e., when
+      // button value is 0, buttons value is 1). Secondary buttons (like the
+      // middle or right button on a standard mouse) MUST NOT fire click
+      // events." (https://w3c.github.io/pointerevents/#dfn-click)
+      if (event.type() == event_type_names::kClick && !event.isTrusted() &&
+          To<MouseEvent>(event).button() ==
+              static_cast<int16_t>(WebPointerProperties::Button::kMiddle)) {
+        UseCounter::Count(GetDocument(),
+                          WebFeature::kSynthesizedMiddleClickSVGAnchor);
+      }
+
       StringView url = StripLeadingAndTrailingHtmlSpaces(HrefString());
 
       if (url.starts_with('#')) {
diff --git a/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-area-use-counter.html b/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-area-use-counter.html
new file mode 100644
index 0000000..d148cad
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-area-use-counter.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+
+<body>
+  <map id="m" name="m">
+    <area href="" shape="default">
+  </map>
+  <img src="" usemap="#m" id="image">
+  <script>
+    const kSynthesizedMiddleClickArea = 5820;
+
+    test(function () {
+      const area = document.querySelector('area');
+
+      internals.clearUseCounter(document, kSynthesizedMiddleClickArea);
+      assert_false(internals.isUseCounted(document, kSynthesizedMiddleClickArea), "Area should not be counted initially");
+      const event = new MouseEvent('click', {
+        button: 1 // Middle button
+      });
+      area.dispatchEvent(event);
+      assert_true(internals.isUseCounted(document, kSynthesizedMiddleClickArea), "Area should be counted after synthesized middle click");
+    }, "Synthesized middle click on area should trigger UseCounter");
+  </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-svg-anchor-use-counter.html b/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-svg-anchor-use-counter.html
new file mode 100644
index 0000000..3596910
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-svg-anchor-use-counter.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+
+<body>
+  <svg xmlns="http://www.w3.org/2000/svg">
+    <a href="" id="svglink"><text>link</text></a>
+  </svg>
+  <script>
+    const kSynthesizedMiddleClickSVGAnchor = 5821;
+
+    test(function () {
+      const link = document.getElementById('svglink');
+
+      internals.clearUseCounter(document, kSynthesizedMiddleClickSVGAnchor);
+      assert_false(internals.isUseCounted(document, kSynthesizedMiddleClickSVGAnchor), "SVG anchor should not be counted initially");
+      const event = new MouseEvent('click', {
+        button: 1 // Middle button
+      });
+      link.dispatchEvent(event);
+      assert_true(internals.isUseCounted(document, kSynthesizedMiddleClickSVGAnchor), "SVG anchor should be counted after synthesized middle click");
+    }, "Synthesized middle click on SVG anchor should trigger UseCounter");
+  </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-use-counter.html b/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-use-counter.html
new file mode 100644
index 0000000..cf05a372
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-use-counter.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+
+<body>
+  <a id="link" href="#">Link</a>
+  <script>
+    const kSynthesizedMiddleClickAnchor = 5819;
+
+    test(function () {
+      const link = document.getElementById('link');
+
+      internals.clearUseCounter(document, kSynthesizedMiddleClickAnchor);
+      assert_false(internals.isUseCounted(document, kSynthesizedMiddleClickAnchor), "Should not be counted initially");
+      const event = new MouseEvent('click', {
+        button: 1 // Middle button
+      });
+      link.dispatchEvent(event);
+      assert_true(internals.isUseCounted(document, kSynthesizedMiddleClickAnchor), "Should be counted after synthesized middle click");
+    }, "Synthesized middle click on anchor should trigger UseCounter");
+
+    test(function () {
+      const link = document.getElementById('link');
+
+      internals.clearUseCounter(document, kSynthesizedMiddleClickAnchor);
+      const event = new MouseEvent('click', {
+        button: 0 // Left button
+      });
+      link.dispatchEvent(event);
+      assert_false(internals.isUseCounted(document, kSynthesizedMiddleClickAnchor), "Should not be counted after synthesized left click");
+    }, "Synthesized left click on anchor should not trigger UseCounter");
+  </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/tools/metrics/histograms/metadata/blink/enums.xml b/tools/metrics/histograms/metadata/blink/enums.xml
index 1ec1baa..f1d550a 100644
--- a/tools/metrics/histograms/metadata/blink/enums.xml
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-area-use-counter.html b/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-area-use-counter.html
new file mode 100644
index 0000000..d148cad
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-area-use-counter.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+
+<body>
+  <map id="m" name="m">
+    <area href="" shape="default">
+  </map>
+  <img src="" usemap="#m" id="image">
+  <script>
+    const kSynthesizedMiddleClickArea = 5820;
+
+    test(function () {
+      const area = document.querySelector('area');
+
+      internals.clearUseCounter(document, kSynthesizedMiddleClickArea);
+      assert_false(internals.isUseCounted(document, kSynthesizedMiddleClickArea), "Area should not be counted initially");
+      const event = new MouseEvent('click', {
+        button: 1 // Middle button
+      });
+      area.dispatchEvent(event);
+      assert_true(internals.isUseCounted(document, kSynthesizedMiddleClickArea), "Area should be counted after synthesized middle click");
+    }, "Synthesized middle click on area should trigger UseCounter");
+  </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-svg-anchor-use-counter.html b/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-svg-anchor-use-counter.html
new file mode 100644
index 0000000..3596910
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-svg-anchor-use-counter.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+
+<body>
+  <svg xmlns="http://www.w3.org/2000/svg">
+    <a href="" id="svglink"><text>link</text></a>
+  </svg>
+  <script>
+    const kSynthesizedMiddleClickSVGAnchor = 5821;
+
+    test(function () {
+      const link = document.getElementById('svglink');
+
+      internals.clearUseCounter(document, kSynthesizedMiddleClickSVGAnchor);
+      assert_false(internals.isUseCounted(document, kSynthesizedMiddleClickSVGAnchor), "SVG anchor should not be counted initially");
+      const event = new MouseEvent('click', {
+        button: 1 // Middle button
+      });
+      link.dispatchEvent(event);
+      assert_true(internals.isUseCounted(document, kSynthesizedMiddleClickSVGAnchor), "SVG anchor should be counted after synthesized middle click");
+    }, "Synthesized middle click on SVG anchor should trigger UseCounter");
+  </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-use-counter.html b/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-use-counter.html
new file mode 100644
index 0000000..cf05a372
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/pointerevents/synthesized-middle-click-use-counter.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+
+<body>
+  <a id="link" href="#">Link</a>
+  <script>
+    const kSynthesizedMiddleClickAnchor = 5819;
+
+    test(function () {
+      const link = document.getElementById('link');
+
+      internals.clearUseCounter(document, kSynthesizedMiddleClickAnchor);
+      assert_false(internals.isUseCounted(document, kSynthesizedMiddleClickAnchor), "Should not be counted initially");
+      const event = new MouseEvent('click', {
+        button: 1 // Middle button
+      });
+      link.dispatchEvent(event);
+      assert_true(internals.isUseCounted(document, kSynthesizedMiddleClickAnchor), "Should be counted after synthesized middle click");
+    }, "Synthesized middle click on anchor should trigger UseCounter");
+
+    test(function () {
+      const link = document.getElementById('link');
+
+      internals.clearUseCounter(document, kSynthesizedMiddleClickAnchor);
+      const event = new MouseEvent('click', {
+        button: 0 // Left button
+      });
+      link.dispatchEvent(event);
+      assert_false(internals.isUseCounted(document, kSynthesizedMiddleClickAnchor), "Should not be counted after synthesized left click");
+    }, "Synthesized left click on anchor should not trigger UseCounter");
+  </script>
+</body>
+</html>
\ No newline at end of file
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.