diff --git a/dom/workers/ServiceWorker.h b/dom/workers/ServiceWorker.h index 9c438ba5c22d..2eb4cff3b1cb 100644 --- a/dom/workers/ServiceWorker.h +++ b/dom/workers/ServiceWorker.h @@ -53,6 +53,8 @@ public: void GetScriptURL(nsString& aURL) const; + const ServiceWorkerInfo* Info() const { return mInfo; } + void DispatchStateChange(ServiceWorkerState aState) { diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index 27d8ad343275..9fe80cec8d63 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -6,15 +6,18 @@ #include "ServiceWorkerEvents.h" #include "ServiceWorkerClient.h" +#include "ServiceWorkerManager.h" #include "nsIHttpChannelInternal.h" #include "nsINetworkInterceptController.h" #include "nsIOutputStream.h" +#include "nsContentPolicyUtils.h" #include "nsContentUtils.h" #include "nsComponentManagerUtils.h" #include "nsServiceManagerUtils.h" #include "nsStreamUtils.h" #include "nsNetCID.h" +#include "nsNetUtil.h" #include "nsSerializationHelper.h" #include "nsQueryObject.h" @@ -104,13 +107,17 @@ namespace { class FinishResponse final : public nsRunnable { nsMainThreadPtrHandle mChannel; + nsMainThreadPtrHandle mServiceWorker; nsRefPtr mInternalResponse; ChannelInfo mWorkerChannelInfo; + public: FinishResponse(nsMainThreadPtrHandle& aChannel, + nsMainThreadPtrHandle aServiceWorker, InternalResponse* aInternalResponse, const ChannelInfo& aWorkerChannelInfo) : mChannel(aChannel) + , mServiceWorker(aServiceWorker) , mInternalResponse(aInternalResponse) , mWorkerChannelInfo(aWorkerChannelInfo) { @@ -121,6 +128,11 @@ public: { AssertIsOnMainThread(); + if (!CSPPermitsResponse()) { + mChannel->Cancel(NS_ERROR_CONTENT_BLOCKED); + return NS_OK; + } + ChannelInfo channelInfo; if (mInternalResponse->GetChannelInfo().IsInitialized()) { channelInfo = mInternalResponse->GetChannelInfo(); @@ -147,6 +159,34 @@ public: NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to finish synthesized response"); return rv; } + + bool CSPPermitsResponse() + { + AssertIsOnMainThread(); + + nsresult rv; + nsCOMPtr uri; + nsAutoCString url; + mInternalResponse->GetUnfilteredUrl(url); + if (url.IsEmpty()) { + // Synthetic response. The buck stops at the worker script. + url = mServiceWorker->Info()->ScriptSpec(); + } + rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr); + NS_ENSURE_SUCCESS(rv, false); + + nsCOMPtr underlyingChannel; + rv = mChannel->GetChannel(getter_AddRefs(underlyingChannel)); + NS_ENSURE_SUCCESS(rv, false); + NS_ENSURE_TRUE(underlyingChannel, false); + nsCOMPtr loadInfo = underlyingChannel->GetLoadInfo(); + + int16_t decision = nsIContentPolicy::ACCEPT; + rv = NS_CheckContentLoadPolicy(loadInfo->InternalContentPolicyType(), uri, loadInfo->LoadingPrincipal(), + loadInfo->LoadingNode(), EmptyCString(), nullptr, &decision); + NS_ENSURE_SUCCESS(rv, false); + return decision == nsIContentPolicy::ACCEPT; + } }; class RespondWithHandler final : public PromiseNativeHandler @@ -183,13 +223,16 @@ private: struct RespondWithClosure { nsMainThreadPtrHandle mInterceptedChannel; + nsMainThreadPtrHandle mServiceWorker; nsRefPtr mInternalResponse; ChannelInfo mWorkerChannelInfo; RespondWithClosure(nsMainThreadPtrHandle& aChannel, + nsMainThreadPtrHandle& aServiceWorker, InternalResponse* aInternalResponse, const ChannelInfo& aWorkerChannelInfo) : mInterceptedChannel(aChannel) + , mServiceWorker(aServiceWorker) , mInternalResponse(aInternalResponse) , mWorkerChannelInfo(aWorkerChannelInfo) { @@ -202,6 +245,7 @@ void RespondWithCopyComplete(void* aClosure, nsresult aStatus) nsCOMPtr event; if (NS_SUCCEEDED(aStatus)) { event = new FinishResponse(data->mInterceptedChannel, + data->mServiceWorker, data->mInternalResponse, data->mWorkerChannelInfo); } else { @@ -307,7 +351,7 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle aValu } nsAutoPtr closure( - new RespondWithClosure(mInterceptedChannel, ir, worker->GetChannelInfo())); + new RespondWithClosure(mInterceptedChannel, mServiceWorker, ir, worker->GetChannelInfo())); nsCOMPtr body; ir->GetUnfilteredBody(getter_AddRefs(body)); // Errors and redirects may not have a body. diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-csp.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-csp.https.html.ini deleted file mode 100644 index d203be567b75..000000000000 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-csp.https.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[fetch-csp.https.html] - type: testharness - [Verify CSP control of fetch() in a Service Worker] - expected: FAIL - diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-csp-iframe.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-csp-iframe.html index c6b3e022eaf7..cf7175bc83df 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-csp-iframe.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-csp-iframe.html @@ -6,12 +6,6 @@ var host_info = get_host_info(); var results = ''; var port = undefined; -var meta = document.createElement('meta'); -meta.setAttribute('http-equiv', 'Content-Security-Policy'); -meta.setAttribute('content', 'img-src ' + host_info['HTTPS_ORIGIN'] + - '; script-src \'unsafe-inline\''); -document.head.appendChild(meta); - function test1() { var img = document.createElement('img'); document.body.appendChild(img); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-csp-iframe.html.sub.headers b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-csp-iframe.html.sub.headers new file mode 100644 index 000000000000..300efe049b5d --- /dev/null +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-csp-iframe.html.sub.headers @@ -0,0 +1 @@ +Content-Security-Policy: img-src https://{{host}}:{{ports[https][0]}}