Medium chrome Authorization Error 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMissing authorization in Google Lens
DescriptionMissing authorization in Google Lens
ComponentGoogle Lens
Sub-componentLensoverlay
Bug ClassAuthorization Error
Tracker501770489
Fix commitc13c2eeeba9f (chromium/src) +488/-60
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-06

Changed Functions

FunctionChangeNotes
GetLensSuggestInputs
chrome/browser/ui/lens/lens_composebox_controller.cc
modified
if
chrome/browser/ui/lens/lens_overlay_controller.cc
modified
lens_search_controller_
chrome/browser/ui/lens/lens_search_contextualization_controller.cc
modified
if
chrome/browser/ui/lens/lens_search_contextualization_controller.cc
modified

Files Changed

  • chrome/browser/ui/lens/lens_search_controller.h
  • chrome/browser/ui/lens/lens_search_contextualization_controller.cc
  • chrome/browser/ui/lens/lens_composebox_controller_browsertest.cc
  • chrome/browser/ui/lens/lens_search_controller.cc
  • chrome/browser/ui/lens/lens_overlay_controller.cc
  • chrome/browser/ui/lens/lens_search_contextualization_controller.h
  • chrome/browser/ui/lens/lens_composebox_controller.cc
From c13c2eeeba9fb7d7442efd85f0e8d5cf805d6e2d Mon Sep 17 00:00:00 2001
From: Jason Hu <[email protected]>
Date: Mon, 14 Sep 2026 15:17:22 -0700
Subject: [PATCH] [lensoverlay] Prevent cross-origin leaks during contextualization

In flows where the Lens side panel is open without a frozen overlay
(e.g., side-panel-only text queries, or after navigating to a live
page with the overlay hidden), cross-origin navigations could allow
context or session tokens from the initial page to leak into
subsequent queries or in-flight extractions.

This CL prevents cross-origin context leaks by:
1. Setting session_origin_ on LensSearchController and guarding
   contextualization access via IsCurrentTabSameOrigin().
2. Canceling in-flight extraction in
   LensSearchContextualizationController on PrimaryPageChanged via a
   dedicated in_flight_weak_ptr_factory_.
3. Sanitizing suggest inputs and query message payloads in
   LensComposeboxController and blocking overlay reshow / context
   updates in LensOverlayController when the tab has navigated
   cross-origin.

Bug: 501770489
Change-Id: I4e3745783f128a897629bb419198d39b59d6a2c4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8403211
Reviewed-by: Juan Mojica <[email protected]>
Commit-Queue: Jason Hu <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1697593}
---
diff --git a/chrome/browser/ui/lens/lens_composebox_controller.cc b/chrome/browser/ui/lens/lens_composebox_controller.cc
index 4cf389e..88003456 100644
--- a/chrome/browser/ui/lens/lens_composebox_controller.cc
+++ b/chrome/browser/ui/lens/lens_composebox_controller.cc
@@ -146,6 +146,10 @@
     return;
   }
 
