CVE-2026-13939
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImplementationFactory.java |
modified |
Files Changed
chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImplementationFactory.javachrome/browser/bad_message.hcomponents/browser_ui/webshare/android/BUILD.gncomponents/browser_ui/webshare/android/DEPScomponents/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImpl.javacomponents/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImplTest.java
Patch
From 38749a08df65e12cbcb07bb3d00233d4c8be1931 Mon Sep 17 00:00:00 2001 From: Dibyajyoti Pal <[email protected]> Date: Mon, 18 May 2026 09:57:30 -0700 Subject: [PATCH] [WebShare] Validate share URL scheme and terminate compromised renderers The Android WebShare API receives share requests from renderer processes containing a target URL. Previously, the URL scheme was not explicitly validated in Java before being processed. To prevent compromised renderers from passing arbitrary or malicious URL schemes (such as `javascript:`), this change updates ShareServiceImpl to explicitly validate that the share URL uses HTTP or HTTPS. If an invalid URL or disallowed scheme is received, the request is rejected with ShareError.PERMISSION_DENIED and the offending renderer is terminated with RFH_INVALID_WEB_SHARE. Also updates WebShareDelegate to support renderer termination and adds unit tests verifying the invalid scheme handling. Fixed: 513149760 Change-Id: I6da922761ce2a64fdd9549b9ef23b7c7230534a3 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7850771 Reviewed-by: Avi Drissman <[email protected]> Commit-Queue: Dibyajyoti Pal <[email protected]> Cr-Commit-Position: refs/heads/main@{#1632237} --- diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImplementationFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImplementationFactory.java index e28578b..aaa75a06 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImplementationFactory.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImplementationFactory.java @@ -16,6 +16,7 @@ import org.chromium.components.browser_ui.share.ShareParams; import org.chromium.components.browser_ui.webshare.ShareServiceImpl; import org.chromium.content_public.browser.PermissionsPolicyFeature; +import org.chromium.content_public.browser.RenderFrameHost; import org.chromium.content_public.browser.WebContents; import org.chromium.services.service_manager.InterfaceFactory; import org.chromium.ui.base.WindowAndroid; @@ -75,6 +76,14 @@ return mWindowAndroid; } + @Override + public void terminateRendererDueToBadMessage(int reason) { + RenderFrameHost mainFrame = mWebContents.getMainFrame(); + if (mainFrame != null) { + mainFrame.terminateRendererDueToBadMessage(reason); + } + } + /** * Returns the current {@link ShareDelegate}, and updates it when the {@link * WindowAndroid} has changed. diff --git a/chrome/browser/bad_message.h b/chrome/browser/bad_message.h index 061c6d31..e7f5d6a 100644 --- a/chrome/browser/bad_message.h +++ b/chrome/browser/bad_message.h @@ -31,6 +31,7 @@ PVMB_SCRIPTED_PRINT_FENCED_FRAME = 8, SSI_CREATE_FENCED_FRAME = 9, CCU_SUPERFLUOUS_BIND = 10, + RFH_INVALID_WEB_FRAME_URL = 11, // Please add new elements here. The naming convention is abbreviated class // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the diff --git a/components/browser_ui/webshare/android/BUILD.gn b/components/browser_ui/webshare/android/BUILD.gn index ff9542a..2550b3d8 100644 --- a/components/browser_ui/webshare/android/BUILD.gn +++ b/components/browser_ui/webshare/android/BUILD.gn @@ -13,6 +13,7 @@ deps = [ "//base:base_java", "//components/browser_ui/share/android:java", + "//components/embedder_support/android:util_java", "//content/public/android:content_java", "//mojo/public/java:system_java", "//mojo/public/java/system:system_impl_java", @@ -20,6 +21,7 @@ "//third_party/androidx:androidx_annotation_annotation_java", "//third_party/blink/public/mojom:android_mojo_bindings_java", "//ui/android:ui_java", + "//url:url_java", "//url/mojom:url_mojom_gurl_java", ] } @@ -34,8 +36,11 @@ "//base:base_java_test_support", "//base:base_junit_test_support", "//base/test:test_support_java", + "//components/browser_ui/share/android:java", "//third_party/androidx:androidx_test_runner_java", "//third_party/blink/public/mojom:android_mojo_bindings_java", "//third_party/junit", + "//ui/android:ui_no_recycler_view_java", + "//url/mojom:url_mojom_gurl_java", ] } diff --git a/components/browser_ui/webshare/android/DEPS b/components/browser_ui/webshare/android/DEPS index bb31137..abd6bc44 100644 --- a/components/browser_ui/webshare/android/DEPS +++ b/components/browser_ui/webshare/android/DEPS @@ -1,5 +1,6 @@ include_rules = [ - "+mojo/public/java/system", + "+components/embedder_support/android/java", "+content/public/android/java", + "+mojo/public/java/system", "+ui/android/java", ] diff --git a/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImpl.java b/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImpl.java index dcb6a08..7ea720c 100644 --- a/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImpl.java +++ b/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImpl.java @@ -24,8 +24,10 @@ import org.chromium.build.annotations.Nullable; import org.chromium.components.browser_ui.share.ShareImageFileUtils; import org.chromium.components.browser_ui.share.ShareParams; +import org.chromium.components.embedder_support.util.UrlConstants; import org.chromium.mojo.system.MojoException; import org.chromium.ui.base.WindowAndroid; +import org.chromium.url.GURL; import org.chromium.url.mojom.Url; import org.chromium.webshare.mojom.ShareError; import org.chromium.webshare.mojom.ShareService; @@ -129,6 +131,13 @@ * @return The current {@link WindowAndroid} used to perform sharing. */ WindowAndroid getWindowAndroid(); + + /** + * Kills the renderer process when it is detected to have made a bad request. + * + * @param reason The BadMessageReason code from content::bad_message::BadMessageReason. + */ + void terminateRendererDueToBadMessage(int reason); } public ShareServiceImpl(WebShareDelegate delegate) { @@ -160,6 +169,16 @@ return; } + GURL shareUrl = new GURL(url.url); + boolean hasAllowedSchemes = + UrlConstants.HTTPS_SCHEME.equals(shareUrl.getScheme()) + || UrlConstants.HTTP_SCHEME.equals(shareUrl.getScheme()); + if (GURL.isEmptyOrInvalid(shareUrl) || !hasAllowedSchemes) { + callback.call(ShareError.PERMISSION_DENIED); + mDelegate.terminateRendererDueToBadMessage(11 /* RFH_INVALID_WEB_FRAME_URL */); + return; + } + ShareParams.TargetChosenCallback innerCallback = new ShareParams.TargetChosenCallback() { @Override diff --git a/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImplTest.java b/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImplTest.java index 5c23846..a9d0ba3 100644 --- a/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImplTest.java +++ b/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImplTest.java @@ -12,6 +12,11 @@ import org.robolectric.annotation.Config; import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.components.browser_ui.share.ShareParams; +import org.chromium.ui.base.WindowAndroid; +import org.chromium.url.mojom.Url; +import org.chromium.webshare.mojom.ShareError; +import org.chromium.webshare.mojom.ShareService; /** Unit tests for {@link ShareServiceImpl}. */ @RunWith(BaseRobolectricTestRunner.class) @@ -97,4 +102,53 @@ Assert.assertFalse(ShareServiceImpl.isDangerousMimeType("text/plain")); Assert.assertFalse(ShareServiceImpl.isDangerousMimeType("video/mpeg")); } + + @Test + @SmallTest + public void testInvalidScheme() { + // Using 1-element arrays to allow anonymous inner classes (WebShareDelegate and + // Share_Response) to modify local state, as captured variables must be effectively final. + int[] badMessageReason = new int[1]; + int[] shareError = new int[1]; + + ShareServiceImpl.WebShareDelegate mockDelegate = + new ShareServiceImpl.WebShareDelegate() { + @Override + public boolean canShare() { + return true; + } + + @Override + public void share(ShareParams params) {} + + @Override + public WindowAndroid getWindowAndroid() { + return null; + } +
Original Bug Report
Android ShareServiceImpl.share() lacks browser-side URL scheme validation
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: The Android implementation of the Web Share API fails to validate the URL scheme provided by the renderer in the browser process. This allows a compromised renderer to inject arbitrary schemes like ‘javascript:’ or ‘intent:’ into trusted browser UI components and system share intents.
Affected files:
components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImpl.javacomponents/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareParams.javacomponents/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareHelper.java
Estimated timestamp from git blame: 2020-09-24
Summary
The Android Java implementation of the blink.mojom.ShareService Mojo interface accepts renderer-supplied URLs without validating their scheme. This is inconsistent with the desktop C++ implementation, which enforces that shared URLs must be HTTP or HTTPS and terminates the calling renderer if this invariant is violated.
Root Cause Analysis
In the desktop implementation (chrome/browser/webshare/share_service_impl.cc), the following check is performed when a share request is received:
if (!share_url.is_empty() && !share_url.SchemeIsHTTPOrHTTPS()) {
std::move(callback).Run(blink::mojom::ShareError::PERMISSION_DENIED);
ReportBadMessageAndDeleteThis(
"Web Share URL scheme must be http or https.");
return;
}
However, the Android implementation in components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImpl.java lacks any equivalent validation. The raw URL string received from the Mojo call is passed directly into a ShareParams object and subsequently used by the browser UI and system share intents.
Potential Impact
A compromised renderer on Android could potentially cause the browser process to process arbitrary-scheme URLs (e.g., javascript:, file:, intent:, chrome:). While no immediate browser-process navigation was identified, this lack of validation has several consequences:
- UI Spoofing: The malicious URL is displayed in the Chrome-branded share sheet preview header. Because
UrlFormatter.formatUrlForSecurityDisplayfalls back to displaying the full string for non-standard schemes, an attacker can present arbitrary payloads in a trusted UI surface. - Clipboard Injection: If the user selects the ‘Copy URL’ action in the share sheet, the attacker-controlled string is written to the system clipboard.
- Intent Propagation: The URL is included as plain text in the
Intent.EXTRA_TEXTextra when sharing to third-party applications, potentially triggering unexpected behavior in the recipient apps. - QR Code Generation: The ‘QR Code’ sharing feature will encode the malicious string without further validation.
Suggested Reproduction Steps (Untested)
- From a compromised Android renderer, bind the
blink.mojom.ShareServiceMojo interface. - Send a
sharerequest with a non-HTTP(S) URL, for example:url = {url: "javascript:alert(document.domain)"}. - Observe that the Android share sheet is displayed and the renderer is NOT terminated (unlike desktop behavior).
- Verify that the
javascript:URL is visible in the share sheet preview and is forwarded as text in sharing actions (Copy, QR Code, or external Intents).
Proposed Fix
Implement a scheme check in org.chromium.components.browser_ui.webshare.ShareServiceImpl.share that mirrors the desktop implementation. If the provided URL is not empty and its scheme is not HTTP or HTTPS, the request should be rejected and the renderer should be reported for a bad message.
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.