Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in Google Lens
DescriptionInsufficient validation of untrusted input in Google Lens
ComponentGoogle Lens
Bug ClassLogic Error
Tracker521864362
Fix commitdfbdb381d206 (chromium/src) +106/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Files Changed

  • chrome/browser/ui/lens/BUILD.gn
  • chrome/browser/ui/lens/lens_overlay_controller.cc
  • chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
From dfbdb381d206398745619f900f321b760d11b023 Mon Sep 17 00:00:00 2001
From: Duncan Mercer <mercerd@google.com>
Date: Wed, 10 Jun 2026 11:40:27 -0700
Subject: [PATCH] [Lens Overlay] Check foreground state before writing to clipboard

Prevent clipboard poisoning from backgrounded Lens overlay WebUI. Verify
if the tab is activated before performing CopyText or CopyImage
operations.

Fixed: 521864362
Change-Id: I79ae2fb64d9aff7372b0987486e6802123572014
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7915772
Auto-Submit: Duncan Mercer <mercerd@google.com>
Commit-Queue: Juan Mojica <juanmojica@google.com>
Reviewed-by: Juan Mojica <juanmojica@google.com>
Cr-Commit-Position: refs/heads/main@{#1644741}
---

diff --git a/chrome/browser/ui/lens/BUILD.gn b/chrome/browser/ui/lens/BUILD.gn
index b88516c5..424749a 100644
--- a/chrome/browser/ui/lens/BUILD.gn
+++ b/chrome/browser/ui/lens/BUILD.gn
@@ -335,6 +335,7 @@
     "//third_party/lens_server_proto:lens_overlay_proto",
     "//ui/base",
     "//ui/base:types",
+    "//ui/base/clipboard:clipboard_test_support",
     "//ui/base/unowned_user_data",
     "//ui/compositor",
     "//ui/events:events_base",
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.cc b/chrome/browser/ui/lens/lens_overlay_controller.cc
index 9f6dafbb..c5e4ec3 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller.cc
@@ -499,12 +499,19 @@
 }
 
 void LensOverlayController::CopyText(const std::string& text) {
+  if (!tab_->IsActivated()) {
+    return;
+  }
   ui::ScopedClipboardWriter clipboard_writer(ui::ClipboardBuffer::kCopyPaste);
   clipboard_writer.WriteText(base::UTF8ToUTF16(text));
 }
 
 void LensOverlayController::CopyImage(lens::mojom::CenterRotatedBoxPtr region) {
-  if (initialization_data_->initial_screenshot_.drawsNothing()) {
+  if (!tab_->IsActivated()) {
+    return;
+  }
+  if (!initialization_data_ ||
+      initialization_data_->initial_screenshot_.drawsNothing()) {
     return;
   }
 
diff --git a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
index 7e0d7af..f852ada 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
@@ -150,6 +150,9 @@
 #include "third_party/lens_server_proto/lens_overlay_selection_type.pb.h"
 #include "third_party/lens_server_proto/lens_overlay_server.pb.h"
 #include "third_party/lens_server_proto/lens_overlay_service_deps.pb.h"
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/base/clipboard/clipboard_format_type.h"
+#include "ui/base/clipboard/test/clipboard_test_util.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/base/unowned_user_data/user_data_factory.h"
 #include "ui/base/window_open_disposition.h"
@@ -9814,3 +9817,97 @@
   EXPECT_EQ(box.width(), 1.0f);
   EXPECT_EQ(box.height(), 1.0f);
 }
+
+IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
+                       CopyToClipboardBackgroundCheck) {
+  WaitForPaint();
+
+  auto* controller = GetLensOverlayController();
+  ASSERT_EQ(controller->state(), State::kOff);
+
+  // Show the overlay.
+  OpenLensOverlay(LensOverlayInvocationSource::kAppMenu);
+  ASSERT_EQ(controller->state(), State::kScreenshot);
+  ASSERT_TRUE(base::test::RunUntil(
+      [&]() { return controller->state() == State::kOverlay; }));
+
+  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
+  lens::mojom::LensPageHandler* page_handler = controller;
+
+  // 1. Test CopyText when active.
+  page_handler->CopyText("active text 1");
+  std::u16string clipboard_text = ui::clipboard_test_util::ReadText(
+      clipboard, ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr);
+  EXPECT_EQ(clipboard_text, u"active text 1");
+
+  // Keep track of the active tab index.
+  int active_controller_tab_index =
+      browser()->tab_strip_model()->active_index();
+
+  // 2. Background the tab by opening a new tab.
+  WaitForPaint(kDocumentWithNamedElement,
+               WindowOpenDisposition::NEW_FOREGROUND_TAB,
+               ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
+                   ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+  EXPECT_TRUE(base::test::RunUntil(
+      [&]() { return controller->state() == State::kBackground; }));
+
+  // 3. Test CopyText when backgrounded. It should NOT overwrite the clipboard.
+  page_handler->CopyText("background text");
+  clipboard_text = ui::clipboard_test_util::ReadText(
+      clipboard, ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr);
+  EXPECT_EQ(clipboard_text, u"active text 1");
+
+  // 4. Reactivate the tab.
+  browser()->tab_strip_model()->ActivateTabAt(active_controller_tab_index);
+  EXPECT_TRUE(base::test::RunUntil(
+      [&]() { return controller->state() == State::kOverlay; }));
+
+  // 5. Test CopyText when reactivated.
+  page_handler->CopyText("active text 2");
+  clipboard_text = ui::clipboard_test_util::ReadText(
+      clipboard, ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr);
+  EXPECT_EQ(clipboard_text, u"active text 2");
+
+  // 6. Background the tab again.
+  WaitForPaint(kDocumentWithNamedElement,
+               WindowOpenDisposition::NEW_FOREGROUND_TAB,
+               ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
+                   ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+  EXPECT_TRUE(base::test::RunUntil(
+      [&]() { return controller->state() == State::kBackground; }));
+
+  // 7. Test CopyImage when backgrounded. It should NOT overwrite the clipboard.
+  auto region = lens::mojom::CenterRotatedBox::New();
+  region->box = gfx::RectF(0.1, 0.1, 0.2, 0.2);
+  region->coordinate_type =
+      lens::mojom::CenterRotatedBox::CoordinateType::kNormalized;
+
+  page_handler->CopyImage(std::move(region));
+
+  // Clipboard should still have "active text 2" and NOT have an image.
+  clipboard_text = ui::clipboard_test_util::ReadText(
+      clipboard, ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr);
+  EXPECT_EQ(clipboard_text, u"active text 2");
+  EXPECT_FALSE(ui::clipboard_test_util::IsFormatAvailable(
+      clipboard, ui::ClipboardFormatType::BitmapType(),
+      ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
+
+  // 8. Reactivate the tab.
+  browser()->tab_strip_model()->ActivateTabAt(active_controller_tab_index);
+  EXPECT_TRUE(base::test::RunUntil(
+      [&]() { return controller->state() == State::kOverlay; }));
+
+  // 9. Test CopyImage when active.
+  auto region2 = lens::mojom::CenterRotatedBox::New();
+  region2->box = gfx::RectF(0.1, 0.1, 0.2, 0.2);
+  region2->coordinate_type =
+      lens::mojom::CenterRotatedBox::CoordinateType::kNormalized;
+
+  page_handler->CopyImage(std::move(region2));
+
+  // Clipboard should now have an image format.
+  EXPECT_TRUE(ui::clipboard_test_util::IsFormatAvailable(
+      clipboard, ui::ClipboardFormatType::BitmapType(),
+      ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
+}
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
index 7e0d7af..f852ada 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
@@ -150,6 +150,9 @@
 #include "third_party/lens_server_proto/lens_overlay_selection_type.pb.h"
 #include "third_party/lens_server_proto/lens_overlay_server.pb.h"
 #include "third_party/lens_server_proto/lens_overlay_service_deps.pb.h"
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/base/clipboard/clipboard_format_type.h"
+#include "ui/base/clipboard/test/clipboard_test_util.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/base/unowned_user_data/user_data_factory.h"
 #include "ui/base/window_open_disposition.h"
@@ -9814,3 +9817,97 @@
   EXPECT_EQ(box.width(), 1.0f);
   EXPECT_EQ(box.height(), 1.0f);
 }
+
+IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
+                       CopyToClipboardBackgroundCheck) {
+  WaitForPaint();
+
+  auto* controller = GetLensOverlayController();
+  ASSERT_EQ(controller->state(), State::kOff);
+
+  // Show the overlay.
+  OpenLensOverlay(LensOverlayInvocationSource::kAppMenu);
+  ASSERT_EQ(controller->state(), State::kScreenshot);
+  ASSERT_TRUE(base::test::RunUntil(
+      [&]() { return controller->state() == State::kOverlay; }));
+
+  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
+  lens::mojom::LensPageHandler* page_handler = controller;
+
+  // 1. Test CopyText when active.
+  page_handler->CopyText("active text 1");
+  std::u16string clipboard_text = ui::clipboard_test_util::ReadText(
+      clipboard, ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr);
+  EXPECT_EQ(clipboard_text, u"active text 1");
+
+  // Keep track of the active tab index.
+  int active_controller_tab_index =
+      browser()->tab_strip_model()->active_index();
+
+  // 2. Background the tab by opening a new tab.
+  WaitForPaint(kDocumentWithNamedElement,
+               WindowOpenDisposition::NEW_FOREGROUND_TAB,
+               ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
+                   ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+  EXPECT_TRUE(base::test::RunUntil(
+      [&]() { return controller->state() == State::kBackground; }));
+
+  // 3. Test CopyText when backgrounded. It should NOT overwrite the clipboard.
+  page_handler->CopyText("background text");
+  clipboard_text = ui::clipboard_test_util::ReadText(
+      clipboard, ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr);
+  EXPECT_EQ(clipboard_text, u"active text 1");
+
+  // 4. Reactivate the tab.
+  browser()->tab_strip_model()->ActivateTabAt(active_controller_tab_index);
+  EXPECT_TRUE(base::test::RunUntil(
+      [&]() { return controller->state() == State::kOverlay; }));
+
+  // 5. Test CopyText when reactivated.
+  page_handler->CopyText("active text 2");
+  clipboard_text = ui::clipboard_test_util::ReadText(
+      clipboard, ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr);
+  EXPECT_EQ(clipboard_text, u"active text 2");
+
+  // 6. Background the tab again.
+  WaitForPaint(kDocumentWithNamedElement,
+               WindowOpenDisposition::NEW_FOREGROUND_TAB,
+               ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
+                   ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+  EXPECT_TRUE(base::test::RunUntil(
+      [&]() { return controller->state() == State::kBackground; }));
+
+  // 7. Test CopyImage when backgrounded. It should NOT overwrite the clipboard.
+  auto region = lens::mojom::CenterRotatedBox::New();
+  region->box = gfx::RectF(0.1, 0.1, 0.2, 0.2);
+  region->coordinate_type =
+      lens::mojom::CenterRotatedBox::CoordinateType::kNormalized;
+
+  page_handler->CopyImage(std::move(region));
+
+  // Clipboard should still have "active text 2" and NOT have an image.
+  clipboard_text = ui::clipboard_test_util::ReadText(
+      clipboard, ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr);
+  EXPECT_EQ(clipboard_text, u"active text 2");
+  EXPECT_FALSE(ui::clipboard_test_util::IsFormatAvailable(
+      clipboard, ui::ClipboardFormatType::BitmapType(),
+      ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
+
+  // 8. Reactivate the tab.
+  browser()->tab_strip_model()->ActivateTabAt(active_controller_tab_index);
+  EXPECT_TRUE(base::test::RunUntil(
+      [&]() { return controller->state() == State::kOverlay; }));
+
+  // 9. Test CopyImage when active.
+  auto region2 = lens::mojom::CenterRotatedBox::New();
+  region2->box = gfx::RectF(0.1, 0.1, 0.2, 0.2);
+  region2->coordinate_type =
+      lens::mojom::CenterRotatedBox::CoordinateType::kNormalized;
+
+  page_handler->CopyImage(std::move(region2));
+
+  // Clipboard should now have an image format.
+  EXPECT_TRUE(ui::clipboard_test_util::IsFormatAvailable(
+      clipboard, ui::ClipboardFormatType::BitmapType(),
+      ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
+}
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.