WKContentRuleLists should block requests from service workers
authorachristensen@apple.com <achristensen@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 Oct 2019 22:39:31 +0000 (22:39 +0000)
committerachristensen@apple.com <achristensen@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 Oct 2019 22:39:31 +0000 (22:39 +0000)
https://bugs.webkit.org/show_bug.cgi?id=201980
<rdar://problem/55516735>

Reviewed by Chris Dumez.

Source/WebKit:

Test: http/tests/contentextensions/service-worker.https.html

Also covered by an API test.

* Shared/ServiceWorkerInitializationData.cpp: Added.
(WebKit::ServiceWorkerInitializationData::encode const):
(WebKit::ServiceWorkerInitializationData::decode):
* Shared/ServiceWorkerInitializationData.h: Added.
* Sources.txt:
* UIProcess/UserContent/WebUserContentControllerProxy.cpp:
(WebKit::WebUserContentControllerProxy::addProcess):
(WebKit::WebUserContentControllerProxy::contentRuleListData):
* UIProcess/UserContent/WebUserContentControllerProxy.h:
* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::establishWorkerContextConnectionToNetworkProcess):
(WebKit::WebProcessPool::createWebPage):
* UIProcess/WebProcessPool.h:
* UIProcess/WebProcessProxy.cpp:
(WebKit::WebProcessProxy::createForServiceWorkers):
(WebKit::WebProcessProxy::establishServiceWorkerContext):
(WebKit::contentRuleListsFromIdentifier):
(WebKit::WebProcessProxy::enableServiceWorkers):
* UIProcess/WebProcessProxy.h:
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/Storage/WebSWContextManagerConnection.cpp:
(WebKit::WebSWContextManagerConnection::WebSWContextManagerConnection):
(WebKit::m_userAgent):
(WebKit::WebSWContextManagerConnection::installServiceWorker):
* WebProcess/Storage/WebSWContextManagerConnection.h:
* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::establishWorkerContextConnectionToNetworkProcess):
* WebProcess/WebProcess.h:
* WebProcess/WebProcess.messages.in:

Tools:

NSString initWithContentsOfURL doesn't work with https URLs with certificates without a trusted root,
so I use an ephemeral NSURLSession instead so I can tell it to accept any connection, even our WebKit httpd server.
I also added an API test.

* TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm:
* WebKitTestRunner/mac/TestControllerMac.mm:
(-[WKTRSessionDelegate URLSession:task:didReceiveChallenge:completionHandler:]):
(WTR::TestController::configureContentExtensionForTest):

LayoutTests:

* http/tests/contentextensions/resources/fetch-worker.js: Added.
(event.fetch.string_appeared_here.then):
(event.catch):
* http/tests/contentextensions/resources/serviceworkertest.js: Added.
(testServiceWorker):
(test):
* http/tests/contentextensions/service-worker.https-expected.txt: Added.
* http/tests/contentextensions/service-worker.https.html: Added.
* http/tests/contentextensions/service-worker.https.html.json: Added.

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

25 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/contentextensions/resources/fetch-worker.js [new file with mode: 0644]
LayoutTests/http/tests/contentextensions/resources/serviceworkertest.js [new file with mode: 0644]
LayoutTests/http/tests/contentextensions/service-worker.https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/contentextensions/service-worker.https.html [new file with mode: 0644]
LayoutTests/http/tests/contentextensions/service-worker.https.html.json [new file with mode: 0644]
Source/WebKit/ChangeLog
Source/WebKit/Shared/ServiceWorkerInitializationData.cpp [new file with mode: 0644]
Source/WebKit/Shared/ServiceWorkerInitializationData.h [new file with mode: 0644]
Source/WebKit/Sources.txt
Source/WebKit/UIProcess/UserContent/WebUserContentControllerProxy.cpp
Source/WebKit/UIProcess/UserContent/WebUserContentControllerProxy.h
Source/WebKit/UIProcess/WebProcessPool.cpp
Source/WebKit/UIProcess/WebProcessPool.h
Source/WebKit/UIProcess/WebProcessProxy.cpp
Source/WebKit/UIProcess/WebProcessProxy.h
Source/WebKit/WebKit.xcodeproj/project.pbxproj
Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp
Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h
Source/WebKit/WebProcess/WebProcess.cpp
Source/WebKit/WebProcess/WebProcess.h
Source/WebKit/WebProcess/WebProcess.messages.in
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm
Tools/WebKitTestRunner/mac/TestControllerMac.mm

index 14a3d1e..65b75a0 100644 (file)
@@ -1,3 +1,21 @@
+2019-10-30  Alex Christensen  <achristensen@webkit.org>
+
+        WKContentRuleLists should block requests from service workers
+        https://bugs.webkit.org/show_bug.cgi?id=201980
+        <rdar://problem/55516735>
+
+        Reviewed by Chris Dumez.
+
+        * http/tests/contentextensions/resources/fetch-worker.js: Added.
+        (event.fetch.string_appeared_here.then):
+        (event.catch):
+        * http/tests/contentextensions/resources/serviceworkertest.js: Added.
+        (testServiceWorker):
+        (test):
+        * http/tests/contentextensions/service-worker.https-expected.txt: Added.
+        * http/tests/contentextensions/service-worker.https.html: Added.
+        * http/tests/contentextensions/service-worker.https.html.json: Added.
+
 2019-10-30  Daniel Bates  <dabates@apple.com>
 
         Fix misspelling test named fast/dom/simultaneouslyRegsiteredTimerFireOrder.html
