Chrome · Chrome for iOS
CVE-2026-13795
Logic Error in Chrome for iOS
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fios/chrome/browser/app_launcher/model/app_launcher_browser_agent_unittest.mm |
modified |
Files Changed
ios/chrome/app/strings/ios_strings.grdios/chrome/app/strings/ios_strings_grd/IDS_IOS_OPEN_SHORTCUTS_URL.png.sha1ios/chrome/browser/app_launcher/model/app_launcher_browser_agent.mmios/chrome/browser/app_launcher/model/app_launcher_browser_agent_unittest.mmios/chrome/browser/app_launcher/model/app_launcher_tab_helper.mmios/chrome/browser/app_launcher/model/app_launcher_tab_helper_delegate.hios/chrome/browser/overlays/model/public/web_content_area/app_launcher_overlay.hios/chrome/browser/overlays/model/public/web_content_area/app_launcher_overlay.mm
Patch
From d19ac426145be6a3e365965013bad11f17d84d90 Mon Sep 17 00:00:00 2001 From: Mike Dougherty <[email protected]> Date: Wed, 13 May 2026 15:18:18 -0700 Subject: [PATCH] Show alert before opening a shortcuts URL Fixed: 476591032 Change-Id: I66358c662fd9f3f65d66e5e18fd1b32c67c822e7 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7838361 Reviewed-by: Calder Kitagawa <[email protected]> Reviewed-by: Rohit Rao <[email protected]> Commit-Queue: Mike Dougherty <[email protected]> Cr-Commit-Position: refs/heads/main@{#1630265} --- diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd index 379000d..f8392bab 100644 --- a/ios/chrome/app/strings/ios_strings.grd +++ b/ios/chrome/app/strings/ios_strings.grd @@ -4892,6 +4892,9 @@ <message name="IDS_IOS_OPEN_SETTINGS" desc="Text of the button that will take the user to the Settings page to configure the default browser [iOS only]"> Open Settings </message> + <message name="IDS_IOS_OPEN_SHORTCUTS_URL" desc="Alert to let the user know that the website is trying to open the Shortcuts application. [iOS only]"> + This site is trying to open the "Shortcuts" application. + </message> <message name="IDS_IOS_OPEN_SOURCE_LICENSES" desc="The label to access the open source licenses, displayed in the application settings [Length: 30em] [iOS only]"> Open source licenses </message> diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_OPEN_SHORTCUTS_URL.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_OPEN_SHORTCUTS_URL.png.sha1 new file mode 100644 index 0000000..537b46f --- /dev/null +++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_OPEN_SHORTCUTS_URL.png.sha1 @@ -0,0 +1 @@ +b0ee844e67c69601c46b7b2f9e8b65f2a44d2916 \ No newline at end of file diff --git a/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent.mm b/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent.mm index 598e3d12..7a842eb 100644 --- a/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent.mm +++ b/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent.mm @@ -98,6 +98,11 @@ UMA_HISTOGRAM_BOOLEAN("Tab.ExternalApplicationOpened.Failed", user_accepted); break; + case app_launcher_overlays::AppLaunchConfirmationRequestCause:: + kShortcutsURL: + UMA_HISTOGRAM_BOOLEAN("Tab.ExternalApplicationOpened.ShortcutsURL", + user_accepted); + break; } } @@ -147,6 +152,9 @@ case AppLauncherAlertCause::kAppLaunchFailed: return app_launcher_overlays::AppLaunchConfirmationRequestCause:: kAppLaunchFailed; + case AppLauncherAlertCause::kShortcutsURL: + return app_launcher_overlays::AppLaunchConfirmationRequestCause:: + kShortcutsURL; } } diff --git a/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent_unittest.mm b/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent_unittest.mm index 1580013..dddef67 100644 --- a/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent_unittest.mm +++ b/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent_unittest.mm @@ -431,6 +431,79 @@ [application_ verify]; } +// Tests that the browser agent shows an alert when opening a shortcuts URL. +TEST_F(AppLauncherBrowserAgentTest, ShortcutsURLRequestShowsAlert) { + const base::HistogramTester histogram_tester; + const GURL kShortcutsUrl("shortcuts://1234"); + const GURL kSourcePageUrl("http://www.chromium.test"); + web::WebState* web_state = + AddWebState(/*opener=*/nullptr, /*nav_item_count=*/1); + + // Request an app launch for kShortcutsUrl while the abuse detector returns + // ExternalAppLaunchPolicyPrompt. + abuse_detectors_[web_state].policy = ExternalAppLaunchPolicyPrompt; + AppLauncherTabHelper::FromWebState(web_state)->RequestToLaunchApp( + kShortcutsUrl, kSourcePageUrl, /*link_transition=*/true, + /*is_user_initiated=*/true, /*user_tapped_recently=*/true); + + // Add a response allowing the navigation. + OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState( + web_state, OverlayModality::kWebContentArea); + queue->front_request()->GetCallbackManager()->SetCompletionResponse( + OverlayResponse::CreateWithInfo<AllowAppLaunchResponse>()); + + // Cancel requests in the queue so that the completion callback is executed, + // expecting that the application will open the URL. + OCMExpect([application_ openURL:net::NSURLWithGURL(kShortcutsUrl) + options:@{} + completionHandler:[OCMArg isNotNil]]); + queue->CancelAllRequests(); + + histogram_tester.ExpectBucketCount( + "Tab.ExternalApplicationOpened.ShortcutsURL", + /*true*/ 1, 1); + + // Verify that the application attempts to open the URL. + [application_ verify]; +} + +// Tests that the browser agent shows an alert when opening a workflow +// (Shortcuts appplication) URL. +TEST_F(AppLauncherBrowserAgentTest, WorkflowURLRequestShowsAlert) { + const base::HistogramTester histogram_tester; + const GURL kWorkflowUrl("workflow://1234"); + const GURL kSourcePageUrl("http://www.chromium.test"); + web::WebState* web_state = + AddWebState(/*opener=*/nullptr, /*nav_item_count=*/1); + + // Request an app launch for kShortcutsUrl while the abuse detector returns + // ExternalAppLaunchPolicyPrompt. + abuse_detectors_[web_state].policy = ExternalAppLaunchPolicyPrompt; + AppLauncherTabHelper::FromWebState(web_state)->RequestToLaunchApp( + kWorkflowUrl, kSourcePageUrl, /*link_transition=*/true, + /*is_user_initiated=*/true, /*user_tapped_recently=*/true); + + // Add a response allowing the navigation. + OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState( + web_state, OverlayModality::kWebContentArea); + queue->front_request()->GetCallbackManager()->SetCompletionResponse( + OverlayResponse::CreateWithInfo<AllowAppLaunchResponse>()); + + // Cancel requests in the queue so that the completion callback is executed, + // expecting that the application will open the URL. + OCMExpect([application_ openURL:net::NSURLWithGURL(kWorkflowUrl) + options:@{} + completionHandler:[OCMArg isNotNil]]); + queue->CancelAllRequests(); + + histogram_tester.ExpectBucketCount( + "Tab.ExternalApplicationOpened.ShortcutsURL", + /*true*/ 1, 1); + + // Verify that the application attempts to open the URL. + [application_ verify]; +} + // Tests that completion is called on scene state activation TEST_F(AppLauncherBrowserAgentTest, CompletionCalledOnSceneActivation) { const GURL kAppUrl("some-app://1234"); diff --git a/ios/chrome/browser/app_launcher/model/app_launcher_tab_helper.mm b/ios/chrome/browser/app_launcher/model/app_launcher_tab_helper.mm index 7c248fc..507aa5e 100644 --- a/ios/chrome/browser/app_launcher/model/app_launcher_tab_helper.mm +++ b/ios/chrome/browser/app_launcher/model/app_launcher_tab_helper.mm @@ -139,6 +139,15 @@ return; } + // Prompt user before launching shortcuts. See crbug.com/476591032 for more + // context. + constexpr char kShortcutsScheme[] = "shortcuts"; + constexpr char kWorkflowScheme[] = "workflow"; + if (url.SchemeIs(kShortcutsScheme) || url.SchemeIs(kWorkflowScheme)) { + ShowAppLaunchAlert(AppLauncherAlertCause::kShortcutsURL, url); + return; + } + // Show the a dialog for app store launches and external URL navigations that // did not originate from a link tap. if (UrlHasAppStoreScheme(url) || !link_transition) { diff --git a/ios/chrome/browser/app_launcher/model/app_launcher_tab_helper_delegate.h b/ios/chrome/browser/app_launcher/model/app_launcher_tab_helper_delegate.h index 8ea8c68..d12b3261 100644 --- a/ios/chrome/browser/app_launcher/model/app_launcher_tab_helper_delegate.h +++ b/ios/chrome/browser/app_launcher/model/app_launcher_tab_helper_delegate.h @@ -16,6 +16,7 @@ kRepeatedLaunchDetected, kOpenFromIncognito, kNoUserInteraction, + kShortcutsURL, kAppLaunchFailed, }; diff --git a/ios/chrome/browser/overlays/model/public/web_content_area/app_launcher_overlay.h b/ios/chrome/browser/overlays/model/public/web_content_area/app_launcher_overlay.h index 6cbd64d5..99ff0553 100644 --- a/ios/chrome/browser/overlays/model/public/web_content_area/app_launcher_overlay.h +++ b/ios/chrome/browser/overlays/model/public/web_content_area/app_launcher_overlay.h @@ -17,6 +17,7 @@ kOpenFromIncognito, kNoUserInteraction, kAppLaunchFailed, + kShortcutsURL, }; // Configuration object for OverlayRequests for alerts notifying the user that diff --git a/ios/chrome/browser/overlays/model/public/web_content_area/app_launcher_overlay.mm b/ios/chrome/browser/overlays/model/public/web_content_area/app_launcher_overlay.mm index a75f582..73ee1f2 100644 --- a/ios/chrome/browser/overlays/model/public/web_content_area/app_launcher_overlay.mm +++ b/ios/chrome/browser/overlays/model/public/web_content_area/app_launcher_overlay.mm @@ -77,6 +77,13 @@ reject_button_title = l10n_util::GetNSString(IDS_IOS_OPEN_ANOTHER_APP_BLOCK); break; + case AppLaunchConfirmationRequestCause::kShortcutsURL: + alert_message = l10n_util::GetNSString(IDS_IOS_OPEN_SHORTCUTS_URL);
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent_unittest.mm b/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent_unittest.mm
index 1580013..dddef67 100644
--- a/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent_unittest.mm
+++ b/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent_unittest.mm
@@ -431,6 +431,79 @@
[application_ verify];
}
+// Tests that the browser agent shows an alert when opening a shortcuts URL.
+TEST_F(AppLauncherBrowserAgentTest, ShortcutsURLRequestShowsAlert) {
+ const base::HistogramTester histogram_tester;
+ const GURL kShortcutsUrl("shortcuts://1234");
+ const GURL kSourcePageUrl("http://www.chromium.test");
+ web::WebState* web_state =
+ AddWebState(/*opener=*/nullptr, /*nav_item_count=*/1);
+
+ // Request an app launch for kShortcutsUrl while the abuse detector returns
+ // ExternalAppLaunchPolicyPrompt.
+ abuse_detectors_[web_state].policy = ExternalAppLaunchPolicyPrompt;
+ AppLauncherTabHelper::FromWebState(web_state)->RequestToLaunchApp(
+ kShortcutsUrl, kSourcePageUrl, /*link_transition=*/true,
+ /*is_user_initiated=*/true, /*user_tapped_recently=*/true);
+
+ // Add a response allowing the navigation.
+ OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState(
+ web_state, OverlayModality::kWebContentArea);
+ queue->front_request()->GetCallbackManager()->SetCompletionResponse(
+ OverlayResponse::CreateWithInfo<AllowAppLaunchResponse>());
+
+ // Cancel requests in the queue so that the completion callback is executed,
+ // expecting that the application will open the URL.
+ OCMExpect([application_ openURL:net::NSURLWithGURL(kShortcutsUrl)
+ options:@{}
+ completionHandler:[OCMArg isNotNil]]);
+ queue->CancelAllRequests();
+
+ histogram_tester.ExpectBucketCount(
+ "Tab.ExternalApplicationOpened.ShortcutsURL",
+ /*true*/ 1, 1);
+
+ // Verify that the application attempts to open the URL.
+ [application_ verify];
+}
+
+// Tests that the browser agent shows an alert when opening a workflow
+// (Shortcuts appplication) URL.
+TEST_F(AppLauncherBrowserAgentTest, WorkflowURLRequestShowsAlert) {
+ const base::HistogramTester histogram_tester;
+ const GURL kWorkflowUrl("workflow://1234");
+ const GURL kSourcePageUrl("http://www.chromium.test");
+ web::WebState* web_state =
+ AddWebState(/*opener=*/nullptr, /*nav_item_count=*/1);
+
+ // Request an app launch for kShortcutsUrl while the abuse detector returns
+ // ExternalAppLaunchPolicyPrompt.
+ abuse_detectors_[web_state].policy = ExternalAppLaunchPolicyPrompt;
+ AppLauncherTabHelper::FromWebState(web_state)->RequestToLaunchApp(
+ kWorkflowUrl, kSourcePageUrl, /*link_transition=*/true,
+ /*is_user_initiated=*/true, /*user_tapped_recently=*/true);
+
+ // Add a response allowing the navigation.
+ OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState(
+ web_state, OverlayModality::kWebContentArea);
+ queue->front_request()->GetCallbackManager()->SetCompletionResponse(
+ OverlayResponse::CreateWithInfo<AllowAppLaunchResponse>());
+
+ // Cancel requests in the queue so that the completion callback is executed,
+ // expecting that the application will open the URL.
+ OCMExpect([application_ openURL:net::NSURLWithGURL(kWorkflowUrl)
+ options:@{}
+ completionHandler:[OCMArg isNotNil]]);
+ queue->CancelAllRequests();
+
+ histogram_tester.ExpectBucketCount(
+ "Tab.ExternalApplicationOpened.ShortcutsURL",
+ /*true*/ 1, 1);
+
+ // Verify that the application attempts to open the URL.
+ [application_ verify];
+}
+
// Tests that completion is called on scene state activation
TEST_F(AppLauncherBrowserAgentTest, CompletionCalledOnSceneActivation) {
const GURL kAppUrl("some-app://1234");
diff --git a/ios/chrome/browser/overlays/model/public/web_content_area/app_launcher_overlay_unittest.mm b/ios/chrome/browser/overlays/model/public/web_content_area/app_launcher_overlay_unittest.mm
index dc8739b..f04f2eb 100644
--- a/ios/chrome/browser/overlays/model/public/web_content_area/app_launcher_overlay_unittest.mm
+++ b/ios/chrome/browser/overlays/model/public/web_content_area/app_launcher_overlay_unittest.mm
@@ -172,3 +172,29 @@
config->response_converter().Run(std::move(alert_response));
EXPECT_FALSE(response);
}
+
+// Tests that the alert overlay request is set correctly for a launch request
+// with a shortcuts URL.
+TEST_F(AppLauncherOverlayTest, ShortcutsURLRequestAlertSetup) {
+ std::unique_ptr<OverlayRequest> request = OverlayRequest::CreateWithConfig<
+ AppLaunchConfirmationRequest>(
+ app_launcher_overlays::AppLaunchConfirmationRequestCause::kShortcutsURL);
+ AlertRequest* config = request->GetConfig<AlertRequest>();
+ ASSERT_TRUE(config);
+
+ EXPECT_NSEQ(l10n_util::GetNSString(IDS_IOS_OPEN_SHORTCUTS_URL),
+ config->message());
+
+ // There is an OK button and a Cancel button in app launch alerts.
+ ASSERT_EQ(2U, config->button_configs().size());
+ const ButtonConfig& ok_button_config = config->button_configs()[0][0];
+ const ButtonConfig& cancel_button_config = config->button_configs()[1][0];
+
+ EXPECT_EQ(UIAlertActionStyleDefault, ok_button_config.style);
+ EXPECT_NSEQ(l10n_util::GetNSString(IDS_IOS_OPEN_ANOTHER_APP_ALLOW),
+ ok_button_config.title);
+
+ EXPECT_EQ(UIAlertActionStyleCancel, cancel_button_config.style);
+ EXPECT_NSEQ(l10n_util::GetNSString(IDS_IOS_OPEN_ANOTHER_APP_BLOCK),
+ cancel_button_config.title);
+}
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