Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Sharing
DescriptionInappropriate implementation in Sharing
ComponentSharing
Bug ClassLogic Error
Tracker513001690
Fix commit03a83a4d8e36 (chromium/src) +57/-7
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Files Changed

  • chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
  • chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinatorTest.java
From 03a83a4d8e36a2745fde446e17e03de1a261d94f Mon Sep 17 00:00:00 2001
From: Ben Chin <lungc@chromium.org>
Date: Mon, 18 May 2026 18:47:07 -0700
Subject: [PATCH] Cancel request on all cross-document navigations

Previously, LinkToTextCoordinator only cancelled a pending selector
request if the navigation was user-initiated from the omnibox (TYPED).
This created a TOCTOU vulnerability where a renderer-initiated
navigation (e.g., a link click) could change the page origin while a
request was pending, potentially leaking the new page's URL and title
into the share sheet if the renderer later replied.

This CL broadens the cancellation logic in
onDidStartNavigationInPrimaryMainFrame to include all cross-document
navigations. This ensures that the content being shared is always tied
to the document where the user initiated the share action.

Bug: 513001690
Test: LinkToTextCoordinatorTest
Change-Id: I156fb84c65231560e727240ec450206b6fc726a9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7850195
Commit-Queue: ben chin <lungc@chromium.org>
Reviewed-by: Elly <ellyjones@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1632556}
---

diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
index 2056883..3d87049 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
@@ -28,7 +28,6 @@
 import org.chromium.chrome.browser.tab.TabHidingType;
 import org.chromium.components.browser_ui.share.ShareParams;
 import org.chromium.content_public.browser.NavigationHandle;
-import org.chromium.ui.base.PageTransition;
 import org.chromium.url.GURL;
 
 /** Handles the Link To Text action in the Sharing Hub. */
@@ -254,14 +253,9 @@
         }
     }
 