diff --git a/LayoutTests/http/tests/contentextensions/resources/fetch-worker.js b/LayoutTests/http/tests/contentextensions/resources/fetch-worker.js
new file mode 100644 (file)
index 0000000..586965d
--- /dev/null
@@ -0,0 +1,11 @@
+self.addEventListener("message", (event) => {
+    fetch("/resources/dummy.js").then(() => {
+        event.source.postMessage("FAIL - should have blocked dummy.js");
+    }).catch(() => {
+        fetch("/resources/dummy.css").then(() => {
+            event.source.postMessage("PASS - blocked dummy.js, allowed dummy.css");
+        }).catch(() => {
+            event.source.postMessage("FAIL - should have allowed dummy.css");
+        });
+    });
+});
diff --git a/LayoutTests/http/tests/contentextensions/resources/serviceworkertest.js b/LayoutTests/http/tests/contentextensions/resources/serviceworkertest.js
new file mode 100644 (file)
index 0000000..def3c96
--- /dev/null
@@ -0,0 +1,44 @@
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+}
+
+function testServiceWorker() {
+    navigator.serviceWorker.addEventListener("message", function(event) {
+        alert("Message from worker: " + event.data);
+        if (window.testRunner)
+            testRunner.notifyDone();
+    });
+
+    try {
+        navigator.serviceWorker.register('resources/fetch-worker.js').then(function(reg) {
+            worker = reg.installing ? reg.installing : reg.active;
+            worker.postMessage("Hello from the web page");
+        }).catch(function(error) {
+            alert("Registration failed with: " + error);
+            if (window.testRunner)
+                testRunner.notifyDone();
+        });
+    } catch(e) {
+        alert("Exception: " + e);
+        if (window.testRunner)
+            testRunner.notifyDone();
+    }
+}
+
+function test() {
+    fetch("/resources/dummy.js").then(() => {
+        alert("FAIL - should have blocked request to dummy.js");
+        if (window.testRunner)
+            testRunner.notifyDone();
+    }).catch(() => {
+        alert("PASS - blocked request to dummy.js");
+        fetch("/resources/dummy.css").then(() => {
+            testServiceWorker();
+        }).catch(() => {
+            alert("FAIL - should have allowed request to dummy.css");
+            if (window.testRunner)
+                testRunner.notifyDone();
+        });
+    });
+}
diff --git a/LayoutTests/http/tests/contentextensions/service-worker.https-expected.txt b/LayoutTests/http/tests/contentextensions/service-worker.https-expected.txt
new file mode 100644 (file)
index 0000000..f989b79
--- /dev/null
@@ -0,0 +1,6 @@
+CONSOLE MESSAGE: line 30: Content blocker prevented frame displaying https://127.0.0.1:8443/contentextensions/service-worker.https.html from loading a resource from https://127.0.0.1:8443/resources/dummy.js
+CONSOLE MESSAGE: line 30: Resource blocked by content blocker
+CONSOLE MESSAGE: line 30: Fetch API cannot load https://127.0.0.1:8443/resources/dummy.js due to access control checks.
+ALERT: PASS - blocked request to dummy.js
+ALERT: Message from worker: PASS - blocked dummy.js, allowed dummy.css
+
diff --git a/LayoutTests/http/tests/contentextensions/service-worker.https.html b/LayoutTests/http/tests/contentextensions/service-worker.https.html
new file mode 100644 (file)
index 0000000..58a23fc
--- /dev/null
@@ -0,0 +1,4 @@
+<head>
+<script src="resources/serviceworkertest.js"></script>
+</head>
+<body onload="test()"></body>
diff --git a/LayoutTests/http/tests/contentextensions/service-worker.https.html.json b/LayoutTests/http/tests/contentextensions/service-worker.https.html.json
new file mode 100644 (file)
index 0000000..c9dd46a
--- /dev/null
@@ -0,0 +1,10 @@
+[
+    {
+        "action": {
+            "type": "block"
+        },
+        "trigger": {
+            "url-filter": "dummy.js"
+        }
+    }
+]
index ea95c10..a668d6a 100644 (file)
@@ -1,3 +1,45 @@
+2019-10-30  Alex Christensen  <achristensen@webkit.org>
+
+        WKContentRuleLists should block requests from service workers
+        https://bugs.webkit.org/show_bug.cgi?id=201980
+        <rdar://problem/55516735>
+
+        Reviewed by Chris Dumez.
+
+        Test: http/tests/contentextensions/service-worker.https.html
+
+        Also covered by an API test.
+
+        * Shared/ServiceWorkerInitializationData.cpp: Added.
+        (WebKit::ServiceWorkerInitializationData::encode const):
+        (WebKit::ServiceWorkerInitializationData::decode):
+        * Shared/ServiceWorkerInitializationData.h: Added.
+        * Sources.txt:
+        * UIProcess/UserContent/WebUserContentControllerProxy.cpp:
+        (WebKit::WebUserContentControllerProxy::addProcess):
+        (WebKit::WebUserContentControllerProxy::contentRuleListData):
+        * UIProcess/UserContent/WebUserContentControllerProxy.h:
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::establishWorkerContextConnectionToNetworkProcess):
+        (WebKit::WebProcessPool::createWebPage):
+        * UIProcess/WebProcessPool.h:
+        * UIProcess/WebProcessProxy.cpp:
+        (WebKit::WebProcessProxy::createForServiceWorkers):
+        (WebKit::WebProcessProxy::establishServiceWorkerContext):
+        (WebKit::contentRuleListsFromIdentifier):
+        (WebKit::WebProcessProxy::enableServiceWorkers):
+        * UIProcess/WebProcessProxy.h:
+        * WebKit.xcodeproj/project.pbxproj:
+        * WebProcess/Storage/WebSWContextManagerConnection.cpp:
+        (WebKit::WebSWContextManagerConnection::WebSWContextManagerConnection):
+        (WebKit::m_userAgent):
+        (WebKit::WebSWContextManagerConnection::installServiceWorker):
+        * WebProcess/Storage/WebSWContextManagerConnection.h:
+        * WebProcess/WebProcess.cpp:
+        (WebKit::WebProcess::establishWorkerContextConnectionToNetworkProcess):
+        * WebProcess/WebProcess.h:
+        * WebProcess/WebProcess.messages.in:
+
 2019-10-30  Andres Gonzalez  <andresg_22@apple.com>
 
         Create base class common to AccessibilityObject and AXIsolatedTreeNode.
