Prevent scheme handlers from handling all built-in URL schemes.
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 2 Jun 2017 23:07:08 +0000 (23:07 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 2 Jun 2017 23:07:08 +0000 (23:07 +0000)
<rdar://problem/32404790> and https://bugs.webkit.org/show_bug.cgi?id=172869

Reviewed by Andy Estes.

Source/WebCore:

Covered by API test.

This patch refactors SchemeRegistry to keep a base, constant set of each of the special
URL schemes that WebKit knows about by default.

It then exposes that list through a new method to support WK2 API.

* platform/SchemeRegistry.cpp:
(WebCore::allBuiltinSchemes):
(WebCore::builtinLocalURLSchemes):
(WebCore::localURLSchemes):
(WebCore::builtinSecureSchemes):
(WebCore::secureSchemes):
(WebCore::builtinSchemesWithUniqueOrigins):
(WebCore::schemesWithUniqueOrigins):
(WebCore::builtinEmptyDocumentSchemes):
(WebCore::emptyDocumentSchemes):
(WebCore::builtinCanDisplayOnlyIfCanRequestSchemes):
(WebCore::canDisplayOnlyIfCanRequestSchemes):
(WebCore::SchemeRegistry::removeURLSchemeRegisteredAsLocal):
(WebCore::builtinCORSEnabledSchemes):
(WebCore::CORSEnabledSchemes):
(WebCore::SchemeRegistry::isBuiltinScheme):
* platform/SchemeRegistry.h:

Source/WebKit2:

* UIProcess/API/Cocoa/WKWebView.mm:
(+[WKWebView handlesURLScheme:]): Check against WebCore's new master list of URL schemes.

Tools:

* TestWebKitAPI/Tests/WebKit2Cocoa/WKURLSchemeHandler-1.mm:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@217738 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/platform/SchemeRegistry.cpp
Source/WebCore/platform/SchemeRegistry.h
Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebKit2Cocoa/WKURLSchemeHandler-1.mm

index 5b78952..3a6a02d 100644 (file)
@@ -1,3 +1,35 @@
+2017-06-02  Brady Eidson  <beidson@apple.com>
+
+        Prevent scheme handlers from handling all built-in URL schemes.
+        <rdar://problem/32404790> and https://bugs.webkit.org/show_bug.cgi?id=172869
+
+        Reviewed by Andy Estes.
+
+        Covered by API test.
+
+        This patch refactors SchemeRegistry to keep a base, constant set of each of the special
+        URL schemes that WebKit knows about by default.
+        
+        It then exposes that list through a new method to support WK2 API.
+
+        * platform/SchemeRegistry.cpp:
+        (WebCore::allBuiltinSchemes):
+        (WebCore::builtinLocalURLSchemes):
+        (WebCore::localURLSchemes):
+        (WebCore::builtinSecureSchemes):
+        (WebCore::secureSchemes):
+        (WebCore::builtinSchemesWithUniqueOrigins):
+        (WebCore::schemesWithUniqueOrigins):
+        (WebCore::builtinEmptyDocumentSchemes):
+        (WebCore::emptyDocumentSchemes):
+        (WebCore::builtinCanDisplayOnlyIfCanRequestSchemes):
+        (WebCore::canDisplayOnlyIfCanRequestSchemes):
+        (WebCore::SchemeRegistry::removeURLSchemeRegisteredAsLocal):
+        (WebCore::builtinCORSEnabledSchemes):
+        (WebCore::CORSEnabledSchemes):
+        (WebCore::SchemeRegistry::isBuiltinScheme):
+        * platform/SchemeRegistry.h:
+
 2017-06-02  Simon Fraser  <simon.fraser@apple.com>
 
         All scroll peformance logging should happen in the UI process
index 64e3563..6076158 100644 (file)
  */
 #include "config.h"
 #include "SchemeRegistry.h"
+
+#include "URLParser.h"
 #include <wtf/MainThread.h>
 #include <wtf/NeverDestroyed.h>
 
+#if ENABLE(CONTENT_FILTERING)
+#include "ContentFilter.h"
+#endif
+#if USE(QUICK_LOOK)
+#include "QuickLook.h"
+#endif
+
 namespace WebCore {
 
+static const URLSchemesMap& builtinLocalURLSchemes();
+static const Vector<String>& builtinSecureSchemes();
+static const Vector<String>& builtinSchemesWithUniqueOrigins();
+static const Vector<String>& builtinEmptyDocumentSchemes();
+static const Vector<String>& builtinCanDisplayOnlyIfCanRequestSchemes();
+static const Vector<String>& builtinCORSEnabledSchemes();
+
+static const URLSchemesMap& allBuiltinSchemes()
+{
+    static NeverDestroyed<URLSchemesMap> schemes;
+    if (schemes.get().isEmpty()) {
+        for (const auto& scheme : builtinLocalURLSchemes())
+            schemes.get().add(scheme);
+        for (const auto& scheme : builtinSecureSchemes())
+            schemes.get().add(scheme);
+        for (const auto& scheme : builtinSchemesWithUniqueOrigins())
+            schemes.get().add(scheme);
+        for (const auto& scheme : builtinEmptyDocumentSchemes())
+            schemes.get().add(scheme);
+        for (const auto& scheme : builtinCanDisplayOnlyIfCanRequestSchemes())
+            schemes.get().add(scheme);
+        for (const auto& scheme : builtinCORSEnabledSchemes())
+            schemes.get().add(scheme);
+
+        // Other misc schemes that the SchemeRegistry doesn't know about.
+        schemes.get().add("webkit-fake-url");
+#if PLATFORM(MAC)
+        schemes.get().add("safari-extension");
+#endif
+#if USE(QUICK_LOOK)
+        schemes.get().add(QLPreviewProtocol());
+#endif
+#if ENABLE(CONTENT_FILTERING)
+        schemes.get().add(ContentFilter::urlScheme());
+#endif
+    }
+
+    return schemes;
+}
+
+const URLSchemesMap& builtinLocalURLSchemes()
+{
+    static NeverDestroyed<URLSchemesMap> schemes;
+    if (schemes.get().isEmpty()) {
+        schemes.get().add("file");
+#if PLATFORM(COCOA)
+        schemes.get().add("applewebdata");
+#endif
+    }
+
+    return schemes;
+}
+
 static URLSchemesMap& localURLSchemes()
 {
     static NeverDestroyed<URLSchemesMap> localSchemes;
 
     if (localSchemes.get().isEmpty()) {
-        localSchemes.get().add("file");
-#if PLATFORM(COCOA)
-        localSchemes.get().add("applewebdata");
-#endif
+        for (const auto& scheme : builtinLocalURLSchemes())
+            localSchemes.get().add(scheme);
     }
 
     return localSchemes;
@@ -50,44 +110,81 @@ static URLSchemesMap& displayIsolatedURLSchemes()
     return displayIsolatedSchemes;
 }
 
+const Vector<String>& builtinSecureSchemes()
+{
+    static NeverDestroyed<Vector<String>> schemes;
+    if (schemes.get().isEmpty()) {
+        schemes.get().append("https");
+        schemes.get().append("about");
+        schemes.get().append("data");
+        schemes.get().append("wss");
+#if PLATFORM(GTK) || PLATFORM(WPE)
+        schemes.get().append("resource");
+#endif
+        schemes.get().shrinkToFit();
+    }
+
+    return schemes;
+}
+
 static URLSchemesMap& secureSchemes()
 {
     static NeverDestroyed<URLSchemesMap> secureSchemes;
 
     if (secureSchemes.get().isEmpty()) {
-        secureSchemes.get().add("https");
-        secureSchemes.get().add("about");
-        secureSchemes.get().add("data");
-        secureSchemes.get().add("wss");
-#if PLATFORM(GTK) || PLATFORM(WPE)
-        secureSchemes.get().add("resource");
-#endif
+        for (const auto& scheme : builtinSecureSchemes())
+            secureSchemes.get().add(scheme);
     }
 
     return secureSchemes;
 }
 
+const Vector<String>& builtinSchemesWithUniqueOrigins()
+{
+    static NeverDestroyed<Vector<String>> schemes;
+    if (schemes.get().isEmpty()) {
+        schemes.get().append("about");
+        schemes.get().append("javascript");
+        // This is a willful violation of HTML5.
+        // See https://bugs.webkit.org/show_bug.cgi?id=11885
+        schemes.get().append("data");
+        schemes.get().shrinkToFit();
+    }
+
+    return schemes;
+}
+
 static URLSchemesMap& schemesWithUniqueOrigins()
 {
     static NeverDestroyed<URLSchemesMap> schemesWithUniqueOrigins;
 
     if (schemesWithUniqueOrigins.get().isEmpty()) {
-        schemesWithUniqueOrigins.get().add("about");
-        schemesWithUniqueOrigins.get().add("javascript");
-        // This is a willful violation of HTML5.
-        // See https://bugs.webkit.org/show_bug.cgi?id=11885
-        schemesWithUniqueOrigins.get().add("data");
+        for (const auto& scheme : builtinSchemesWithUniqueOrigins())
+            schemesWithUniqueOrigins.get().add(scheme);
     }
 
     return schemesWithUniqueOrigins;
 }
 
+const Vector<String>& builtinEmptyDocumentSchemes()
+{
+    static NeverDestroyed<Vector<String>> schemes;
+    if (schemes.get().isEmpty()) {
+        schemes.get().append("about");
+        schemes.get().shrinkToFit();
+    }
+
+    return schemes;
+}
+
 static URLSchemesMap& emptyDocumentSchemes()
 {
     static NeverDestroyed<URLSchemesMap> emptyDocumentSchemes;
 
-    if (emptyDocumentSchemes.get().isEmpty())
-        emptyDocumentSchemes.get().add("about");
+    if (emptyDocumentSchemes.get().isEmpty()) {
+        for (const auto& scheme : builtinEmptyDocumentSchemes())
+            emptyDocumentSchemes.get().add(scheme);
+    }
 
     return emptyDocumentSchemes;
 }
@@ -98,12 +195,24 @@ static HashSet<String, ASCIICaseInsensitiveHash>& schemesForbiddenFromDomainRela
     return schemes;
 }
 
+const Vector<String>& builtinCanDisplayOnlyIfCanRequestSchemes()
+{
+    static NeverDestroyed<Vector<String>> schemes;
+    if (schemes.get().isEmpty()) {
+        schemes.get().append("blob");
+        schemes.get().shrinkToFit();
+    }
+
+    return schemes;
+}
+
 static URLSchemesMap& canDisplayOnlyIfCanRequestSchemes()
 {
     static NeverDestroyed<URLSchemesMap> canDisplayOnlyIfCanRequestSchemes;
 
     if (canDisplayOnlyIfCanRequestSchemes.get().isEmpty()) {
-        canDisplayOnlyIfCanRequestSchemes.get().add("blob");
+        for (const auto& scheme : builtinCanDisplayOnlyIfCanRequestSchemes())
+            canDisplayOnlyIfCanRequestSchemes.get().add(scheme);
     }
 
     return canDisplayOnlyIfCanRequestSchemes;
@@ -122,12 +231,9 @@ void SchemeRegistry::registerURLSchemeAsLocal(const String& scheme)
 
 void SchemeRegistry::removeURLSchemeRegisteredAsLocal(const String& scheme)
 {
-    if (equalLettersIgnoringASCIICase(scheme, "file"))
+    if (builtinLocalURLSchemes().contains(scheme))
         return;
-#if PLATFORM(COCOA)
-    if (equalLettersIgnoringASCIICase(scheme, "applewebdata"))
-        return;
-#endif
+
     localURLSchemes().remove(scheme);
 }
 
@@ -148,14 +254,26 @@ static URLSchemesMap& schemesAllowingDatabaseAccessInPrivateBrowsing()
     return schemesAllowingDatabaseAccessInPrivateBrowsing;
 }
 
+const Vector<String>& builtinCORSEnabledSchemes()
+{
+    static NeverDestroyed<Vector<String>> schemes;
+    if (schemes.get().isEmpty()) {
+        schemes.get().append("http");
+        schemes.get().append("https");
+        schemes.get().shrinkToFit();
+    }
+
+    return schemes;
+}
+
 static URLSchemesMap& CORSEnabledSchemes()
 {
     // FIXME: http://bugs.webkit.org/show_bug.cgi?id=77160
     static NeverDestroyed<URLSchemesMap> CORSEnabledSchemes;
 
     if (CORSEnabledSchemes.get().isEmpty()) {
-        CORSEnabledSchemes.get().add("http");
-        CORSEnabledSchemes.get().add("https");
+        for (const auto& scheme : builtinCORSEnabledSchemes())
+            CORSEnabledSchemes.get().add(scheme);
     }
 
     return CORSEnabledSchemes;
@@ -363,4 +481,9 @@ bool SchemeRegistry::isUserExtensionScheme(const String& scheme)
     return false;
 }
 
+bool SchemeRegistry::isBuiltinScheme(const String& scheme)
+{
+    return allBuiltinSchemes().contains(scheme) || URLParser::isSpecialScheme(scheme);
+}
+
 } // namespace WebCore
