Medium chrome Authorization Error 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect authorization in PDF
DescriptionIncorrect authorization in PDF
ComponentPDF
Bug ClassAuthorization Error
Tracker518091868
Fix commitb868931d1055 (chromium/src) +116/-8
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-06

Changed Functions

FunctionChangeNotes
TEST
chrome/browser/ui/startup/url_util_unittest.cc
modified

Files Changed

  • chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
  • chrome/android/junit/src/org/chromium/chrome/browser/IntentHandlerRobolectricTest.java
  • chrome/android/java/src/org/chromium/chrome/browser/ExternalIntentUrlChecker.java
  • chrome/android/junit/src/org/chromium/chrome/browser/handoff/HandoffControllerUnitTest.java
  • chrome/android/junit/src/org/chromium/chrome/browser/ExternalIntentUrlCheckerTest.java
  • chrome/browser/ui/startup/url_util_unittest.cc
From b868931d1055af3089b25c61f4742a317510dc44 Mon Sep 17 00:00:00 2001
From: Shu Yang <[email protected]>
Date: Fri, 11 Sep 2026 13:05:03 -0700
Subject: [PATCH] Prevent external intent validation bypass for local PDF and content URIs

This CL addresses an external intent validation bypass that could allow
a local malicious app to force Chrome to read and share its own
protected files via a Confused Deputy attack.

Part 1: Validate original target URLs Previously, IntentHandler
automatically encoded incoming content URIs with PDF MIME types into
internal chrome-native:// URLs prior to URL validation. This CL
separates target URL extraction from internal encoding by introducing
getTargetUrlFromIntent(). This allows IntentHandler to validate the
original target URL before any internal transformations. Consequently,
the allowlist exception for UrlConstants.PDF_URL in
ExternalIntentUrlChecker is removed.

Part 2: Block external intents targeting Chrome's Content Providers To
further prevent malicious apps from instructing Chrome to open internal
Chrome-owned content URIs, ExternalIntentUrlChecker is updated to
explicitly reject intents containing a content:// scheme if the
authority belongs to Chrome (starts with Chrome's package name). This
prevents external intents from weaponizing any of Chrome's internal
Content Providers.

Bug: 518091868
Change-Id: I3bcea41255f8f3bd406413bb1eddf8c2b5eecdc2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8383017
Commit-Queue: Shu Yang <[email protected]>
Reviewed-by: Wenyu Fu <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1696532}
---
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ExternalIntentUrlChecker.java b/chrome/android/java/src/org/chromium/chrome/browser/ExternalIntentUrlChecker.java
index cc5312f1..c1773e30 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ExternalIntentUrlChecker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ExternalIntentUrlChecker.java
@@ -7,6 +7,7 @@
 import org.jni_zero.JNINamespace;
 import org.jni_zero.NativeMethods;
 
+import org.chromium.base.ContextUtils;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.components.embedder_support.util.UrlConstants;
@@ -48,6 +49,19 @@
             return true;
         }
 
