From 4d2986ea595d7bd29c1253c33d2171bbd7665ece Mon Sep 17 00:00:00 2001 From: attila Date: Wed, 22 May 2024 15:28:29 +0200 Subject: [PATCH] Add new WebBrowserComponent option for file access control on Apple platforms If allowAccessToEnclosingDirectory is set to true, it is now possible for the WkWebView implementation to access sibling files relative to a file specified with the file:// scheme. This allows an iOS app to load an HTML file in the documents directory, and that HTML file can reference and load image files inside the HTML file's parent directory. --- .../misc/juce_WebBrowserComponent.h | 32 +++++++++++++++++++ .../native/juce_WebBrowserComponent_mac.mm | 24 ++++++++++++-- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h b/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h index 7f5b836ff1..2d9b18b437 100644 --- a/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h +++ b/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h @@ -212,6 +212,29 @@ public: Colour backgroundColour; }; + /** Options specific to the WkWebView backend used on Apple systems. These options will be + ignored on non-Apple platforms. + */ + class AppleWkWebView + { + public: + /** Specifies whether the WebView is allowed to access siblings of files specified with + the file:// URL scheme. + + Allowing this is a potential security vulnerability if you don't have full control + over the file that you are opening. + */ + [[nodiscard]] AppleWkWebView withAllowAccessToEnclosingDirectory (bool x) const + { + return withMember (*this, &AppleWkWebView::allowAccessToEnclosingDirectory, x); + } + + auto getAllowAccessToEnclosingDirectory() const { return allowAccessToEnclosingDirectory; } + + private: + bool allowAccessToEnclosingDirectory = false; + }; + /** Specifies options that apply to the Windows implementation when the WebView2 feature is enabled. @@ -222,6 +245,13 @@ public: return withMember (*this, &Options::winWebView2, winWebView2Options); } + /** Specifies options that influence the WebBrowserComponent's behaviour on Apple systems. + */ + [[nodiscard]] Options withAppleWkWebViewOptions (const AppleWkWebView& appleWkWebViewOptions) const + { + return withMember (*this, &Options::appleWkWebView, appleWkWebViewOptions); + } + /** Enables native integration features for the code running inside the WebBrowserComponent. This injects data and function objects under `window.__JUCE__.backend` through which @@ -342,6 +372,7 @@ public: auto keepsPageLoadedWhenBrowserIsHidden() const noexcept { return keepPageLoadedWhenBrowserIsHidden; } auto getUserAgent() const { return userAgent; } auto getWinWebView2BackendOptions() const { return winWebView2; } + auto getAppleWkWebViewOptions() const { return appleWkWebView; } auto getNativeIntegrationsEnabled() const { return enableNativeIntegration; } const auto& getNativeFunctions() const { return nativeFunctions; } const auto& getEventListeners() const { return eventListeners; } @@ -357,6 +388,7 @@ public: bool enableNativeIntegration = false; String userAgent; WinWebView2 winWebView2; + AppleWkWebView appleWkWebView; std::map nativeFunctions; std::vector> eventListeners; StringArray userScripts; diff --git a/modules/juce_gui_extra/native/juce_WebBrowserComponent_mac.mm b/modules/juce_gui_extra/native/juce_WebBrowserComponent_mac.mm index 7c4cad144b..c91417a683 100644 --- a/modules/juce_gui_extra/native/juce_WebBrowserComponent_mac.mm +++ b/modules/juce_gui_extra/native/juce_WebBrowserComponent_mac.mm @@ -818,7 +818,9 @@ public: delegateConnector (implIn.owner, [this] (const auto& m) { owner.handleNativeEvent (m); }, [this] (const auto& r) { return owner.handleResourceRequest (r); }, - browserOptions) + browserOptions), + allowAccessToEnclosingDirectory (browserOptions.getAppleWkWebViewOptions() + .getAllowAccessToEnclosingDirectory()) { ObjCObjectHandle config { [WKWebViewConfiguration new] }; id preferences = [config.get() preferences]; @@ -987,8 +989,23 @@ public: { auto file = URL (url).getLocalFile(); - if (NSURL* nsUrl = [NSURL fileURLWithPath: juceStringToNS (file.getFullPathName())]) - [webView.get() loadFileURL: appendParametersToFileURL (url, nsUrl) allowingReadAccessToURL: nsUrl]; + NSURL* nsUrl = [NSURL fileURLWithPath: juceStringToNS (file.getFullPathName())]; + + auto* accessPath = [&] + { + if (! allowAccessToEnclosingDirectory) + return nsUrl; + + auto* parentUrl = [NSURL fileURLWithPath: juceStringToNS (file.getParentDirectory().getFullPathName())]; + + if (parentUrl == nullptr) + return nsUrl; + + return parentUrl; + }(); + + if (nsUrl != nullptr) + [webView.get() loadFileURL: appendParametersToFileURL (url, nsUrl) allowingReadAccessToURL: accessPath]; } else if (NSMutableURLRequest* request = getRequestForURL (url, headers, postData)) { @@ -1061,6 +1078,7 @@ public: private: WebBrowserComponent::Impl& owner; DelegateConnector delegateConnector; + bool allowAccessToEnclosingDirectory = false; LastFocusChange lastFocusChange; ObjCObjectHandle webView; ObjCObjectHandle webViewDelegate;