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 <michaeldo@chromium.org>
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 <ckitagawa@chromium.org>
Reviewed-by: Rohit Rao <rohitrao@chromium.org>
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
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