Medium chrome Logic Error 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in Glic
DescriptionInsufficient validation of untrusted input in Glic
ComponentGlic
Bug ClassLogic Error
Tracker497604407
Fix commit0906aba512c8 (chromium/src) +187/-13
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Changed Functions

FunctionChangeNotes
Profile
chrome/browser/glic/host/context/glic_tab_data_observer.h
modified
GlicTabDataObserver
chrome/browser/glic/host/context/glic_tab_data_observer.h
modified
Profile
chrome/browser/glic/host/context/glic_tab_favicon_observer.h
modified
GlicTabFaviconObserver
chrome/browser/glic/host/context/glic_tab_favicon_observer.h
modified
if
chrome/browser/glic/host/glic_page_handler.cc
modified

Files Changed

  • chrome/browser/glic/host/context/glic_sharing_manager_impl.cc
  • chrome/browser/glic/host/context/glic_tab_data_observer.cc
  • chrome/browser/glic/host/context/glic_tab_data_observer.h
  • chrome/browser/glic/host/context/glic_tab_favicon_observer.cc
  • chrome/browser/glic/host/context/glic_tab_favicon_observer.h
  • chrome/browser/glic/host/glic_page_handler.cc
  • chrome/browser/glic/host/new_glic_api_browsertest.cc
From 0906aba512c8f7949f313b725c26db54262bb1d5 Mon Sep 17 00:00:00 2001
From: Dan Harrington <harringtond@chromium.org>
Date: Wed, 15 Apr 2026 14:24:42 -0700
Subject: [PATCH] glic: check tab profile before returning data

Check that a tab's profile matches the profile using glic before
returning data. Adds regression tests.

Bug: 497604407
Link: https://chromium-review.googlesource.com/id/Ib94e11490b47bf3951e347f914ec8a0e6a6a6964
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7744663
Reviewed-by: Nick Birnie <birnie@google.com>
Commit-Queue: Dan H <harringtond@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1615420}
---

diff --git a/chrome/browser/glic/host/context/glic_sharing_manager_impl.cc b/chrome/browser/glic/host/context/glic_sharing_manager_impl.cc
index ead7096..84c2696 100644
--- a/chrome/browser/glic/host/context/glic_sharing_manager_impl.cc
+++ b/chrome/browser/glic/host/context/glic_sharing_manager_impl.cc
@@ -273,7 +273,11 @@
         GlicGetContextFromTabError::kTabNotFound, "tab not found"}));
     return;
   }
-
+  if (tab->GetBrowserWindowInterface()->GetProfile() != profile_) {
+    std::move(callback).Run(base::unexpected(GlicGetContextError{
+        GlicGetContextFromTabError::kPermissionDenied, "profile mismatch"}));
+    return;
+  }
   GetContextFromTabImpl(tab, options, std::move(callback));
 }
 
diff --git a/chrome/browser/glic/host/context/glic_tab_data_observer.cc b/chrome/browser/glic/host/context/glic_tab_data_observer.cc
index f8efdbe..aadd721c 100644
--- a/chrome/browser/glic/host/context/glic_tab_data_observer.cc
+++ b/chrome/browser/glic/host/context/glic_tab_data_observer.cc
@@ -10,6 +10,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/glic/common/future_browser_features.h"
 #include "chrome/browser/glic/host/context/glic_tab_data.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "components/tabs/public/tab_interface.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -154,7 +155,8 @@
   base::WeakPtrFactory<TabObserver> weak_ptr_factory_{this};
 };
 
-GlicTabDataObserver::GlicTabDataObserver() = default;
+GlicTabDataObserver::GlicTabDataObserver(Profile* profile)
+    : profile_(profile) {}
 GlicTabDataObserver::~GlicTabDataObserver() = default;
 
 void GlicTabDataObserver::OnTabWillClose(tabs::TabHandle tab_handle) {
@@ -170,6 +172,10 @@
     remote.reset();
     return;
   }
