Overview

Medium
Severity
8.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactConfused deputy in DataTransfer
DescriptionConfused deputy in DataTransfer
ComponentDataTransfer
Bug ClassAuthorization Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker500094528
Fix commit4939275abb61 (chromium/src) +29/-1
CWECWE-441
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-04-06 (per vendor advisory)
Fix landed2026-08-03 (public git)
Disclosed2026-09-08
Patch gap36 days — fix public in source before the advisory

Background

`ClipboardHostImpl::WriteBookmark`
the browser-process Mojo endpoint that accepts a free-form URL string and title from a renderer and stages them onto the system clipboard as a bookmark/URL entry.
`ui::ClipboardMac::WriteURL`
the macOS clipboard backend that, for file:// URLs, routes them through PasteboardItemsFromUrls and writes them to the native pasteboard as NSPasteboardTypeFileURL (a file entry, not a plain URL).
`ReadFiles()` / `clipboard.mojom` invariant
the clipboard read path that is documented to only return file paths the user deliberately placed there, so a renderer must not be able to synthesize its own file entries.
Confused deputy
a lower-privileged caller (the renderer) tricks a higher-privileged component (the browser process) into performing an action on its behalf that the caller could not perform directly.

Root Cause Analysis

ClipboardHostImpl::WriteBookmark treated the renderer-supplied URL as opaque and, after only its existing activation/invalid-URL guards, forwarded it straight into clipboard_writer_->WriteURL(ui::ClipboardUrlInfo{.url = GURL(url), ...}). On macOS, ui::ClipboardMac::WriteURL interprets a file:// scheme specially and writes it to the pasteboard as an NSPasteboardTypeFileURL file entry rather than a plain URL, so a subsequent ReadFiles() returns that same local path back to the renderer. That breaks the invariant documented on ReadFiles in clipboard.mojom — that read-back file entries reflect user-chosen files, not arbitrary renderer-supplied paths — making the browser a confused deputy that launders an attacker-chosen path into the trusted clipboard file list.

The fix constructs GURL gurl(url) once and adds an early return when gurl.SchemeIsFile() is true, so file:// bookmarks never reach WriteURL; the branch runs before the sink, and legitimate non-file bookmarks proceed via std::move(gurl). This is a tightening: a new scheme check is added alongside the existing invalid-URL guard, closing the write path that fed the file-typed pasteboard entry.

Key insight
The single mistake was trusting the renderer’s URL scheme and letting a file:// bookmark flow into ClipboardMac::WriteURL, which promotes it to a file entry readable via ReadFiles(); the fix rejects file:// schemes in the browser process before the value is ever written to the clipboard.

Attack Path

  1. Malicious renderer issues WriteBookmark A compromised or hostile renderer calls the WriteBookmark Mojo method with a file:// URL such as file:///etc/passwd and any title.
  2. Guard is bypassed pre-patch The pre-patch WriteBookmark applies only its activation and invalid-URL checks, then constructs GURL(url) and forwards it to clipboard_writer_->WriteURL, with no scheme filtering.
  3. macOS backend promotes it to a file entry ui::ClipboardMac::WriteURL sees the file:// scheme and, via PasteboardItemsFromUrls, writes the path to the native pasteboard as NSPasteboardTypeFileURL rather than a plain URL string.
  4. Renderer reads the path back A subsequent ReadFiles() returns the attacker-supplied local path to the renderer as if it were a user-selected file, violating the ReadFiles invariant in clipboard.mojom.
  5. Confused-deputy authorization bypass The renderer has thereby caused the trusted browser process to place an arbitrary local filesystem path into the clipboard file list it can observe, potentially feeding downstream code that treats those paths as legitimately user-granted file access (inferred).

Impact Assessment

A malicious or compromised renderer can make the browser process place an attacker-chosen local file path into the clipboard’s file list and read it back via ReadFiles(), bypassing the authorization boundary that clipboard file entries reflect user intent (an AuthorizationError-class confused deputy in DataTransfer), on macOS specifically. This requires the renderer to already be able to call the WriteBookmark Mojo endpoint; any further escalation, such as using the leaked path to obtain the file’s contents or broader local file access, is not demonstrated by the patch (inferred). The vendor advisory rates this medium severity and does not report in-the-wild exploitation; NVD scores it CVSS 8.3.

