CVE-2026-9977
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcomponents/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImpl.java |
modified |
Files Changed
components/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 a6afd7141e02d9dfa0c25849ff758707879b61f7 Mon Sep 17 00:00:00 2001 From: Dibyajyoti Pal <[email protected]> Date: Tue, 12 May 2026 09:23:54 -0700 Subject: [PATCH] [WebShare] Enforce SafeBaseName validations in Java The Android WebShare API receives file sharing requests from renderer processes. SharedFile objects use the mojo_base.mojom.SafeBaseName type for filenames. In C++, SafeBaseName traits validate that filenames do not contain directory separators or traversal components. However, on Android, Mojo IPCs are forwarded directly to Java, bypassing C++ traits. To prevent compromised renderers from attempting path traversal attacks, this change updates ShareServiceImpl.isDangerousFilename to explicitly reject filenames containing directory separators or traversal components. Also includes tests for this change. Fixed: 511741173 Change-Id: I65a96cbf966cd0284b063d18283483d8dd300ba5 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7837663 Reviewed-by: Kunjan Patel <[email protected]> Commit-Queue: Dibyajyoti Pal <[email protected]> Reviewed-by: Adriana Ixba <[email protected]> Cr-Commit-Position: refs/heads/main@{#1629383} --- 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 b45a3a3..dcb6a08 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 @@ -308,10 +308,42 @@ }.executeOnTaskRunner(TASK_RUNNER); } + // This function mimics the checks created by `SafeBaseName::Create()` as much as possible. static boolean isDangerousFilename(String name) { - // Reject filenames without a permitted extension. - return name.indexOf('.') <= 0 - || !PERMITTED_EXTENSIONS.contains(FileUtils.getExtension(name)); + // Empty name, invalid. + if (name == null || name.isEmpty()) { + return true; + } + + // 1. Check for directory traversal components ".." + if (name.contains("..") || name.equals(".")) { + return true; + } + + // 2. Check for directory separators '/' or '\\', as a `SafeBaseName` shouldn't have those. + if (name.contains("/") || name.contains("\\")) { + return true; + } + + // 3. Check for leading/trailing spaces or dots that can be problematic. + String trimmedName = name.trim(); + if (!name.equals(trimmedName) || trimmedName.endsWith(".")) { + return true; + } + + // 4. Original extension check: Reject filenames without a permitted extension. + int dotIndex = trimmedName.lastIndexOf('.'); + if (dotIndex <= 0) { + return true; + } + + String extension = FileUtils.getExtension(trimmedName); + if (!PERMITTED_EXTENSIONS.contains(extension)) { + return true; + } + + // If the above validation passed, the file is safe to be shared. + return false; } static boolean isDangerousMimeType(String contentType) { 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 5371021..5c23846 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 @@ -17,22 +17,36 @@ @RunWith(BaseRobolectricTestRunner.class) @Config(manifest = Config.NONE) public class ShareServiceImplTest { + // Verifies all the file names that are not allowed to be shared on Android. @Test @SmallTest - public void testExtensionFormatting() { - Assert.assertFalse(ShareServiceImpl.isDangerousFilename("foo/bar.txt")); - Assert.assertFalse(ShareServiceImpl.isDangerousFilename("foo\\bar\u03C0.txt")); + public void testExtensionFormattingDisallowed() { + Assert.assertTrue(ShareServiceImpl.isDangerousFilename("foo/bar.txt")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename("foo\\bar\u03C0.txt")); Assert.assertTrue(ShareServiceImpl.isDangerousFilename("foo\\bar.tx\u03C0t")); - Assert.assertFalse(ShareServiceImpl.isDangerousFilename("https://example.com/a/b.html")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename("https://example.com/a/b.html")); Assert.assertTrue(ShareServiceImpl.isDangerousFilename("foo/bar.txt/")); Assert.assertTrue(ShareServiceImpl.isDangerousFilename("foobar.tx\\t")); Assert.assertTrue(ShareServiceImpl.isDangerousFilename("hello")); Assert.assertTrue(ShareServiceImpl.isDangerousFilename("hellotxt")); Assert.assertTrue(ShareServiceImpl.isDangerousFilename(".txt")); - Assert.assertFalse(ShareServiceImpl.isDangerousFilename("https://example.com/a/.txt")); - Assert.assertFalse(ShareServiceImpl.isDangerousFilename("/.txt")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename("https://example.com/a/.txt")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename("/.txt")); Assert.assertTrue(ShareServiceImpl.isDangerousFilename("..")); - Assert.assertTrue(ShareServiceImpl.isDangerousFilename(".hello.txt")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename("bar.tx\u03C0t")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename(".")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename(" my_name ")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename(" . ")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename(".config")); + } + + // Verifies all the file names that are allowed to be shared on Android. + @Test + @SmallTest + public void testExtensionFormattingAllowed() { + Assert.assertFalse(ShareServiceImpl.isDangerousFilename(".hello.txt")); + Assert.assertFalse(ShareServiceImpl.isDangerousFilename("bar.txt")); + Assert.assertFalse(ShareServiceImpl.isDangerousFilename("bar\u03C0.txt")); } @Test
Original Bug Report
Potential Sandbox Escape on Android via WebShare API Path Traversal
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 WebShare API is potentially vulnerable to path traversal because the SafeBaseName Mojo type lacks Java-side validation. A compromised renderer can bypass filename checks to write arbitrary files into the browser process’s FileProvider-exported directories. This occurs because Mojo pipes are forwarded directly to Java, skipping the validation traits defined in C++.
Affected files:
components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImpl.javachrome/browser/chrome_browser_interface_binders.ccmojo/public/mojom/base/BUILD.gnthird_party/blink/public/mojom/webshare/webshare.mojom
Estimated timestamp from git blame: 2022-11-07
Vulnerability Details
The WebShare API on Android is potentially vulnerable to a path traversal attack that allows a compromised renderer process to write files in the browser process’s private data directory.
The vulnerability stems from a lack of validation for the SafeBaseName Mojo type in the Java implementation of ShareService. The SharedFile struct in webshare.mojom uses SafeBaseName for filenames, which is intended to represent a path component without directory separators or .. components.
In C++, SafeBaseName is protected by StructTraits (in mojo/public/cpp/base/safe_base_name_mojom_traits.cc) that enforce these constraints during deserialization. However, on Android, the ShareService Mojo pipe is forwarded directly to Java via ForwardToJavaWebContents in chrome_browser_interface_binders.cc. Because there is no Java typemap for SafeBaseName defined in mojo/public/mojom/base/BUILD.gn, the Java bindings deserialize it as a raw struct, and the validation logic in the C++ traits is completely bypassed.
In ShareServiceImpl.java, the isDangerousFilename method performs insufficient validation on the resulting string:
static boolean isDangerousFilename(String name) {
// Reject filenames without a permitted extension.
return name.indexOf('.') <= 0
|| !PERMITTED_EXTENSIONS.contains(FileUtils.getExtension(name));
}
An attacker can bypass this check by providing a path starting with a slash and containing traversal components, such as /../../../../cache/webapks/evil.html. In this scenario:
name.indexOf('.')returns 1 (the first dot), so the checkindexOf('.') <= 0evaluates tofalse.FileUtils.getExtension(name)successfully parses the string and extractshtml, which is a permitted extension, making the second conditionfalse.
The method incorrectly assumes the filename is safe. When ShareServiceImpl creates the temporary file for sharing, it uses the following logic:
tempFile = new File(tempDir, file.name.path.path);
On Android’s Unix-style filesystem, if the child path provided to the File(File, String) constructor starts with a /, it concatenates the child path to the parent path. This allows an attacker to use .. components to escape the intended tempDir. For example, if tempDir is /data/user/0/com.android.chrome/files/images/screenshot/share123/, the resulting path resolves to /data/user/0/com.android.chrome/cache/webapks/evil.html.
Potential Exploit Scenario
Note: These are suggested steps, as we do not have a working proof of concept that has been successfully run.
- An attacker compromises the renderer process (e.g., via a V8 vulnerability).
- The attacker crafts a
blink::mojom::ShareService::ShareMojo IPC message. - The attacker populates the
SharedFilestruct, setting thenamefield to/../../../../cache/webapks/evil.htmland providing a malicious payload in theblobfield. - The
cache/webapks/directory is deliberately chosen because it corresponds to<cache-path name="webapk" path="webapks/" />infile_paths.xml. This ensures the subsequent call toFileProviderUtils.getContentUriFromFile(tempFile)succeeds without throwing anIllegalArgumentException. - The browser process processes the request, bypassing
SafeBaseNamechecks, creating the file in the cache directory, and writing the attacker’s payload to it.
This constitutes an arbitrary file write within the browser process’s filesystem, representing a sandbox escape.
Suggested Fix
- Implement a proper Java typemap and validation for the
SafeBaseNameMojo type to ensure consistency with the C++StructTraits. - Update
ShareServiceImpl.isDangerousFilenameto explicitly reject strings containing directory separators (/or\) or traversal components (..).
Evaluated with Chrome root at commit: eca8648a4e1cdfdda68c495a6003059fed641955
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.