Medium CVSS 7.5 webkit Bypass 🔧 Commit mapped

Overview

Medium
Severity
7.5
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionProcessing maliciously crafted web content may disclose sensitive user information
ComponentWebKit
Sub-componentWebKit UIProcess
Bug ClassBypass
Tracker309698
Fix commitc6cd4e07e9af (WebKit/WebKit) +148/-4
CWECWE-200 (Information exposure)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CISA KEVNot listed
CreditedLuke Francis, Vaagn Vardanian, kwak kiyong / kakaogames, Vitaly Simonovich, Adel Bouachraoui, greenbynox
Disclosed2026-05-11

Background

Attachment elements
WebKit’s <attachment> support (ENABLE(ATTACHMENT_ELEMENT)) for embedding file attachments, which can carry local file paths for upload.
WebPasteboardProxy
The UI-process side of pasteboard IPC that vends pasteboard data (including file paths) to the WebContent process.
Allow-list of vended paths
The set of file paths the UI process actually provided; incoming paths from WebContent must be checked against it.
Sandbox extension
A token the UI/Network process grants so a sandboxed process may access a specific file; must only be issued for legitimately vended paths.

Root Cause Analysis

With attachment elements enabled, WebPasteboardProxy (UI process) returns file paths (pathsForFileUpload) obtained from the pasteboard to the WebContent process, but it did not record which paths it had legitimately vended — ‘Missing validation for incoming file paths from web content process when attachment elements are enabled’. Without an allow-list, a malicious or compromised WebContent process could later reference arbitrary file paths (including path-traversal outside the intended files) in attachment/file operations, and the trusted UI/Network process would act on them, disclosing sensitive local files.

The fix adds addAllowedAttachmentFilePaths() and invokes it on every path that vends pasteboard file paths — getPasteboardPathnamesForType, allPasteboardItemInfo (including the iOS HEIC-transcoding continuation, which now captures the IPC::Connection by Ref so the async main-thread hop can still register paths), and informationForItemAtIndex — registering each vended path through WebProcessProxy::addAllowedAttachmentFilePath.

The restored invariant is that the WebContent process may only reference attachment file paths the UI process actually provided, so incoming paths can be validated against this per-process allow-list. INFERENCE: the enforcement check that rejects non-allow-listed incoming paths (the consumer of addAllowedAttachmentFilePath) is outside this hunk; the diff establishes population of the allow-list.

Key insight
File paths that cross from WebContent to the UI process for attachments must be validated against what the UI process actually vended; trusting web-process-supplied paths permits traversal to arbitrary local files.

Attack Path

  1. Enable attachment elements and compromise WebContent On a build with ENABLE(ATTACHMENT_ELEMENT), the attacker controls (or compromises) a WebContent process that speaks the pasteboard/attachment IPC.
  2. Send an arbitrary file path over IPC WebContent references a file path it never legitimately received (e.g. a path-traversal to a sensitive file) in an attachment/file-upload operation.
  3. UI/Network process trusts the path Pre-patch, with no allow-list of vended paths, the trusted process accepts the incoming path and grants access / creates a sandbox extension for it.
  4. Disclose sensitive local files The contents of files outside the intended set are exposed to web content, disclosing sensitive user information.

Impact Assessment

This is a local-file information-disclosure / path-traversal issue across the WebContent->UI process boundary, not a memory-safety bug. It requires attachment elements to be enabled and a WebContent process willing to send crafted IPC (a malicious or already-compromised web process), so it is a defense-in-depth boundary hardening rather than a direct remote primitive; its payoff is reading files outside the intended attachment set.

Changed Functions

FunctionChangeNotes
if
Source/WebKit/UIProcess/Cocoa/WebPasteboardProxyCocoa.mm
modified
for
Source/WebKit/UIProcess/WebPageProxy.cpp
modified
for
Tools/TestWebKitAPI/Tests/WebKit/WKWebView/WKAttachmentTests.mm
modified