+  if (!lens_search_controller_->IsCurrentTabSameOrigin()) {
+    return;
+  }
+
   // Ignore if contextualization is not active.
   if (!lens_search_controller_->lens_search_contextualization_controller()
            ->IsActive()) {
@@ -270,6 +274,9 @@
 
 lens::proto::LensOverlaySuggestInputs
 LensComposeboxController::GetLensSuggestInputs() const {
+  if (!lens_search_controller_->IsCurrentTabSameOrigin()) {
+    return lens::proto::LensOverlaySuggestInputs();
+  }
   if (!lens::features::GetAimSuggestionsEnabled()) {
     return lens::proto::LensOverlaySuggestInputs();
   }
@@ -329,13 +336,15 @@
   lens_image_query_data->set_search_session_id(
       query_controller->search_session_id());
 
+  const bool is_same_origin = lens_search_controller_->IsCurrentTabSameOrigin();
   const auto& primary_content_type =
-      contextualization_controller->primary_content_type();
+      is_same_origin ? contextualization_controller->primary_content_type()
+                     : lens::MimeType::kUnknown;
   const auto media_type =
       overlay_controller->HasRegionSelection()
           ? lens::LensOverlayRequestId::MEDIA_TYPE_DEFAULT_IMAGE
           : MimeTypeToMediaType(primary_content_type,
-                                /*has_viewport_screenshot=*/true);
+                                /*has_viewport_screenshot=*/is_same_origin);
   lens_image_query_data->mutable_request_id()->CopyFrom(
       *query_controller->GetNextRequestId(lens::RequestIdUpdateMode::kSearchUrl,
                                           media_type));
@@ -346,7 +355,7 @@
   std::optional<lens::LensOverlayVisualSearchInteractionData>
       visual_search_interaction_data =
           query_controller->GetVisualSearchInteractionData();
-  if (visual_search_interaction_data &&
+  if (is_same_origin && visual_search_interaction_data &&
       overlay_controller->HasRegionSelection()) {
     lens_image_query_data->mutable_visual_search_interaction_data()->CopyFrom(
         visual_search_interaction_data.value());
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.cc b/chrome/browser/ui/lens/lens_overlay_controller.cc
index 60e3b79..ce44f4e 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller.cc
@@ -906,6 +906,9 @@
     GetLensSessionMetricsLogger()->OnSearchboxFocused();
 
     if (state() == State::kHidden) {
+      if (!lens_search_controller_->IsCurrentTabSameOrigin()) {
+        return;
+      }
       // If the live page is showing and the searchbox becomes focused, showing
       // intent to issue a new query, upload the new page content for
       // contextualization.
@@ -1136,6 +1139,11 @@
   if (page_context_start_time.has_value()) {
     lens::RecordTimeToGetPageContext(base::TimeTicks::Now() - invocation_time_);
   }
+  if (!lens_search_controller_->IsCurrentTabSameOrigin()) {
+    page_contents.clear();
+    primary_content_type = lens::MimeType::kUnknown;
+    page_count = std::nullopt;
+  }
   initialization_data->page_contents_ = page_contents;
   initialization_data->primary_content_type_ = primary_content_type;
   initialization_data->pdf_page_count_ = page_count;
@@ -1912,6 +1920,9 @@
 }
 
 void LensOverlayController::ReshowOverlay() {
+  if (!lens_search_controller_->IsCurrentTabSameOrigin()) {
+    return;
+  }
   OverlayBaseController::ReshowOverlay();
   use_aim_for_visual_search_ = true;
 
@@ -2109,6 +2120,9 @@
 }
 
 void LensOverlayController::NotifyPageContentUpdated() {
+  if (!lens_search_controller_->IsCurrentTabSameOrigin()) {
+    return;
+  }
   auto page_content_type = lens::StringMimeTypeToMojoPageContentType(
       tab_->GetContents()->GetContentsMimeType());
   if (page_) {
@@ -2235,6 +2249,9 @@
 }
 
 void LensOverlayController::ReshowOverlayPart2() {
+  if (!lens_search_controller_->IsCurrentTabSameOrigin()) {
+    return;
+  }
   ReshowScreenshot(GetContextualizationController()->viewport_screenshot(),
                    base::BindOnce(&LensOverlayController::ReshowOverlayPart3,
                                   weak_factory_.GetWeakPtr()));
diff --git a/chrome/browser/ui/lens/lens_search_contextualization_controller.cc b/chrome/browser/ui/lens/lens_search_contextualization_controller.cc
index 5b1da1e..4f539de8 100644
--- a/chrome/browser/ui/lens/lens_search_contextualization_controller.cc
+++ b/chrome/browser/ui/lens/lens_search_contextualization_controller.cc
@@ -143,10 +143,38 @@
 
 LensSearchContextualizationController::LensSearchContextualizationController(
     LensSearchController* lens_search_controller)
-    : lens_search_controller_(lens_search_controller) {}
+    : content::WebContentsObserver(
+          lens_search_controller->GetTabInterface()
+              ? lens_search_controller->GetTabInterface()->GetContents()
+              : nullptr),
+      lens_search_controller_(lens_search_controller) {}
 LensSearchContextualizationController::
     ~LensSearchContextualizationController() = default;
 
+void LensSearchContextualizationController::CancelInFlightContextualization() {
+  in_flight_weak_ptr_factory_.InvalidateWeakPtrs();
+  is_page_context_eligible_ = false;
+  page_contents_.clear();
+  primary_content_type_ = lens::MimeType::kUnknown;
+  viewport_screenshot_.reset();
+  last_retrieved_most_visible_page_ = std::nullopt;
+  pdf_partial_page_text_retrieved_callback_.Reset();
+  pdf_pages_text_.clear();
+  page_context_eligibility_callback_.Reset();
+  pending_context_eligibility_params_.reset();
+  if (state_ == State::kInitializing) {
+    state_ = State::kOff;
+  }
+  if (on_page_context_updated_callback_) {
+    std::move(on_page_context_updated_callback_).Run();
+  }
+}
+
+void LensSearchContextualizationController::PrimaryPageChanged(
+    content::Page& page) {
+  CancelInFlightContextualization();
+}
+
 void LensSearchContextualizationController::StartContextualization(
     lens::LensOverlayInvocationSource invocation_source,
     OnPageContextUpdatedCallback callback) {
@@ -157,14 +185,16 @@
   // needed.
   StartScreenshotFlow(base::BindOnce(
       &LensSearchContextualizationController::OnScreenshotTakenForContextual,
-      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+      in_flight_weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void LensSearchContextualizationController::GetPageContextualization(
     PageContentRetrievedCallback callback) {
-  // If the contextual searchbox is disabled, exit early.
+  // If the contextual searchbox is disabled or tab is not same origin, exit
+  // early.
   if (!lens::IsLensOverlayContextualSearchboxEnabled(
-          lens_search_controller_->GetProfile())) {
+          lens_search_controller_->GetProfile()) ||
+      !lens_search_controller_->IsCurrentTabSameOrigin()) {
     std::move(callback).Run(/*page_contents=*/{}, lens::MimeType::kUnknown,
                             std::nullopt);
     return;
@@ -209,6 +239,10 @@
     // controller finishes initializing.
     return;
   }
+  if (!lens_search_controller_->IsCurrentTabSameOrigin()) {
+    std::move(callback).Run();
+    return;
+  }
   if (state_ == State::kOff) {
     // TODO(crbug.com/418825720): The viewport screenshot should be only be set
     // in this controller in the future.
@@ -230,7 +264,7 @@
   on_page_context_updated_callback_ = std::move(callback);
   GetPageContextualization(base::BindOnce(
       &LensSearchContextualizationController::UpdatePageContextualization,
-      weak_ptr_factory_.GetWeakPtr()));
+      in_flight_weak_ptr_factory_.GetWeakPtr()));
 }
 
 #if BUILDFLAG(ENABLE_PDF)
@@ -258,24 +292,16 @@
       /*page_index=*/0,
       base::BindOnce(
           &LensSearchContextualizationController::GetPartialPdfTextCallback,
-          weak_ptr_factory_.GetWeakPtr(), /*page_index=*/0, page_count,
+          in_flight_weak_ptr_factory_.GetWeakPtr(), /*page_index=*/0,
+          page_count,
           /*total_characters_retrieved=*/0));
 }
 #endif  // BUILDFLAG(ENABLE_PDF)
 
 void LensSearchContextualizationController::ResetState() {
   on_page_context_updated_callback_.Reset();
-  is_page_context_eligible_ = false;
+  CancelInFlightContextualization();
   ocr_dom_similarity_recorded_in_session_ = false;
-  page_contents_.clear();
-  primary_content_type_ = lens::MimeType::kUnknown;
-  viewport_screenshot_.reset();
-  last_retrieved_most_visible_page_ = std::nullopt;
-  pdf_partial_page_text_retrieved_callback_.Reset();
-  pdf_pages_text_.clear();
-  // Reset the page context eligibility API state.
-  page_context_eligibility_callback_.Reset();
-  pending_context_eligibility_params_.reset();
   weak_ptr_factory_.InvalidateWeakPtrs();
   state_ = State::kOff;
 }
@@ -319,7 +345,7 @@
         *render_frame_host, /*node_id=*/std::nullopt,
         base::BindOnce(
             &LensSearchContextualizationController::RecordInnerTextSize,
-            weak_ptr_factory_.GetWeakPtr()));
+            in_flight_weak_ptr_factory_.GetWeakPtr()));
   }
 
   // Try and record the OCR DOM similarity since the page content is now