+        // Block external intents that target Chrome's own content providers (e.g., FileProvider).
+        // Untrusted apps should not be able to force Chrome to open its own protected files,
+        // which could lead to Confused Deputy vulnerabilities.
+        if (UrlConstants.CONTENT_SCHEME.equals(scheme)) {
+            String host = url.getHost();
+            if (host != null && !host.isEmpty()) {
+                String packageName = ContextUtils.getApplicationContext().getPackageName();
+                if (host.equals(packageName) || host.startsWith(packageName + ".")) {
+                    return true;
+                }
+            }
+        }
+
         String urlString = url.getSpec();
 
         if (!ExternalIntentUrlCheckerJni.get().validateUrl(url)) {
@@ -56,8 +70,7 @@
             if (urlString.equals(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL)
                     || urlString.equals(ContentUrlConstants.ABOUT_BLANK_URL)
                     || urlString.equals(UrlConstants.CHROME_DINO_URL)
-                    || urlString.startsWith(UrlConstants.CHROME_EXTENSIONS_URL)
-                    || urlString.startsWith(UrlConstants.PDF_URL)) {
+                    || urlString.startsWith(UrlConstants.CHROME_EXTENSIONS_URL)) {
                 return false;
             }
             return true;
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 9b25dcf..77057dc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
@@ -1072,7 +1072,7 @@
                 return tabIdsToUrls.isEmpty();
             } else {
                 return shouldIgnoreIntentUrl(
-                        intent, context, getUrlFromIntent(intent), isCustomTab);
+                        intent, context, getTargetUrlFromIntent(intent), isCustomTab);
             }
         } catch (Throwable t) {
             return true;
@@ -1275,6 +1275,20 @@
     }
 
     /**
+     * Extracts the target URL from the intent, unwrapping googlechrome:// if present.
+     *
+     * @param intent Intent to examine.
+     * @return Target URL from the Intent, or null if a valid URL couldn't be found.
+     */
+    private static @Nullable String getTargetUrlFromIntent(@Nullable Intent intent) {
+        String url = extractUrlFromIntent(intent);
+        if (isGoogleChromeScheme(url)) {
+            url = ExternalNavigationHandler.getUrlFromSelfSchemeUrl(GOOGLECHROME_SCHEME, url);
+        }
+        return url;
+    }
+
+    /**
      * Retrieve the URL from the Intent, which may be in multiple locations. If the URL is
      * googlechrome:// scheme, parse the actual navigation URL.
      *
@@ -1282,10 +1296,7 @@
      * @return URL from the Intent, or null if a valid URL couldn't be found.
      */
     public static @Nullable String getUrlFromIntent(@Nullable Intent intent) {
-        String url = extractUrlFromIntent(intent);
-        if (isGoogleChromeScheme(url)) {
-            url = ExternalNavigationHandler.getUrlFromSelfSchemeUrl(GOOGLECHROME_SCHEME, url);
-        }
+        String url = getTargetUrlFromIntent(intent);
         // To display a PDF in Chrome, the content URI must be encoded.
         String encodedPdfUrl =
                 PdfUtils.getEncodedContentUri(url, ContextUtils.getApplicationContext());
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ExternalIntentUrlCheckerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ExternalIntentUrlCheckerTest.java
index ede4dcbc..a5c92521 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ExternalIntentUrlCheckerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ExternalIntentUrlCheckerTest.java
@@ -17,6 +17,7 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.url.GURL;
 import org.chromium.url.JUnitTestGURLs;
@@ -92,9 +93,36 @@
         assertFalse(
                 ExternalIntentUrlChecker.isUnsafeExternalIntentUrl(
                         new GURL("chrome://extensions/?id=abcdef")));
-        assertFalse(
+        assertTrue(
                 ExternalIntentUrlChecker.isUnsafeExternalIntentUrl(
                         new GURL("chrome-native://pdf/viewer.html")));
+        assertTrue(
+                ExternalIntentUrlChecker.isUnsafeExternalIntentUrl(
+                        new GURL("chrome-native://pdf/link?url=content://media/external/file/1")));
+    }
+
+    @Test
+    public void testIsUnsafeExternalIntentUrl_BlocksChromeFileProvider() {
+        doReturn(true).when(mNativeMock).validateUrl(any());
+
+        String packageName = ContextUtils.getApplicationContext().getPackageName();
+        assertTrue(
+                ExternalIntentUrlChecker.isUnsafeExternalIntentUrl(
+                        new GURL("content://" + packageName + ".FileProvider/document.pdf")));
+        assertTrue(
+                ExternalIntentUrlChecker.isUnsafeExternalIntentUrl(
+                        new GURL(
+                                "content://userinfo@"
+                                        + packageName
+                                        + ".FileProvider/document.pdf")));
+        assertTrue(
+                ExternalIntentUrlChecker.isUnsafeExternalIntentUrl(
+                        new GURL("content://" + packageName + "/document.pdf")));
+
+        // Should not block legitimate external providers that start with Chrome's package name
+        assertFalse(
+                ExternalIntentUrlChecker.isUnsafeExternalIntentUrl(
+                        new GURL("content://" + packageName + "cast.FileProvider/document.pdf")));
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/IntentHandlerRobolectricTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/IntentHandlerRobolectricTest.java
index 688f62c0..cdb2502 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/IntentHandlerRobolectricTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/IntentHandlerRobolectricTest.java
@@ -754,6 +754,10 @@
 
         trustedIntent.setData(Uri.parse("chrome-native://newtab"));
         Assert.assertFalse(IntentHandler.shouldIgnoreIntent(trustedIntent, null));
+
+        trustedIntent.setData(
+                Uri.parse("chrome-native://pdf/link?url=content%3A%2F%2Ftest.provider%2Ftest.pdf"));
+        Assert.assertFalse(IntentHandler.shouldIgnoreIntent(trustedIntent, null));
     }
 
     @Test
@@ -769,6 +773,10 @@
         untrustedIntent.setData(Uri.parse("chrome-native://newtab"));
         Assert.assertTrue(IntentHandler.shouldIgnoreIntent(untrustedIntent, null));
 
+        untrustedIntent.setData(
+                Uri.parse("chrome-native://pdf/link?url=content%3A%2F%2Ftest.provider%2Ftest.pdf"));
+        Assert.assertTrue(IntentHandler.shouldIgnoreIntent(untrustedIntent, null));
+
         untrustedIntent.addCategory("com.fake.category");
         Assert.assertTrue(IntentHandler.shouldIgnoreIntent(untrustedIntent, null));
     }