index cd6f4be..9c0a60a 100644 (file)
@@ -42,7 +42,8 @@ public:
     static const URLSchemesMap& localSchemes();
 
     WEBCORE_EXPORT static bool shouldTreatURLSchemeAsLocal(const String&);
-
+    WEBCORE_EXPORT static bool isBuiltinScheme(const String&);
+    
     // Secure schemes do not trigger mixed content warnings. For example,
     // https and data are secure schemes because they cannot be corrupted by
     // active network attackers.
index 251b13d..dedc997 100644 (file)
@@ -1,3 +1,13 @@
+2017-06-02  Brady Eidson  <beidson@apple.com>
+
+        Prevent scheme handlers from handling all built-in URL schemes.
+        <rdar://problem/32404790> and https://bugs.webkit.org/show_bug.cgi?id=172869
+
+        Reviewed by Andy Estes.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (+[WKWebView handlesURLScheme:]): Check against WebCore's new master list of URL schemes.
+
 2017-06-02  Simon Fraser  <simon.fraser@apple.com>
 
         All scroll peformance logging should happen in the UI process
index afa512d..1923af4 100644 (file)
@@ -99,9 +99,9 @@
 #import <WebCore/PlatformScreen.h>
 #import <WebCore/RuntimeApplicationChecks.h>
 #import <WebCore/SQLiteDatabaseTracker.h>