@@ -376,8 +402,12 @@
     std::vector<lens::PageContent> page_contents,
     lens::MimeType primary_content_type,
     std::optional<uint32_t> page_count) {
-  // Exit early if the controller is off.
-  if (state_ == State::kOff) {
+  // Exit early if the controller is off or the tab is not same origin.
+  if (state_ == State::kOff ||
+      !lens_search_controller_->IsCurrentTabSameOrigin()) {
+    if (on_page_context_updated_callback_) {
+      std::move(on_page_context_updated_callback_).Run();
+    }
     return;
   }
 
@@ -407,8 +437,8 @@
   // Begin the process of grabbing a screenshot.
   CaptureScreenshot(base::BindOnce(
       &LensSearchContextualizationController::UpdatePageContextualizationPart2,
-      weak_ptr_factory_.GetWeakPtr(), page_contents, primary_content_type,
-      page_count));
+      in_flight_weak_ptr_factory_.GetWeakPtr(), page_contents,
+      primary_content_type, page_count));
 }
 
 void LensSearchContextualizationController::UpdatePageContextualizationPart2(
@@ -430,8 +460,8 @@
   if (pdf_helper) {
     pdf_helper->GetMostVisiblePageIndex(base::BindOnce(
         &LensSearchContextualizationController::UpdatePageContext,
-        weak_ptr_factory_.GetWeakPtr(), page_contents, primary_content_type,
-        page_count, bitmap));
+        in_flight_weak_ptr_factory_.GetWeakPtr(), page_contents,
+        primary_content_type, page_count, bitmap));
     return;
   }
 #endif  // BUILDFLAG(ENABLE_PDF)
