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 <[email protected]> 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 <[email protected]> Commit-Queue: Jason Hu <[email protected]> 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