+#import <WebCore/SchemeRegistry.h>
 #import <WebCore/Settings.h>
 #import <WebCore/TextStream.h>
-#import <WebCore/URLParser.h>
 #import <WebCore/ValidationBubble.h>
 #import <WebCore/ViewportArguments.h>
 #import <WebCore/WritingMode.h>
@@ -3644,7 +3644,7 @@ WEBCORE_COMMAND(yankAndSelect)
 
 + (BOOL)handlesURLScheme:(NSString *)urlScheme
 {
-    return WebCore::URLParser::isSpecialScheme(urlScheme);
+    return WebCore::SchemeRegistry::isBuiltinScheme(urlScheme);
 }
 
 @end
index 445bd76..3a7b034 100644 (file)
@@ -1,3 +1,12 @@
+2017-06-02  Brady Eidson  <beidson@apple.com>
+
+        Prevent scheme handlers from handling all built-in URL schemes.
+        <rdar://problem/32404790> and https://bugs.webkit.org/show_bug.cgi?id=172869
+
+        Reviewed by Andy Estes.
+
+        * TestWebKitAPI/Tests/WebKit2Cocoa/WKURLSchemeHandler-1.mm:
+
 2017-06-02  Stephan Szabo  <stephan.szabo@am.sony.com>
 
         [JSCOnly] Build static jsc.exe on Windows