@@ -449,7 +479,11 @@
   // It's possible the Lens session could have been closed while updating the
   // page context. Return early and do not run the callback as it should have
   // been cleared.
-  if (state_ == State::kOff) {
+  if (state_ == State::kOff ||
+      !lens_search_controller_->IsCurrentTabSameOrigin()) {
+    if (on_page_context_updated_callback_) {
+      std::move(on_page_context_updated_callback_).Run();
+    }
     return;
   }
 
@@ -551,7 +585,7 @@
         page_count.value_or(0),
         base::BindOnce(&LensSearchContextualizationController::
                            OnPdfPartialPageTextRetrieved,
-                       weak_ptr_factory_.GetWeakPtr()));
+                       in_flight_weak_ptr_factory_.GetWeakPtr()));
   }
 #endif
 
@@ -583,8 +617,8 @@
       *render_frame_host, /*node_id=*/std::nullopt,
       base::BindOnce(
           &LensSearchContextualizationController::OnInnerTextReceived,
-          weak_ptr_factory_.GetWeakPtr(), page_contents, render_frame_host,
-          std::move(callback)));
+          in_flight_weak_ptr_factory_.GetWeakPtr(), page_contents,
+          render_frame_host, std::move(callback)));
 }
 
 void LensSearchContextualizationController::OnInnerTextReceived(
@@ -592,6 +626,12 @@
     content::RenderFrameHost* render_frame_host,
     PageContentRetrievedCallback callback,
     std::unique_ptr<content_extraction::InnerTextResult> result) {
+  if (!lens_search_controller_->IsCurrentTabSameOrigin()) {
+    std::move(callback).Run(/*page_contents=*/{}, lens::MimeType::kUnknown,
+                            std::nullopt);
+    return;
+  }
+
   const bool was_successful =
       result && result->inner_text.size() <=
                     lens::features::GetLensOverlayFileUploadLimitBytes();
@@ -633,7 +673,7 @@
       std::move(ai_page_content_options),
       base::BindOnce(&LensSearchContextualizationController::
                          OnAnnotatedPageContentReceived,
-                     weak_ptr_factory_.GetWeakPtr(), page_contents,
+                     in_flight_weak_ptr_factory_.GetWeakPtr(), page_contents,
                      std::move(callback)));
 }
 