diff --git a/Source/WebKit/Shared/ServiceWorkerInitializationData.cpp b/Source/WebKit/Shared/ServiceWorkerInitializationData.cpp
new file mode 100644 (file)
index 0000000..be9c045
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ServiceWorkerInitializationData.h"
+
+#include "Decoder.h"
+#include "Encoder.h"
+#include "WebCompiledContentRuleListData.h"
+
+namespace WebKit {
+
+void ServiceWorkerInitializationData::encode(IPC::Encoder& encoder) const
+{
+    encoder << userContentControllerIdentifier;
+#if ENABLE(CONTENT_EXTENSIONS)
+    encoder << contentRuleLists;
+#endif
+}
+
+Optional<ServiceWorkerInitializationData> ServiceWorkerInitializationData::decode(IPC::Decoder& decoder)
+{
+    Optional<Optional<UserContentControllerIdentifier>> userContentControllerIdentifier;
+    decoder >> userContentControllerIdentifier;
+    if (!userContentControllerIdentifier)
+        return WTF::nullopt;
+    
+#if ENABLE(CONTENT_EXTENSIONS)
+    Optional<Vector<std::pair<String, WebCompiledContentRuleListData>>> contentRuleLists;
+    decoder >> contentRuleLists;
+    if (!contentRuleLists)
+        return WTF::nullopt;
+#endif
+    
+    return {{
+        WTFMove(*userContentControllerIdentifier),
+#if ENABLE(CONTENT_EXTENSIONS)
+        WTFMove(*contentRuleLists),
+#endif
+    }};
+}
+
+}
diff --git a/Source/WebKit/Shared/ServiceWorkerInitializationData.h b/Source/WebKit/Shared/ServiceWorkerInitializationData.h
new file mode 100644 (file)
index 0000000..401ba6c
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "UserContentControllerIdentifier.h"
+#include <wtf/Forward.h>
+
+namespace IPC {
+class Decoder;
+class Encoder;
+}
+
+namespace WebKit {
+
+class WebCompiledContentRuleListData;
+
+struct ServiceWorkerInitializationData {
+
+    void encode(IPC::Encoder&) const;
+    static Optional<ServiceWorkerInitializationData> decode(IPC::Decoder&);
+
+    Optional<UserContentControllerIdentifier> userContentControllerIdentifier;
+#if ENABLE(CONTENT_EXTENSIONS)
+    Vector<std::pair<String, WebCompiledContentRuleListData>> contentRuleLists;
+#endif
+};
+
+} // namespace WebKit
index 9bd5082..edbfb56 100644 (file)
@@ -133,6 +133,7 @@ Shared/PlatformPopupMenuData.cpp
 Shared/PrintInfo.cpp
 Shared/RTCNetwork.cpp
 Shared/RTCPacketOptions.cpp
+Shared/ServiceWorkerInitializationData.cpp
 Shared/SessionState.cpp
 Shared/ShareableBitmap.cpp @no-unify
 Shared/ShareableResource.cpp
index 236da66..224ad7c 100644 (file)
@@ -121,11 +121,21 @@ void WebUserContentControllerProxy::addProcess(WebProcessProxy& webProcessProxy,
 
 #if ENABLE(CONTENT_EXTENSIONS)
     ASSERT(parameters.contentRuleLists.isEmpty());
-    for (const auto& contentRuleList : m_contentRuleLists.values())
-        parameters.contentRuleLists.append(std::make_pair(contentRuleList->name(), contentRuleList->compiledRuleList().data()));
+    parameters.contentRuleLists = contentRuleListData();
 #endif
 }
 
