Skip to content
Permalink
Browse files
Downloading DMG using WKDownload errors with "You do not have permiss…
…ion"

https://bugs.webkit.org/show_bug.cgi?id=234249

Reviewed by Chris Dumez.

Applications downloaded from the sandboxed network process can't be opened
on macOS because they have QTN_FLAG_HARD set.  The network process can't remove
this and shouldn't be given such permission.  However, WKDownload may be used
by unsandboxed applications such as TestWebKitAPI that do have permission to
remove QTN_FLAG_HARD.  If this is the case, we should try to make the application
able to be opened when finishing the download.  I verified you still get the warning
"Application Name" is an app downloaded from the Internet. Are you sure you want to open it?
but you no longer get the warning that can't be bypassed.

* Source/WebCore/PAL/PAL.xcodeproj/project.pbxproj:
* Source/WebCore/PAL/pal/spi/mac/QuarantineSPI.h: Renamed from Source/WebKit/Platform/spi/mac/QuarantineSPI.h.
(QuarantineFileDeleter::operator ()):
Added helper for std::unique_ptr use with qtn_file_t
* Source/WebKit/NetworkProcess/Downloads/Download.cpp:
(WebKit::Download::Download):
* Source/WebKit/NetworkProcess/Downloads/Download.h:
(WebKit::Download::sessionID const):
(WebKit::Download::suggestedName const): Deleted.
* Source/WebKit/Shared/mac/AuxiliaryProcessMac.mm:
* Source/WebKit/UIProcess/Cocoa/WKShareSheet.mm:
* Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm:
* Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp:
(WebKit::DownloadProxy::updateQuarantinePropertiesIfPossible):
(WebKit::DownloadProxy::didFinish):
* Source/WebKit/UIProcess/Downloads/DownloadProxy.h:
* Source/WebKit/WebKit.xcodeproj/project.pbxproj:
* Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm:
(tempFileThatDoesNotExist):
Introduce a non-ASCII character to make sure file system representation code works as expected