+  if (tab->GetBrowserWindowInterface()->GetProfile() != profile_) {
+    remote.reset();
+    return;
+  }
   TabObserver* observer_ptr = nullptr;
   auto iter = observers_.find(handle);
   if (iter != observers_.end()) {
diff --git a/chrome/browser/glic/host/context/glic_tab_data_observer.h b/chrome/browser/glic/host/context/glic_tab_data_observer.h
index 162db323..088f9b4 100644
--- a/chrome/browser/glic/host/context/glic_tab_data_observer.h
+++ b/chrome/browser/glic/host/context/glic_tab_data_observer.h
@@ -7,10 +7,13 @@
 
 #include <map>
 
+#include "base/memory/raw_ptr.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/glic/host/context/glic_tab_data.h"
 #include "components/tabs/public/tab_interface.h"
 
+class Profile;
+
 namespace glic {
 
 // This is a collection of observers each of which observe a tab for changes
@@ -25,7 +28,7 @@
 // API.
 class GlicTabDataObserver {
  public:
-  GlicTabDataObserver();
+  explicit GlicTabDataObserver(Profile* profile);
   ~GlicTabDataObserver();
 
   GlicTabDataObserver(const GlicTabDataObserver&) = delete;
@@ -51,6 +54,7 @@
   std::map<tabs::TabHandle, std::unique_ptr<TabObserver>> observers_;
   std::set<tabs::TabHandle> pending_cleanup_;
   base::OneShotTimer cleanup_timer_;
+  const raw_ptr<Profile> profile_;
 };
 
 }  // namespace glic
diff --git a/chrome/browser/glic/host/context/glic_tab_favicon_observer.cc b/chrome/browser/glic/host/context/glic_tab_favicon_observer.cc
index 65dd922..2d194594 100644
--- a/chrome/browser/glic/host/context/glic_tab_favicon_observer.cc
+++ b/chrome/browser/glic/host/context/glic_tab_favicon_observer.cc
@@ -11,6 +11,8 @@
 #include "chrome/browser/glic/common/future_browser_features.h"
 #include "chrome/browser/glic/host/context/glic_tab_data.h"
 #include "chrome/browser/glic/host/glic.mojom.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/common/chrome_features.h"
 #include "components/favicon/content/content_favicon_driver.h"
 #include "components/favicon/core/favicon_driver_observer.h"
@@ -253,7 +255,8 @@
   base::CallbackListSubscription will_discard_contents_subscription_;
 };
 
-GlicTabFaviconObserver::GlicTabFaviconObserver() = default;
+GlicTabFaviconObserver::GlicTabFaviconObserver(Profile* profile)
+    : profile_(profile) {}
 GlicTabFaviconObserver::~GlicTabFaviconObserver() = default;
 
 void GlicTabFaviconObserver::OnTabWillClose(tabs::TabHandle tab_handle) {
@@ -269,6 +272,10 @@
     remote.reset();
     return;
   }
+  if (tab->GetBrowserWindowInterface()->GetProfile() != profile_) {
+    remote.reset();
+    return;
+  }
   TabObserver* observer_ptr = nullptr;
   auto iter = observers_.find(handle);
   if (iter != observers_.end()) {
diff --git a/chrome/browser/glic/host/context/glic_tab_favicon_observer.h b/chrome/browser/glic/host/context/glic_tab_favicon_observer.h
index 4f2ed82..4598fdf 100644
--- a/chrome/browser/glic/host/context/glic_tab_favicon_observer.h
+++ b/chrome/browser/glic/host/context/glic_tab_favicon_observer.h
@@ -9,6 +9,7 @@
 #include <memory>
 #include <set>
 
+#include "base/memory/raw_ptr.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/glic/host/glic.mojom.h"
 #include "components/tabs/public/tab_interface.h"
@@ -16,13 +17,15 @@
 #include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
 #include "third_party/abseil-cpp/absl/container/flat_hash_set.h"
 
+class Profile;
+
 namespace glic {
 
 // Observers tabs for favicon changes. Plumbs these changes to the provided
 // mojo receiver.
 class GlicTabFaviconObserver {
  public:
-  GlicTabFaviconObserver();
+  explicit GlicTabFaviconObserver(Profile* profile);
   ~GlicTabFaviconObserver();
 
   GlicTabFaviconObserver(const GlicTabFaviconObserver&) = delete;
@@ -47,6 +50,7 @@
   absl::flat_hash_map<tabs::TabHandle, std::unique_ptr<TabObserver>> observers_;
   absl::flat_hash_set<tabs::TabHandle> pending_cleanup_;
   base::OneShotTimer cleanup_timer_;
+  const raw_ptr<Profile> profile_;
 };
 
 }  // namespace glic
diff --git a/chrome/browser/glic/host/glic_page_handler.cc b/chrome/browser/glic/host/glic_page_handler.cc
index 3b8d0c9..62ac2d0 100644
--- a/chrome/browser/glic/host/glic_page_handler.cc
+++ b/chrome/browser/glic/host/glic_page_handler.cc
@@ -744,7 +744,7 @@
         &GlicWebClientHandler::WebClientDisconnected, base::Unretained(this)));
 
     page_metadata_manager_ =
