Low chrome UAF 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Editing
DescriptionUse after free in Editing
ComponentEditing
Bug ClassUAF
Tracker563021080
Fix commit77750ea9a9fd (chromium/src) +55/-5
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-20

Changed Functions

FunctionChangeNotes
TEST
third_party/blink/renderer/core/editing/commands/clipboard_commands_test.cc
modified

Files Changed

  • third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/call_function/user_activation.py
  • chrome/browser/extensions/content_capabilities_browsertest.cc
  • third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/evaluate/user_activation.py
  • third_party/blink/renderer/core/editing/commands/clipboard_commands.cc
  • third_party/blink/renderer/core/editing/commands/clipboard_commands_test.cc
From 77750ea9a9fd3a226c8e9581c2e18fcc147d9f06 Mon Sep 17 00:00:00 2001
From: Gaston Rodriguez <gastonr@microsoft.com>
Date: Thu, 01 Oct 2026 13:18:35 -0700
Subject: [PATCH] Reland "Add focus requirement to insert content into clipboard"

This is a reland of commit c50edc4214d81e4f944c3415d55104a49e46d214

The original CL was reverted due to tests that failed with the new focus
requirement which did not run on my CQ Dry Run. I made the tests require
focus now, and they pass. Added (win/linux/mac)-webdriver-bidi-rel bot
runs to CQ to ensure there are no other missing tests that would fail
with these changes.

Original change's description:
> Add focus requirement to insert content into clipboard
>
> Currently, focus is not required to insert content into the clipboard
> under certain circumstances (running on x11). The new async clipboard
> API requires focus for a script to inject content into the clipboard.
>
> This seems reasonable since a webpage may require you to open a second
> window to process additional input (like a SSO page), and injecting
> content from an unfocused page could be a vector of attack to an
> unassuming user.
>
> This CL adds the requirement for focus on the non-async path and a unit
> test that verifies that this is enforced.
>
> Additionally, I was able to repro the issue on the provided html in the
> linked bug and verified that this CL patches the repro.
>
> Bug: 563021080
> Change-Id: I928f5769e75f4c3d1c4ba9e69976f14dd85c50e6
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8422732
> Reviewed-by: Dan Clark <daniec@microsoft.com>
> Reviewed-by: James Cook <jamescook@chromium.org>
> Commit-Queue: Gaston Rodriguez <gastonr@microsoft.com>
> Cr-Commit-Position: refs/heads/main@{#1706539}

Bug: 563021080
Change-Id: I6314dcff954ec3a8a004742d5873d346f39ee253
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8488666
Commit-Queue: Gaston Rodriguez <gastonr@microsoft.com>
Reviewed-by: James Cook <jamescook@chromium.org>
Reviewed-by: Dan Clark <daniec@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1709342}
---
diff --git a/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc b/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc
index 6706ec48..2168cf76 100644
--- a/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc
+++ b/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc
@@ -162,8 +162,12 @@
   if (source == EditorCommandSource::kMenuOrKeyBinding)
     return true;
   Settings* const settings = frame.GetSettings();
