Chrome · Mobile
CVE-2026-106427
Authorization Error in Mobile
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator.mm |
modified | |
TEST_Fios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator_unittest.mm |
modified | |
forios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator_unittest.mm |
modified |
Files Changed
ios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator_unittest.mmios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator.mm
Patch
From 12e9eb9b62c28bbf4dfdca947baae2bf30cf4cff Mon Sep 17 00:00:00 2001 From: Louis Romero <[email protected]> Date: Thu, 10 Sep 2026 04:42:55 -0700 Subject: [PATCH] Fix navigation trust escalation in Assistant AIM When a main-frame navigation to a third-party domain occurs within the Assistant AIM sheet, AssistantAIMMediator cancels the navigation and replays the target URL into a new browser tab. Previously, this load was dispatched without scheme validation and with the default is_renderer_initiated set to false. This allowed renderer-initiated navigations to be laundered into trusted browser-initiated loads, bypassing security checks for privileged pages such as about: or chrome:// WebUI URLs. This change validates that target URLs use an HTTP or HTTPS scheme before replaying, and explicitly propagates the renderer context by setting is_renderer_initiated to true and the transition type to PAGE_TRANSITION_LINK. Fixed: 513423334 Change-Id: Ice7d3ad572c5103e230705b3d596125543744f20 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8378851 Auto-Submit: Louis Romero <[email protected]> Reviewed-by: Ameur Hosni <[email protected]> Commit-Queue: Louis Romero <[email protected]> Cr-Commit-Position: refs/heads/main@{#1695343} --- diff --git a/ios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator.mm b/ios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator.mm index 8d458432..52608bd7 100644 --- a/ios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator.mm +++ b/ios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator.mm @@ -48,6 +48,7 @@ #import "net/base/apple/url_conversions.h" #import "third_party/lens_server_proto/aim_communication.pb.h" #import "ui/base/l10n/l10n_util.h" +#import "ui/base/page_transition_types.h" #import "url/gurl.h" @interface AssistantAIMMediator () <CRWWebFramesManagerObserver, @@ -242,10 +243,11 @@ // the main browser instead. if (requestInfo.target_frame_is_main) { decisionHandler(web::WebStatePolicyDecider::PolicyDecision::Cancel()); - // Filter out about:blank initialization navigations to prevent spawning - // empty tabs in the main browser upon loading the Assistant AIM sheet. - if (URL.is_valid() && !URL.IsAboutBlank()) { + // Only replay valid HTTP or HTTPS URLs in the main browser. + if (URL.is_valid() && URL.SchemeIsHTTPOrHTTPS()) { UrlLoadParams params = UrlLoadParams::InNewTab(URL); + params.web_params.is_renderer_initiated = true; + params.web_params.transition_type = ui::PAGE_TRANSITION_LINK; _urlLoader->Load(params); [_containerHandler animateAssistantContainerToDetent:AssistantContainerDetent::kMinimized diff --git a/ios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator_unittest.mm b/ios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator_unittest.mm index faa54ca6b..e20df3b 100644 --- a/ios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator_unittest.mm +++ b/ios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator_unittest.mm @@ -4,9 +4,11 @@ #import "ios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator.h" +#import "base/functional/callback_helpers.h" #import "base/memory/raw_ptr.h" #import "base/strings/sys_string_conversions.h" #import "base/test/scoped_feature_list.h" +#import "base/test/test_future.h" #import "components/contextual_tasks/public/features.h" #import "components/strings/grit/components_strings.h" #import "ios/chrome/browser/assistant/coordinator/assistant_container_commands.h" @@ -242,6 +244,10 @@ EXPECT_TRUE(blocked_decision.ShouldCancelNavigation()); EXPECT_EQ(url_loader_->load_new_tab_call_count, 1); EXPECT_EQ(url_loader_->last_params.web_params.url, third_party_url); + EXPECT_TRUE(url_loader_->last_params.web_params.is_renderer_initiated); + EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs( + url_loader_->last_params.web_params.transition_type, + ui::PAGE_TRANSITION_LINK)); } // Tests that the navigation policy decider intercepts and cancels @@ -271,6 +277,49 @@ EXPECT_TRUE(blocked_decision.ShouldCancelNavigation()); EXPECT_EQ(url_loader_->load_new_tab_call_count, 1); EXPECT_EQ(url_loader_->last_params.web_params.url, third_party_url); + EXPECT_TRUE(url_loader_->last_params.web_params.is_renderer_initiated); + EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs( + url_loader_->last_params.web_params.transition_type, + ui::PAGE_TRANSITION_LINK)); +} + +// Tests that the navigation policy decider cancels navigation to non-HTTP/HTTPS +// URLs (such as about: or chrome://) without opening a new tab. +TEST_F(AssistantAIMMediatorTest, CancelsNonHttpOrHttpsURLWithoutOpeningNewTab) { + id<CRWWebStatePolicyDecider> policy_decider = + static_cast<id<CRWWebStatePolicyDecider>>(mediator_); + + [[mock_container_handler_ reject] + animateAssistantContainerToDetent:AssistantContainerDetent::kMinimized + duration:kSheetDetentAnimationDuration + curve:UIViewAnimationCurveEaseInOut]; + + const GURL test_urls[] = { + GURL("about:version"), + GURL("chrome://flags"), + GURL("javascript:alert(1)"), + GURL("file:///etc/passwd"), + }; + + for (const GURL& test_url : test_urls) { + base::test::TestFuture<web::WebStatePolicyDecider::PolicyDecision> future; + + [policy_decider + shouldAllowRequest:[NSURLRequest + requestWithURL:net::NSURLWithGURL(test_url)] + requestInfo:web::WebStatePolicyDecider::RequestInfo( + ui::PageTransition::PAGE_TRANSITION_LINK, + /*target_frame_is_main=*/true, + /*target_frame_is_cross_origin=*/true, + /*target_window_is_cross_origin=*/false, + /*is_user_initiated=*/true, + /*user_tapped_recently=*/true) + decisionHandler:base::CallbackToBlock(future.GetCallback())]; + EXPECT_TRUE(future.Get().ShouldCancelNavigation()); + EXPECT_EQ(url_loader_->load_new_tab_call_count, 0); + } + + [mock_container_handler_ verify]; } // Tests that the navigation policy decider allows authorized Google
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/ios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator_unittest.mm b/ios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator_unittest.mm
index faa54ca6b..e20df3b 100644
--- a/ios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator_unittest.mm
+++ b/ios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator_unittest.mm
@@ -4,9 +4,11 @@
#import "ios/chrome/browser/cobrowse/coordinator/assistant_aim_mediator.h"
+#import "base/functional/callback_helpers.h"
#import "base/memory/raw_ptr.h"
#import "base/strings/sys_string_conversions.h"
#import "base/test/scoped_feature_list.h"
+#import "base/test/test_future.h"
#import "components/contextual_tasks/public/features.h"
#import "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/assistant/coordinator/assistant_container_commands.h"
@@ -242,6 +244,10 @@
EXPECT_TRUE(blocked_decision.ShouldCancelNavigation());
EXPECT_EQ(url_loader_->load_new_tab_call_count, 1);
EXPECT_EQ(url_loader_->last_params.web_params.url, third_party_url);
+ EXPECT_TRUE(url_loader_->last_params.web_params.is_renderer_initiated);
+ EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
+ url_loader_->last_params.web_params.transition_type,
+ ui::PAGE_TRANSITION_LINK));
}
// Tests that the navigation policy decider intercepts and cancels
@@ -271,6 +277,49 @@
EXPECT_TRUE(blocked_decision.ShouldCancelNavigation());
EXPECT_EQ(url_loader_->load_new_tab_call_count, 1);
EXPECT_EQ(url_loader_->last_params.web_params.url, third_party_url);
+ EXPECT_TRUE(url_loader_->last_params.web_params.is_renderer_initiated);
+ EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
+ url_loader_->last_params.web_params.transition_type,
+ ui::PAGE_TRANSITION_LINK));
+}
+
+// Tests that the navigation policy decider cancels navigation to non-HTTP/HTTPS
+// URLs (such as about: or chrome://) without opening a new tab.
+TEST_F(AssistantAIMMediatorTest, CancelsNonHttpOrHttpsURLWithoutOpeningNewTab) {
+ id<CRWWebStatePolicyDecider> policy_decider =
+ static_cast<id<CRWWebStatePolicyDecider>>(mediator_);
+
+ [[mock_container_handler_ reject]
+ animateAssistantContainerToDetent:AssistantContainerDetent::kMinimized
+ duration:kSheetDetentAnimationDuration
+ curve:UIViewAnimationCurveEaseInOut];
+
+ const GURL test_urls[] = {
+ GURL("about:version"),
+ GURL("chrome://flags"),
+ GURL("javascript:alert(1)"),
+ GURL("file:///etc/passwd"),
+ };
+
+ for (const GURL& test_url : test_urls) {
+ base::test::TestFuture<web::WebStatePolicyDecider::PolicyDecision> future;
+
+ [policy_decider
+ shouldAllowRequest:[NSURLRequest
+ requestWithURL:net::NSURLWithGURL(test_url)]
+ requestInfo:web::WebStatePolicyDecider::RequestInfo(
+ ui::PageTransition::PAGE_TRANSITION_LINK,
+ /*target_frame_is_main=*/true,
+ /*target_frame_is_cross_origin=*/true,
+ /*target_window_is_cross_origin=*/false,
+ /*is_user_initiated=*/true,
+ /*user_tapped_recently=*/true)
+ decisionHandler:base::CallbackToBlock(future.GetCallback())];
+ EXPECT_TRUE(future.Get().ShouldCancelNavigation());
+ EXPECT_EQ(url_loader_->load_new_tab_call_count, 0);
+ }
+
+ [mock_container_handler_ verify];
}
// Tests that the navigation policy decider allows authorized Google
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