Chrome · Google Lens
CVE-2026-18002
Logic Error in Google Lens
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Files Changed
chrome/browser/ui/lens/BUILD.gnchrome/browser/ui/lens/lens_overlay_controller.ccchrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
Patch
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.
References
On This Page