+#if ENABLE(CONTENT_EXTENSIONS)
+Vector<std::pair<String, WebCompiledContentRuleListData>> WebUserContentControllerProxy::contentRuleListData()
+{
+    Vector<std::pair<String, WebCompiledContentRuleListData>> data;
+    data.reserveInitialCapacity(m_contentRuleLists.size());
+    for (const auto& contentRuleList : m_contentRuleLists.values())
+        data.uncheckedAppend(std::make_pair(contentRuleList->name(), contentRuleList->compiledRuleList().data()));
+    return data;
+}
+#endif
+
 void WebUserContentControllerProxy::removeProcess(WebProcessProxy& webProcessProxy)
 {
     ASSERT(m_processes.contains(webProcessProxy));
index 2f532d5..0780c0c 100644 (file)
@@ -60,6 +60,7 @@ class NetworkProcessProxy;
 class WebProcessProxy;
 class WebScriptMessageHandler;
 struct FrameInfoData;
+class WebCompiledContentRuleListData;
 struct WebPageCreationParameters;
 enum class InjectUserScriptImmediately : bool;
 
@@ -104,6 +105,7 @@ public:
     void removeContentRuleList(const String&);
     void removeAllContentRuleLists();
     const HashMap<String, RefPtr<API::ContentRuleList>>& contentExtensionRules() { return m_contentRuleLists; }
+    Vector<std::pair<String, WebCompiledContentRuleListData>> contentRuleListData();
 #endif
 
     UserContentControllerIdentifier identifier() const { return m_identifier; }
index 23ac6e4..5639ebb 100644 (file)
@@ -78,6 +78,7 @@
 #include "WebProcessPoolMessages.h"
 #include "WebProcessProxy.h"
 #include "WebProcessProxyMessages.h"
+#include "WebUserContentControllerProxy.h"
 #include "WebsiteDataStore.h"
 #include "WebsiteDataStoreParameters.h"
 #include <JavaScriptCore/JSCInlines.h>
@@ -722,7 +723,7 @@ void WebProcessPool::establishWorkerContextConnectionToNetworkProcess(NetworkPro
                 continue;
 
             serviceWorkerProcessProxy = process.get();
-            serviceWorkerProcessProxy->enableServiceWorkers();
+            serviceWorkerProcessProxy->enableServiceWorkers(userContentControllerIdentifierForServiceWorkers());
 
             RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), ServiceWorker, "WebProcessPool::establishWorkerContextConnectionToNetworkProcess reusing an existing web process %p, process identifier %d", serviceWorkerProcessProxy, serviceWorkerProcessProxy->processIdentifier());
             break;
@@ -1227,6 +1228,8 @@ Ref<WebPageProxy> WebProcessPool::createWebPage(PageClient& pageClient, Ref<API:
     } else
         process = &processForRegistrableDomain(*pageConfiguration->websiteDataStore(), nullptr, { });
 
+    RefPtr<WebUserContentControllerProxy> userContentController = pageConfiguration->userContentController();
+    
     ASSERT(process);
 
     auto page = process->createWebPage(pageClient, WTFMove(pageConfiguration));
@@ -1237,6 +1240,8 @@ Ref<WebPageProxy> WebProcessPool::createWebPage(PageClient& pageClient, Ref<API:
         for (auto* serviceWorkerProcess : m_serviceWorkerProcesses.values())
             serviceWorkerProcess->updateServiceWorkerPreferencesStore(*m_serviceWorkerPreferences);
     }
+    if (userContentController)
+        m_userContentControllerIDForServiceWorker = userContentController->identifier();
 #endif
 
     bool enableProcessSwapOnCrossSiteNavigation = page->preferences().processSwapOnCrossSiteNavigationEnabled();
index 8da0b17..fe19dff 100644 (file)
@@ -393,6 +393,7 @@ public:
     void setAllowsAnySSLCertificateForServiceWorker(bool allows) { m_allowsAnySSLCertificateForServiceWorker = allows; }
     bool allowsAnySSLCertificateForServiceWorker() const { return m_allowsAnySSLCertificateForServiceWorker; }
     void updateServiceWorkerUserAgent(const String& userAgent);
+    const Optional<UserContentControllerIdentifier>& userContentControllerIdentifierForServiceWorkers() const { return m_userContentControllerIDForServiceWorker; }
 #endif
 
 #if PLATFORM(COCOA)
@@ -611,6 +612,7 @@ private:
     bool m_allowsAnySSLCertificateForServiceWorker { false };
     String m_serviceWorkerUserAgent;
     Optional<WebPreferencesStore> m_serviceWorkerPreferences;
+    Optional<UserContentControllerIdentifier> m_userContentControllerIDForServiceWorker;
 #endif
 
     Ref<WebPageGroup> m_defaultPageGroup;