@@ -776,6 +784,27 @@
     @Test
     @SmallTest
     @Feature({"Android-AppBase"})
+    public void testContentPdfUrlAllowedForExternalSources() {
+        Mockito.doReturn(true).when(mExternalIntentUrlCheckerNativeMock).validateUrl(any());
+        Intent externalIntent = new Intent(Intent.ACTION_VIEW);
+        externalIntent.setData(Uri.parse("content://test.provider/document.pdf"));
+        Assert.assertFalse(IntentHandler.shouldIgnoreIntent(externalIntent, null));
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Android-AppBase"})
+    public void testChromeFileProviderBlockedForUntrustedIntent() {
+        Intent untrustedIntent = new Intent(Intent.ACTION_VIEW);
+        String packageName = ContextUtils.getApplicationContext().getPackageName();
+        untrustedIntent.setData(
+                Uri.parse("content://" + packageName + ".FileProvider/document.pdf"));
+        Assert.assertTrue(IntentHandler.shouldIgnoreIntent(untrustedIntent, null));
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Android-AppBase"})
     public void testChromeInternalUrlsAllowedForWhitelistedUrls() {
         Intent untrustedIntent = new Intent(Intent.ACTION_VIEW);
         Assert.assertFalse(IntentHandler.wasIntentSenderChrome(untrustedIntent));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/handoff/HandoffControllerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/handoff/HandoffControllerUnitTest.java
index 1d4a898..31d269ec 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/handoff/HandoffControllerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/handoff/HandoffControllerUnitTest.java
@@ -227,6 +227,21 @@
     }
 
     @Test
+    public void testUpdateHandoffState_PdfUrl_Disabled() {
+        when(mTab.getUrl())
+                .thenReturn(
+                        new GURL(
+                                "chrome-native://pdf/"
+                                    + "link?url=content%3A%2F%2Fcom.example.provider%2Ffile.pdf"));
+        mController =
+                new HandoffController(
+                        mActivity, mTabModelSelector, mActivityTabProvider, mDelegate);
+        ShadowLooper.idleMainLooper();
+
+        verify(mDelegate, atLeastOnce()).setHandoffEnabled(eq(mActivity), eq(false));
+    }
+
+    @Test
     public void testOnHandoffActivityDataRequested_UnsafeUrl_ReturnsNull() throws Exception {
         initializeController();
         when(mTab.getUrl()).thenReturn(new GURL("chrome://flags"));
diff --git a/chrome/browser/ui/startup/url_util_unittest.cc b/chrome/browser/ui/startup/url_util_unittest.cc
index a403d42..e232c28 100644
--- a/chrome/browser/ui/startup/url_util_unittest.cc
+++ b/chrome/browser/ui/startup/url_util_unittest.cc
@@ -43,6 +43,12 @@
   EXPECT_FALSE(ValidateLaunchUrlWebUnsafe(GURL("about:about")));
   EXPECT_FALSE(ValidateLaunchUrlWebUnsafe(GURL("about:")));
 
+  // chrome-native: URLs should be rejected
+  EXPECT_FALSE(ValidateLaunchUrlWebUnsafe(GURL("chrome-native://pdf/")));
+  EXPECT_FALSE(ValidateLaunchUrlWebUnsafe(GURL(
+      "chrome-native://pdf/link?url=https%3A%2F%2Fexample.com%2Fdoc.pdf")));
+  EXPECT_FALSE(ValidateLaunchUrlWebUnsafe(GURL("chrome-native://newtab/")));
+
 #if BUILDFLAG(IS_ANDROID)
   EXPECT_TRUE(ValidateLaunchUrlWebUnsafe(
       GURL("content://packagename.providername/path")));
@@ -71,6 +77,12 @@
   EXPECT_FALSE(ValidateLaunchUrlWebSafe(GURL("about:settings")));
   EXPECT_FALSE(ValidateLaunchUrlWebSafe(GURL("about:about")));
   EXPECT_FALSE(ValidateLaunchUrlWebSafe(GURL("about:")));
+
+  // chrome-native: URLs should be rejected in WebSafe context.
+  EXPECT_FALSE(ValidateLaunchUrlWebSafe(GURL("chrome-native://pdf/")));
+  EXPECT_FALSE(ValidateLaunchUrlWebSafe(GURL(
+      "chrome-native://pdf/link?url=https%3A%2F%2Fexample.com%2Fdoc.pdf")));
+  EXPECT_FALSE(ValidateLaunchUrlWebSafe(GURL("chrome-native://newtab/")));
 }
 
 TEST(UrlUtilTest, ValidateUrlRejectsNestedSchemes) {
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/ui/startup/url_util_unittest.cc b/chrome/browser/ui/startup/url_util_unittest.cc
index a403d42..e232c28 100644
--- a/chrome/browser/ui/startup/url_util_unittest.cc
+++ b/chrome/browser/ui/startup/url_util_unittest.cc
@@ -43,6 +43,12 @@
   EXPECT_FALSE(ValidateLaunchUrlWebUnsafe(GURL("about:about")));
   EXPECT_FALSE(ValidateLaunchUrlWebUnsafe(GURL("about:")));
 
+  // chrome-native: URLs should be rejected
+  EXPECT_FALSE(ValidateLaunchUrlWebUnsafe(GURL("chrome-native://pdf/")));
+  EXPECT_FALSE(ValidateLaunchUrlWebUnsafe(GURL(
+      "chrome-native://pdf/link?url=https%3A%2F%2Fexample.com%2Fdoc.pdf")));
+  EXPECT_FALSE(ValidateLaunchUrlWebUnsafe(GURL("chrome-native://newtab/")));
+
 #if BUILDFLAG(IS_ANDROID)
   EXPECT_TRUE(ValidateLaunchUrlWebUnsafe(
       GURL("content://packagename.providername/path")));
@@ -71,6 +77,12 @@
   EXPECT_FALSE(ValidateLaunchUrlWebSafe(GURL("about:settings")));
   EXPECT_FALSE(ValidateLaunchUrlWebSafe(GURL("about:about")));
   EXPECT_FALSE(ValidateLaunchUrlWebSafe(GURL("about:")));
+
+  // chrome-native: URLs should be rejected in WebSafe context.
+  EXPECT_FALSE(ValidateLaunchUrlWebSafe(GURL("chrome-native://pdf/")));
+  EXPECT_FALSE(ValidateLaunchUrlWebSafe(GURL(
+      "chrome-native://pdf/link?url=https%3A%2F%2Fexample.com%2Fdoc.pdf")));
+  EXPECT_FALSE(ValidateLaunchUrlWebSafe(GURL("chrome-native://newtab/")));
 }
 
 TEST(UrlUtilTest, ValidateUrlRejectsNestedSchemes) {
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.