-        std::make_unique<PageMetadataManager>(web_client_.get());
+        std::make_unique<PageMetadataManager>(profile_, web_client_.get());
 
     // Listen for changes to prefs.
     pref_change_registrar_.Init(pref_service_);
@@ -1418,6 +1418,9 @@
     if (!tab) {
       return;
     }
+    if (tab->GetBrowserWindowInterface()->GetProfile() != profile_) {
+      return;
+    }
     glic_service_->DeleteCapturedRegion(tab, id);
 #else
     NOTIMPLEMENTED();
diff --git a/chrome/browser/glic/host/new_glic_api_browsertest.cc b/chrome/browser/glic/host/new_glic_api_browsertest.cc
index a8fec3b1..fc6a9d410 100644
--- a/chrome/browser/glic/host/new_glic_api_browsertest.cc
+++ b/chrome/browser/glic/host/new_glic_api_browsertest.cc
@@ -5,6 +5,7 @@
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/glic/host/new_glic_api_browsertest.cc b/chrome/browser/glic/host/new_glic_api_browsertest.cc
index a8fec3b1..fc6a9d410 100644
--- a/chrome/browser/glic/host/new_glic_api_browsertest.cc
+++ b/chrome/browser/glic/host/new_glic_api_browsertest.cc
@@ -5,6 +5,7 @@
 #include "base/test/gmock_expected_support.h"
 #include "base/test/scoped_logging_settings.h"
 #include "base/values.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/enterprise/browser_management/management_service_factory.h"
 #include "chrome/browser/glic/host/glic_features.mojom-features.h"
 #include "chrome/browser/glic/host/glic_web_contents_warming_pool.h"
@@ -19,11 +20,15 @@
 #include "chrome/browser/glic/test_support/new_glic_api_test.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_test_util.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/chrome_signin_client_test_util.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/browser/skills/skills_ui_tab_controller_interface.h"
+#include "chrome/browser/tab_list/tab_list_interface.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/webui_url_constants.h"
@@ -40,6 +45,7 @@
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/skills/features.h"
 #include "components/skills/public/skills_service.h"
+#include "components/tabs/public/tab_interface.h"
 #include "content/public/browser/favicon_status.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
@@ -56,6 +62,10 @@
 #include "chrome/test/base/ui_test_utils.h"
 #endif
 
+#if !BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/ui/browser.h"
+#endif
+
 #if BUILDFLAG(IS_ANDROID)
 #include "base/android/device_info.h"
 #include "chrome/browser/flags/android/chrome_feature_list.h"
@@ -80,6 +90,7 @@
       "NewGlicApiTestWithWebContentsWarming",
       "NewGlicApiTestWithPixelOutput",
       "NewGlicApiTestWithGeminiActOnWebPolicy",
+      "NewGlicApiMultiProfileTest",
 #if !BUILDFLAG(IS_ANDROID)
       "NewGlicApiTestWithSkills",
 #endif
@@ -88,6 +99,10 @@
   return names;
 }
 
+std::string GlicTabId(tabs::TabHandle tab_handle) {
+  return base::NumberToString(tab_handle.raw_value());
+}
+
 }  // namespace
 
 // All tests in this file use the same test params here.
@@ -196,6 +211,22 @@
   base::test::ScopedFeatureList features_;
 };
 
