From b5de58dc9173aac7795109e23a85f892dc4005df Mon Sep 17 00:00:00 2001 From: Pete Miller Date: Thu, 27 Nov 2025 04:09:21 -0800 Subject: [PATCH] [AI Chat] [Content Agent] Block navigation to and using tools on extension store pages (#32576) --- .../content_agent_tools_browsertest.cc | 34 +++++++++++++++++++ .../chrome/browser/actor/site_policy.cc | 34 +++++++++++++++++++ .../chrome-browser-actor-site_policy.cc.patch | 12 +++++++ 3 files changed, 80 insertions(+) create mode 100644 chromium_src/chrome/browser/actor/site_policy.cc create mode 100644 patches/chrome-browser-actor-site_policy.cc.patch diff --git a/browser/ai_chat/content_agent_tools_browsertest.cc b/browser/ai_chat/content_agent_tools_browsertest.cc index d93665684aa..e9d2af944d8 100644 --- a/browser/ai_chat/content_agent_tools_browsertest.cc +++ b/browser/ai_chat/content_agent_tools_browsertest.cc @@ -19,6 +19,7 @@ #include "brave/components/ai_chat/core/common/features.h" #include "brave/components/ai_chat/core/common/test_utils.h" #include "chrome/browser/actor/actor_keyed_service_factory.h" +#include "chrome/browser/actor/actor_policy_checker.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/test/base/in_process_browser_test.h" @@ -348,6 +349,39 @@ IN_PROC_BROWSER_TEST_F(ContentAgentToolsTest, NavigationTool_BasicNavigation) { EXPECT_EQ(test_url.path(), final_url.path()); } +IN_PROC_BROWSER_TEST_F(ContentAgentToolsTest, BlockExtensionStore) { + // Verify that navigating to the extension store is blocked + NavigateToChromiumTestFile("/actor/page_with_clickable_element.html"); + + auto nav_tool = FindToolByName("web_page_navigator"); + ASSERT_TRUE(nav_tool); + + // Get initial URL + GURL initial_url = web_contents()->GetVisibleURL(); + + // Create input for navigating to a different test page + base::Value::Dict input; + input.Set("website_url", "https://chromewebstore.google.com/example"); + + auto result = ExecuteToolAndWait(nav_tool, *base::WriteJson(input), false); + EXPECT_GT(result.size(), 0u); + EXPECT_THAT(result, ContentBlockText(testing::HasSubstr("Error"))); + + // Verify the page could not navigate to the URL + GURL final_url = web_contents()->GetURL(); + EXPECT_EQ(initial_url, final_url); + + // Also verify that other actions won't be able to execute against tabs + // already on an extension store URL. + base::test::TestFuture allowed; + auto* actor_service = + actor::ActorKeyedServiceFactory::GetActorKeyedService(agent_profile_); + actor_service->GetPolicyChecker().MayActOnUrl( + GURL("https://chromewebstore.google.com/example"), false, agent_profile_, + actor_service->GetJournal(), actor::TaskId(), allowed.GetCallback()); + EXPECT_NE(allowed.Take(), actor::MayActOnUrlBlockReason::kAllowed); +} + // Test drag and release tool with coordinates (since drag needs from/to) IN_PROC_BROWSER_TEST_F(ContentAgentToolsTest, DragAndReleaseTool_CoordinateTargets) { diff --git a/chromium_src/chrome/browser/actor/site_policy.cc b/chromium_src/chrome/browser/actor/site_policy.cc new file mode 100644 index 00000000000..490141b743c --- /dev/null +++ b/chromium_src/chrome/browser/actor/site_policy.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2025 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "extensions/buildflags/buildflags.h" +#if BUILDFLAG(ENABLE_EXTENSIONS) +#include "extensions/common/extension_urls.h" +#endif + +namespace { + +bool IsChromeWebStoreURL(const GURL& url) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + return (url.GetHost() == extension_urls::GetWebstoreLaunchURL().GetHost()) || + (url.GetHost() == extension_urls::GetNewWebstoreLaunchURL().GetHost()); +#else + return false; +#endif +} + +} // namespace + +// Add Brave-specific restrictions +#define BRAVE_MAY_ACT_ON_URL_INTERNAL \ + if (IsChromeWebStoreURL(url)) { \ + decision_wrapper->Reject( \ + "Extension store URL", \ + actor::MayActOnUrlBlockReason::kUrlNotInAllowlist); \ + return; \ + } + +#include +#undef BRAVE_MAY_ACT_ON_URL_INTERNAL diff --git a/patches/chrome-browser-actor-site_policy.cc.patch b/patches/chrome-browser-actor-site_policy.cc.patch new file mode 100644 index 00000000000..e2e00234327 --- /dev/null +++ b/patches/chrome-browser-actor-site_policy.cc.patch @@ -0,0 +1,12 @@ +diff --git a/chrome/browser/actor/site_policy.cc b/chrome/browser/actor/site_policy.cc +index e4dc851408f20fbc02d06110c10f441a3e77c79c..b5a670ac861c6c7840877ca8260f5f45b44d49fc 100644 +--- a/chrome/browser/actor/site_policy.cc ++++ b/chrome/browser/actor/site_policy.cc +@@ -169,6 +169,7 @@ void MayActOnUrlInternal( + return; + } + ++ BRAVE_MAY_ACT_ON_URL_INTERNAL + if (DisableSafetyChecks()) { + decision_wrapper->Accept(); + return;