index 2f9ed3e..b832736 100644 (file)
@@ -147,5 +147,49 @@ TEST(URLSchemeHandler, NoMIMEType)
     EXPECT_TRUE([[handler.get().stoppedURLs objectAtIndex:0] isEqual:[NSURL URLWithString:@"testing:main"]]);
 }
 
+static NSString *schemes[] = {
+    @"about",
+    @"applewebdata",
+    @"blob",
+    @"data",
+    @"file",
+    @"ftp",
+    @"gopher",
+    @"http",
+    @"https",
+    @"javascript",
+    @"webkit-fake-url",
+    @"ws",
+    @"wss",
+#if PLATFORM(MAC)
+    @"safari-extension",
+#endif
+#if ENABLE(CONTENT_FILTERING)
+    @"x-apple-content-filter",
+#endif
+#if USE(QUICK_LOOK)
+    @"x-apple-ql-id",
+#endif
+};
+
+TEST(URLSchemeHandler, BuiltinSchemes)
+{
+    RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    RetainPtr<SchemeHandler> handler = adoptNS([[SchemeHandler alloc] initWithData:nil mimeType:nil]);
+
+    for (NSString *scheme : schemes) {
+        EXPECT_TRUE([WKWebView handlesURLScheme:scheme]);
+
+        bool exceptionRaised = false;
+        @try {
+            [configuration setURLSchemeHandler:handler.get() forURLScheme:scheme];
+        } @catch (NSException *exception) {
+            EXPECT_WK_STREQ(NSInvalidArgumentException, exception.name);
+            exceptionRaised = true;
+        }
+        EXPECT_TRUE(exceptionRaised);
+    }
+}
+
 
 #endif