index ed54d4a..e9b07c6 100644 (file)
@@ -143,7 +143,7 @@ Ref<WebProcessProxy> WebProcessProxy::createForServiceWorkers(WebProcessPool& pr
 {
     auto proxy = adoptRef(*new WebProcessProxy(processPool, &websiteDataStore, IsPrewarmed::No));
     proxy->m_registrableDomain = WTFMove(registrableDomain);
-    proxy->enableServiceWorkers();
+    proxy->enableServiceWorkers(processPool.userContentControllerIdentifierForServiceWorkers());
     proxy->connect();
     return proxy;
 }
@@ -1541,7 +1541,7 @@ void WebProcessProxy::endBackgroundActivityForFullscreenInput()
 #if ENABLE(SERVICE_WORKER)
 void WebProcessProxy::establishServiceWorkerContext(const WebPreferencesStore& store)
 {
-    send(Messages::WebProcess::EstablishWorkerContextConnectionToNetworkProcess { processPool().defaultPageGroup().pageGroupID(), m_serviceWorkerInformation->serviceWorkerPageProxyID, m_serviceWorkerInformation->serviceWorkerPageID, store, *m_registrableDomain }, 0);
+    send(Messages::WebProcess::EstablishWorkerContextConnectionToNetworkProcess { processPool().defaultPageGroup().pageGroupID(), m_serviceWorkerInformation->serviceWorkerPageProxyID, m_serviceWorkerInformation->serviceWorkerPageID, store, *m_registrableDomain, m_serviceWorkerInformation->initializationData }, 0);
 }
 
 void WebProcessProxy::setServiceWorkerUserAgent(const String& userAgent)
@@ -1572,11 +1572,38 @@ void WebProcessProxy::disableServiceWorkers()
     maybeShutDown();
 }
 
-void WebProcessProxy::enableServiceWorkers()
+#if ENABLE(CONTENT_EXTENSIONS)
+static Vector<std::pair<String, WebCompiledContentRuleListData>> contentRuleListsFromIdentifier(const Optional<UserContentControllerIdentifier>& userContentControllerIdentifier)
+{
+    if (!userContentControllerIdentifier) {
+        ASSERT_NOT_REACHED();
+        return { };
+    }
+
+    auto* userContentController = WebUserContentControllerProxy::get(*userContentControllerIdentifier);
+    if (!userContentController) {
+        ASSERT_NOT_REACHED();
+        return { };
+    }
+
+    return userContentController->contentRuleListData();
+}
+#endif
+
+void WebProcessProxy::enableServiceWorkers(const Optional<UserContentControllerIdentifier>& userContentControllerIdentifier)
 {
     ASSERT(m_registrableDomain && !m_registrableDomain->isEmpty());
     ASSERT(!m_serviceWorkerInformation);
-    m_serviceWorkerInformation = ServiceWorkerInformation { WebPageProxyIdentifier::generate(), PageIdentifier::generate() };
+    m_serviceWorkerInformation = ServiceWorkerInformation {
+        WebPageProxyIdentifier::generate(),
+        PageIdentifier::generate(),
+        ServiceWorkerInitializationData {
+            userContentControllerIdentifier,
+#if ENABLE(CONTENT_EXTENSIONS)
+            contentRuleListsFromIdentifier(userContentControllerIdentifier),
+#endif
+        },
+    };
 }
 
 } // namespace WebKit
index d84e464..4941305 100644 (file)
@@ -35,6 +35,8 @@
 #include "ProcessThrottler.h"
 #include "ProcessThrottlerClient.h"
 #include "ResponsivenessTimer.h"
+#include "ServiceWorkerInitializationData.h"
+#include "UserContentControllerIdentifier.h"
 #include "VisibleWebPageCounter.h"
 #include "WebConnectionToWebProcess.h"
 #include "WebPageProxyIdentifier.h"
@@ -74,6 +76,7 @@ class ProvisionalPageProxy;
 class UserMediaCaptureManagerProxy;
 class VisitedLinkStore;
 class WebBackForwardListItem;
+class WebCompiledContentRuleListData;
 class WebFrameProxy;
 class WebPageGroup;
 class WebPageProxy;
@@ -131,7 +134,7 @@ public:
     void setIsInProcessCache(bool);
     bool isInProcessCache() const { return m_isInProcessCache; }
 
-    void enableServiceWorkers();
+    void enableServiceWorkers(const Optional<UserContentControllerIdentifier>&);
     void disableServiceWorkers();
 
     WebsiteDataStore& websiteDataStore() const { ASSERT(m_websiteDataStore); return *m_websiteDataStore; }
@@ -522,6 +525,7 @@ private:
     struct ServiceWorkerInformation {
         WebPageProxyIdentifier serviceWorkerPageProxyID;
         WebCore::PageIdentifier serviceWorkerPageID;
+        ServiceWorkerInitializationData initializationData;
     };
     Optional<ServiceWorkerInformation> m_serviceWorkerInformation;
 };
index 33619e2..add5ab1 100644 (file)
                5C7C88DC1D0F41A0009D2F6D /* WebSocketProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebSocketProvider.h; path = Network/WebSocketProvider.h; sourceTree = "<group>"; };
                5C7FB46E21E97C0B009E3241 /* WebCookieJar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebCookieJar.cpp; sourceTree = "<group>"; };
                5C7FB46F21E97C0C009E3241 /* WebCookieJar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebCookieJar.h; sourceTree = "<group>"; };
+               5C80B3DB23690D8D0086E6DE /* ServiceWorkerInitializationData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ServiceWorkerInitializationData.h; sourceTree = "<group>"; };
+               5C80B3DD23690F100086E6DE /* ServiceWorkerInitializationData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ServiceWorkerInitializationData.cpp; sourceTree = "<group>"; };
                5C84CF901F96AC4E00B6705A /* NetworkSessionCreationParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkSessionCreationParameters.h; sourceTree = "<group>"; };
                5C85C7861C3F23C50061A4FA /* PendingDownload.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PendingDownload.cpp; sourceTree = "<group>"; };
                5C89DF5621AF61FF004645E8 /* NetworkSessionCreationParameters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkSessionCreationParameters.cpp; sourceTree = "<group>"; };
                                BC2D021612AC41CB00E732A3 /* SameDocumentNavigationType.h */,
                                1AAB4A8C1296F0A20023952F /* SandboxExtension.h */,
                                E1E552C316AE065E004ED653 /* SandboxInitializationParameters.h */,