Changed Functions

FunctionChangeNotes
TEST_F
content/browser/renderer_host/clipboard_host_impl_unittest.cc
modified

Files Changed

  • content/browser/renderer_host/clipboard_host_impl.h
  • content/browser/renderer_host/clipboard_host_impl.cc
  • content/browser/renderer_host/clipboard_host_impl_unittest.cc

Audit Directions

  • Renderer-controlled URLs into privileged sinks
    Audit every browser-process Mojo endpoint that accepts a free-form URL/scheme from a renderer (clipboard, downloads, drag-and-drop, navigation helpers) and traces it into OS or platform APIs that treat file:// differently, ensuring scheme validation happens before the sink.
  • Write-then-read invariant mismatches
    Look for cases where a renderer-writable channel (WriteBookmark, WriteURL, image/format writers) feeds a read path documented to only return user-chosen or trusted data, since a scheme or type coercion in the backend can silently violate that invariant.
  • Platform-specific type promotion
    Review per-OS clipboard/pasteboard backends (e.g. ClipboardMac, and the equivalent Windows/Linux paths) for logic that reclassifies a value into a more privileged type (URL → file entry), and confirm the cross-platform layer filters such inputs uniformly.
From 4939275abb618e309bdbcf4d9931f671eed71825 Mon Sep 17 00:00:00 2001
From: Avi Drissman <avi@chromium.org>
Date: Mon, 03 Aug 2026 08:56:46 -0700
Subject: [PATCH] Drop file:// URLs in ClipboardHostImpl::WriteBookmark

WriteBookmark accepts a free-form URL string from the renderer. On
macOS, ClipboardMac::WriteURL forwards file:// URLs to
PasteboardItemsFromUrls, which writes them to the system pasteboard as
NSPasteboardTypeFileURL. A subsequent ReadFiles() then returns the same
path back to the renderer, contrary to the invariant documented on
ReadFiles in clipboard.mojom.

Renderers have no need to write file:// bookmarks, so drop them in
ClipboardHostImpl alongside the existing invalid-URL guard.

Fixed: 500094528
Change-Id: Iabbef8189dd88c955bcc1cf83874ab8b6a6a6964
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8177579
Auto-Submit: Avi Drissman <avi@chromium.org>
Reviewed-by: Bryan Oltman <bryanoltman@google.com>
Commit-Queue: Bryan Oltman <bryanoltman@google.com>
Cr-Commit-Position: refs/heads/main@{#1672699}
---
diff --git a/content/browser/renderer_host/clipboard_host_impl.cc b/content/browser/renderer_host/clipboard_host_impl.cc
index b48457b10d..5c2ca77c 100644
--- a/content/browser/renderer_host/clipboard_host_impl.cc
+++ b/content/browser/renderer_host/clipboard_host_impl.cc
@@ -706,8 +706,16 @@
           DisallowActivationReasonId::kClipboard)) {
     return;
   }
+
+  GURL gurl(url);
+  // Drop file:// URLs so a renderer cannot place a local path on the clipboard
+  // via WriteBookmark and read it back via ReadFiles().
+  if (gurl.SchemeIsFile()) {
+    return;
+  }
+
   clipboard_writer_->WriteURL(
-      ui::ClipboardUrlInfo{.url = GURL(url), .title = title});
+      ui::ClipboardUrlInfo{.url = std::move(gurl), .title = title});
 }
 
 void ClipboardHostImpl::WriteImage(const SkBitmap& bitmap) {
diff --git a/content/browser/renderer_host/clipboard_host_impl.h b/content/browser/renderer_host/clipboard_host_impl.h
index ec953f6..5efad4d 100644
--- a/content/browser/renderer_host/clipboard_host_impl.h
+++ b/content/browser/renderer_host/clipboard_host_impl.h
@@ -85,6 +85,7 @@
   FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplWriteTest,
                            WriteBookmark_InvalidUrl_DoesNotCrash);
   FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplWriteTest, WriteBookmark_EmptyUrl);