-  if ((settings && settings->GetJavaScriptCanAccessClipboard()) ||
-      LocalFrame::HasTransientUserActivation(&frame)) {
+  if (settings && settings->GetJavaScriptCanAccessClipboard()) {
+    return true;
+  }
+  if (LocalFrame::HasTransientUserActivation(&frame) &&
+      ((settings && settings->GetClipboardFocusExempt()) ||
+       frame.GetDocument()->hasFocus())) {
     return true;
   }
   return frame.GetContentSettingsClient() &&
diff --git a/chrome/browser/extensions/content_capabilities_browsertest.cc b/chrome/browser/extensions/content_capabilities_browsertest.cc
index 4c35b612..d8223c6 100644
--- a/chrome/browser/extensions/content_capabilities_browsertest.cc
+++ b/chrome/browser/extensions/content_capabilities_browsertest.cc
@@ -247,11 +247,13 @@
   EXPECT_TRUE(
       CanWriteClipboardInAboutBlankFrame(extension.get(),
                                           GetTestURLFor("foo.example.com")));
-  // TODO(dcheng): This should be false, but we cannot currently execute testing
-  // script without a user gesture.
+  // EvalJs supplies a user gesture, so the focused top-level document can
+  // write despite not having the clipboardWrite capability.
   EXPECT_TRUE(
       CanWriteClipboard(extension.get(), GetTestURLFor("bar.example.com")));
-  EXPECT_TRUE(CanWriteClipboardInAboutBlankFrame(
+  // The about:blank child is not focused, so transient user activation alone
+  // does not allow it to write without the clipboardWrite capability.
+  EXPECT_FALSE(CanWriteClipboardInAboutBlankFrame(
       extension.get(), GetTestURLFor("bar.example.com")));
 
   EXPECT_FALSE(
diff --git a/third_party/blink/renderer/core/editing/commands/clipboard_commands_test.cc b/third_party/blink/renderer/core/editing/commands/clipboard_commands_test.cc
index 6548b6b..3fce2b9b 100644
--- a/third_party/blink/renderer/core/editing/commands/clipboard_commands_test.cc
+++ b/third_party/blink/renderer/core/editing/commands/clipboard_commands_test.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "third_party/blink/renderer/core/editing/commands/clipboard_commands.h"
+
 #include <memory>
 
 #include "base/memory/raw_ptr.h"
@@ -16,9 +18,11 @@
 #include "third_party/blink/renderer/core/editing/visible_selection.h"
 #include "third_party/blink/renderer/core/event_type_names.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/keywords.h"
+#include "third_party/blink/renderer/core/page/focus_controller.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/core/testing/mock_clipboard_host.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
@@ -154,4 +158,36 @@
   ExpectDefaultPasteResult(&event_type_names::kBeforeinput, "");
 }
 
+TEST(ClipboardCommandsCopyTest, DomWriteRequiresFocusedDocumentUnlessExempt) {
+  test::TaskEnvironment task_environment;
+  auto page_holder = std::make_unique<DummyPageHolder>(gfx::Size(1, 1));
+  LocalFrame& frame = page_holder->GetFrame();
+  FocusController& focus_controller =
+      page_holder->GetPage().GetFocusController();
+  focus_controller.SetActive(true);
+  focus_controller.SetFocused(true);
+  LocalFrame::NotifyUserActivation(
+      &frame, mojom::blink::UserActivationNotificationType::kTest);
+
+  ASSERT_TRUE(LocalFrame::HasTransientUserActivation(&frame));
+  EXPECT_TRUE(
+      ClipboardCommands::CanWriteClipboard(frame, EditorCommandSource::kDom));
+
+  focus_controller.SetFocused(false);
+
+  // Verify that a webpage can't write to the clipboard when the page is
+  // unfocused.
+  EXPECT_TRUE(LocalFrame::HasTransientUserActivation(&frame));
+  EXPECT_FALSE(
+      ClipboardCommands::CanWriteClipboard(frame, EditorCommandSource::kDom));
+  EXPECT_TRUE(ClipboardCommands::CanWriteClipboard(
+      frame, EditorCommandSource::kMenuOrKeyBinding));
+
+  // Verify that a webpage can write to the clipboard if the page is unfocused
+  // but it is focus exempt.
+  frame.GetSettings()->SetClipboardFocusExempt(true);
+  EXPECT_TRUE(
+      ClipboardCommands::CanWriteClipboard(frame, EditorCommandSource::kDom));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/call_function/user_activation.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/call_function/user_activation.py
index b7e90afa..cbf9c8a 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/call_function/user_activation.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/call_function/user_activation.py
@@ -31,6 +31,10 @@
                                            top_context["context"]),
                                        await_promise=False)
 
+    # Clipboard writes require document focus independently of user activation.
+    await bidi_session.browsing_context.activate(
+        context=top_context["context"])
+
     result = await bidi_session.script.call_function(
         function_declaration=
         "() => document.body.appendChild(document.createTextNode('test')) && " +
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/evaluate/user_activation.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/evaluate/user_activation.py
index 273832f..38d68b8 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/evaluate/user_activation.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/evaluate/user_activation.py
@@ -31,6 +31,10 @@
                                            top_context["context"]),
                                        await_promise=False)
 
+    # Clipboard writes require document focus independently of user activation.
+    await bidi_session.browsing_context.activate(
+        context=top_context["context"])
+
     result = await bidi_session.script.evaluate(
         expression=
         "document.body.appendChild(document.createTextNode('test')) && document.execCommand('selectAll') && document.execCommand('copy')",
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/extensions/content_capabilities_browsertest.cc b/chrome/browser/extensions/content_capabilities_browsertest.cc
index 4c35b612..d8223c6 100644
--- a/chrome/browser/extensions/content_capabilities_browsertest.cc
+++ b/chrome/browser/extensions/content_capabilities_browsertest.cc
@@ -247,11 +247,13 @@
   EXPECT_TRUE(
       CanWriteClipboardInAboutBlankFrame(extension.get(),
                                           GetTestURLFor("foo.example.com")));
-  // TODO(dcheng): This should be false, but we cannot currently execute testing
-  // script without a user gesture.
+  // EvalJs supplies a user gesture, so the focused top-level document can
+  // write despite not having the clipboardWrite capability.
   EXPECT_TRUE(
       CanWriteClipboard(extension.get(), GetTestURLFor("bar.example.com")));
-  EXPECT_TRUE(CanWriteClipboardInAboutBlankFrame(
+  // The about:blank child is not focused, so transient user activation alone
+  // does not allow it to write without the clipboardWrite capability.
+  EXPECT_FALSE(CanWriteClipboardInAboutBlankFrame(
       extension.get(), GetTestURLFor("bar.example.com")));
 
   EXPECT_FALSE(
diff --git a/third_party/blink/renderer/core/editing/commands/clipboard_commands_test.cc b/third_party/blink/renderer/core/editing/commands/clipboard_commands_test.cc
index 6548b6b..3fce2b9b 100644
--- a/third_party/blink/renderer/core/editing/commands/clipboard_commands_test.cc
+++ b/third_party/blink/renderer/core/editing/commands/clipboard_commands_test.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "third_party/blink/renderer/core/editing/commands/clipboard_commands.h"
+
 #include <memory>
 
 #include "base/memory/raw_ptr.h"
@@ -16,9 +18,11 @@
 #include "third_party/blink/renderer/core/editing/visible_selection.h"
 #include "third_party/blink/renderer/core/event_type_names.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/keywords.h"
+#include "third_party/blink/renderer/core/page/focus_controller.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/core/testing/mock_clipboard_host.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
@@ -154,4 +158,36 @@
   ExpectDefaultPasteResult(&event_type_names::kBeforeinput, "");
 }
 
+TEST(ClipboardCommandsCopyTest, DomWriteRequiresFocusedDocumentUnlessExempt) {
+  test::TaskEnvironment task_environment;
+  auto page_holder = std::make_unique<DummyPageHolder>(gfx::Size(1, 1));
+  LocalFrame& frame = page_holder->GetFrame();
+  FocusController& focus_controller =
+      page_holder->GetPage().GetFocusController();
+  focus_controller.SetActive(true);
+  focus_controller.SetFocused(true);
+  LocalFrame::NotifyUserActivation(
+      &frame, mojom::blink::UserActivationNotificationType::kTest);
+
+  ASSERT_TRUE(LocalFrame::HasTransientUserActivation(&frame));
+  EXPECT_TRUE(
+      ClipboardCommands::CanWriteClipboard(frame, EditorCommandSource::kDom));
+
+  focus_controller.SetFocused(false);
+
+  // Verify that a webpage can't write to the clipboard when the page is
+  // unfocused.
+  EXPECT_TRUE(LocalFrame::HasTransientUserActivation(&frame));
+  EXPECT_FALSE(
+      ClipboardCommands::CanWriteClipboard(frame, EditorCommandSource::kDom));
+  EXPECT_TRUE(ClipboardCommands::CanWriteClipboard(
+      frame, EditorCommandSource::kMenuOrKeyBinding));
+
+  // Verify that a webpage can write to the clipboard if the page is unfocused
+  // but it is focus exempt.
+  frame.GetSettings()->SetClipboardFocusExempt(true);
+  EXPECT_TRUE(
+      ClipboardCommands::CanWriteClipboard(frame, EditorCommandSource::kDom));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/call_function/user_activation.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/call_function/user_activation.py
index b7e90afa..cbf9c8a 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/call_function/user_activation.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/call_function/user_activation.py
@@ -31,6 +31,10 @@
                                            top_context["context"]),
                                        await_promise=False)
 
+    # Clipboard writes require document focus independently of user activation.
+    await bidi_session.browsing_context.activate(
+        context=top_context["context"])
+
     result = await bidi_session.script.call_function(
         function_declaration=
         "() => document.body.appendChild(document.createTextNode('test')) && " +
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/evaluate/user_activation.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/evaluate/user_activation.py
index 273832f..38d68b8 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/evaluate/user_activation.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/script/evaluate/user_activation.py
@@ -31,6 +31,10 @@
                                            top_context["context"]),
                                        await_promise=False)
 
+    # Clipboard writes require document focus independently of user activation.
+    await bidi_session.browsing_context.activate(
+        context=top_context["context"])
+
     result = await bidi_session.script.evaluate(
         expression=
         "document.body.appendChild(document.createTextNode('test')) && document.execCommand('selectAll') && document.execCommand('copy')",
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.