Canonical link: https://commits.webkit.org/251736@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@295731 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
achristensen07 committed Jun 22, 2022
1 parent 43e883c commit 12bc4cbaf8693b514b926caf0db1dbdfe687679d
Showing 11 changed files with 80 additions and 13 deletions.
@@ -76,6 +76,7 @@
57F1C90A25DCF0CF00E8F6EA /* CryptoKitPrivateSoftLink.mm in Sources */ = {isa = PBXBuildFile; fileRef = 57F1C90825DCF0CF00E8F6EA /* CryptoKitPrivateSoftLink.mm */; };
57FD318A22B3593E008D0E8B /* AppSSOSoftLink.mm in Sources */ = {isa = PBXBuildFile; fileRef = 57FD318922B3593E008D0E8B /* AppSSOSoftLink.mm */; };
5C7C787423AC3E770065F47E /* ManagedConfigurationSoftLink.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C7C787223AC3E770065F47E /* ManagedConfigurationSoftLink.mm */; };
5CB898B2286274FA00CA3485 /* QuarantineSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CB898B1286274FA00CA3485 /* QuarantineSPI.h */; settings = {ATTRIBUTES = (Private, ); }; };
7B47F2A328587B9700E793C8 /* CoreGraphicsSoftLink.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B47F2A128587B9700E793C8 /* CoreGraphicsSoftLink.h */; settings = {ATTRIBUTES = (Private, ); }; };
7B47F2A428587B9700E793C8 /* CoreGraphicsSoftLink.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7B47F2A228587B9700E793C8 /* CoreGraphicsSoftLink.cpp */; };
93B38EC025821CD800198E63 /* SpeechSoftLink.mm in Sources */ = {isa = PBXBuildFile; fileRef = 93B38EBF25821CD700198E63 /* SpeechSoftLink.mm */; };
@@ -884,6 +885,7 @@
5C7C787123AC3E770065F47E /* ManagedConfigurationSoftLink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ManagedConfigurationSoftLink.h; sourceTree = "<group>"; };
5C7C787223AC3E770065F47E /* ManagedConfigurationSoftLink.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ManagedConfigurationSoftLink.mm; sourceTree = "<group>"; };
5C7C787523AC3E850065F47E /* ManagedConfigurationSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ManagedConfigurationSPI.h; sourceTree = "<group>"; };
5CB898B1286274FA00CA3485 /* QuarantineSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QuarantineSPI.h; sourceTree = "<group>"; };
63E369F921AFA83F001C14BC /* NSProgressSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSProgressSPI.h; sourceTree = "<group>"; };
71B1141F26823ACD004D6701 /* SystemPreviewSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SystemPreviewSPI.h; sourceTree = "<group>"; };
7A36D0F8223AD9AB00B0522E /* CommonCryptoSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonCryptoSPI.h; sourceTree = "<group>"; };
@@ -1162,6 +1164,7 @@
0C7785841F45130F00F4EBB6 /* NSWindowSPI.h */,
0C7785851F45130F00F4EBB6 /* PIPSPI.h */,
E34F26F52846B7550076E549 /* PowerLogSPI.h */,
5CB898B1286274FA00CA3485 /* QuarantineSPI.h */,
0C7785871F45130F00F4EBB6 /* QuickLookMacSPI.h */,
07035D3027A9B32E00FB03E4 /* ScreenCaptureKitSPI.h */,
71B1141F26823ACD004D6701 /* SystemPreviewSPI.h */,
@@ -1988,6 +1991,7 @@
DD20DDAE27BC90D70093D175 /* WebGPUTextureAspect.h in Headers */,
DD20DDAF27BC90D70093D175 /* WebGPUTextureBindingLayout.h in Headers */,
DD20DDB027BC90D70093D175 /* WebGPUTextureDescriptor.h in Headers */,
5CB898B2286274FA00CA3485 /* QuarantineSPI.h in Headers */,
DD20DDB127BC90D70093D175 /* WebGPUTextureDimension.h in Headers */,
DD20DDB227BC90D70093D175 /* WebGPUTextureFormat.h in Headers */,
DD20DD3E27BC90D60093D175 /* WebGPUTextureImpl.h in Headers */,
@@ -40,6 +40,7 @@ enum qtn_error_code {
enum qtn_flags {
QTN_FLAG_DOWNLOAD = 0x0001,
QTN_FLAG_SANDBOX = 0x0002,
QTN_FLAG_HARD = 0x0004,
QTN_FLAG_USER_APPROVED = 0x0040,
};

@@ -77,4 +78,11 @@ int qtn_file_init_with_path(qtn_file_t qf, const char *path);

WTF_EXTERN_C_END

struct QuarantineFileDeleter {
void operator()(qtn_file_t file)
{
qtn_file_free(file);
}
};

#endif // PLATFORM(MAC)
@@ -55,7 +55,6 @@ Download::Download(DownloadManager& downloadManager, DownloadID downloadID, Netw
, m_client(downloadManager.client())
, m_download(&download)
, m_sessionID(session.sessionID())
, m_suggestedName(suggestedName)
, m_testSpeedMultiplier(session.testSpeedMultiplier())
{
ASSERT(m_downloadID);
@@ -70,7 +69,6 @@ Download::Download(DownloadManager& downloadManager, DownloadID downloadID, NSUR
, m_client(downloadManager.client())
, m_downloadTask(download)
, m_sessionID(session.sessionID())
, m_suggestedName(suggestedName)
, m_testSpeedMultiplier(session.testSpeedMultiplier())
{
ASSERT(m_downloadID);
@@ -81,7 +81,6 @@ class Download : public IPC::MessageSender, public CanMakeWeakPtr<Download> {

DownloadID downloadID() const { return m_downloadID; }
PAL::SessionID sessionID() const { return m_sessionID; }
const String& suggestedName() const { return m_suggestedName; }

void setSandboxExtension(RefPtr<SandboxExtension>&& sandboxExtension) { m_sandboxExtension = WTFMove(sandboxExtension); }
void didReceiveChallenge(const WebCore::AuthenticationChallenge&, ChallengeCompletionHandler&&);
@@ -117,7 +116,6 @@ class Download : public IPC::MessageSender, public CanMakeWeakPtr<Download> {
RetainPtr<NSProgress> m_progress;
#endif
PAL::SessionID m_sessionID;
String m_suggestedName;
bool m_hasReceivedData { false };
IgnoreDidFailCallback m_ignoreDidFailCallback { IgnoreDidFailCallback::No };
DownloadMonitor m_monitor { *this };
@@ -30,7 +30,6 @@

#import "ApplicationServicesSPI.h"
#import "CodeSigning.h"
#import "QuarantineSPI.h"
#import "SandboxInitializationParameters.h"
#import "SandboxUtilities.h"
#import "WKFoundation.h"
@@ -44,6 +43,7 @@
#import <pal/spi/cocoa/CoreServicesSPI.h>
#import <pal/spi/cocoa/LaunchServicesSPI.h>
#import <pal/spi/cocoa/NotifySPI.h>
#import <pal/spi/mac/QuarantineSPI.h>
#import <pwd.h>
#import <stdlib.h>
#import <sys/sysctl.h>
@@ -28,11 +28,11 @@

#if PLATFORM(COCOA) && !PLATFORM(WATCHOS) && !PLATFORM(APPLETV)

#import "QuarantineSPI.h"
#import "WKWebViewInternal.h"
#import "WebPageProxy.h"
#import <WebCore/RuntimeApplicationChecks.h>
#import <WebCore/ShareData.h>
#import <pal/spi/mac/QuarantineSPI.h>
#import <wtf/RetainPtr.h>
#import <wtf/Scope.h>
#import <wtf/UUID.h>
@@ -38,7 +38,6 @@
#import "ModalContainerControlClassifier.h"
#import "PageClient.h"
#import "PlaybackSessionManagerProxy.h"
#import "QuarantineSPI.h"
#import "QuickLookThumbnailLoader.h"
#import "SafeBrowsingSPI.h"
#import "SafeBrowsingWarning.h"
@@ -66,6 +65,7 @@
#import <WebCore/TextAlternativeWithRange.h>
#import <WebCore/ValidationBubble.h>
#import <pal/spi/cocoa/QuartzCoreSPI.h>
#import <pal/spi/mac/QuarantineSPI.h>
#import <wtf/BlockPtr.h>
#import <wtf/SoftLinking.h>
#import <wtf/cf/TypeCastsCF.h>
@@ -45,6 +45,10 @@
#include <wtf/text/CString.h>
#include <wtf/text/WTFString.h>

#if PLATFORM(MAC)
#include <pal/spi/mac/QuarantineSPI.h>
#endif

namespace WebKit {
using namespace WebCore;

@@ -180,8 +184,36 @@ void DownloadProxy::didCreateDestination(const String& path)
m_client->didCreateDestination(*this, path);
}

#if PLATFORM(MAC)
void DownloadProxy::updateQuarantinePropertiesIfPossible()
{
auto fileURL = URL::fileURLWithFileSystemPath(m_destinationFilename);
auto path = fileURL.fileSystemPath().utf8();

auto file = std::unique_ptr<_qtn_file, QuarantineFileDeleter>(qtn_file_alloc());
if (!file)
return;

auto error = qtn_file_init_with_path(file.get(), path.data());
if (error)
return;

uint32_t flags = qtn_file_get_flags(file.get());
ASSERT_WITH_MESSAGE(flags & QTN_FLAG_HARD, "Downloaded files written by the sandboxed network process should have QTN_FLAG_HARD");
flags &= ~QTN_FLAG_HARD;
error = qtn_file_set_flags(file.get(), flags);
if (error)
return;

qtn_file_apply_to_path(file.get(), path.data());
}
#endif

void DownloadProxy::didFinish()
{
#if PLATFORM(MAC)
updateQuarantinePropertiesIfPossible();
#endif
m_client->didFinish(*this);

// This can cause the DownloadProxy object to be deleted.
@@ -104,7 +104,9 @@ class DownloadProxy : public API::ObjectImpl<API::Object::Type::Download>, publi
void setProgress(NSProgress *progress) { m_progress = progress; }
NSProgress *progress() const { return m_progress.get(); }
#endif

#if PLATFORM(MAC)
void updateQuarantinePropertiesIfPossible();
#endif
API::FrameInfo& frameInfo() { return m_frameInfo.get(); }

API::DownloadClient& client() { return m_client.get(); }
@@ -1601,7 +1601,6 @@
A1B4DCE125A7923C007D178C /* MediaSampleByteRange.h in Headers */ = {isa = PBXBuildFile; fileRef = A1B4DCDF25A79211007D178C /* MediaSampleByteRange.h */; };
A1C512C9190656E500448914 /* WebPreviewLoaderClient.h in Headers */ = {isa = PBXBuildFile; fileRef = A1C512C7190656E500448914 /* WebPreviewLoaderClient.h */; };
A1DF631318E0B7C8003A3E2A /* LegacyDownloadClient.h in Headers */ = {isa = PBXBuildFile; fileRef = A1DF631118E0B7C8003A3E2A /* LegacyDownloadClient.h */; };
A1E688701F6E2BAB007006A6 /* QuarantineSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E6886F1F6E2BAB007006A6 /* QuarantineSPI.h */; };
A1EA02381DABFF7E0096021F /* WKContextMenuListener.h in Headers */ = {isa = PBXBuildFile; fileRef = A1EA02361DABFF7E0096021F /* WKContextMenuListener.h */; settings = {ATTRIBUTES = (Private, ); }; };
A1EA02401DAC31DB0096021F /* WebContextMenuListenerProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = A1EA023E1DAC31DB0096021F /* WebContextMenuListenerProxy.h */; };
A1EF36C02581F76F0090B02A /* MediaFormatReader.bundle in Copy Plug-ins */ = {isa = PBXBuildFile; fileRef = A16E66002581930800EE1749 /* MediaFormatReader.bundle */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
@@ -5950,7 +5949,6 @@
A1C512C7190656E500448914 /* WebPreviewLoaderClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebPreviewLoaderClient.h; path = ios/WebPreviewLoaderClient.h; sourceTree = "<group>"; };
A1DF631018E0B7C8003A3E2A /* LegacyDownloadClient.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LegacyDownloadClient.mm; sourceTree = "<group>"; };
A1DF631118E0B7C8003A3E2A /* LegacyDownloadClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LegacyDownloadClient.h; sourceTree = "<group>"; };
A1E6886F1F6E2BAB007006A6 /* QuarantineSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QuarantineSPI.h; sourceTree = "<group>"; };
A1EA02351DABFF7E0096021F /* WKContextMenuListener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WKContextMenuListener.cpp; sourceTree = "<group>"; };
A1EA02361DABFF7E0096021F /* WKContextMenuListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKContextMenuListener.h; sourceTree = "<group>"; };
A1EA023D1DAC31DB0096021F /* WebContextMenuListenerProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebContextMenuListenerProxy.cpp; sourceTree = "<group>"; };
@@ -11287,7 +11285,6 @@
children = (
F48D2A8421583A0200C6752B /* AppKitSPI.h */,
29D04E2821F7C73D0076741D /* ApplicationServicesSPI.h */,
A1E6886F1F6E2BAB007006A6 /* QuarantineSPI.h */,
);
path = mac;
sourceTree = "<group>";
@@ -14366,7 +14363,6 @@
517B5F95275EBA63002DC22D /* PushMessageForTesting.h in Headers */,
EB36B16827A7B4500050E00D /* PushService.h in Headers */,
EBA8D3B527A5E33F00CB7900 /* PushServiceConnection.h in Headers */,
A1E688701F6E2BAB007006A6 /* QuarantineSPI.h in Headers */,
411B89CC27B2B89800F9EBD3 /* QueryPermissionResultCallback.h in Headers */,
1A0C227E2451130A00ED614D /* QuickLookThumbnailingSoftLink.h in Headers */,
1AEE57252409F142002005D6 /* QuickLookThumbnailLoader.h in Headers */,
@@ -60,6 +60,10 @@
#import <wtf/WeakObjCPtr.h>
#import <wtf/text/WTFString.h>

#if PLATFORM(MAC)
#include <pal/spi/mac/QuarantineSPI.h>
#endif

static unsigned redirectCount = 0;
static bool hasReceivedResponse;
static NSURL *sourceURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
@@ -1259,7 +1263,7 @@ static void checkFileContents(NSURL *file, const String& expectedContents)

static NSURL *tempFileThatDoesNotExist()
{
NSURL *tempDir = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"DownloadTest"] isDirectory:YES];
NSURL *tempDir = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"DownloadTëst"] isDirectory:YES];
[[NSFileManager defaultManager] createDirectoryAtURL:tempDir withIntermediateDirectories:YES attributes:nil error:nil];
NSURL *file = [tempDir URLByAppendingPathComponent:@"example.txt"];
[[NSFileManager defaultManager] removeItemAtURL:file error:nil];
@@ -1448,6 +1452,28 @@ static void checkCallbackRecord(TestDownloadDelegate *delegate, Vector<DownloadC
EXPECT_EQ(actualCallbacks[i], expectedCallbacks[i]);
}

#if PLATFORM(MAC)
static void expectHardQuarantine(NSURL *url, bool expected)
{
auto file = std::unique_ptr<_qtn_file, QuarantineFileDeleter>(qtn_file_alloc());
if (!file) {
ASSERT_NOT_REACHED();
return;
}

auto error = qtn_file_init_with_path(file.get(), url.fileSystemRepresentation);
if (error) {
ASSERT_NOT_REACHED();
return;
}

uint32_t flags = qtn_file_get_flags(file.get());
EXPECT_EQ(!!(flags & QTN_FLAG_HARD), expected);
}
#else
static void expectHardQuarantine(NSURL *, bool) { }
#endif

TEST(WKDownload, FinishSuccessfully)
{
auto server = simpleDownloadTestServer();
@@ -1469,6 +1495,7 @@ static void checkCallbackRecord(TestDownloadDelegate *delegate, Vector<DownloadC
[delegate waitForDownloadDidFinish];

checkFileContents(expectedDownloadFile, longString<5000>('a'));
expectHardQuarantine(expectedDownloadFile, false);

checkCallbackRecord(delegate.get(), {
DownloadCallback::NavigationAction,
@@ -1570,6 +1597,7 @@ static void waitForFirst5k(RetainPtr<WKDownload>& download)

[webView loadRequest:server.request()];
waitForFirst5k(retainedDownload);
expectHardQuarantine(expectedDownloadFile, true);

__block RetainPtr<NSData> retainedResumeData;
[retainedDownload cancel:^(NSData *resumeData) {
@@ -1579,6 +1607,7 @@ static void waitForFirst5k(RetainPtr<WKDownload>& download)
while (!retainedResumeData)
Util::spinRunLoop();
resumeAndFinishDownload(retainedResumeData.get(), expectedDownloadFile);
expectHardQuarantine(expectedDownloadFile, false);
checkCallbackRecord(delegate.get(), {
DownloadCallback::NavigationAction,
DownloadCallback::NavigationResponse,

0 comments on commit 12bc4cb

Please sign in to comment.