+  FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplWriteTest, WriteBookmark_FileUrl);
   FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplWriteTest, WriteBitmap);
   FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplWriteTest, WriteBitmap_Empty);
   FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplWriteTest,
diff --git a/content/browser/renderer_host/clipboard_host_impl_unittest.cc b/content/browser/renderer_host/clipboard_host_impl_unittest.cc
index cb2cf6f0..194a384 100644
--- a/content/browser/renderer_host/clipboard_host_impl_unittest.cc
+++ b/content/browser/renderer_host/clipboard_host_impl_unittest.cc
@@ -545,6 +545,25 @@
   EXPECT_TRUE(title.empty());
 }
 
+// file:// URLs are dropped so a renderer cannot place a local path on the
+// clipboard via WriteBookmark and read it back via ReadFiles.
+TEST_F(ClipboardHostImplWriteTest, WriteBookmark_FileUrl) {
+  clipboard_host_impl()->WriteBookmark("file:///etc/passwd", u"some title");
+  clipboard_host_impl()->CommitWrite();
+
+  std::u16string title;
+  std::string url;
+  ui::clipboard_test_util::ReadBookmark(system_clipboard(),
+                                        /*data_dst=*/nullptr, &title, &url);
+  EXPECT_TRUE(url.empty()) << "Got url='" << url << "'";
+  EXPECT_TRUE(title.empty()) << "Got title='" << title << "'";
+
+  std::vector<ui::FileInfo> files = ui::clipboard_test_util::ReadFilenames(
+      system_clipboard(), ui::ClipboardBuffer::kCopyPaste,
+      /*data_dst=*/nullptr);
+  EXPECT_TRUE(files.empty());
+}
+
 TEST_F(ClipboardHostImplWriteTest, WriteBitmap) {
   const SkBitmap kBitmap = gfx::test::CreateBitmap(3, 2);
   clipboard_host_impl()->WriteImage(kBitmap);
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/renderer_host/clipboard_host_impl_unittest.cc b/content/browser/renderer_host/clipboard_host_impl_unittest.cc
index cb2cf6f0..194a384 100644
--- a/content/browser/renderer_host/clipboard_host_impl_unittest.cc
+++ b/content/browser/renderer_host/clipboard_host_impl_unittest.cc
@@ -545,6 +545,25 @@
   EXPECT_TRUE(title.empty());
 }
 