+                               5C80B3DD23690F100086E6DE /* ServiceWorkerInitializationData.cpp */,
+                               5C80B3DB23690D8D0086E6DE /* ServiceWorkerInitializationData.h */,
                                1AFDE6571954A42B00C48FFA /* SessionState.cpp */,
                                1AFDE6581954A42B00C48FFA /* SessionState.h */,
                                1A6420E212DCE2FF00CAAE2C /* ShareableBitmap.cpp */,
index a2abab9..ecf7a3b 100644 (file)
@@ -34,7 +34,9 @@
 #include "NetworkConnectionToWebProcessMessages.h"
 #include "NetworkProcessMessages.h"
 #include "ServiceWorkerFetchTaskMessages.h"
+#include "ServiceWorkerInitializationData.h"
 #include "WebCacheStorageProvider.h"
+#include "WebCompiledContentRuleListData.h"
 #include "WebCoreArgumentCoders.h"
 #include "WebDatabaseProvider.h"
 #include "WebDocumentLoader.h"
@@ -47,6 +49,7 @@
 #include "WebSWServerToContextConnectionMessages.h"
 #include "WebServiceWorkerFetchTaskClient.h"
 #include "WebSocketProvider.h"
+#include "WebUserContentController.h"
 #include <WebCore/EditorClient.h>
 #include <WebCore/EmptyClients.h>
 #include <WebCore/LibWebRTCProvider.h>
@@ -83,7 +86,7 @@ Ref<DocumentLoader> ServiceWorkerFrameLoaderClient::createDocumentLoader(const R
     return WebDocumentLoader::create(request, substituteData);
 }
 
-WebSWContextManagerConnection::WebSWContextManagerConnection(Ref<IPC::Connection>&& connection, WebCore::RegistrableDomain&& registrableDomain, uint64_t pageGroupID, WebPageProxyIdentifier webPageProxyID, PageIdentifier pageID, const WebPreferencesStore& store)
+WebSWContextManagerConnection::WebSWContextManagerConnection(Ref<IPC::Connection>&& connection, WebCore::RegistrableDomain&& registrableDomain, uint64_t pageGroupID, WebPageProxyIdentifier webPageProxyID, PageIdentifier pageID, const WebPreferencesStore& store, ServiceWorkerInitializationData&& initializationData)
     : m_connectionToNetworkProcess(WTFMove(connection))
     , m_registrableDomain(WTFMove(registrableDomain))
     , m_pageGroupID(pageGroupID)