Files Changed

  • Source/WebKit/UIProcess/Cocoa/WebPasteboardProxyCocoa.mm
  • Source/WebKit/UIProcess/WebPageProxy.cpp
  • Source/WebKit/UIProcess/WebProcessProxy.cpp
  • Source/WebKit/UIProcess/WebProcessProxy.h
  • Tools/TestWebKitAPI/Tests/WebKit/WKWebView/WKAttachmentTests.mm

Audit Directions

  • Other IPC accepting file paths from WebContent
    Audit UI/Network-process IPC endpoints that receive file paths or create SandboxExtension handles from WebContent-supplied strings without checking an allow-list; the tell is SandboxExtension::createHandle(pathFromWebProcess, …).
  • Attachment / file-upload path flows
    Trace pathsForFileUpload and attachment file paths end-to-end and confirm every consumer validates against addAllowedAttachmentFilePath (the allow-list this commit populates).
  • Async IPC continuations dropping the connection
    Look for async transcoding/dispatch continuations that previously dropped the IPC::Connection; ensure they capture it (by Ref) so per-connection allow-listing/validation still runs on the main-thread hop.
diff --git a/Source/WebKit/UIProcess/Cocoa/WebPasteboardProxyCocoa.mm b/Source/WebKit/UIProcess/Cocoa/WebPasteboardProxyCocoa.mm
index d21fdd1127ac..99542418e02d 100644
--- a/Source/WebKit/UIProcess/Cocoa/WebPasteboardProxyCocoa.mm
+++ b/Source/WebKit/UIProcess/Cocoa/WebPasteboardProxyCocoa.mm
@@ -84,6 +84,24 @@ static bool shouldTranscodeHEICImagesForPage(std::optional<WebPageProxyIdentifie
     return protect(page->preferences())->needsSiteSpecificQuirks() && Quirks::shouldTranscodeHeicImagesForURL(URL { page->currentURL() });
 }
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+
+static void addAllowedAttachmentFilePaths(const IPC::Connection& connection, std::optional<WebPageProxyIdentifier> pageID, const Vector<String>& paths)
+{
+    if (!pageID)
+        return;
+
+    RefPtr page = WebProcessProxy::webPage(*pageID);
+    if (!page)
+        return;
+
+    for (auto& path : paths)
+        WebProcessProxy::fromConnection(connection)->addAllowedAttachmentFilePath(path);
+}
+
+#endif // ENABLE(ATTACHMENT_ELEMENT)
+
+
 void WebPasteboardProxy::grantAccessToCurrentTypes(WebProcessProxy& process, const String& pasteboardName)
 {
     grantAccess(process, pasteboardName, PasteboardAccessType::Types);
@@ -252,6 +270,9 @@ void WebPasteboardProxy::getPasteboardPathnamesForType(IPC::Connection& connecti
                     return SandboxExtension::Handle { };
                 return valueOrDefault(SandboxExtension::createHandle(filename, SandboxExtension::Type::ReadOnly));
             });
+#if ENABLE(ATTACHMENT_ELEMENT)
+            addAllowedAttachmentFilePaths(connection, pageID, pathnames);
+#endif
             completionHandler(WTF::move(pathnames), WTF::move(sandboxExtensions));
             return;
         }
