CVE-2026-11287
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroidImpl.java |
modified | |
forchrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroidImpl.java |
modified |
Files Changed
chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroidImpl.javachrome/browser/android/tab_web_contents_delegate_android.hchrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java
Patch
From 05bf5a2b510c9ba1e4463d5a240dd08fbfe3baa9 Mon Sep 17 00:00:00 2001 From: Fabio Muratori <[email protected]> Date: Fri, 17 Apr 2026 12:11:03 -0700 Subject: [PATCH] Removed unused methods TabWebContentsDelegateAndroid#openInAppOrChromeFromCct Method was only used by previous CL to force open a new tab from CCT when middle click navigation is processed. Handling this logic here is very problematic because we are skipping all the policies enforced by ExternalNavigationHandler. This is the root cause of security vulnerability "Potential silent external app launch from CCT via OpenURL with NEW_BACKGROUND_TAB". To prevent further mis-use we should directly remove the method in TabWebContentsDelegateAndroid. Bug: 502173136 Remove stale handling of middle click navigations This is an old attempt at modifyling the middle click navigation flow and outcome. Flag was never progressed and this slice is basically deadcode. On top of this, it has been reported by AI tools that the flag could enable a vulnerability "Potential silent external app launch from CCT via OpenURL with NEW_BACKGROUND_TAB" Bug: 502173136 Change-Id: Ibc931608281e768d93b1654e26d7314a6d0357d4 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7770415 Commit-Queue: Fabio Muratori <[email protected]> Reviewed-by: Calder Kitagawa <[email protected]> Cr-Commit-Position: refs/heads/main@{#1616749} --- diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroidImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroidImpl.java index f330da6..27c14cf2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroidImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroidImpl.java @@ -4,18 +4,12 @@ package org.chromium.chrome.browser.tab; -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; -import android.net.Uri; import android.os.Handler; import android.view.KeyEvent; @@ -28,7 +22,6 @@ import org.chromium.base.Callback; import org.chromium.base.ContextUtils; import org.chromium.base.ObserverList.RewindableIterator; -import org.chromium.base.PackageManagerUtils; import org.chromium.base.lifetime.Destroyable; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; @@ -38,10 +31,8 @@ import org.chromium.chrome.browser.app.serial.SerialNotificationService; import org.chromium.chrome.browser.app.usb.UsbNotificationService; import org.chromium.chrome.browser.bluetooth.BluetoothNotificationManager; -import org.chromium.chrome.browser.document.ChromeLauncherActivity; import org.chromium.chrome.browser.gesturenav.NativePageBitmapCapturer; import org.chromium.chrome.browser.media.MediaCaptureNotificationServiceImpl; -import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; import org.chromium.chrome.browser.policy.PolicyAuditor; import org.chromium.chrome.browser.policy.PolicyAuditorJni; import org.chromium.chrome.browser.serial.SerialNotificationManager; @@ -188,55 +179,6 @@ mDelegate.setContentsBounds(source, bounds); } - @CalledByNative - @Override - protected boolean openInAppOrChromeFromCct(GURL gurl) { - Intent intent = - new Intent(Intent.ACTION_VIEW, Uri.parse(gurl.getSpec())) - .addCategory(Intent.CATEGORY_BROWSABLE); - - ResolveInfo defaultActivity = - PackageManagerUtils.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); - - if (defaultActivity != null) { - // Check if the default activity is a chooser - List<ResolveInfo> handlers = - PackageManagerUtils.queryIntentActivities( - intent, PackageManager.GET_RESOLVED_FILTER); - for (ResolveInfo handler : handlers) { - String packageName = handler.activityInfo.packageName; - String activityName = handler.activityInfo.name; - if (packageName.equals(defaultActivity.activityInfo.packageName) - && activityName.equals(defaultActivity.activityInfo.name)) { - intent.setClassName(packageName, activityName); - break; - } - } - } - - // Fallback to Chrome if no supporting app was found - if (intent.getComponent() == null) { - intent.setClass(ContextUtils.getApplicationContext(), ChromeLauncherActivity.class); - } - - Context context = mTab.getContext(); - - int flags = Intent.FLAG_ACTIVITY_NEW_TASK; - // If we're in in multi window it's fine to open multiple instances - if (context instanceof Activity - && MultiWindowUtils.getInstance().isInMultiWindowMode((Activity) context)) { - flags |= Intent.FLAG_ACTIVITY_MULTIPLE_TASK; - } - - intent.setFlags(flags); - try { - context.startActivity(intent); - return true; - } catch (RuntimeException e) { - return false; - } - } - // WebContentsDelegateAndroid @Override diff --git a/chrome/browser/android/tab_web_contents_delegate_android.h b/chrome/browser/android/tab_web_contents_delegate_android.h index 3b66b56..5c4658e 100644 --- a/chrome/browser/android/tab_web_contents_delegate_android.h +++ b/chrome/browser/android/tab_web_contents_delegate_android.h @@ -146,7 +146,6 @@ bool IsInstalledWebappDelegateGeolocation() const; bool IsModalContextMenu() const; bool IsDynamicSafeAreaInsetsEnabled() const; - bool OpenInAppOrChromeFromCct(GURL url); void DraggableRegionsChanged( const std::vector<blink::mojom::DraggableRegionPtr>& regions, diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java index 5593e452c..d26b473 100644 --- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java +++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java @@ -134,10 +134,6 @@ return false; } - protected boolean openInAppOrChromeFromCct(GURL gurl) { - return false; - } - /** Called when WebContents reports a change to the non-draggable regions in header content. */ protected void nonDraggableRegionsChanged(List<Rect> regions) {}
Original Bug Report
Potential silent external app launch from CCT via OpenURL with NEW_BACKGROUND_TAB
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 without the Chrome Security team.
Overview: When the kNavigationCaptureRefactorAndroid feature is enabled, a compromised renderer in a Chrome Custom Tab can trigger silent external app launches. This occurs by requesting a navigation with the NEW_BACKGROUND_TAB disposition, which bypasses the popup blocker and standard external navigation security checks.
Affected files:
chrome/browser/android/tab_web_contents_delegate_android.ccchrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroidImpl.java
Estimated timestamp from git blame: 2025-05-07
Background
When external_intents::kNavigationCaptureRefactorAndroid is enabled, a logic flaw in TabWebContentsDelegateAndroid::OpenURLFromTab allows for unauthorized external application launches from Chrome Custom Tabs (CCT).
In chrome/browser/android/tab_web_contents_delegate_android.cc, OpenURLFromTab contains a short-circuit branch for CCTs requesting a NEW_BACKGROUND_TAB disposition:
if (base::FeatureList::IsEnabled(
external_intents::kNavigationCaptureRefactorAndroid)) {
if (IsCustomTab() &&
disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB) {
if (OpenInAppOrChromeFromCct(params.url)) {
// Navigation handled, stop here. Otherwise proceed normally.
return nullptr;
}
}
}
Vulnerability Details
This early return completely bypasses the standard popup blocking logic (blocked_content::ConsiderForPopupBlocking) and the subsequent routing to TabModelList::HandlePopupNavigation. As a result, the navigation never reaches ExternalNavigationHandler (ENH).
The Java sink, TabWebContentsDelegateAndroidImpl.openInAppOrChromeFromCct, directly constructs an ACTION_VIEW intent with CATEGORY_BROWSABLE and calls context.startActivity(intent).
Because ENH is bypassed, several critical security safeguards are missing:
- Popup Blocker Bypass: The app launch occurs immediately, without requiring a user gesture or displaying a popup warning.
- Incognito Bypass: If the CCT is in Incognito mode, the standard “Leave Incognito?” warning dialog is skipped, breaking the Incognito security boundary.
- Intent Sanitization: Standard intent sanitization (e.g., stripping dangerous extras) performed by ENH is omitted.
Chromium’s IPC validation in content/browser/renderer_host/ipc_utils.cc (IsValidRendererDisposition) explicitly permits the NEW_BACKGROUND_TAB disposition from renderers. Therefore, a compromised renderer can spoof this disposition to trigger the vulnerability.
Potential Attack Steps
Note: These steps are based on static analysis; a working proof-of-concept has not been executed.
- Ensure the flag
chrome://flags/#navigation-capture-refactor-androidis enabled on an Android device. - The user opens a Chrome Custom Tab (e.g., via a host application).
- An attacker compromises the Chrome renderer process associated with the CCT.
- From the compromised renderer, the attacker sends a
blink::mojom::FrameHost::OpenURLMojo IPC message to the Browser process with:url: A target external application URI (e.g.,tel:+15551234567,market://details?id=com.malicious.app, or a verified App Link).disposition:WindowOpenDisposition::NEW_BACKGROUND_TAB.user_gesture:false(ortrue).
- The browser process validates the IPC, accepts the disposition, and routes the request to
TabWebContentsDelegateAndroid::OpenURLFromTab. - The CCT check passes, and
OpenInAppOrChromeFromCctlaunches the target app silently without any user interaction or security prompts.
Suggested Fix
Refactor TabWebContentsDelegateAndroid::OpenURLFromTab to ensure that even when kNavigationCaptureRefactorAndroid is enabled, background tab navigations from CCTs are subjected to blocked_content::ConsiderForPopupBlocking (to verify user gestures) and ideally routed through ExternalNavigationHandler to enforce Incognito checks and intent sanitization before calling startActivity.
Evaluated with Chrome root at commit: 096fc8fdbfacf2546485756d03f160a3d04fcc9b
Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; 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.