CVE-2026-43732
Overview
Background
- File-backed Blob URL
- A Blob whose data is a file on disk; the NetworkProcess reads the file when the blob URL is fetched.
- NetworkProcess path allow-list
- m_allowedFilePaths: the set of on-disk paths a given WebContent connection is permitted to access via blobs.
- MESSAGE_CHECK
- An IPC guard that validates a message argument (here, that a requested file path is allowed) and terminates the connection on failure.
- IndexedDB blob files
- IDB stores large values as separate on-disk blob files under the storage directory, per origin.
Root Cause Analysis
File-backed Blob URLs let the WebContent process reference on-disk files (e.g. IndexedDB blob files) that the NetworkProcess reads on its behalf; the NetworkProcess must validate that a path the web process asks to register/read is one it is actually allowed to access.
Pre-patch, NetworkConnectionToWebProcess::isFilePathAllowed(session, path) allowed a path not only when it was in the explicit allow-list (m_allowedFilePaths) but ALSO when its parent directory equaled the session’s storageManager().path() or customIDBStoragePath(). That parent-directory rule is over-broad: it authorizes ANY file directly under the storage directories, so a malicious/compromised WebContent could register file-backed blobs for arbitrary files in those directories (other origins’ IndexedDB blob files) and read their contents over IPC – disclosure of sensitive user information across the storage boundary.
The fix tightens isFilePathAllowed(path) to require the path be explicitly in m_allowedFilePaths (dropping the parentPath == storage path shortcuts), and adds the machinery to grant exactly the right paths: IDBStorageConnectionToClient gains resultBlobFilePaths(resultData) (collecting blobFilePaths from Get/GetAll/OpenCursor/IterateCursor results and prefetched records) and sendResultWithBlobFileAccess<Message>(), which registers those specific blob file paths as allowed for the connection before sending the IDB result. So the web process is authorized for precisely the blob files contained in its own IDB results, rather than for everything under the storage directory.
The restored invariant is that file-backed blob access is allow-listed per specific, result-derived path, not granted wholesale by parent directory. The added IPC test validates register-file-backed-blob path validation.
Attack Path
- Control a WebContent process A malicious page (or a compromised WebContent) sends NetworkProcess IPC to register a file-backed blob URL for a chosen path.
- Abuse the parent-directory rule Pre-patch, isFilePathAllowed accepts any path whose parent is the storage/customIDB directory, so paths to other origins’ IndexedDB blob files pass the MESSAGE_CHECK.
- Register and fetch the blob Register the file-backed blob and read it via the blob URL; the NetworkProcess serves the file contents back.
- Disclose sensitive data The web process reads another origin’s stored blob files – sensitive user information disclosed across the storage boundary.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
whileSource/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp |
modified | |
ifSource/WebKit/NetworkProcess/storage/IDBStorageConnectionToClient.cpp |
modified | |
ifLayoutTests/ipc/register-file-backed-blob-path-validation.html |
modified |
Files Changed
LayoutTests/ipc/register-file-backed-blob-path-validation-expected.txtLayoutTests/ipc/register-file-backed-blob-path-validation.htmlSource/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cppSource/WebKit/NetworkProcess/NetworkConnectionToWebProcess.hSource/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.inSource/WebKit/NetworkProcess/storage/IDBStorageConnectionToClient.cppSource/WebKit/NetworkProcess/storage/IDBStorageConnectionToClient.hSource/WebKit/NetworkProcess/storage/IDBStorageRegistry.cppSource/WebKit/NetworkProcess/storage/IDBStorageRegistry.hSource/WebKit/NetworkProcess/storage/NetworkStorageManager.cppSource/WebKit/NetworkProcess/storage/NetworkStorageManager.h
Audit Directions
- Other parent-directory allow rulesgrep NetworkProcess for path checks that authorize by parentPath/prefix equality against storage directories rather than an exact allow-list.
- Blob file path grantingAudit all IDB/storage result senders to ensure every blob file path returned to a web process is registered as allowed for exactly that connection.
- MESSAGE_CHECK path argumentsReview file-path arguments crossing IPC into the NetworkProcess for canonicalization and exact allow-listing (no directory-prefix shortcuts, symlink/.. handling).
Patch
diff --git a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp
index cf4eec65d80b..6c7c047279cc 100644
--- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp
+++ b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp
@@ -1151,12 +1151,12 @@ void NetworkConnectionToWebProcess::cookieEnabledStateMayHaveChanged()
m_connection->send(Messages::NetworkProcessConnection::UpdateCachedCookiesEnabled(), 0);
}
-bool NetworkConnectionToWebProcess::isFilePathAllowed(NetworkSession& session, String path)
+bool NetworkConnectionToWebProcess::isFilePathAllowed(String path)
{
path = FileSystem::lexicallyNormal(path);
auto parentPath = FileSystem::parentPath(path);
while (parentPath != path) {
- if (m_allowedFilePaths.contains(path) || parentPath == session.storageManager().path() || parentPath == session.storageManager().customIDBStoragePath())
+ if (m_allowedFilePaths.contains(path))
return true;
path = parentPath;
parentPath = FileSystem::parentPath(path);
@@ -1182,7 +1182,7 @@ void NetworkConnectionToWebProcess::registerInternalFileBlobURL(const URL& url,
if (!session)
return;
if (blobFileAccessEnforcementEnabled() && shouldCheckBlobFileAccess())
- MESSAGE_CHECK(isFilePathAllowed(*session, path));
+ MESSAGE_CHECK(isFilePathAllowed(path));
RefPtr sandboxExtension = SandboxExtension::create(WTF::move(extensionHandle));
@@ -1198,7 +1198,7 @@ void NetworkConnectionToWebProcess::registerInternalFileBlobURL(const URL& url,
MESSAGE_CHECK(false);
}
#else
- MESSAGE_CHECK(isFilePathAllowed(*session, replacementPath));
+ MESSAGE_CHECK(isFilePathAllowed(replacementPath));
#endif
}
@@ -1233,7 +1233,7 @@ void NetworkConnectionToWebProcess::registerInternalBlobURLOptionallyFileBacked(
if (!session)
return;
if (blobFileAccessEnforcementEnabled() && shouldCheckBlobFileAccess())
- MESSAGE_CHECK(isFilePathAllowed(*session, fileBackedPath));
+ MESSAGE_CHECK(isFilePathAllowed(fileBackedPath));
m_blobURLs.add({ url, std::nullopt });
session->blobRegistry().registerInternalBlobURLOptionallyFileBacked(url, srcURL, BlobDataFileReferenceWithSandboxExtension::create(fileBackedPath), contentType, { });
@@ -1329,6 +1329,14 @@ void NetworkConnectionToWebProcess::registerBlobPathForTesting(const String& pat
completion();
}
+void NetworkConnectionToWebProcess::generalStoragePathForTesting(CompletionHandler<void(String&&)>&& completion)
+{
+ CheckedPtr session = networkSession();
+ if (!session)
+ return completion({ });
+ completion(String { session->storageManager().path() });
+}
+
void NetworkConnectionToWebProcess::allowAccessToFile(const String& path)
{
m_allowedFilePaths.add(FileSystem::lexicallyNormal(path));
diff --git a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h
index 827774201378..a9c081afbf41 100644
--- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h
+++ b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h
@@ -345,7 +345,8 @@ private:
void unregisterBlobURL(const URL&, const std::optional<WebCore::SecurityOriginData>& topOrigin);
void writeBlobsToTemporaryFilesForIndexedDB(const Vector<String>& blobURLs, CompletionHandler<void(Vector<String>&&)>&&);
void registerBlobPathForTesting(const String& path, CompletionHandler<void()>&&);
- bool isFilePathAllowed(NetworkSession&, String path);
+ void generalStoragePathForTesting(CompletionHandler<void(String&&)>&&);
+ bool isFilePathAllowed(String path);
void registerBlobURLHandle(const URL&, const std::optional<WebCore::SecurityOriginData>& topOrigin);
void unregisterBlobURLHandle(const URL&, const std::optional<WebCore::SecurityOriginData>& topOrigin);
diff --git a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in
index 6310206e0cad..00fe84f0441a 100644
--- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in
+++ b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in
@@ -76,6 +76,7 @@ messages -> NetworkConnectionToWebProcess WantsDispatchMessage {
RegisterBlobURLHandle(URL url, std::optional<WebCore::SecurityOriginData> topOrigin);
UnregisterBlobURLHandle(URL url, std::optional<WebCore::SecurityOriginData> topOrigin);
RegisterBlobPathForTesting(String path) -> ();
+ [EnabledBy=AllowTestOnlyIPC] GeneralStoragePathForTesting() -> (String path);
SetCaptureExtraNetworkLoadMetricsEnabled(bool enabled)
diff --git a/Source/WebKit/NetworkProcess/storage/IDBStorageConnectionToClient.cpp b/Source/WebKit/NetworkProcess/storage/IDBStorageConnectionToClient.cpp
index e62fcd676a49..caf7a835ab79 100644
--- a/Source/WebKit/NetworkProcess/storage/IDBStorageConnectionToClient.cpp
+++ b/Source/WebKit/NetworkProcess/storage/IDBStorageConnectionToClient.cpp
@@ -161,14 +161,49 @@ WebIDBResult IDBStorageConnectionToClient::prepareCursorResult(const WebCore::ID
});
}
+static Vector<String> resultBlobFilePaths(const WebCore::IDBResultData& resultData)
+{
+ Vector<String> paths;
+ switch (resultData.type()) {
+ case WebCore::IDBResultType::GetRecordSuccess:
+ case WebCore::IDBResultType::OpenCursorSuccess:
+ case WebCore::IDBResultType::IterateCursorSuccess:
+ paths.appendVector(resultData.getResult().value().blobFilePaths());
+ for (auto& record : resultData.getResult().prefetchedRecords())
+ paths.appendVector(record.value.blobFilePaths());
+ break;
+ case WebCore::IDBResultType::GetAllRecordsSuccess:
+ for (auto& value : resultData.getAllResult().values())
+ paths.appendVector(value.blobFilePaths());
+ break;
+ default:
+ break;
+ }
+ return paths;
+}
+
+template<typename Message>
+void IDBStorageConnectionToClient::sendResultWithBlobFileAccess(WebIDBResult&& result)
+{
+ auto blobFilePaths = resultBlobFilePaths(result.resultData());
+ RefPtr networkStorageManager = m_networkStorageManager.get();
+ if (!networkStorageManager) {
+ IPC::Connection::send(m_connection, Message(WTF::move(result)), 0);
+ return;
+ }
+ networkStorageManager->allowAccessToBlobFilesForProcess(m_identifier, WTF::move(blobFilePaths), [connection = m_connection, result = WTF::move(result)]() mutable {
+ IPC::Connection::send(connection, Message(WTF::move(result)), 0);
+ });
+}
+
void IDBStorageConnectionToClient::didGetRecord(const WebCore::IDBResultData& resultData)
{
- IPC::Connection::send(m_connection, Messages::WebIDBConnectionToServer::DidGetRecord(prepareGetResult(resultData)), 0);
+ sendResultWithBlobFileAccess<Messages::WebIDBConnectionToServer::DidGetRecord>(prepareGetResult(resultData));
}
void IDBStorageConnectionToClient::didGetAllRecords(const WebCore::IDBResultData& resultData)
{
- IPC::Connection::send(m_connection, Messages::WebIDBConnectionToServer::DidGetAllRecords(prepareGetAllResult(resultData)), 0);
+ sendResultWithBlobFileAccess<Messages::WebIDBConnectionToServer::DidGetAllRecords>(prepareGetAllResult(resultData));
}
void IDBStorageConnectionToClient::didGetCount(const WebCore::IDBResultData& resultData)
@@ -183,12 +218,12 @@ void IDBStorageConnectionToClient::didDeleteRecord(const WebCore::IDBResultData&
void IDBStorageConnectionToClient::didOpenCursor(const WebCore::IDBResultData& resultData)
{
- IPC::Connection::send(m_connection, Messages::WebIDBConnectionToServer::DidOpenCursor(prepareCursorResult(resultData)), 0);
+ sendResultWithBlobFileAccess<Messages::WebIDBConnectionToServer::DidOpenCursor>(prepareCursorResult(resultData));
}
void IDBStorageConnectionToClient::didIterateCursor(const WebCore::IDBResultData& resultData)
{
- IPC::Connection::send(m_connection, Messages::WebIDBConnectionToServer::DidIterateCursor(prepareCursorResult(resultData)), 0);
+ sendResultWithBlobFileAccess<Messages::WebIDBConnectionToServer::DidIterateCursor>(prepareCursorResult(resultData));
}
void IDBStorageConnectionToClient::didGetAllDatabaseNamesAndVersions(const WebCore::IDBResourceIdentifier& requestIdentifier, Vector<WebCore::IDBDatabaseNameAndVersion>&& databases)
@@ -203,7 +238,13 @@ void IDBStorageConnectionToClient::fireVersionChangeEvent(WebCore::IDBServer::Un
void IDBStorageConnectionToClient::generateIndexKeyForRecord(const WebCore::IDBResourceIdentifier& requestIdentifier, const WebCore::IDBIndexInfo& indexInfo, const std::optional<WebCore::IDBKeyPath>& keyPath, const WebCore::IDBKeyData& key, const WebCore::IDBValue& value, std::optional<int64_t> recordID)
{
- IPC::Connection::send(m_connection, Messages::WebIDBConnectionToServer::GenerateIndexKeyForRecord(requestIdentifier, indexInfo, keyPath, key, value, recordID), 0);
+ auto sendResult = [connection = m_connection, requestIdentifier, indexInfo, keyPath, key, value, recordID] {
+ IPC::Connection::send(connection, Messages::WebIDBConnectionToServer::GenerateIndexKeyForRecord(requestIdentifier, indexInfo, keyPath, key, value, recordID), 0);
+ };
+ RefPtr networkStorageManager = m_networkStorageManager.get();
+ if (!networkStorageManager)
+ return sendResult();
+ networkStorageManager->allowAccessToBlobFilesForProcess(m_identifier, Vector<String> { value.blobFilePaths() }, WTF::move(sendResult));
}
void IDBStorageConnectionToClient::didCloseFromServer(WebCore::IDBServer::UniqueIDBDatabaseConnection& connection, const WebCore::IDBError& error)
diff --git a/Source/WebKit/NetworkProcess/storage/IDBStorageConnectionToClient.h b/Source/WebKit/NetworkProcess/storage/IDBStorageConnectionToClient.h
index 550109028e21..87d25a01c31b 100644
--- a/Source/WebKit/NetworkProcess/storage/IDBStorageConnectionToClient.h
+++ b/Source/WebKit/NetworkProcess/storage/IDBStorageConnectionToClient.h
@@ -73,6 +73,7 @@ private:
void fireVersionChangeEvent(WebCore::IDBServer::UniqueIDBDatabaseConnection&, const WebCore::IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion) final;
void generateIndexKeyForRecord(const WebCore::IDBResourceIdentifier& requestIdentifier, const WebCore::IDBIndexInfo&, const std::optional<WebCore::IDBKeyPath>&, const WebCore::IDBKeyData&, const WebCore::IDBValue&, std::optional<int64_t> recordID);
void didCloseFromServer(WebCore::IDBServer::UniqueIDBDatabaseConnection&, const WebCore::IDBError&) final;
+ template<typename Message> void sendResultWithBlobFileAccess(WebIDBResult&&);
WebIDBResult prepareGetResult(const WebCore::IDBResultData&);
WebIDBResult prepareGetAllResult(const WebCore::IDBResultData&);
diff --git a/Source/WebKit/NetworkProcess/storage/IDBStorageRegistry.cpp b/Source/WebKit/NetworkProcess/storage/IDBStorageRegistry.cpp
index 15f4ee4826af..dcd466448ca3 100644
--- a/Source/WebKit/NetworkProcess/storage/IDBStorageRegistry.cpp
+++ b/Source/WebKit/NetworkProcess/storage/IDBStorageRegistry.cpp
@@ -28,6 +28,7 @@
#include "IDBStorageConnectionToClient.h"
#include "Logging.h"
+#include "NetworkStorageManager.h"
#include <WebCore/UniqueIDBDatabaseConnection.h>
#include <WebCore/UniqueIDBDatabaseTransaction.h>
#include <wtf/TZoneMallocInlines.h>
@@ -36,7 +37,10 @@
namespace WebKit {
-IDBStorageRegistry::IDBStorageRegistry() = default;
+IDBStorageRegistry::IDBStorageRegistry(NetworkStorageManager& manager)
+ : m_manager(manager)
+{
+}
IDBStorageRegistry::~IDBStorageRegistry() = default;
diff --git a/Source/WebKit/NetworkProcess/storage/IDBStorageRegistry.h b/Source/WebKit/NetworkProcess/storage/IDBStorageRegistry.h
index b74835a627f2..c3788a5eb51f 100644
--- a/Source/WebKit/NetworkProcess/storage/IDBStorageRegistry.h
+++ b/Source/WebKit/NetworkProcess/storage/IDBStorageRegistry.h
@@ -48,7 +48,7 @@ class IDBStorageRegistry : public CanMakeThreadSafeCheckedPtr<IDBStorageRegistry
WTF_MAKE_TZONE_ALLOCATED(IDBStorageRegistry);
WTF_OVERRIDE_DELETE_FOR_CHECKED_PTR(IDBStorageRegistry);
public:
- IDBStorageRegistry();
+ explicit IDBStorageRegistry(NetworkStorageManager&);
~IDBStorageRegistry();
WebCore::IDBServer::IDBConnectionToClient* ensureConnectionToClient(IPC::Connection&, const WebCore::IDBResourceIdentifier&, NetworkStorageManager&);
WebCore::IDBServer::IDBConnectionToClient* existingConnectionToClient(WebCore::IDBConnectionIdentifier);
@@ -63,6 +63,7 @@ public:
private:
bool isValidConnectionForIPC(WebCore::IDBServer::UniqueIDBDatabaseConnection&, IPC::Connection&);
+ ThreadSafeWeakRef<NetworkStorageManager> m_manager;
HashMap<WebCore::IDBConnectionIdentifier, std::unique_ptr<IDBStorageConnectionToClient>> m_connectionsToClient;
HashMap<WebCore::IDBDatabaseConnectionIdentifier, WeakPtr<WebCore::IDBServer::UniqueIDBDatabaseConnection>> m_connections;
HashMap<WebCore::IDBResourceIdentifier, WeakPtr<WebCore::IDBServer::UniqueIDBDatabaseTransaction>> m_transactions;
diff --git a/Source/WebKit/NetworkProcess/storage/NetworkStorageManager.cpp b/Source/WebKit/NetworkProcess/storage/NetworkStorageManager.cpp
index 25ee7e11ce95..13151709d619 100644
--- a/Source/WebKit/NetworkProcess/storage/NetworkStorageManager.cpp
+++ b/Source/WebKit/NetworkProcess/storage/NetworkStorageManager.cpp
@@ -218,7 +218,7 @@ NetworkStorageManager::NetworkStorageManager(NetworkProcess& process, PAL::Sessi
setStorageSiteValidationEnabledInternal(storageSiteValidationEnabled);
m_fileSystemStorageHandleRegistry = FileSystemStorageHandleRegistry::create();
lazyInitialize(m_storageAreaRegistry, makeUnique<StorageAreaRegistry>());
- lazyInitialize(m_idbStorageRegistry, makeUnique<IDBStorageRegistry>());
+ lazyInitialize(m_idbStorageRegistry, makeUnique<IDBStorageRegistry>(*this));
lazyInitialize(m_cacheStorageRegistry, CacheStorageRegistry::create());
m_unifiedOriginStorageLevel = level;
m_path = path;
@@ -1157,7 +1157,7 @@ void NetworkStorageManager::resolve(WebCore::FileSystemHandleIdentifier identifi
completionHandler(handle->resolve(targetIdentifier));
}
-void NetworkStorageManager::getFile(WebCore::FileSystemHandleIdentifier identifier, CompletionHandler<void(Expected<String, FileSystemStorageError>)>&& completionHandler)
+void NetworkStorageManager::getFile(IPC::Connection& connection, WebCore::FileSystemHandleIdentifier identifier, CompletionHandler<void(Expected<String, FileSystemStorageError>)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
@@ -1168,7 +1168,15 @@ void NetworkStorageManager::getFile(WebCore::FileSystemHandleIdentifier identifi
if (!FileSystem::fileExists(handle->path()))
return completionHandler(makeUnexpected(FileSystemStorageError::FileNotFound));
- completionHandler(handle->path());
+ RunLoop::mainSingleton().dispatch([protectedThis = Ref { *this }, connection = Ref { connection }, path = crossThreadCopy(handle->path()), completionHandler = WTF::move(completionHandler)] mutable {
+ if (RefPtr process = protectedThis->m_process.get()) {
+ if (RefPtr webConnection = process->webProcessConnection(connection.get()))
+ webConnection->allowAccessToFile(path);
+ }
+ protectedThis->workQueue().dispatch([path = crossThreadCopy(WTF::move(path)), completionHandler = WTF::move(completionHandler)] mutable {
+ completionHandler(WTF::move(path));
+ });
+ });
}
void NetworkStorageManager::createSyncAccessHandle(WebCore::FileSystemHandleIdentifier identifier, CompletionHandler<void(Expected<FileSystemSyncAccessHandleInfo, FileSystemStorageError>)>&& completionHandler)
@@ -1683,6 +1691,23 @@ void NetworkStorageManager::registerTemporaryBlobFilePaths(IPC::Connection& conn
});
}
+void NetworkStorageManager::allowAccessToBlobFilesForProcess(WebCore::ProcessIdentifier processIdentifier, Vector<String>&& filePaths, CompletionHandler<void()>&& completionHandler)
+{
+ assertIsCurrent(workQueue());
+
+ if (filePaths.isEmpty())
+ return completionHandler();
+
+ RunLoop::mainSingleton().dispatch([protectedThis = Ref { *this }, processIdentifier, filePaths = crossThreadCopy(WTF::move(filePaths)), completionHandler = WTF::move(completionHandler)] mutable {
+ RefPtr process = protectedThis->m_process.get();
+ if (!process)
+ return protectedThis->workQueue().dispatch(WTF::move(completionHandler));
+ process->allowFilesAccessFromWebProcess(processIdentifier, filePaths, [protectedThis = WTF::move(protectedThis), completionHandler = WTF::move(completionHandler)] mutable {
+ protectedThis->workQueue().dispatch(WTF::move(completionHandler));
+ });
+ });
+}
+
void NetworkStorageManager::requestSpace(const WebCore::ClientOrigin& origin, uint64_t size, CompletionHandler<void(bool)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
diff --git a/Source/WebKit/NetworkProcess/storage/NetworkStorageManager.h b/Source/WebKit/NetworkProcess/storage/NetworkStorageManager.h
index c41443623ac5..431f3b280fc8 100644
--- a/Source/WebKit/NetworkProcess/storage/NetworkStorageManager.h
+++ b/Source/WebKit/NetworkProcess/storage/NetworkStorageManager.h
@@ -144,6 +144,7 @@ public:
void fetchLocalStorage(CompletionHandler<void(std::optional<HashMap<WebCore::ClientOrigin, HashMap<String, String>>>&&)>&&);
void restoreLocalStorage(HashMap<WebCore::ClientOrigin, HashMap<String, String>>&&, CompletionHandler<void(bool)>&&);
void registerTemporaryBlobFilePaths(IPC::Connection&, const Vector<String>&);
+ void allowAccessToBlobFilesForProcess(WebCore::ProcessIdentifier, Vector<String>&&, CompletionHandler<void()>&&);
void requestSpace(const WebCore::ClientOrigin&, uint64_t size, CompletionHandler<void(bool)>&&);
void resetQuotaForTesting(CompletionHandler<void()>&&);
void resetQuotaUpdatedBasedOnUsageForTesting(WebCore::ClientOrigin&&);
@@ -211,7 +212,7 @@ private:
void getDirectoryHandle(IPC::Connection&, WebCore::FileSystemHandleIdentifier, String&& name, bool createIfNecessary, CompletionHandler<void(Expected<std::pair<WebCore::FileSystemHandleGlobalIdentifier, WebCore::FileSystemHandleIdentifier>, FileSystemStorageError>)>&&);
void removeEntry(WebCore::FileSystemHandleIdentifier, const String& name, bool deleteRecursively, CompletionHandler<void(std::optional<FileSystemStorageError>)>&&);
void resolve(WebCore::FileSystemHandleIdentifier, WebCore::FileSystemHandleIdentifier, CompletionHandler<void(Expected<std::optional<Vector<String>>, FileSystemStorageError>)>&&);
- void getFile(WebCore::FileSystemHandleIdentifier, CompletionHandler<void(Expected<String, FileSystemStorageError>)>&&);
+ void getFile(IPC::Connection&, WebCore::FileSystemHandleIdentifier, CompletionHandler<void(Expected<String, FileSystemStorageError>)>&&);
void createSyncAccessHandle(WebCore::FileSystemHandleIdentifier, CompletionHandler<void(Expected<FileSystemSyncAccessHandleInfo, FileSystemStorageError>)>&&);
void closeSyncAccessHandle(WebCore::FileSystemHandleIdentifier, WebCore::FileSystemSyncAccessHandleIdentifier, CompletionHandler<void()>&&);
void requestNewCapacityForSyncAccessHandle(WebCore::FileSystemHandleIdentifier, WebCore::FileSystemSyncAccessHandleIdentifier, uint64_t newCapacity, CompletionHandler<void(std::optional<uint64_t>)>&&);
diff --git a/LayoutTests/ipc/register-file-backed-blob-path-validation-expected.txt b/LayoutTests/ipc/register-file-backed-blob-path-validation-expected.txt
new file mode 100644
index 000000000000..05d932b06d4f
--- /dev/null
+++ b/LayoutTests/ipc/register-file-backed-blob-path-validation-expected.txt
@@ -0,0 +1,2 @@
+Unauthorized file-backed blob registration rejected: PASS
+
diff --git a/LayoutTests/ipc/register-file-backed-blob-path-validation.html b/LayoutTests/ipc/register-file-backed-blob-path-validation.html
new file mode 100644
index 000000000000..1dd09d93dace
--- /dev/null
+++ b/LayoutTests/ipc/register-file-backed-blob-path-validation.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html><!-- webkit-test-runner [ IPCTestingAPIEnabled=true IgnoreInvalidMessageWhenIPCTestingAPIEnabled=true BlobFileAccessEnforcementEnabled=true allowTestOnlyIPC=true ] -->
+<pre id="out"></pre>
+<script>
+if (window.testRunner) {
+ testRunner.dumpAsText();
+ testRunner.waitUntilDone();
+}
+</script>
+<script type="module">
+const out = document.getElementById('out');
+const log = s => { out.textContent += s + '\n'; };
+
+if (!window.IPC) {
+ log('Unauthorized file-backed blob registration rejected: PASS');
+ testRunner?.notifyDone();
+} else {
+
+const { CoreIPC } = await import("./coreipc.js");
+
+function blobSize(u) {
+ return new Promise(res => {
+ CoreIPC.Networking.NetworkConnectionToWebProcess.BlobSize(0, { url: { string: u } },
+ (parsed) => res(parsed && parsed.resultSize !== undefined ? Number(parsed.resultSize) : -1));
+ });
+}
+
+function generalStoragePath() {
+ return new Promise(res => {
+ CoreIPC.Networking.NetworkConnectionToWebProcess.GeneralStoragePathForTesting(0, {},
+ (parsed) => res(parsed && parsed.path ? parsed.path : ''));
+ });
+}
+
+(async () => {
+
+ localStorage.setItem('seed', '1');
+
+ let storagePath = await generalStoragePath();
+ if (!storagePath) {
+ log('FAIL: could not retrieve general storage path');
+ testRunner?.notifyDone();
+ return;
+ }
+
+ let targetPath = storagePath + '/salt';
+ let blobURL = URL.createObjectURL(new Blob([new Uint8Array([0])]));
+ CoreIPC.Networking.NetworkConnectionToWebProcess.RegisterInternalBlobURLOptionallyFileBacked(0, {
+ url: { string: blobURL },
+ srcURL: { string: `blob:webkit-internal://${crypto.randomUUID()}` },
+ fileBackedPath: targetPath,
+ contentType: 'application/octet-stream',
+ });
+ let size = await blobSize(blobURL);
+ log('Unauthorized file-backed blob registration rejected: ' + (size === 1 ? 'PASS' : 'FAIL (BlobSize=' + size + ')'));
+
+ testRunner?.notifyDone();
+})().catch(e => { log('FAIL: ' + e); testRunner?.notifyDone(); });
+
+}
+</script>
Regression Test / PoC
diff --git a/LayoutTests/ipc/register-file-backed-blob-path-validation-expected.txt b/LayoutTests/ipc/register-file-backed-blob-path-validation-expected.txt
new file mode 100644
index 000000000000..05d932b06d4f
--- /dev/null
+++ b/LayoutTests/ipc/register-file-backed-blob-path-validation-expected.txt
@@ -0,0 +1,2 @@
+Unauthorized file-backed blob registration rejected: PASS
+
diff --git a/LayoutTests/ipc/register-file-backed-blob-path-validation.html b/LayoutTests/ipc/register-file-backed-blob-path-validation.html
new file mode 100644
index 000000000000..1dd09d93dace
--- /dev/null
+++ b/LayoutTests/ipc/register-file-backed-blob-path-validation.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html><!-- webkit-test-runner [ IPCTestingAPIEnabled=true IgnoreInvalidMessageWhenIPCTestingAPIEnabled=true BlobFileAccessEnforcementEnabled=true allowTestOnlyIPC=true ] -->
+<pre id="out"></pre>
+<script>
+if (window.testRunner) {
+ testRunner.dumpAsText();
+ testRunner.waitUntilDone();
+}
+</script>
+<script type="module">
+const out = document.getElementById('out');
+const log = s => { out.textContent += s + '\n'; };
+
+if (!window.IPC) {
+ log('Unauthorized file-backed blob registration rejected: PASS');
+ testRunner?.notifyDone();
+} else {
+
+const { CoreIPC } = await import("./coreipc.js");
+
+function blobSize(u) {
+ return new Promise(res => {
+ CoreIPC.Networking.NetworkConnectionToWebProcess.BlobSize(0, { url: { string: u } },
+ (parsed) => res(parsed && parsed.resultSize !== undefined ? Number(parsed.resultSize) : -1));
+ });
+}
+
+function generalStoragePath() {
+ return new Promise(res => {
+ CoreIPC.Networking.NetworkConnectionToWebProcess.GeneralStoragePathForTesting(0, {},
+ (parsed) => res(parsed && parsed.path ? parsed.path : ''));
+ });
+}
+
+(async () => {
+
+ localStorage.setItem('seed', '1');
+
+ let storagePath = await generalStoragePath();
+ if (!storagePath) {
+ log('FAIL: could not retrieve general storage path');
+ testRunner?.notifyDone();
+ return;
+ }
+
+ let targetPath = storagePath + '/salt';
+ let blobURL = URL.createObjectURL(new Blob([new Uint8Array([0])]));
+ CoreIPC.Networking.NetworkConnectionToWebProcess.RegisterInternalBlobURLOptionallyFileBacked(0, {
+ url: { string: blobURL },
+ srcURL: { string: `blob:webkit-internal://${crypto.randomUUID()}` },
+ fileBackedPath: targetPath,
+ contentType: 'application/octet-stream',
+ });
+ let size = await blobSize(blobURL);
+ log('Unauthorized file-backed blob registration rejected: ' + (size === 1 ? 'PASS' : 'FAIL (BlobSize=' + size + ')'));
+
+ testRunner?.notifyDone();
+})().catch(e => { log('FAIL: ' + e); testRunner?.notifyDone(); });
+
+}
+</script>