@@ -643,12 +664,22 @@ void WebPasteboardProxy::allPasteboardItemInfo(IPC::Connection& connection, cons
 
 #if PLATFORM(IOS_FAMILY)
         if (!allInfo || !process) {
+#if ENABLE(ATTACHMENT_ELEMENT)
+            if (allInfo) {
+                for (auto& info : *allInfo)
+                    addAllowedAttachmentFilePaths(connection, pageID, info.pathsForFileUpload);
+            }
+#endif
             completionHandler(WTF::move(allInfo));
             return;
         }
 
         auto transcodingInfo = findHEICPathsForTranscoding(*allInfo, pageID);
         if (transcodingInfo.isEmpty()) {
+#if ENABLE(ATTACHMENT_ELEMENT)
+            for (auto& info : *allInfo)
+                addAllowedAttachmentFilePaths(connection, pageID, info.pathsForFileUpload);
+#endif
             completionHandler(WTF::move(allInfo));
             return;
         }
@@ -656,12 +687,12 @@ void WebPasteboardProxy::allPasteboardItemInfo(IPC::Connection& connection, cons
         auto pathsToTranscode = extractPathsToTranscode(transcodingInfo);
         auto [transcodingUTI, transcodingExtension] = heicTranscodingParameters();
 
-        sharedImageTranscodingQueueSingleton().dispatch([process = WTF::move(process), allInfo = WTF::move(*allInfo), pathsToTranscode = crossThreadCopy(WTF::move(pathsToTranscode)), transcodingInfo = WTF::move(transcodingInfo), transcodingUTI = crossThreadCopy(WTF::move(transcodingUTI)), transcodingExtension = crossThreadCopy(WTF::move(transcodingExtension)), completionHandler = WTF::move(completionHandler)] mutable {
+        sharedImageTranscodingQueueSingleton().dispatch([protectedConnection = Ref { connection }, pageID, process = WTF::move(process), allInfo = WTF::move(*allInfo), pathsToTranscode = crossThreadCopy(WTF::move(pathsToTranscode)), transcodingInfo = WTF::move(transcodingInfo), transcodingUTI = crossThreadCopy(WTF::move(transcodingUTI)), transcodingExtension = crossThreadCopy(WTF::move(transcodingExtension)), completionHandler = WTF::move(completionHandler)] mutable {
             ASSERT(!RunLoop::isMain());
 
             auto transcodedPaths = transcodeImages(pathsToTranscode, transcodingUTI, transcodingExtension);
 
-            RunLoop::mainSingleton().dispatch([process = WTF::move(process), allInfo = WTF::move(allInfo), transcodedPaths = crossThreadCopy(WTF::move(transcodedPaths)), transcodingInfo = WTF::move(transcodingInfo), completionHandler = WTF::move(completionHandler)] mutable {
+            RunLoop::mainSingleton().dispatch([protectedConnection = WTF::move(protectedConnection), pageID, process = WTF::move(process), allInfo = WTF::move(allInfo), transcodedPaths = crossThreadCopy(WTF::move(transcodedPaths)), transcodingInfo = WTF::move(transcodingInfo), completionHandler = WTF::move(completionHandler)] mutable {
                 for (size_t i = 0; i < transcodingInfo.size(); ++i) {
                     if (!transcodedPaths[i].isEmpty())
                         allInfo[transcodingInfo[i].infoIndex].pathsForFileUpload[transcodingInfo[i].pathIndex] = transcodedPaths[i];
@@ -669,10 +700,21 @@ void WebPasteboardProxy::allPasteboardItemInfo(IPC::Connection& connection, cons
 
                 notifyNetworkProcessOfTranscodedFiles(process, transcodedPaths);
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+                for (auto& info : allInfo)
+                    addAllowedAttachmentFilePaths(protectedConnection.get(), pageID, info.pathsForFileUpload);
+#endif
+
                 completionHandler(WTF::move(allInfo));
             });
         });
 #else
+#if ENABLE(ATTACHMENT_ELEMENT)
+        if (allInfo) {
+            for (auto& info : *allInfo)
+                addAllowedAttachmentFilePaths(connection, pageID, info.pathsForFileUpload);
+        }
+#endif
         completionHandler(WTF::move(allInfo));
 #endif
     });
@@ -693,12 +735,19 @@ void WebPasteboardProxy::informationForItemAtIndex(IPC::Connection& connection,
 
 #if PLATFORM(IOS_FAMILY)
         if (!info || !process) {
+#if ENABLE(ATTACHMENT_ELEMENT)
+            if (info)
+                addAllowedAttachmentFilePaths(connection, pageID, info->pathsForFileUpload);
+#endif
             completionHandler(WTF::move(info));
             return;
         }
 
         auto transcodingInfo = findHEICPathsForTranscoding(*info, pageID);
         if (transcodingInfo.isEmpty()) {
+#if ENABLE(ATTACHMENT_ELEMENT)
+            addAllowedAttachmentFilePaths(connection, pageID, info->pathsForFileUpload);
+#endif
             completionHandler(WTF::move(info));
             return;
         }
@@ -706,12 +755,12 @@ void WebPasteboardProxy::informationForItemAtIndex(IPC::Connection& connection,
         auto pathsToTranscode = extractPathsToTranscode(transcodingInfo);
         auto [transcodingUTI, transcodingExtension] = heicTranscodingParameters();
 
-        sharedImageTranscodingQueueSingleton().dispatch([process = WTF::move(process), info = WTF::move(*info), pathsToTranscode = crossThreadCopy(WTF::move(pathsToTranscode)), transcodingInfo = WTF::move(transcodingInfo), transcodingUTI = crossThreadCopy(WTF::move(transcodingUTI)), transcodingExtension = crossThreadCopy(WTF::move(transcodingExtension)), completionHandler = WTF::move(completionHandler)] mutable {
+        sharedImageTranscodingQueueSingleton().dispatch([protectedConnection = Ref { connection }, pageID, process = WTF::move(process), info = WTF::move(*info), pathsToTranscode = crossThreadCopy(WTF::move(pathsToTranscode)), transcodingInfo = WTF::move(transcodingInfo), transcodingUTI = crossThreadCopy(WTF::move(transcodingUTI)), transcodingExtension = crossThreadCopy(WTF::move(transcodingExtension)), completionHandler = WTF::move(completionHandler)] mutable {
             ASSERT(!RunLoop::isMain());
 
             auto transcodedPaths = transcodeImages(pathsToTranscode, transcodingUTI, transcodingExtension);
 
-            RunLoop::mainSingleton().dispatch([process = WTF::move(process), info = WTF::move(info), transcodedPaths = crossThreadCopy(WTF::move(transcodedPaths)), transcodingInfo = WTF::move(transcodingInfo), completionHandler = WTF::move(completionHandler)] mutable {
+            RunLoop::mainSingleton().dispatch([protectedConnection = WTF::move(protectedConnection), pageID, process = WTF::move(process), info = WTF::move(info), transcodedPaths = crossThreadCopy(WTF::move(transcodedPaths)), transcodingInfo = WTF::move(transcodingInfo), completionHandler = WTF::move(completionHandler)] mutable {
                 for (size_t i = 0; i < transcodingInfo.size(); ++i) {
                     if (!transcodedPaths[i].isEmpty())
                         info.pathsForFileUpload[transcodingInfo[i].pathIndex] = transcodedPaths[i];
@@ -719,10 +768,18 @@ void WebPasteboardProxy::informationForItemAtIndex(IPC::Connection& connection,
 
                 notifyNetworkProcessOfTranscodedFiles(process, transcodedPaths);
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+                addAllowedAttachmentFilePaths(protectedConnection.get(), pageID, info.pathsForFileUpload);
+#endif
+
                 completionHandler(WTF::move(info));
             });
         });
 #else
+#if ENABLE(ATTACHMENT_ELEMENT)
+        if (info)
+            addAllowedAttachmentFilePaths(connection, pageID, info->pathsForFileUpload);
+#endif
         completionHandler(WTF::move(info));
 #endif
     });
diff --git a/Source/WebKit/UIProcess/WebPageProxy.cpp b/Source/WebKit/UIProcess/WebPageProxy.cpp
index 38d217dd82f6..84b689dc1fdc 100644
--- a/Source/WebKit/UIProcess/WebPageProxy.cpp
+++ b/Source/WebKit/UIProcess/WebPageProxy.cpp
@@ -4174,6 +4174,11 @@ void WebPageProxy::performDragOperation(DragData& dragData, const String& dragSt
         if (!protectedThis->m_mainFrame)
             return;
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+        for (auto& filename : dragData.fileNames())
+            protect(protectedThis->legacyMainFrameProcess())->addAllowedAttachmentFilePath(filename);
+#endif
+
         DragData dragDataCopy(dragData);
 
         protectedThis->sendWithAsyncReplyToProcessContainingFrame(protectedThis->m_mainFrame->frameID(), Messages::WebPage::PerformDragOperation(protectedThis->m_mainFrame->frameID(), WTF::move(dragData), WTF::move(sandboxExtensionHandle), WTF::move(sandboxExtensionsForUpload)), [protectedThis, frameID = protectedThis->m_mainFrame->frameID(), dragDataCopy = WTF::move(dragDataCopy), dragStorageName] (DragOperationResult dragOperationResult) mutable {
@@ -16817,6 +16822,7 @@ void WebPageProxy::registerAttachmentIdentifierFromFilePath(IPC::Connection& con
 {
     MESSAGE_CHECK_BASE(protect(preferences())->attachmentElementEnabled(), connection);
     MESSAGE_CHECK_BASE(IdentifierToAttachmentMap::isValidKey(identifier), connection);
+    MESSAGE_CHECK_BASE(WebProcessProxy::fromConnection(connection)->isAllowedAttachmentFilePath(filePath), connection);
 
     if (attachmentForIdentifier(identifier))
         return;
@@ -16842,6 +16848,7 @@ void WebPageProxy::registerAttachmentsFromSerializedData(IPC::Connection& connec
     MESSAGE_CHECK_BASE(protect(preferences())->attachmentElementEnabled(), connection);
 
     for (auto& serializedData : data) {
+        MESSAGE_CHECK_BASE(IdentifierToAttachmentMap::isValidKey(serializedData.identifier), connection);
         auto identifier = WTF::move(serializedData.identifier);
         if (!attachmentForIdentifier(identifier)) {
             Ref attachment = ensureAttachment(identifier);
diff --git a/Source/WebKit/UIProcess/WebProcessProxy.cpp b/Source/WebKit/UIProcess/WebProcessProxy.cpp
index 2c239bf014cc..1d4609894990 100644
--- a/Source/WebKit/UIProcess/WebProcessProxy.cpp
+++ b/Source/WebKit/UIProcess/WebProcessProxy.cpp
@@ -1513,6 +1513,19 @@ void WebProcessProxy::setIgnoreInvalidMessageForTesting()
 }
 #endif
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+void WebProcessProxy::addAllowedAttachmentFilePath(const String& filePath)
+{
+    if (!filePath.isEmpty())
+        m_allowedAttachmentFilePaths.add(filePath);
+}
+
+bool WebProcessProxy::isAllowedAttachmentFilePath(const String& filePath) const
+{
+    return m_allowedAttachmentFilePaths.contains(filePath);
+}
+#endif
+
 void WebProcessProxy::didFinishLaunching(ProcessLauncher* launcher, IPC::Connection::Identifier&& connectionIdentifier)
 {
     WEBPROCESSPROXY_RELEASE_LOG(Process, "didFinishLaunching:");
diff --git a/Source/WebKit/UIProcess/WebProcessProxy.h b/Source/WebKit/UIProcess/WebProcessProxy.h
index df560e2b0fd5..40da3f33e9f2 100644
--- a/Source/WebKit/UIProcess/WebProcessProxy.h
+++ b/Source/WebKit/UIProcess/WebProcessProxy.h
@@ -514,6 +514,11 @@ public:
     bool ignoreInvalidMessageForTesting() const { return m_ignoreInvalidMessageForTesting; }
     void setIgnoreInvalidMessageForTesting();
 #endif
+
+#if ENABLE(ATTACHMENT_ELEMENT)
+    void addAllowedAttachmentFilePath(const String&);
+    bool isAllowedAttachmentFilePath(const String&) const;
+#endif
     
     bool allowTestOnlyIPC() const { return m_allowTestOnlyIPC; }
     void setAllowTestOnlyIPC(bool allowTestOnlyIPC) { m_allowTestOnlyIPC = allowTestOnlyIPC; }
@@ -909,6 +914,10 @@ private:
 #if ENABLE(IPC_TESTING_API)
     bool m_ignoreInvalidMessageForTesting { false };
 #endif
+
+#if ENABLE(ATTACHMENT_ELEMENT)
+    HashSet<String> m_allowedAttachmentFilePaths;
+#endif
     
     bool m_allowTestOnlyIPC { false };
 
diff --git a/Tools/TestWebKitAPI/Tests/WebKit/WKWebView/WKAttachmentTests.mm b/Tools/TestWebKitAPI/Tests/WebKit/WKWebView/WKAttachmentTests.mm
index 4aee71f4c649..4a7e3ea9d288 100644
--- a/Tools/TestWebKitAPI/Tests/WebKit/WKWebView/WKAttachmentTests.mm
+++ b/Tools/TestWebKitAPI/Tests/WebKit/WKWebView/WKAttachmentTests.mm
@@ -45,6 +45,7 @@
 #import <WebKit/WKWebViewPrivateForTesting.h>
 #import <WebKit/WebArchive.h>
 #import <WebKit/WebKitPrivate.h>
+#import <WebKit/_WKFeature.h>
 #import <WebKit/_WKUserStyleSheet.h>
 #import <ranges>
 #import <wtf/RetainPtr.h>
@@ -2746,6 +2747,63 @@ TEST(WKAttachmentTestsIOS, PasteRichTextCopiedFromNotes)
 
 #endif // PLATFORM(IOS_FAMILY)
 
+#if PLATFORM(MAC) && ENABLE(IPC_TESTING_API)
+
+TEST(WKAttachmentTestsMac, RegisterAttachmentIdentifierFromFilePathWithUnauthorizedPathTerminatesProcess)
+{
+    RetainPtr configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    [configuration _setAttachmentElementEnabled:YES];
+
+    WKPreferencesSetCustomPasteboardDataEnabled((__bridge WKPreferencesRef)[configuration preferences], YES);
+    for (_WKFeature *feature in [WKPreferences _features]) {
+        if ([feature.key isEqualToString:@"IPCTestingAPIEnabled"])
+            [[configuration preferences] _setEnabled:YES forFeature:feature];
+        if ([feature.key isEqualToString:@"IgnoreInvalidMessageWhenIPCTestingAPIEnabled"])
+            [[configuration preferences] _setEnabled:NO forFeature:feature];
+    }
+
+    RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 500, 500) configuration:configuration.get()]);
+    [webView synchronouslyLoadHTMLString:attachmentEditingTestMarkup];
+
+    RetainPtr navigationDelegate = adoptNS([TestNavigationDelegate new]);
+    [webView setNavigationDelegate:navigationDelegate.get()];
+
+    [webView evaluateJavaScript:
+        @"IPC.sendMessage('UI', IPC.webPageProxyID, IPC.messages.WebPageProxy_RegisterAttachmentIdentifierFromFilePath.name, ["
+        "    {type: 'String', value: 'fake-identifier'},"
+        "    {type: 'String', value: 'application/octet-stream'},"
+        "    {type: 'String', value: '/etc/passwd'}"
+        "])"
+    completionHandler:nil];
+
+    [navigationDelegate waitForWebContentProcessDidTerminate];
+}
+
+TEST(WKAttachmentTestsMac, PastedFileURLsUseAuthorizedPaths)
+{
+    NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+    [pasteboard clearContents];
+    [pasteboard declareTypes:@[NSFilenamesPboardType] owner:nil];
+    [pasteboard setPropertyList:@[testPDFFileURL().path, testImageFileURL().path] forType:NSFilenamesPboardType];
+
+    RetainPtr<NSArray<_WKAttachment *>> insertedAttachments;
+    RetainPtr webView = webViewForTestingAttachments();
+    {
+        ObserveAttachmentUpdatesForScope observer(webView.get());
+        [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
+        insertedAttachments = [observer.observer() inserted];
+        EXPECT_EQ(2U, [insertedAttachments count]);
+    }
+
+    [webView expectElementCount:1 querySelector:@"ATTACHMENT"];
+    [webView expectElementCount:1 querySelector:@"IMG"];
+
+    for (_WKAttachment *attachment in insertedAttachments.get())
+        EXPECT_GT(attachment.info.data.length, 0U);
+}
+
+#endif // PLATFORM(MAC) && ENABLE(IPC_TESTING_API)
+
 } // namespace TestWebKitAPI
 
 #endif // PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(VISION)
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/Tools/TestWebKitAPI/Tests/WebKit/WKWebView/WKAttachmentTests.mm b/Tools/TestWebKitAPI/Tests/WebKit/WKWebView/WKAttachmentTests.mm
index 4aee71f4c649..4a7e3ea9d288 100644
--- a/Tools/TestWebKitAPI/Tests/WebKit/WKWebView/WKAttachmentTests.mm
+++ b/Tools/TestWebKitAPI/Tests/WebKit/WKWebView/WKAttachmentTests.mm
@@ -45,6 +45,7 @@
 #import <WebKit/WKWebViewPrivateForTesting.h>
 #import <WebKit/WebArchive.h>
 #import <WebKit/WebKitPrivate.h>
+#import <WebKit/_WKFeature.h>
 #import <WebKit/_WKUserStyleSheet.h>
 #import <ranges>
 #import <wtf/RetainPtr.h>
@@ -2746,6 +2747,63 @@ TEST(WKAttachmentTestsIOS, PasteRichTextCopiedFromNotes)
 
 #endif // PLATFORM(IOS_FAMILY)
 
+#if PLATFORM(MAC) && ENABLE(IPC_TESTING_API)
+
+TEST(WKAttachmentTestsMac, RegisterAttachmentIdentifierFromFilePathWithUnauthorizedPathTerminatesProcess)
+{
+    RetainPtr configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    [configuration _setAttachmentElementEnabled:YES];
+
+    WKPreferencesSetCustomPasteboardDataEnabled((__bridge WKPreferencesRef)[configuration preferences], YES);
+    for (_WKFeature *feature in [WKPreferences _features]) {
+        if ([feature.key isEqualToString:@"IPCTestingAPIEnabled"])
+            [[configuration preferences] _setEnabled:YES forFeature:feature];
+        if ([feature.key isEqualToString:@"IgnoreInvalidMessageWhenIPCTestingAPIEnabled"])
+            [[configuration preferences] _setEnabled:NO forFeature:feature];
+    }
+
+    RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 500, 500) configuration:configuration.get()]);
+    [webView synchronouslyLoadHTMLString:attachmentEditingTestMarkup];
+
+    RetainPtr navigationDelegate = adoptNS([TestNavigationDelegate new]);
+    [webView setNavigationDelegate:navigationDelegate.get()];
+
+    [webView evaluateJavaScript:
+        @"IPC.sendMessage('UI', IPC.webPageProxyID, IPC.messages.WebPageProxy_RegisterAttachmentIdentifierFromFilePath.name, ["
+        "    {type: 'String', value: 'fake-identifier'},"
+        "    {type: 'String', value: 'application/octet-stream'},"
+        "    {type: 'String', value: '/etc/passwd'}"
+        "])"
+    completionHandler:nil];
+
+    [navigationDelegate waitForWebContentProcessDidTerminate];
+}
+
+TEST(WKAttachmentTestsMac, PastedFileURLsUseAuthorizedPaths)
+{
+    NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+    [pasteboard clearContents];
+    [pasteboard declareTypes:@[NSFilenamesPboardType] owner:nil];
+    [pasteboard setPropertyList:@[testPDFFileURL().path, testImageFileURL().path] forType:NSFilenamesPboardType];
+
+    RetainPtr<NSArray<_WKAttachment *>> insertedAttachments;
+    RetainPtr webView = webViewForTestingAttachments();
+    {
+        ObserveAttachmentUpdatesForScope observer(webView.get());
+        [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
+        insertedAttachments = [observer.observer() inserted];
+        EXPECT_EQ(2U, [insertedAttachments count]);
+    }
+
+    [webView expectElementCount:1 querySelector:@"ATTACHMENT"];
+    [webView expectElementCount:1 querySelector:@"IMG"];
+
+    for (_WKAttachment *attachment in insertedAttachments.get())
+        EXPECT_GT(attachment.info.data.length, 0U);
+}
+
+#endif // PLATFORM(MAC) && ENABLE(IPC_TESTING_API)
+
 } // namespace TestWebKitAPI
 
 #endif // PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(VISION)
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker.