Chrome · Glic
CVE-2026-11027
Logic Error in Glic
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
Profilechrome/browser/glic/host/context/glic_tab_data_observer.h |
modified | |
GlicTabDataObserverchrome/browser/glic/host/context/glic_tab_data_observer.h |
modified | |
Profilechrome/browser/glic/host/context/glic_tab_favicon_observer.h |
modified | |
GlicTabFaviconObserverchrome/browser/glic/host/context/glic_tab_favicon_observer.h |
modified | |
ifchrome/browser/glic/host/glic_page_handler.cc |
modified |
Files Changed
chrome/browser/glic/host/context/glic_sharing_manager_impl.ccchrome/browser/glic/host/context/glic_tab_data_observer.ccchrome/browser/glic/host/context/glic_tab_data_observer.hchrome/browser/glic/host/context/glic_tab_favicon_observer.ccchrome/browser/glic/host/context/glic_tab_favicon_observer.hchrome/browser/glic/host/glic_page_handler.ccchrome/browser/glic/host/new_glic_api_browsertest.cc
Patch
From 0906aba512c8f7949f313b725c26db54262bb1d5 Mon Sep 17 00:00:00 2001 From: Dan Harrington <[email protected]> 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 <[email protected]> Commit-Queue: Dan H <[email protected]> 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.
References
On This Page