Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in Mobile
DescriptionInsufficient validation of untrusted input in Mobile
ComponentMobile
Bug ClassLogic Error
Tracker520407381
Fix commita26459a49f16 (chromium/src) +6/-66
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Changed Functions

FunctionChangeNotes
if
chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
modified

Files Changed

  • chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
  • tools/metrics/histograms/metadata/android/enums.xml
  • tools/metrics/histograms/metadata/android/histograms.xml
From a26459a49f16ec5f7cd10c50b0c1c4fbcb70b3f9 Mon Sep 17 00:00:00 2001
From: Michael Thiessen <[email protected]>
Date: Fri, 26 Jun 2026 09:45:05 -0700
Subject: [PATCH] Don't allow external intents to set navigation Transition Type

In order to reduce reliance on
IntentHandler.notSecureIsIntentChromeOrFirstParty we're removing the
ability for 1st party apps to set a navigation transition type. Also we
shouldn't allow third parties to set the TYPED transition type (it's
unclear whether this was intentional when this was originally landed).

From metrics, 0.00001% of launch intents specify a TYPED transition type
and there is zero usage from first party apps.

Bug: 520407381, 40091085
Change-Id: If823f1b57510c3ca5acf21919ef94ca532159c58
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7996137
Commit-Queue: Michael Thiessen <[email protected]>
Reviewed-by: Yaron Friedman <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1653225}
---

diff --git a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
index 97b71a5..386acc6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
@@ -377,8 +377,6 @@
             "BRING_TAB_GROUP_TO_FRONT_SOURCE";
     public static final String DAYDREAM_CATEGORY = "com.google.intent.category.DAYDREAM";
     public static final String TRUSTED_REFERRER_HISTOGRAM = "Android.Intent.TrustedReferrer";