+class NewGlicApiMultiProfileTest : public NewGlicApiTest {
+ public:
+  BrowserWindowInterface* CreateBrowserWithNewProfile() {
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+    ProfileManager* profile_manager = g_browser_process->profile_manager();
+    base::FilePath new_path =
+        profile_manager->GenerateNextProfileDirectoryPath();
+    Profile& new_profile =
+        profiles::testing::CreateProfileSync(profile_manager, new_path);
+    return CreateBrowser(&new_profile);
+#else
+    NOTREACHED();
+#endif
+  }
+};
+
 class NewGlicApiTestWithWebContentsWarming : public NewGlicApiTest {
  public:
   NewGlicApiTestWithWebContentsWarming() {
@@ -462,6 +493,67 @@
   ExecuteJsTest();
 }
 
+IN_PROC_BROWSER_TEST_P(NewGlicApiMultiProfileTest,
+                       testPageMetadataCrossProfile) {
+#if !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_MAC)
+  GTEST_SKIP() << "Multi-profile tests only supported on Desktop";
+#endif
+  ASSERT_OK(OpenGlicForActiveTab());
+  BrowserWindowInterface* other_browser = CreateBrowserWithNewProfile();
+  ASSERT_TRUE(other_browser);
+  ASSERT_TRUE(content::NavigateToURL(
+      TabListInterface::From(other_browser)->GetActiveTab()->GetContents(),
+      GetTestUrl("page.html")));
+  auto other_tab_handle =
+      TabListInterface::From(other_browser)->GetTab(0)->GetHandle();
+  ExecuteJsTest({.params = base::Value(GlicTabId(other_tab_handle))});
+}
+
+IN_PROC_BROWSER_TEST_P(NewGlicApiMultiProfileTest, testTabDataCrossProfile) {
+#if !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_MAC)
+  GTEST_SKIP() << "Multi-profile tests only supported on Desktop";
+#endif
+  ASSERT_OK(OpenGlicForActiveTab());
+  BrowserWindowInterface* other_browser = CreateBrowserWithNewProfile();
+  ASSERT_TRUE(other_browser);
+  ASSERT_TRUE(content::NavigateToURL(
+      TabListInterface::From(other_browser)->GetActiveTab()->GetContents(),
+      GetTestUrl("page.html")));
+  auto other_tab_handle =
+      TabListInterface::From(other_browser)->GetTab(0)->GetHandle();
+  ExecuteJsTest({.params = base::Value(GlicTabId(other_tab_handle))});
+}
+
+IN_PROC_BROWSER_TEST_P(NewGlicApiMultiProfileTest, testTabFaviconCrossProfile) {
+#if !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_MAC)
+  GTEST_SKIP() << "Multi-profile tests only supported on Desktop";
+#endif
+  ASSERT_OK(OpenGlicForActiveTab());
+  BrowserWindowInterface* other_browser = CreateBrowserWithNewProfile();
+  ASSERT_TRUE(other_browser);
+  ASSERT_TRUE(content::NavigateToURL(
+      TabListInterface::From(other_browser)->GetActiveTab()->GetContents(),
+      GetTestUrl("page.html")));
+  auto other_tab_handle =
+      TabListInterface::From(other_browser)->GetTab(0)->GetHandle();
+  ExecuteJsTest({.params = base::Value(GlicTabId(other_tab_handle))});
+}
+
+IN_PROC_BROWSER_TEST_P(NewGlicApiMultiProfileTest, testGetContextCrossProfile) {
+#if !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_MAC)
+  GTEST_SKIP() << "Multi-profile tests only supported on Desktop";
+#endif
+  ASSERT_OK(OpenGlicForActiveTab());
+  BrowserWindowInterface* other_browser = CreateBrowserWithNewProfile();
+  ASSERT_TRUE(other_browser);
+  ASSERT_TRUE(content::NavigateToURL(
+      TabListInterface::From(other_browser)->GetActiveTab()->GetContents(),
+      GetTestUrl("page.html")));
+  auto other_tab_handle =
+      TabListInterface::From(other_browser)->GetTab(0)->GetHandle();
+  ExecuteJsTest({.params = base::Value(GlicTabId(other_tab_handle))});
+}
+
 IN_PROC_BROWSER_TEST_P(NewGlicApiTestWithWebContentsWarming,
                        testWebClientReadyOnFullLoad) {
   service()->web_contents_warming_pool().EnsurePreload();
@@ -923,6 +1015,11 @@
                          DefaultTestParamSet(),
                          &WithTestParams::PrintTestVariant);
 
+INSTANTIATE_TEST_SUITE_P(,
+                         NewGlicApiMultiProfileTest,
+                         DefaultTestParamSet(),
+                         &WithTestParams::PrintTestVariant);
+
 // Skills are not supported yet on Android.
 #if !BUILDFLAG(IS_ANDROID)
 INSTANTIATE_TEST_SUITE_P(,
@@ -930,7 +1027,6 @@
                          DefaultTestParamSet(),
                          &WithTestParams::PrintTestVariant);
 #endif
-
 #else
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NewGlicApiTest);
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
@@ -938,6 +1034,7 @@
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NewGlicApiTestWithPixelOutput);
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
     NewGlicApiTestWithGeminiActOnWebPolicy);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NewGlicApiMultiProfileTest);
 #if !BUILDFLAG(IS_ANDROID)
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NewGlicApiTestWithSkills);
 #endif
