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 <[email protected]> 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 <[email protected]> Commit-Queue: Juan Mojica <[email protected]> Reviewed-by: Juan Mojica <[email protected]> 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