-    // Discard results if tab content is changed by typing new URL in omnibox.
+    // Discard results if tab content is changed by a cross-document navigation.
     @Override
     public void onDidStartNavigationInPrimaryMainFrame(Tab tab, NavigationHandle navigationHandle) {
-        // Only cancel if the navigation was user-initiated from the omnibox.
-        if ((navigationHandle.pageTransition() & PageTransition.CORE_MASK)
-                != PageTransition.TYPED) {
-            return;
-        }
         if (navigationHandle.isSameDocument()) return;
 
         if (mChromeShareExtras.isReshareHighlightedText()) {
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinatorTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinatorTest.java
index 6e38b44..46f3d70 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinatorTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinatorTest.java
@@ -381,6 +381,31 @@
 
     @Test
     @SmallTest
+    public void shareLinkToTextTest_LinkNavigation_BeforeRemoteRequestComplete() {
+        mLinkToTextCoordinator.initLinkToTextCoordinator(
+                mTab, mShareCallback, mChromeShareExtras, SHARE_START_TIME, VISIBLE_URL, "", false);
+        mLinkToTextCoordinator.shareLinkToText();
+        mLinkToTextCoordinator.onDidStartNavigationInPrimaryMainFrame(
+                mTab,
+                NavigationHandle.createForTesting(
+                        new GURL(VISIBLE_URL),
+                        /* isInPrimaryMainFrame= */ true,
+                        /* isSameDocument= */ false,
+                        /* isRendererInitiated= */ true,
+                        PageTransition.LINK,
+                        /* hasUserGesture= */ false,
+                        /* isReload= */ false));
+
+        // check doesn't show share sheet
+        verify(mShareCallback, times(0)).showShareSheet(any(), any(), anyLong());
+
+        // Check that histogram will be recorded correctly.
+        verify(mLinkToTextBridge, times(1))
+                .logFailureMetrics(any(), eq(LinkGenerationError.OMNIBOX_NAVIGATION));
+    }
+
+    @Test
+    @SmallTest
     public void shareLinkToTextTest_Reshare_Success() {
         mLinkToTextCoordinator.initLinkToTextCoordinator(
                 mTab,
@@ -494,4 +519,35 @@
         // No new histogram is recorded.
         verify(mLinkToTextBridge, times(1)).logLinkToTextReshareStatus(anyInt());
     }
+
+    @Test
+    @SmallTest
+    public void shareLinkToTextTest_Reshare_LinkNavigation_BeforeRemoteRequestComplete() {
+        mLinkToTextCoordinator.initLinkToTextCoordinator(
+                mTab,
+                mShareCallback,
+                mReshareChromeShareExtras,
+                SHARE_START_TIME,
+                VISIBLE_URL,
+                "",
+                false);
+        mLinkToTextCoordinator.shareLinkToText();
+        mLinkToTextCoordinator.onDidStartNavigationInPrimaryMainFrame(
+                mTab,
+                NavigationHandle.createForTesting(
+                        new GURL(VISIBLE_URL),
+                        /* isInPrimaryMainFrame= */ true,
+                        /* isSameDocument= */ false,
+                        /* isRendererInitiated= */ true,
+                        PageTransition.LINK,
+                        /* hasUserGesture= */ false,
+                        /* isReload= */ false));
+
+        // check doesn't show share sheet
+        verify(mShareCallback, times(0)).showShareSheet(any(), any(), anyLong());
+
+        // Check that histogram will be recorded correctly.
+        verify(mLinkToTextBridge, times(1))
+                .logLinkToTextReshareStatus(LinkToTextReshareStatus.OMNIBOX_NAVIGATION);
+    }
 }
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential cross-origin information leak in Android LinkToTextCoordinator via navigation

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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: LinkToTextCoordinator fails to cancel pending text selector requests during renderer-initiated navigations. A compromised renderer can trigger a navigation to a victim site and fulfill the request, causing the browser to leak the victim’s canonical URL and title into the share sheet.

Affected files:

  • chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
  • chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextHelper.java

Estimated timestamp from git blame: 2025-10-31

Vulnerability Details

A potential cross-origin information leak exists in the LinkToTextCoordinator component of Chrome for Android. This component manages the “Link to Text” feature, which generates a specific URL fragment for selected text when a user initiates a “Share” action.

When a share is started, the browser requests a text selector from the renderer via the TextFragmentReceiver Mojo interface. If a navigation occurs before the renderer responds, the in-flight request should be cancelled to prevent the selector or the resulting metadata from being associated with a different page.

However, LinkToTextCoordinator.onDidStartNavigationInPrimaryMainFrame contains a check that only cancels the request if the navigation’s transition type is TYPED (e.g., a URL entered in the omnibox). Renderer-initiated navigations, such as link clicks or script-based navigations, do not trigger a cancellation.

If a compromised renderer initiates a cross-origin navigation to a victim page and subsequently sends a RequestSelector response before the 100ms timeout, the browser-side handler onRemoteRequestCompleted will execute. This handler calls LinkToTextHelper.requestCanonicalUrl, which retrieves the canonical URL of the current primary main frame. If the navigation to the victim page has committed, the browser will fetch the victim’s canonical URL and the current tab title (the victim’s title) and use them to populate the Android share sheet.

Root Cause Analysis

  1. Insufficient Cancellation Logic: In LinkToTextCoordinator.java, the onDidStartNavigationInPrimaryMainFrame method ignores non-TYPED navigations:
    @Override
    public void onDidStartNavigationInPrimaryMainFrame(Tab tab, NavigationHandle navigationHandle) {
        if ((navigationHandle.pageTransition() & PageTransition.CORE_MASK)
                != PageTransition.TYPED) {
            return; // Renderer-initiated navigations are not cancelled
        }
        // ...
    }
    
  2. Late Binding of Frame: LinkToTextHelper.requestCanonicalUrl resolves the main frame at the time of the callback rather than binding to the specific frame that initiated the share request.

Potential Impact

An attacker with control over a renderer process could leak the canonical URL and document title of a cross-origin victim site. This metadata is exposed in the Android share sheet UI and can be exfiltrated if the user completes the share action to an external target. This constitutes a bypass of the Same-Origin Policy for page metadata.

Potential Steps to Reproduce

(Note: These steps are based on code analysis and have not been verified with a live exploit.)

  1. From a compromised renderer on https://attacker.example, wait for the user to select text and trigger the Share action.
  2. Upon receiving the RequestSelector Mojo call from the browser, initiate a navigation to https://victim.example.
  3. Ensure the navigation commits within the 100ms timeout (e.g., by utilizing prerendering or high-speed navigation).
  4. Respond to the RequestSelector Mojo call with a spoofed selector.
  5. Observe that the Android share sheet displays the title and canonical URL of https://victim.example instead of the original attacker page.

Suggested Fix

Modify LinkToTextCoordinator.onDidStartNavigationInPrimaryMainFrame to cancel the pending request for all cross-document navigations, regardless of the transition type. Additionally, the metadata (canonical URL and title) should be retrieved from the RenderFrameHost that was active when the share was initiated, rather than resolving the current main frame at completion time.

Evaluated with Chrome root at commit: b3153093eb3c78c3e88ccf562bcbc20437a04b0e


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