diff --git a/chrome/test/data/webui/glic/browser_tests/new_glic_api_browsertest.ts b/chrome/test/data/webui/glic/browser_tests/new_glic_api_browsertest.ts
index 8b56477..0e0f3160 100644
--- a/chrome/test/data/webui/glic/browser_tests/new_glic_api_browsertest.ts
+++ b/chrome/test/data/webui/glic/browser_tests/new_glic_api_browsertest.ts
@@ -5,7 +5,7 @@
 import {ClientCapabilities, SkillSource} from '/glic/glic_api/glic_api.js';
 import type {GlicWebClient, InvokeOptions, Observable, OpenPanelInfo, PageMetadata, PanelOpeningData, PanelState, TabData} from '/glic/glic_api/glic_api.js';
 
-import {ApiTestError, ApiTestFixtureBase, assertDefined, assertEquals, assertTrue, assertUndefined, checkDefined, mapObservable, observeSequence, runUntil, sleep, testMain, waitFor, WebClient} from './browser_test_base.js';
+import {ApiTestError, ApiTestFixtureBase, assertDefined, assertEquals, assertRejects, assertTrue, assertUndefined, checkDefined, mapObservable, observeSequence, runUntil, sleep, testMain, waitFor, WebClient} from './browser_test_base.js';
 
 class ApiTests extends ApiTestFixtureBase {
   override async setUpTest() {
@@ -240,6 +240,45 @@
     await this.advanceToNextStep();
     await actOnWebCapabilitySequence.waitForValue(false);
   }
+
+  async testPageMetadataCrossProfile() {
+    const otherTabId = this.testParams as string;
+    assertDefined(this.host.getPageMetadata);
+    const observable = this.host.getPageMetadata(otherTabId, ['title']);
+    const sequence = observeSequence(observable);
+    await sequence.waitForComplete();
+    assertEquals(
+        true, sequence.isEmpty(),
+        'Expected no page metadata for cross-profile tab');
+  }
+
+  async testTabDataCrossProfile() {
+    const otherTabId = this.testParams as string;
+    assertDefined(this.host.getTabById);
+    const observable = this.host.getTabById(otherTabId);
+    const sequence = observeSequence(observable);
+    await sequence.waitForComplete();
+    assertEquals(
+        true, sequence.isEmpty(), 'Expected no tab data for cross-profile tab');
+  }
+
+  async testTabFaviconCrossProfile() {
+    const otherTabId = this.testParams as string;
+    assertDefined(this.host.getTabFaviconById);
+    const observable = this.host.getTabFaviconById(otherTabId);
+    const sequence = observeSequence(observable);
+    await sequence.waitForComplete();
+    assertEquals(
+        true, sequence.isEmpty(), 'Expected no favicon for cross-profile tab');
+  }
+
+  async testGetContextCrossProfile() {
+    const otherTabId = this.testParams as string;
+    assertDefined(this.host.getContextForActorFromTab);
+    await assertRejects(this.host.getContextForActorFromTab(otherTabId, {}), {
+      withErrorMessage: 'tabContext failed: profile mismatch',
+    });
+  }
 }
 
 class FaviconTest extends ApiTests {
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.