-    public static final String TRUSTED_TRANSITION_TYPE_HISTOGRAM =
-            "Android.Intent.TrustedTransitionTypeUsed";
 
     /**
      * Represents popular external applications that can load a page in Chrome via intent. DO NOT
@@ -454,26 +452,6 @@
         int NUM_ENTRIES = 6;
     }
 
-    /** Histogram for insecure usage of first party transition type. */
-    @IntDef({
-        IntentTransitionType.IGNORED,
-        IntentTransitionType.ALLOWED_TYPED,
-        IntentTransitionType.ALLOWED_INSECURE,
-        IntentTransitionType.NUM_ENTRIES
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface IntentTransitionType {
-        /* The intent's specified referrer was ignored. */
-        int IGNORED = 0;
-        /* The intent was allowed to set a Typed transition type. */
-        int ALLOWED_TYPED = 1;
-        /* The intent was allowed to specify a transition type through an insecure PendingIntent
-         * check. */
-        int ALLOWED_INSECURE = 2;
-
-        int NUM_ENTRIES = 6;
-    }
-
     /** Intent extra to open an incognito tab. */
     public static final String EXTRA_OPEN_NEW_INCOGNITO_TAB =
             "com.google.android.apps.chrome.EXTRA_OPEN_NEW_INCOGNITO_TAB";
@@ -1506,39 +1484,17 @@
     }
 
     /**
-     * Some applications may request to load the URL with a particular transition type.
+     * Chrome itself may request to load the URL with a particular transition type.
+     *
      * @param intent Intent causing the URL load, may be null.
      * @param defaultTransition The transition to return if none specified in the intent.
      * @return The transition type to use for loading the URL.
      */
     public static int getTransitionTypeFromIntent(Intent intent, int defaultTransition) {
-        if (intent == null) return defaultTransition;
-        int transitionType =
-                IntentUtils.safeGetIntExtra(
-                        intent, IntentHandler.EXTRA_PAGE_TRANSITION_TYPE, PageTransition.LINK);
-        if (transitionType == PageTransition.TYPED) {
-            RecordHistogram.recordEnumeratedHistogram(
-                    TRUSTED_TRANSITION_TYPE_HISTOGRAM,
-                    IntentTransitionType.ALLOWED_TYPED,
-                    IntentTransitionType.NUM_ENTRIES);
-            return transitionType;
-        } else if (transitionType != PageTransition.LINK) {
-            if (IntentUtils.isTrustedIntentFromSelf(intent)) return transitionType;
-            if (notSecureIsIntentChromeOrFirstParty(intent)) {
-                RecordHistogram.recordEnumeratedHistogram(
-                        TRUSTED_TRANSITION_TYPE_HISTOGRAM,
-                        IntentTransitionType.ALLOWED_INSECURE,
-                        IntentTransitionType.NUM_ENTRIES);
-
-                // 1st party applications may specify any transition type.
-                return transitionType;
-            }
-            RecordHistogram.recordEnumeratedHistogram(
-                    TRUSTED_TRANSITION_TYPE_HISTOGRAM,
-                    IntentTransitionType.IGNORED,
-                    IntentTransitionType.NUM_ENTRIES);
-        }
-        return defaultTransition;
+        if (!IntentUtils.isTrustedIntentFromSelf(intent)) return defaultTransition;
+        if (!intent.hasExtra(IntentHandler.EXTRA_PAGE_TRANSITION_TYPE)) return defaultTransition;
+        return IntentUtils.safeGetIntExtra(
+                intent, IntentHandler.EXTRA_PAGE_TRANSITION_TYPE, PageTransition.LINK);
     }
 
     /**
diff --git a/tools/metrics/histograms/metadata/android/enums.xml b/tools/metrics/histograms/metadata/android/enums.xml
index 4194783..109f667 100644
--- a/tools/metrics/histograms/metadata/android/enums.xml
+++ b/tools/metrics/histograms/metadata/android/enums.xml
@@ -1339,12 +1339,6 @@
   <int value="2" label="AllowedInsecure"/>
 </enum>
 
-<enum name="IntentTransitionType">
-  <int value="0" label="Ignored"/>
-  <int value="1" label="AllowedTyped"/>
-  <int value="2" label="AllowedInsecure"/>
-</enum>
-
 <!-- LINT.IfChange(LauncherShortcutUpdateFailure) -->
 
 <enum name="LauncherShortcutUpdateFailure">
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index f53c74f..a616c31 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -3141,16 +3141,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.Intent.TrustedTransitionTypeUsed"
-    enum="IntentTransitionType" expires_after="2027-06-09">
-  <owner>[email protected]</owner>
-  <owner>[email protected]</owner>
-  <summary>
-    Records whether, and why, we used a trusted TransitionType on an Intent
-    coming from another app.
-  </summary>
-</histogram>
-
 <histogram
     name="Android.IsolatedSplits.ClassLoaderReplaced.{AndroidFeatureModuleName}"
     enum="BooleanYesNo" expires_after="2023-06-26">
Loading diff…

Original Bug Report

reported by [email protected]

Potential Omnibox autocomplete poisoning via untrusted transition type extra in Android intents

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: An issue in the Android IntentHandler allows untrusted third-party applications to set the transition type of triggered Custom Tabs or VIEW intents to PageTransition.TYPED without signature or first-party trust checks. This can potentially allow an attacker to artificially increase the typed_count of an arbitrary URL in the user’s history backend. Consequently, the attacker’s URL can be promoted to an inline autocomplete suggestion in the Omnibox, hijacking user navigations.

Affected files:

  • chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
  • chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java
  • content/browser/renderer_host/navigation_controller_android.cc
  • chrome/browser/history/history_tab_helper.cc
  • components/history/core/browser/history_backend.cc
  • components/omnibox/browser/history_url_provider.cc
  • components/omnibox/browser/scored_history_match.cc

Estimated timestamp from git blame: 2016-05-26

Root Cause Analysis

In chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java (lines 1464-1477), the method getTransitionTypeFromIntent extracts a transition type specified by an incoming intent extra com.google.chrome.transition_type (EXTRA_PAGE_TRANSITION_TYPE). However, it contains an exemption that bypasses the first-party trust check for the PageTransition.TYPED transition:

public static int getTransitionTypeFromIntent(Intent intent, int defaultTransition) {
    if (intent == null) return defaultTransition;
    int transitionType =
            IntentUtils.safeGetIntExtra(
                    intent, IntentHandler.EXTRA_PAGE_TRANSITION_TYPE, PageTransition.LINK);
    if (transitionType == PageTransition.TYPED) {
        return transitionType;
    } else if (transitionType != PageTransition.LINK
            && notSecureIsIntentChromeOrFirstParty(intent)) {
        // 1st party applications may specify any transition type.
        return transitionType;
    }
    return defaultTransition;
}

Because of the unconditional check if (transitionType == PageTransition.TYPED), any untrusted external application can pass a transition type of PageTransition.TYPED (1), which is returned directly, bypassing signature verification (notSecureIsIntentChromeOrFirstParty).

Potential Data Flow and Impact

  1. Intent Extraction: A potentially malicious app launches a Custom Tab or a VIEW intent targeting Chrome, passing the intent extra "com.google.chrome.transition_type" = 1 along with a malicious URL (e.g., https://attacker.example/phish).
  2. Transition Propagation: CustomTabActivityNavigationController.java:249-251 uses IntentHandler.getTransitionTypeFromIntent() to retrieve the transition type from the intent, assigning it to the navigation parameters (LoadUrlParams).
  3. Navigation Commit: The navigation proceeds and commits in the browser process with the transition type set to ui::PAGE_TRANSITION_TYPED (converted in content/browser/renderer_host/navigation_controller_android.cc:249). Because this is a browser-initiated navigation, it does not undergo renderer-initiated IPC checks like PageTransitionIsWebTriggerable.
  4. History Backend Increment: Once the navigation commits, HistoryTabHelper::CreateHistoryAddPageArgs (in chrome/browser/history/history_tab_helper.cc:231) reads the transition type. Since it is PAGE_TRANSITION_TYPED, HistoryBackend::IsTypedIncrement (in components/history/core/browser/history_backend.cc:362) evaluates to true. This causes the history database to increment the typed_count of the malicious URL in SQLite.
  5. Omnibox Autocomplete Hijacking: After one or two potential visits, the HistoryURLProvider (components/omnibox/browser/history_url_provider.cc:235) evaluates the URL’s typed_count and promotes it as a high-confidence inline autocompletion match for its matching prefixes. When the user subsequently types a matching prefix and presses Enter, they are redirected to the attacker’s phishing site.
  6. Cross-Device Sync: Because the transition is stored as TYPED and synchronized via Chrome Sync, this autocomplete poisoning potentially propagates to all other signed-in devices (including desktop Chrome).

Potential Steps to Reproduce

Note: These steps are based on static code analysis. Our testing tooling does not currently have the capability to execute code programmatically to verify.

  1. From an Android app, construct an intent targeting Chrome to launch a Custom Tab with the target URL:
    Intent intent = new CustomTabsIntent.Builder().build().intent;
    intent.setData(Uri.parse("https://attacker.example/phish"));
    intent.putExtra("com.google.chrome.transition_type", 1);  // PageTransition.TYPED
    intent.setPackage("com.android.chrome");
    context.startActivity(intent);
    
  2. Wait for the navigation to commit, and then repeat the process once more (to satisfy the typed_count > 1 criteria required for non-host URLs).
  3. Open the Chrome Omnibox and type a prefix matching the malicious URL (e.g., att). Observe if the URL is promoted to the inline autocomplete suggestion.

Suggested Fix

In chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java, remove the exemption for PageTransition.TYPED and ensure that all non-LINK transition types (including TYPED) are subjected to the first-party trust check:

public static int getTransitionTypeFromIntent(Intent intent, int defaultTransition) {
    if (intent == null) return defaultTransition;
    int transitionType =
            IntentUtils.safeGetIntExtra(
                    intent, IntentHandler.EXTRA_PAGE_TRANSITION_TYPE, PageTransition.LINK);
    if (transitionType != PageTransition.LINK
            && notSecureIsIntentChromeOrFirstParty(intent)) {
        // Only verified 1st party applications may specify custom transition types.
        return transitionType;
    }
    return defaultTransition;
}

Evaluated with Chrome root at commit: e9507a33bb4148ee071aaaf8a7e9ad68770359bf


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