Chrome · Google Lens › Lens
CVE-2026-113286
Memory Corruption in Google Lens (Lens)
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
FakeLensQueryFlowRouterchrome/browser/ui/lens/test_lens_search_controller.h |
modified |
Files Changed
chrome/browser/ui/lens/test_lens_search_controller.hchrome/browser/ui/lens/lens_search_controller.hchrome/browser/ui/lens/lens_query_flow_router.ccchrome/browser/ui/lens/lens_query_flow_router_unittest.ccchrome/browser/ui/lens/test_lens_search_controller.cc
Patch
From 1a4e43dc5b30459616d022ef6ec4dd1756958148 Mon Sep 17 00:00:00 2001
From: Jason Hu <hujasonx@google.com>
Date: Mon, 21 Sep 2026 12:59:52 -0700
Subject: [PATCH] [lens] Gate contextual-tasks force-tabs on permissions and origin
In LensQueryFlowRouter::SendInteractionToContextualTasks, force-tabs
contextualization for AIM queries was previously triggered whenever
overlay_tab_context_file_token_ was unset without checking whether the
user had granted Lens permissions, whether full page context was
allowed (region-only mode), or whether the active tab had navigated
cross-origin.
This CL gates the force-tabs block on ShouldPopulateFullPageContext()
and LensSearchController::IsCurrentTabSameOrigin() to prevent
unauthorized tab capture and upload.
Bug: b:563183231
Change-Id: Ie89a5742bd147a7b5537cc8d2fb920cecfaa50a1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8436197
Reviewed-by: Juan Mojica <juanmojica@google.com>
Commit-Queue: Jason Hu <hujasonx@google.com>
Cr-Commit-Position: refs/heads/main@{#1701988}
---
diff --git a/chrome/browser/ui/lens/lens_query_flow_router.cc b/chrome/browser/ui/lens/lens_query_flow_router.cc
index 08b023cf..d7286626 100644
--- a/chrome/browser/ui/lens/lens_query_flow_router.cc
+++ b/chrome/browser/ui/lens/lens_query_flow_router.cc
@@ -784,7 +784,9 @@
if (!overlay_tab_context_file_token_.has_value() &&
ShouldFetchActiveTabForInvocationSource(
pending_search_url_request_->invocation_source,
- GetContextualSearchSessionHandle())) {
+ GetContextualSearchSessionHandle()) &&
+ ShouldPopulateFullPageContext() &&
+ lens_search_controller_->IsCurrentTabSameOrigin()) {
force_tabs.push_back(tab_interface()->GetHandle().raw_value());
}
contextual_tasks::QueryContextualizer::ContextualizeParams params;
diff --git a/chrome/browser/ui/lens/lens_search_controller.h b/chrome/browser/ui/lens/lens_search_controller.h
index 8b561e71..75f9a9d3 100644
--- a/chrome/browser/ui/lens/lens_search_controller.h
+++ b/chrome/browser/ui/lens/lens_search_controller.h
@@ -285,7 +285,7 @@
// Returns whether the current tab's committed origin matches the origin
// of the page when this Lens session was initiated.
- bool IsCurrentTabSameOrigin() const;
+ virtual bool IsCurrentTabSameOrigin() const;
// Returns the origin of the page when this Lens session was initiated.
const url::Origin& session_origin() const { return session_origin_; }
diff --git a/chrome/browser/ui/lens/test_lens_search_controller.cc b/chrome/browser/ui/lens/test_lens_search_controller.cc
index d981ac2b8..a0c67a2 100644
--- a/chrome/browser/ui/lens/test_lens_search_controller.cc
+++ b/chrome/browser/ui/lens/test_lens_search_controller.cc
@@ -97,6 +97,7 @@
.WillByDefault([this](lens::LensOverlayDismissalSource dismissal_source) {
LensSearchController::CloseLensSync(dismissal_source);
});
+ ON_CALL(*this, IsCurrentTabSameOrigin()).WillByDefault(testing::Return(true));
}
MockLensSearchController::~MockLensSearchController() = default;
diff --git a/chrome/browser/ui/lens/test_lens_search_controller.h b/chrome/browser/ui/lens/test_lens_search_controller.h
index f8c77a1..5fd4204c 100644
--- a/chrome/browser/ui/lens/test_lens_search_controller.h
+++ b/chrome/browser/ui/lens/test_lens_search_controller.h
@@ -62,6 +62,8 @@
HandleInteractionResponse,
(lens::mojom::TextPtr text),
(override));
+
+ MOCK_METHOD(bool, IsCurrentTabSameOrigin, (), (const, override));
};
class FakeLensQueryFlowRouter : public LensQueryFlowRouter {
diff --git a/chrome/browser/ui/lens/lens_query_flow_router_unittest.cc b/chrome/browser/ui/lens/lens_query_flow_router_unittest.cc
index eb9bf4d..b7ac007 100644
--- a/chrome/browser/ui/lens/lens_query_flow_router_unittest.cc
+++ b/chrome/browser/ui/lens/lens_query_flow_router_unittest.cc
@@ -2993,6 +2993,7 @@
TEST_F(
LensQueryFlowRouterContextualTaskEnabledTest,
SendContextualTextQuery_WithoutExistingToken_PassesActiveTabToForceTabs) {
+ lens::GrantLensOverlayNeededPermissions(profile_.get());
auto mock_service = std::make_unique<
testing::NiceMock<contextual_tasks::MockContextualTasksService>>();
// FakeQueryContextualizerDelegate must outlive router.
@@ -3072,6 +3073,169 @@
TEST_F(
LensQueryFlowRouterContextualTaskEnabledTest,
+ SendContextualTextQuery_WithoutPermissions_DoesNotForceTabContextualization) {
+ // Do not grant permissions. Full page context should not be populated.
+ profile_->GetPrefs()->SetBoolean(lens::prefs::kLensSharingPageContentEnabled,
+ false);
+ auto mock_service = std::make_unique<
+ testing::NiceMock<contextual_tasks::MockContextualTasksService>>();
+ // FakeQueryContextualizerDelegate must outlive router.
+ auto fake_delegate = std::make_unique<FakeQueryContextualizerDelegate>();
+
+ TestLensQueryFlowRouter router(mock_lens_search_controller_.get(),
+ mock_context_controller_.get(),
+ profile_.get());
+ router.SetTabContextualizationController(
+ mock_tab_contextualization_controller_.get());
+
+ auto mock_query_contextualizer = std::make_unique<MockQueryContextualizer>(
+ mock_service.get(), fake_delegate.get());
+ auto* mock_contextualizer_ptr = mock_query_contextualizer.get();
+ ON_CALL(*mock_contextualizer_ptr,
+ MockContextualizeWithCallbacks(_, _, _, _, _, _))
+ .WillByDefault(
+ [](const std::optional<base::Uuid>& task_id,
+ const std::string& query_text,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_recontextualize,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_force_contextualize,
+ contextual_tasks::QueryContextualizer::TabProcessedCallback&
+ on_processed_callback,
+ contextual_tasks::QueryContextualizer::ContextualizedCallback
+ callback) { std::move(callback).Run(nullptr); });
+ router.SetQueryContextualizerForTesting(std::move(mock_query_contextualizer));
+
+ base::Time query_start_time = base::Time::Now();
+ std::string query_text = "test query";
+ lens::LensOverlaySelectionType selection_type =
+ lens::LensOverlaySelectionType::MULTIMODAL_SUGGEST_TYPEAHEAD;
+ std::map<std::string, std::string> additional_params;
+
+ EXPECT_CALL(*mock_contextualizer_ptr,
+ MockContextualize(testing::Eq(std::nullopt), query_text,
+ testing::IsEmpty(), testing::_))
+ .WillOnce(
+ [](const std::optional<base::Uuid>& task_id,
+ const std::string& query_text,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_recontextualize,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_force_contextualize) {
+ EXPECT_THAT(tabs_to_force_contextualize, testing::IsEmpty());
+ });
+
+ EXPECT_CALL(*router.mock_session_handle(), CreateSearchUrl(_, _))
+ .WillOnce(base::test::RunOnceCallback<1>(
+ GURL("https://www.google.com/search?q=test")));
+
+ auto* service = static_cast<MockContextualTasksUiService*>(
+ contextual_tasks::ContextualTasksUiServiceFactory::GetForBrowserContext(
+ profile_.get()));
+
+ EXPECT_CALL(*service,
+ StartTaskUiInSidePanelImpl(
+ mock_browser_window_interface_.get(), &mock_tab_interface_,
+ GURL("https://www.google.com/search?q=test"),
+ testing::Pointer(router.mock_session_handle()), testing::_))
+ .WillOnce(
+ [&router](
+ BrowserWindowInterface*, tabs::TabInterface*, const GURL&,
+ std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+ handle,
+ contextual_tasks::StartTaskUiOptions options) {
+ router.SetTransferredSessionHandle(std::move(handle));
+ });
+
+ router.SendContextualTextQuery(query_start_time, query_text, selection_type,
+ additional_params,
+ lens::LensOverlayInvocationSource::kAppMenu);
+}
+
+TEST_F(
+ LensQueryFlowRouterContextualTaskEnabledTest,
+ SendContextualTextQuery_CrossOriginTab_DoesNotForceTabContextualization) {
+ lens::GrantLensOverlayNeededPermissions(profile_.get());
+ EXPECT_CALL(*mock_lens_search_controller_, IsCurrentTabSameOrigin())
+ .WillRepeatedly(Return(false));
+
+ auto mock_service = std::make_unique<
+ testing::NiceMock<contextual_tasks::MockContextualTasksService>>();
+ // FakeQueryContextualizerDelegate must outlive router.
+ auto fake_delegate = std::make_unique<FakeQueryContextualizerDelegate>();
+
+ TestLensQueryFlowRouter router(mock_lens_search_controller_.get(),
+ mock_context_controller_.get(),
+ profile_.get());
+ router.SetTabContextualizationController(
+ mock_tab_contextualization_controller_.get());
+
+ auto mock_query_contextualizer = std::make_unique<MockQueryContextualizer>(
+ mock_service.get(), fake_delegate.get());
+ auto* mock_contextualizer_ptr = mock_query_contextualizer.get();
+ ON_CALL(*mock_contextualizer_ptr,
+ MockContextualizeWithCallbacks(_, _, _, _, _, _))
+ .WillByDefault(
+ [](const std::optional<base::Uuid>& task_id,
+ const std::string& query_text,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_recontextualize,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_force_contextualize,
+ contextual_tasks::QueryContextualizer::TabProcessedCallback&
+ on_processed_callback,
+ contextual_tasks::QueryContextualizer::ContextualizedCallback
+ callback) { std::move(callback).Run(nullptr); });
+ router.SetQueryContextualizerForTesting(std::move(mock_query_contextualizer));
+
+ base::Time query_start_time = base::Time::Now();
+ std::string query_text = "test query";
+ lens::LensOverlaySelectionType selection_type =
+ lens::LensOverlaySelectionType::MULTIMODAL_SUGGEST_TYPEAHEAD;
+ std::map<std::string, std::string> additional_params;
+
+ EXPECT_CALL(*mock_contextualizer_ptr,
+ MockContextualize(testing::Eq(std::nullopt), query_text,
+ testing::IsEmpty(), testing::_))
+ .WillOnce(
+ [](const std::optional<base::Uuid>& task_id,
+ const std::string& query_text,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_recontextualize,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_force_contextualize) {
+ EXPECT_THAT(tabs_to_force_contextualize, testing::IsEmpty());
+ });
+
+ EXPECT_CALL(*router.mock_session_handle(), CreateSearchUrl(_, _))
+ .WillOnce(base::test::RunOnceCallback<1>(
+ GURL("https://www.google.com/search?q=test")));
+
+ auto* service = static_cast<MockContextualTasksUiService*>(
+ contextual_tasks::ContextualTasksUiServiceFactory::GetForBrowserContext(
+ profile_.get()));
+
+ EXPECT_CALL(*service,
+ StartTaskUiInSidePanelImpl(
+ mock_browser_window_interface_.get(), &mock_tab_interface_,
+ GURL("https://www.google.com/search?q=test"),
+ testing::Pointer(router.mock_session_handle()), testing::_))
+ .WillOnce(
+ [&router](
+ BrowserWindowInterface*, tabs::TabInterface*, const GURL&,
+ std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+ handle,
+ contextual_tasks::StartTaskUiOptions options) {
+ router.SetTransferredSessionHandle(std::move(handle));
+ });
+
+ router.SendContextualTextQuery(query_start_time, query_text, selection_type,
+ additional_params,
+ lens::LensOverlayInvocationSource::kAppMenu);
+}
+
+TEST_F(
+ LensQueryFlowRouterContextualTaskEnabledTest,
SendContextualTextQuery_OmniboxContextualQuery_WithoutExistingToken_DoesNotForceTabContextualization) {
auto mock_service = std::make_unique<
testing::NiceMock<contextual_tasks::MockContextualTasksService>>();
@@ -3155,6 +3319,7 @@
TEST_F(
LensQueryFlowRouterContextualTaskEnabledTest,
SendContextualTextQuery_OmniboxContextualSuggestion_WithoutExistingToken_ForcesTabContextualization) {
+ lens::GrantLensOverlayNeededPermissions(profile_.get());
auto mock_service = std::make_unique<
testing::NiceMock<contextual_tasks::MockContextualTasksService>>();
// FakeQueryContextualizerDelegate must outlive router.
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/ui/lens/lens_query_flow_router_unittest.cc b/chrome/browser/ui/lens/lens_query_flow_router_unittest.cc
index eb9bf4d..b7ac007 100644
--- a/chrome/browser/ui/lens/lens_query_flow_router_unittest.cc
+++ b/chrome/browser/ui/lens/lens_query_flow_router_unittest.cc
@@ -2993,6 +2993,7 @@
TEST_F(
LensQueryFlowRouterContextualTaskEnabledTest,
SendContextualTextQuery_WithoutExistingToken_PassesActiveTabToForceTabs) {
+ lens::GrantLensOverlayNeededPermissions(profile_.get());
auto mock_service = std::make_unique<
testing::NiceMock<contextual_tasks::MockContextualTasksService>>();
// FakeQueryContextualizerDelegate must outlive router.
@@ -3072,6 +3073,169 @@
TEST_F(
LensQueryFlowRouterContextualTaskEnabledTest,
+ SendContextualTextQuery_WithoutPermissions_DoesNotForceTabContextualization) {
+ // Do not grant permissions. Full page context should not be populated.
+ profile_->GetPrefs()->SetBoolean(lens::prefs::kLensSharingPageContentEnabled,
+ false);
+ auto mock_service = std::make_unique<
+ testing::NiceMock<contextual_tasks::MockContextualTasksService>>();
+ // FakeQueryContextualizerDelegate must outlive router.
+ auto fake_delegate = std::make_unique<FakeQueryContextualizerDelegate>();
+
+ TestLensQueryFlowRouter router(mock_lens_search_controller_.get(),
+ mock_context_controller_.get(),
+ profile_.get());
+ router.SetTabContextualizationController(
+ mock_tab_contextualization_controller_.get());
+
+ auto mock_query_contextualizer = std::make_unique<MockQueryContextualizer>(
+ mock_service.get(), fake_delegate.get());
+ auto* mock_contextualizer_ptr = mock_query_contextualizer.get();
+ ON_CALL(*mock_contextualizer_ptr,
+ MockContextualizeWithCallbacks(_, _, _, _, _, _))
+ .WillByDefault(
+ [](const std::optional<base::Uuid>& task_id,
+ const std::string& query_text,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_recontextualize,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_force_contextualize,
+ contextual_tasks::QueryContextualizer::TabProcessedCallback&
+ on_processed_callback,
+ contextual_tasks::QueryContextualizer::ContextualizedCallback
+ callback) { std::move(callback).Run(nullptr); });
+ router.SetQueryContextualizerForTesting(std::move(mock_query_contextualizer));
+
+ base::Time query_start_time = base::Time::Now();
+ std::string query_text = "test query";
+ lens::LensOverlaySelectionType selection_type =
+ lens::LensOverlaySelectionType::MULTIMODAL_SUGGEST_TYPEAHEAD;
+ std::map<std::string, std::string> additional_params;
+
+ EXPECT_CALL(*mock_contextualizer_ptr,
+ MockContextualize(testing::Eq(std::nullopt), query_text,
+ testing::IsEmpty(), testing::_))
+ .WillOnce(
+ [](const std::optional<base::Uuid>& task_id,
+ const std::string& query_text,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_recontextualize,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_force_contextualize) {
+ EXPECT_THAT(tabs_to_force_contextualize, testing::IsEmpty());
+ });
+
+ EXPECT_CALL(*router.mock_session_handle(), CreateSearchUrl(_, _))
+ .WillOnce(base::test::RunOnceCallback<1>(
+ GURL("https://www.google.com/search?q=test")));
+
+ auto* service = static_cast<MockContextualTasksUiService*>(
+ contextual_tasks::ContextualTasksUiServiceFactory::GetForBrowserContext(
+ profile_.get()));
+
+ EXPECT_CALL(*service,
+ StartTaskUiInSidePanelImpl(
+ mock_browser_window_interface_.get(), &mock_tab_interface_,
+ GURL("https://www.google.com/search?q=test"),
+ testing::Pointer(router.mock_session_handle()), testing::_))
+ .WillOnce(
+ [&router](
+ BrowserWindowInterface*, tabs::TabInterface*, const GURL&,
+ std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+ handle,
+ contextual_tasks::StartTaskUiOptions options) {
+ router.SetTransferredSessionHandle(std::move(handle));
+ });
+
+ router.SendContextualTextQuery(query_start_time, query_text, selection_type,
+ additional_params,
+ lens::LensOverlayInvocationSource::kAppMenu);
+}
+
+TEST_F(
+ LensQueryFlowRouterContextualTaskEnabledTest,
+ SendContextualTextQuery_CrossOriginTab_DoesNotForceTabContextualization) {
+ lens::GrantLensOverlayNeededPermissions(profile_.get());
+ EXPECT_CALL(*mock_lens_search_controller_, IsCurrentTabSameOrigin())
+ .WillRepeatedly(Return(false));
+
+ auto mock_service = std::make_unique<
+ testing::NiceMock<contextual_tasks::MockContextualTasksService>>();
+ // FakeQueryContextualizerDelegate must outlive router.
+ auto fake_delegate = std::make_unique<FakeQueryContextualizerDelegate>();
+
+ TestLensQueryFlowRouter router(mock_lens_search_controller_.get(),
+ mock_context_controller_.get(),
+ profile_.get());
+ router.SetTabContextualizationController(
+ mock_tab_contextualization_controller_.get());
+
+ auto mock_query_contextualizer = std::make_unique<MockQueryContextualizer>(
+ mock_service.get(), fake_delegate.get());
+ auto* mock_contextualizer_ptr = mock_query_contextualizer.get();
+ ON_CALL(*mock_contextualizer_ptr,
+ MockContextualizeWithCallbacks(_, _, _, _, _, _))
+ .WillByDefault(
+ [](const std::optional<base::Uuid>& task_id,
+ const std::string& query_text,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_recontextualize,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_force_contextualize,
+ contextual_tasks::QueryContextualizer::TabProcessedCallback&
+ on_processed_callback,
+ contextual_tasks::QueryContextualizer::ContextualizedCallback
+ callback) { std::move(callback).Run(nullptr); });
+ router.SetQueryContextualizerForTesting(std::move(mock_query_contextualizer));
+
+ base::Time query_start_time = base::Time::Now();
+ std::string query_text = "test query";
+ lens::LensOverlaySelectionType selection_type =
+ lens::LensOverlaySelectionType::MULTIMODAL_SUGGEST_TYPEAHEAD;
+ std::map<std::string, std::string> additional_params;
+
+ EXPECT_CALL(*mock_contextualizer_ptr,
+ MockContextualize(testing::Eq(std::nullopt), query_text,
+ testing::IsEmpty(), testing::_))
+ .WillOnce(
+ [](const std::optional<base::Uuid>& task_id,
+ const std::string& query_text,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_recontextualize,
+ const std::vector<contextual_tasks::QueryContextualizer::TabId>&
+ tabs_to_force_contextualize) {
+ EXPECT_THAT(tabs_to_force_contextualize, testing::IsEmpty());
+ });
+
+ EXPECT_CALL(*router.mock_session_handle(), CreateSearchUrl(_, _))
+ .WillOnce(base::test::RunOnceCallback<1>(
+ GURL("https://www.google.com/search?q=test")));
+
+ auto* service = static_cast<MockContextualTasksUiService*>(
+ contextual_tasks::ContextualTasksUiServiceFactory::GetForBrowserContext(
+ profile_.get()));
+
+ EXPECT_CALL(*service,
+ StartTaskUiInSidePanelImpl(
+ mock_browser_window_interface_.get(), &mock_tab_interface_,
+ GURL("https://www.google.com/search?q=test"),
+ testing::Pointer(router.mock_session_handle()), testing::_))
+ .WillOnce(
+ [&router](
+ BrowserWindowInterface*, tabs::TabInterface*, const GURL&,
+ std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+ handle,
+ contextual_tasks::StartTaskUiOptions options) {
+ router.SetTransferredSessionHandle(std::move(handle));
+ });
+
+ router.SendContextualTextQuery(query_start_time, query_text, selection_type,
+ additional_params,
+ lens::LensOverlayInvocationSource::kAppMenu);
+}
+
+TEST_F(
+ LensQueryFlowRouterContextualTaskEnabledTest,
SendContextualTextQuery_OmniboxContextualQuery_WithoutExistingToken_DoesNotForceTabContextualization) {
auto mock_service = std::make_unique<
testing::NiceMock<contextual_tasks::MockContextualTasksService>>();
@@ -3155,6 +3319,7 @@
TEST_F(
LensQueryFlowRouterContextualTaskEnabledTest,
SendContextualTextQuery_OmniboxContextualSuggestion_WithoutExistingToken_ForcesTabContextualization) {
+ lens::GrantLensOverlayNeededPermissions(profile_.get());
auto mock_service = std::make_unique<
testing::NiceMock<contextual_tasks::MockContextualTasksService>>();
// FakeQueryContextualizerDelegate must outlive router.
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