@@ -95,6 +98,13 @@ WebSWContextManagerConnection::WebSWContextManagerConnection(Ref<IPC::Connection
     , m_userAgent(standardUserAgent())
 #endif
 {
+    if (initializationData.userContentControllerIdentifier) {
+        m_userContentController = WebUserContentController::getOrCreate(*initializationData.userContentControllerIdentifier);
+#if ENABLE(CONTENT_EXTENSIONS)
+        m_userContentController->addContentRuleLists(WTFMove(initializationData.contentRuleLists));
+#endif
+    }
+
     updatePreferencesStore(store);
     m_connectionToNetworkProcess->send(Messages::NetworkConnectionToWebProcess::EstablishSWContextConnection { m_registrableDomain }, 0);
     WebProcess::singleton().disableTermination();
@@ -128,6 +138,7 @@ void WebSWContextManagerConnection::installServiceWorker(const ServiceWorkerCont
     pageConfiguration.databaseProvider = WebDatabaseProvider::getOrCreate(m_pageGroupID);
 #endif
     pageConfiguration.socketProvider = WebSocketProvider::create();
+    pageConfiguration.userContentProvider = m_userContentController;
 
     auto effectiveUserAgent =  WTFMove(userAgent);
     if (effectiveUserAgent.isNull())
index bf8b47f..297351f 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "Connection.h"
 #include "MessageReceiver.h"
+#include "UserContentControllerIdentifier.h"
 #include "WebPageProxyIdentifier.h"
 #include "WebSWContextManagerConnectionMessagesReplies.h"
 #include <WebCore/EmptyFrameLoaderClient.h>
@@ -49,11 +50,13 @@ struct ServiceWorkerContextData;
 namespace WebKit {
 
 class ServiceWorkerFrameLoaderClient;
+struct ServiceWorkerInitializationData;
 struct WebPreferencesStore;
+class WebUserContentController;
 
 class WebSWContextManagerConnection final : public WebCore::SWContextManager::Connection, public IPC::MessageReceiver {
 public:
-    WebSWContextManagerConnection(Ref<IPC::Connection>&&, WebCore::RegistrableDomain&&, uint64_t pageGroupID, WebPageProxyIdentifier, WebCore::PageIdentifier, const WebPreferencesStore&);
+    WebSWContextManagerConnection(Ref<IPC::Connection>&&, WebCore::RegistrableDomain&&, uint64_t pageGroupID, WebPageProxyIdentifier, WebCore::PageIdentifier, const WebPreferencesStore&, ServiceWorkerInitializationData&&);
     ~WebSWContextManagerConnection();
 
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) final;
@@ -115,6 +118,7 @@ private:
     uint64_t m_previousRequestIdentifier { 0 };
     String m_userAgent;
     bool m_isThrottleable { true };
+    RefPtr<WebUserContentController> m_userContentController;
 };
 
 class ServiceWorkerFrameLoaderClient final : public WebCore::EmptyFrameLoaderClient {
index d003c0f..6c16fa0 100644 (file)
@@ -1738,13 +1738,13 @@ LibWebRTCNetwork& WebProcess::libWebRTCNetwork()
 }
 
 #if ENABLE(SERVICE_WORKER)
-void WebProcess::establishWorkerContextConnectionToNetworkProcess(uint64_t pageGroupID, WebPageProxyIdentifier webPageProxyID, PageIdentifier pageID, const WebPreferencesStore& store, RegistrableDomain&& registrableDomain)
+void WebProcess::establishWorkerContextConnectionToNetworkProcess(uint64_t pageGroupID, WebPageProxyIdentifier webPageProxyID, PageIdentifier pageID, const WebPreferencesStore& store, RegistrableDomain&& registrableDomain, ServiceWorkerInitializationData&& initializationData)
 {
     // We are in the Service Worker context process and the call below establishes our connection to the Network Process
     // by calling ensureNetworkProcessConnection. SWContextManager needs to use the same underlying IPC::Connection as the
     // NetworkProcessConnection for synchronization purposes.
     auto& ipcConnection = ensureNetworkProcessConnection().connection();
-    SWContextManager::singleton().setConnection(makeUnique<WebSWContextManagerConnection>(ipcConnection, WTFMove(registrableDomain), pageGroupID, webPageProxyID, pageID, store));
+    SWContextManager::singleton().setConnection(makeUnique<WebSWContextManagerConnection>(ipcConnection, WTFMove(registrableDomain), pageGroupID, webPageProxyID, pageID, store, WTFMove(initializationData)));
 }
 
 void WebProcess::registerServiceWorkerClients()
index 4980223..c24bcbd 100644 (file)
@@ -35,6 +35,7 @@
 #include "SandboxExtension.h"
 #include "StorageAreaIdentifier.h"
 #include "TextCheckerState.h"
+#include "UserContentControllerIdentifier.h"
 #include "ViewUpdateDispatcher.h"
 #include "WebInspectorInterruptDispatcher.h"
 #include "WebPageProxyIdentifier.h"
@@ -109,11 +110,13 @@ class InjectedBundle;
 class LibWebRTCNetwork;
 class NetworkProcessConnection;
 class ObjCObjectGraph;
+struct ServiceWorkerInitializationData;
 class StorageAreaMap;
 class UserData;
 class WaylandCompositorDisplay;
 class WebAutomationSessionProxy;
 class WebCacheStorageProvider;
+class WebCompiledContentRuleListData;
 class WebConnectionToUIProcess;
 class WebFrame;
 class WebLoaderStrategy;
@@ -371,7 +374,7 @@ private:
 #endif
 
 #if ENABLE(SERVICE_WORKER)
-    void establishWorkerContextConnectionToNetworkProcess(uint64_t pageGroupID, WebPageProxyIdentifier, WebCore::PageIdentifier, const WebPreferencesStore&, WebCore::RegistrableDomain&&);
+    void establishWorkerContextConnectionToNetworkProcess(uint64_t pageGroupID, WebPageProxyIdentifier, WebCore::PageIdentifier, const WebPreferencesStore&, WebCore::RegistrableDomain&&, ServiceWorkerInitializationData&&);
     void registerServiceWorkerClients();
 #endif
 
index b9f9f60..10941f0 100644 (file)
@@ -106,7 +106,7 @@ messages -> WebProcess LegacyReceiver {
 #endif
 
 #if ENABLE(SERVICE_WORKER)
-    EstablishWorkerContextConnectionToNetworkProcess(uint64_t pageGroupID, WebKit::WebPageProxyIdentifier webPageProxyID, WebCore::PageIdentifier pageID, struct WebKit::WebPreferencesStore store, WebCore::RegistrableDomain domain)
+    EstablishWorkerContextConnectionToNetworkProcess(uint64_t pageGroupID, WebKit::WebPageProxyIdentifier webPageProxyID, WebCore::PageIdentifier pageID, struct WebKit::WebPreferencesStore store, WebCore::RegistrableDomain domain, struct WebKit::ServiceWorkerInitializationData initializationData)
     RegisterServiceWorkerClients()
 #endif
 
index eb05a67..e5511a4 100644 (file)
@@ -1,3 +1,20 @@
+2019-10-30  Alex Christensen  <achristensen@webkit.org>
+
+        WKContentRuleLists should block requests from service workers
+        https://bugs.webkit.org/show_bug.cgi?id=201980
+        <rdar://problem/55516735>
+
+        Reviewed by Chris Dumez.
+
+        NSString initWithContentsOfURL doesn't work with https URLs with certificates without a trusted root,
+        so I use an ephemeral NSURLSession instead so I can tell it to accept any connection, even our WebKit httpd server.
+        I also added an API test.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm:
+        * WebKitTestRunner/mac/TestControllerMac.mm:
+        (-[WKTRSessionDelegate URLSession:task:didReceiveChallenge:completionHandler:]):
+        (WTR::TestController::configureContentExtensionForTest):
+
 2019-10-30  Daniel Bates  <dabates@apple.com>
 
         Add pretty printer for CompactPointerTuple
index 2464c9f..49747a4 100644 (file)
@@ -1913,3 +1913,76 @@ TEST(ServiceWorkers, ProcessPerSession)
 
     EXPECT_EQ(2U, processPool._serviceWorkerProcessCount);
 }
+
+static const char* contentRuleListWorkerScript =
+"self.addEventListener('message', (event) => {"
+"    fetch('blockedsubresource').then(() => {"
+"        event.source.postMessage('FAIL - should have blocked first request');"
+"    }).catch(() => {"
+"        fetch('allowedsubresource').then(() => {"
+"            event.source.postMessage('PASS - blocked first request, allowed second');"
+"        }).catch(() => {"
+"            event.source.postMessage('FAIL - should have allowed second request');"
+"        });"
+"    });"
+"});";
+
+TEST(ServiceWorkers, ContentRuleList)
+{
+    [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
+
+    __block bool doneCompiling = false;
+    __block RetainPtr<WKContentRuleList> contentRuleList;
+    [[WKContentRuleListStore defaultStore] compileContentRuleListForIdentifier:@"ServiceWorkerRuleList" encodedContentRuleList:@"[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"blockedsubresource\"}}]" completionHandler:^(WKContentRuleList *list, NSError *error) {
+        EXPECT_NOT_NULL(list);
+        EXPECT_NULL(error);
+        contentRuleList = list;
+        doneCompiling = true;
+    }];
+    TestWebKitAPI::Util::run(&doneCompiling);
+
+    // Start with a clean slate data store
+    [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+
+    auto messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
+    [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
+    [[configuration userContentController] addContentRuleList:contentRuleList.get()];
+
+    using namespace TestWebKitAPI;
+    TCPServer server([] (int socket) {
+        auto respond = [socket] (const char* body, const char* mimeType) {
+            NSString *format = @"HTTP/1.1 200 OK\r\n"
+            "Content-Type: %s\r\n"
+            "Content-Length: %d\r\n\r\n"
+            "%s";
+            NSString *response = [NSString stringWithFormat:format, mimeType, strlen(body), body];
+            TCPServer::write(socket, response.UTF8String, response.length);
+        };
+        TCPServer::read(socket);
+        respond(mainBytes, "text/html");
+        TCPServer::read(socket);
+        respond(contentRuleListWorkerScript, "application/javascript");
+        auto lastRequest = TCPServer::read(socket);
+        EXPECT_TRUE(strstr((const char*)lastRequest.data(), "allowedsubresource"));
+        respond("successful fetch", "application/octet-stream");
+    });
+
+    expectedMessage = @"Message from worker: PASS - blocked first request, allowed second";
+
+    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]]];
+    TestWebKitAPI::Util::run(&done);
+    
+    __block bool doneRemoving = false;
+    [[WKContentRuleListStore defaultStore] removeContentRuleListForIdentifier:@"ServiceWorkerRuleList" completionHandler:^(NSError *error) {
+        EXPECT_NULL(error);
+        doneRemoving = true;
+    }];
+    TestWebKitAPI::Util::run(&doneRemoving);
+}
index 3c838f4..2303590 100644 (file)
 + (void)_setAlertType:(NSUInteger)alertType;
 @end
 
+@interface WKTRSessionDelegate : NSObject <NSURLSessionDataDelegate>
+@end
+@implementation WKTRSessionDelegate
+- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
+{
+    completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
+}
+@end
+
 namespace WTR {
 
 void TestController::notifyDone()
@@ -125,11 +134,20 @@ void TestController::configureContentExtensionForTest(const TestInvocation& test
     RetainPtr<CFURLRef> testURL = adoptCF(WKURLCopyCFURL(kCFAllocatorDefault, test.url()));
     NSURL *filterURL = [(__bridge NSURL *)testURL.get() URLByAppendingPathExtension:@"json"];
 
-    NSStringEncoding encoding;
-    NSString *contentExtensionString = [[NSString alloc] initWithContentsOfURL:filterURL usedEncoding:&encoding error:NULL];
-    if (!contentExtensionString)
-        return;
-    
+    __block NSString *contentExtensionString;
+    __block bool doneFetchingContentExtension = false;
+    auto delegate = adoptNS([WKTRSessionDelegate new]);
+    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] delegate:delegate.get() delegateQueue:[NSOperationQueue mainQueue]];
+    NSURLSessionDataTask *task = [session dataTaskWithRequest:[NSURLRequest requestWithURL:filterURL] completionHandler:^(NSData * data, NSURLResponse *response, NSError *error) {
+        ASSERT(data);
+        ASSERT(response);
+        ASSERT(!error);
+        contentExtensionString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+        doneFetchingContentExtension = true;
+    }];
+    [task resume];
+    platformRunUntil(doneFetchingContentExtension, noTimeout);
+
     __block bool doneCompiling = false;
 
     NSURL *tempDir;