+// file:// URLs are dropped so a renderer cannot place a local path on the
+// clipboard via WriteBookmark and read it back via ReadFiles.
+TEST_F(ClipboardHostImplWriteTest, WriteBookmark_FileUrl) {
+  clipboard_host_impl()->WriteBookmark("file:///etc/passwd", u"some title");
+  clipboard_host_impl()->CommitWrite();
+
+  std::u16string title;
+  std::string url;
+  ui::clipboard_test_util::ReadBookmark(system_clipboard(),
+                                        /*data_dst=*/nullptr, &title, &url);
+  EXPECT_TRUE(url.empty()) << "Got url='" << url << "'";
+  EXPECT_TRUE(title.empty()) << "Got title='" << title << "'";
+
+  std::vector<ui::FileInfo> files = ui::clipboard_test_util::ReadFilenames(
+      system_clipboard(), ui::ClipboardBuffer::kCopyPaste,
+      /*data_dst=*/nullptr);
+  EXPECT_TRUE(files.empty());
+}
+
 TEST_F(ClipboardHostImplWriteTest, WriteBitmap) {
   const SkBitmap kBitmap = gfx::test::CreateBitmap(3, 2);
   clipboard_host_impl()->WriteImage(kBitmap);
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential macOS sandbox escape / arbitrary file read via ClipboardHost

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 security team.

Overview: A compromised renderer on macOS can potentially achieve an arbitrary file read and escape the sandbox by abusing the ClipboardHost Mojo interface. By sending a local file URL via WriteBookmark and then reading it back via ReadFiles after a user click, the renderer can trick the browser into granting it read permissions and a DataTransferToken to any file on the disk.

Affected files:

  • ui/base/clipboard/clipboard_util_mac.mm
  • content/browser/renderer_host/clipboard_host_impl.cc
  • ui/base/clipboard/clipboard_mac.mm
  • ui/base/clipboard/clipboard_util.cc
  • content/browser/file_system/browser_file_system_helper.cc
  • content/browser/file_system_access/file_system_access_manager_impl.cc
  • content/browser/child_process_security_policy_impl.cc
  • third_party/blink/public/mojom/clipboard/clipboard.mojom

Estimated timestamp from git blame: 2026-03-22

Vulnerability Detail

There is a potential logic flaw in how Chromium handles clipboard operations on macOS, allowing a compromised renderer to bypass the sandbox and read arbitrary local files. The vulnerability stems from a lack of validation in ClipboardHost.WriteBookmark combined with automatic file promotion logic in the macOS clipboard implementation.

The attack leverages two distinct phases:

1. Write Phase (Staging the File)

  • A compromised renderer calls the ClipboardHost.WriteBookmark Mojo IPC method with a file:// URL pointing to a sensitive file (e.g., file:///Users/victim/.ssh/id_rsa).
  • ClipboardHostImpl::WriteBookmark (content/browser/renderer_host/clipboard_host_impl.cc:623) receives this string and passes it directly to clipboard_writer_->WriteURL() without validating the scheme or verifying if the renderer has permission to access the file.
  • On macOS, the write operation eventually reaches clipboard_util::PasteboardItemsFromUrls (ui/base/clipboard/clipboard_util_mac.mm:326).
  • Here, the unsandboxed browser process checks if the URL is a file and if it exists (url.isFileURL && [url checkResourceIsReachableAndReturnError:nil]). Because the browser is unsandboxed, this check succeeds for any existing local file. The browser then promotes the string to an NSPasteboardTypeFileURL on the macOS system pasteboard.

2. Read Phase (Exfiltration)

  • The compromised renderer waits for a minor user interaction (e.g., a single click anywhere on the page) to satisfy IsRendererPasteAllowed (which relies on WebContents::HasRecentInteraction()).
  • The renderer sends the ClipboardHost.ReadFiles(kStandard) IPC call.
  • The browser reads the NSPasteboardTypeFileURL from the system pasteboard. Believing the user genuinely copied a file via the OS UI, the browser blindly grants the renderer process read access via ChildProcessSecurityPolicyImpl::GrantReadFile.
  • A File System Access DataTransferToken is generated and returned to the renderer.
  • The renderer redeems this token (which bypasses sensitive directory checks in ResolveDataTransferTokenWithFileType because it is a file handle, not a directory) and reads the file contents, completing the sandbox escape.

Potential Reproduction Steps

Note: These are suggested/potential steps to trigger the vulnerability. Our tooling agent does not currently have the ability to run code or execute a live proof-of-concept.

  1. From a compromised renderer process on macOS, initiate a Mojo IPC call: ClipboardHost.WriteBookmark("file:///Users/<user>/.ssh/id_rsa", "title").
  2. Send a ClipboardHost.CommitWrite() IPC message to flush the URL to the OS clipboard.
  3. Pause execution and wait for any user interaction (e.g., an arbitrary click on the page surface) to satisfy the 5-second HasRecentInteraction() window.
  4. Send a ClipboardHost.ReadFiles(kStandard) IPC call.
  5. Receive the Mojo response containing a DataTransferFile entry with a valid file_system_access_token.
  6. Redeem the token via the File System Access API (e.g., getFile()) to read the raw contents of the user’s SSH key.

Suggested Fix

  1. Input Validation: In ClipboardHostImpl::WriteBookmark, explicitly validate the URL scheme. If the URL is a file:// scheme, the browser must check if the renderer process already has read access to that specific file path via ChildProcessSecurityPolicyImpl. If it does not, the request should be rejected or the process terminated.
  2. Policy Enforcement: Consider routing WriteBookmark through IsClipboardCopyAllowedByPolicy to align it with WriteText, WriteHtml, and WriteImage.
  3. macOS Clipboard Hardening: In clipboard_util::PasteboardItemsFromUrls, avoid promoting file:// URLs to NSPasteboardTypeFileURL when the data originates from an untrusted web origin.

Evaluated with Chrome root at commit: f200f57a19490707ff8bc7aa5de3cbc443a3afad


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.

View on issue tracker