@@ -641,6 +681,12 @@
     std::vector<lens::PageContent> page_contents,
     PageContentRetrievedCallback callback,
     optimization_guide::AIPageContentResultOrError result) {
+  if (!lens_search_controller_->IsCurrentTabSameOrigin()) {
+    std::move(callback).Run(/*page_contents=*/{}, lens::MimeType::kUnknown,
+                            std::nullopt);
+    return;
+  }
+
   // The tab URL is used to check if the page is context eligible.
   const auto& tab_url = lens_search_controller_->GetTabInterface()
                             ->GetContents()
@@ -659,17 +705,18 @@
         tab_url, std::move(frame_metadata_structs),
         base::BindOnce(&LensSearchContextualizationController::
                            OnPageContextEligibilityFetched,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(page_contents),
-                       std::move(callback), std::move(result.value())));
+                       in_flight_weak_ptr_factory_.GetWeakPtr(),
+                       std::move(page_contents), std::move(callback),
+                       std::move(result.value())));
     return;
   }
 
-  IsPageContextEligible(
-      tab_url, {},
-      base::BindOnce(&LensSearchContextualizationController::
-                         OnPageContextEligibilityFetched,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(page_contents),
-                     std::move(callback), std::nullopt));
+  IsPageContextEligible(tab_url, {},
+                        base::BindOnce(&LensSearchContextualizationController::
+                                           OnPageContextEligibilityFetched,
+                                       in_flight_weak_ptr_factory_.GetWeakPtr(),
+                                       std::move(page_contents),
+                                       std::move(callback), std::nullopt));
 }
 
 void LensSearchContextualizationController::OnPageContextEligibilityFetched(
@@ -677,6 +724,12 @@
     PageContentRetrievedCallback callback,
     std::optional<optimization_guide::AIPageContentResult> result,
     bool is_page_context_eligible) {
+  if (!lens_search_controller_->IsCurrentTabSameOrigin()) {
+    std::move(callback).Run(/*page_contents=*/{}, lens::MimeType::kUnknown,
+                            std::nullopt);
+    return;
+  }
+
... (file diff truncated)
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/ui/lens/lens_composebox_controller_browsertest.cc b/chrome/browser/ui/lens/lens_composebox_controller_browsertest.cc
index dc83fdba..3091ad22 100644
--- a/chrome/browser/ui/lens/lens_composebox_controller_browsertest.cc
+++ b/chrome/browser/ui/lens/lens_composebox_controller_browsertest.cc
@@ -39,6 +39,7 @@
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
+#include "net/dns/mock_host_resolver.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/lens_server_proto/aim_communication.pb.h"
 #include "ui/base/unowned_user_data/user_data_factory.h"
@@ -197,6 +198,7 @@
 
   void SetUpOnMainThread() override {
     InProcessBrowserTest::SetUpOnMainThread();
+    host_resolver()->AddRule("*", "127.0.0.1");
     embedded_test_server()->StartAcceptingConnections();
 
     // Permits sharing the page screenshot by default.
@@ -1396,3 +1398,213 @@
   // Simulate focus. This should not crash.
   GetLensComposeboxController()->OnFocusChanged(true);
 }
+
+IN_PROC_BROWSER_TEST_F(LensComposeboxControllerBrowserTest,
+                       LensButtonClickDoesNotReshowOnDifferentOrigin) {
+  const GURL initial_url =
+      embedded_test_server()->GetURL("a.com", "/select.html");
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
+
+  auto* controller = GetLensSearchController();
+  ASSERT_TRUE(controller->IsOff());
+
+  // Issue a text search request to open the side panel without the overlay.
+  controller->IssueTextSearchRequest(
+      lens::LensOverlayInvocationSource::kContentAreaContextMenuText, "query",
+      {}, AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED,
+      /*is_zero_prefix_suggestion=*/false,
+      /*suppress_contextualization=*/true);
+
+  // Wait for side panel to be visible.
+  ASSERT_TRUE(
+      base::test::RunUntil([&]() { return IsResultsSidePanelShowing(); }));
+
+  // Wait for the composebox handler to be set.
+  ASSERT_TRUE(base::test::RunUntil([&]() {
+    return GetLensComposeboxController()->composebox_handler_for_testing() !=
+           nullptr;
+  }));
+
+  auto* overlay_controller = GetLensOverlayController();
+  EXPECT_FALSE(overlay_controller->IsOverlayActive());
+
+  // Navigate the main tab to a different origin.
+  const GURL different_origin_url =
+      embedded_test_server()->GetURL("b.com", "/title1.html");
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), different_origin_url));
+
+  // Trigger Lens button click from composebox handler.
+  GetLensComposeboxController()
+      ->composebox_handler_for_testing()
+      ->HandleLensButtonClick();
+
+  // The overlay must not open on the different origin.
+  EXPECT_FALSE(overlay_controller->IsOverlayActive());
+  EXPECT_NE(overlay_controller->state(), State::kOverlay);
+}
+
+IN_PROC_BROWSER_TEST_F(LensComposeboxControllerBrowserTest,
+                       FocusChangedDoesNotContextualizeOnDifferentOrigin) {
+  const GURL initial_url =
+      embedded_test_server()->GetURL("a.com", "/select.html");
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
+
+  auto* controller = GetLensSearchController();
+  ASSERT_TRUE(controller->IsOff());
+
+  // Issue a text search request to open the side panel without the overlay.
+  controller->IssueTextSearchRequest(
+      lens::LensOverlayInvocationSource::kContentAreaContextMenuText, "query",
+      {}, AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED,
+      /*is_zero_prefix_suggestion=*/false,
+      /*suppress_contextualization=*/true);
+
+  // Wait for side panel to be visible.
+  ASSERT_TRUE(
+      base::test::RunUntil([&]() { return IsResultsSidePanelShowing(); }));
+
+  // Wait for the composebox handler to be set.
+  ASSERT_TRUE(base::test::RunUntil([&]() {
+    return GetLensComposeboxController()->composebox_handler_for_testing() !=
+           nullptr;
+  }));
+
+  auto* fake_query_controller =
+      static_cast<lens::TestLensOverlayQueryController*>(
+          controller->lens_overlay_query_controller());
+  ASSERT_TRUE(base::test::RunUntil([&]() {
+    return fake_query_controller->num_full_image_requests_sent() == 1 &&
+           fake_query_controller->num_page_content_update_requests_sent() == 1;
+  }));
+
+  const int initial_updates =
+      fake_query_controller->num_page_content_update_requests_sent();
+
+  auto* composebox_controller = GetLensComposeboxController();
+  auto* test_composebox_controller =
+      static_cast<TestLensComposeboxController*>(composebox_controller);
+  ASSERT_TRUE(test_composebox_controller);
+  MockSearchboxPage& mock_searchbox_page =
+      test_composebox_controller->mock_searchbox_page();
+  EXPECT_CALL(mock_searchbox_page, SetThumbnail(testing::_, testing::_))
+      .Times(0);
+
+  // Navigate the main tab to a different origin.
+  const GURL different_origin_url =
+      embedded_test_server()->GetURL("b.com", "/title1.html");
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), different_origin_url));
+
+  // Trigger FocusChanged from composebox handler.
+  GetLensComposeboxController()->composebox_handler_for_testing()->FocusChanged(
+      true);
+
+  // Verify that no new page content update was sent for the different origin.
+  EXPECT_EQ(fake_query_controller->num_page_content_update_requests_sent(),
+            initial_updates);
+}
+
+IN_PROC_BROWSER_TEST_F(LensComposeboxControllerBrowserTest,
+                       OpenLensOverlayInCurrentSessionRequiresPermission) {
+  WaitForPaint();
+
+  auto* controller = GetLensSearchController();
+  ASSERT_TRUE(controller->IsOff());
+
+  // Issue a text search request to open the side panel without the overlay.
+  controller->IssueTextSearchRequest(
+      lens::LensOverlayInvocationSource::kContentAreaContextMenuText, "query",
+      {}, AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED,
+      /*is_zero_prefix_suggestion=*/false,
+      /*suppress_contextualization=*/true);
+
+  // Wait for side panel to be visible.
+  ASSERT_TRUE(
+      base::test::RunUntil([&]() { return IsResultsSidePanelShowing(); }));
+
+  // Wait for the composebox handler to be set.
+  ASSERT_TRUE(base::test::RunUntil([&]() {
+    return GetLensComposeboxController()->composebox_handler_for_testing() !=
+           nullptr;
+  }));
+
+  auto* overlay_controller = GetLensOverlayController();
+  EXPECT_FALSE(overlay_controller->IsOverlayActive());
+
+  // Revoke screenshot and content permissions.
+  PrefService* prefs = browser()->GetProfile()->GetPrefs();
+  prefs->SetBoolean(lens::prefs::kLensSharingPageScreenshotEnabled, false);
+  prefs->SetBoolean(lens::prefs::kLensSharingPageContentEnabled, false);
+
+  // Attempt to open overlay in current session without permission.
+  controller->OpenLensOverlayInCurrentSession();
+
+  // Overlay should not open because eligibility checks require permission.
+  EXPECT_FALSE(overlay_controller->IsOverlayActive());
+  EXPECT_NE(overlay_controller->state(), State::kOverlay);
+}
+
+IN_PROC_BROWSER_TEST_F(LensComposeboxControllerBrowserTest,
+                       SuggestInputsAndSubmitQueryClearedOnDifferentOrigin) {
+  const GURL initial_url =
+      embedded_test_server()->GetURL("a.com", "/select.html");
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
+
+  auto* controller = GetLensSearchController();
+  ASSERT_TRUE(controller->IsOff());
+
+  // Issue a text search request to open the side panel without the overlay.
+  controller->IssueTextSearchRequest(
+      lens::LensOverlayInvocationSource::kContentAreaContextMenuText, "query",
+      {}, AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED,
+      /*is_zero_prefix_suggestion=*/false,
+      /*suppress_contextualization=*/true);
+
+  ASSERT_TRUE(
+      base::test::RunUntil([&]() { return IsResultsSidePanelShowing(); }));
+
+  ASSERT_TRUE(base::test::RunUntil([&]() {
+    return GetLensComposeboxController()->composebox_handler_for_testing() !=
+           nullptr;
+  }));
+
+  EXPECT_TRUE(controller->IsCurrentTabSameOrigin());
+
+  // Navigate to different origin.
+  const GURL different_origin_url =
+      embedded_test_server()->GetURL("b.com", "/title1.html");
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), different_origin_url));
+
+  EXPECT_FALSE(controller->IsCurrentTabSameOrigin());
+
+  // Suggest inputs should be empty on different origin.
+  auto suggest_inputs = GetLensComposeboxController()->GetLensSuggestInputs();
+  EXPECT_TRUE(
+      suggest_inputs.encoded_visual_search_interaction_log_data().empty());
+  EXPECT_TRUE(suggest_inputs.search_session_id().empty());
+
+  // Mock a handshake call so the composebox controller can send query messages.
+  lens::AimToClientMessage aim_to_client_message;
+  aim_to_client_message.mutable_handshake_response()->add_capabilities(
+      lens::FeatureCapability::DEFAULT);
+  MockAimToClientMessage(aim_to_client_message);
+
+  // SubmitQuery message should not include visual search interaction data or
+  // valid visual input type for the different origin.
+  GetLensComposeboxController()->composebox_handler_for_testing()->SubmitQuery(
+      "new query", /*mouse_button=*/0, /*alt_key=*/false, /*ctrl_key=*/false,
+      /*meta_key=*/false, /*shift_key=*/false, /*is_voice_search=*/false);
+
+  auto* test_side_panel_coordinator = GetLensSidePanelCoordinator();
+  ASSERT_TRUE(test_side_panel_coordinator);
+  ASSERT_TRUE(test_side_panel_coordinator->last_sent_client_message_to_aim_
+                  .has_submit_query());
+  const auto& submit_query =
+      test_side_panel_coordinator->last_sent_client_message_to_aim_
+          .submit_query();
+  ASSERT_EQ(submit_query.payload().lens_image_query_data_size(), 1);
+  const auto& image_query_data =
+      submit_query.payload().lens_image_query_data(0);
+  EXPECT_FALSE(image_query_data.has_visual_search_interaction_data());
+  EXPECT_EQ(image_query_data.visual_input_type(),
+            lens::LensOverlayVisualInputType::VISUAL_INPUT_TYPE_UNKNOWN);
+}
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.