diff --git a/dom/base/nsContentPolicyUtils.h b/dom/base/nsContentPolicyUtils.h index 77cab8a87d34..1256efe91bd3 100644 --- a/dom/base/nsContentPolicyUtils.h +++ b/dom/base/nsContentPolicyUtils.h @@ -144,8 +144,10 @@ inline const char* NS_CP_ContentTypeName(nsContentPolicyType contentType) { CASE_RETURN(TYPE_INTERNAL_FRAME_MESSAGEMANAGER_SCRIPT); CASE_RETURN(TYPE_INTERNAL_FETCH_PRELOAD); CASE_RETURN(TYPE_UA_FONT); + CASE_RETURN(TYPE_INTERNAL_WORKER_STATIC_MODULE); CASE_RETURN(TYPE_PROXIED_WEBRTC_MEDIA); CASE_RETURN(TYPE_WEB_IDENTITY); + CASE_RETURN(TYPE_END); case nsIContentPolicy::TYPE_INVALID: break; // Do not add default: so that compilers can catch the missing case. diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 0dfb4d790f70..957fb4b0e332 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -3495,6 +3495,7 @@ nsContentUtils::InternalContentPolicyTypeToExternal(nsContentPolicyType aType) { case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER: case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER: case nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS: + case nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE: case nsIContentPolicy::TYPE_INTERNAL_AUDIOWORKLET: case nsIContentPolicy::TYPE_INTERNAL_PAINTWORKLET: case nsIContentPolicy::TYPE_INTERNAL_CHROMEUTILS_COMPILED_SCRIPT: diff --git a/dom/base/nsIContentPolicy.idl b/dom/base/nsIContentPolicy.idl index 484abe322bbe..283705a9d08a 100644 --- a/dom/base/nsIContentPolicy.idl +++ b/dom/base/nsIContentPolicy.idl @@ -433,6 +433,20 @@ interface nsIContentPolicy : nsISupports */ TYPE_WEB_IDENTITY = 57, + /** + * Indicates the load of a static module on workers. + */ + TYPE_INTERNAL_WORKER_STATIC_MODULE = 58, + + /** + * Used to indicate the end of this list, not a content policy. If you want + * to add a new content policy type, place it before this sentinel value + * TYPE_END, have it use TYPE_END's current value, and increment TYPE_END by + * one. (TYPE_END should always have the highest numerical value.) + */ + TYPE_END = 59, + + /* When adding new content types, please update * NS_CP_ContentTypeName, nsCSPContext, CSP_ContentTypeToDirective, * DoContentSecurityChecks, all nsIContentPolicy implementations, the diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp index 9f1bb9606cb4..158073eca1d0 100644 --- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -342,7 +342,9 @@ static_assert( nsIContentPolicy::TYPE_INTERNAL_FRAME_MESSAGEMANAGER_SCRIPT == 53 && nsIContentPolicy::TYPE_INTERNAL_FETCH_PRELOAD == 54 && nsIContentPolicy::TYPE_UA_FONT == 55 && - nsIContentPolicy::TYPE_WEB_IDENTITY == 57, + nsIContentPolicy::TYPE_WEB_IDENTITY == 57 && + nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE == 58 && + nsIContentPolicy::TYPE_END == 59, "nsContentPolicyType values are as expected"); namespace { diff --git a/dom/fetch/InternalRequest.cpp b/dom/fetch/InternalRequest.cpp index 1ce9739dcc92..bcc0511fb4c4 100644 --- a/dom/fetch/InternalRequest.cpp +++ b/dom/fetch/InternalRequest.cpp @@ -274,6 +274,7 @@ RequestDestination InternalRequest::MapContentPolicyTypeToRequestDestination( case nsIContentPolicy::TYPE_SCRIPT: return RequestDestination::Script; case nsIContentPolicy::TYPE_INTERNAL_WORKER: + case nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE: return RequestDestination::Worker; case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER: return RequestDestination::Sharedworker; @@ -350,6 +351,7 @@ RequestDestination InternalRequest::MapContentPolicyTypeToRequestDestination( case nsIContentPolicy::TYPE_WEB_IDENTITY: return RequestDestination::_empty; case nsIContentPolicy::TYPE_INVALID: + case nsIContentPolicy::TYPE_END: break; // Do not add default: so that compilers can catch the missing case. } diff --git a/dom/fetch/InternalRequest.h b/dom/fetch/InternalRequest.h index cef1916a03db..3dfbb9284dc5 100644 --- a/dom/fetch/InternalRequest.h +++ b/dom/fetch/InternalRequest.h @@ -70,7 +70,7 @@ namespace dom { * | TYPE_STYLESHEET * "track" | TYPE_INTERNAL_TRACK * "video" | TYPE_INTERNAL_VIDEO - * "worker" | TYPE_INTERNAL_WORKER + * "worker" | TYPE_INTERNAL_WORKER, TYPE_INTERNAL_WORKER_STATIC_MODULE * "xslt" | TYPE_XSLT * "" | Default for everything else. * diff --git a/dom/security/SecFetch.cpp b/dom/security/SecFetch.cpp index 06aa3de699eb..8ed850ee3420 100644 --- a/dom/security/SecFetch.cpp +++ b/dom/security/SecFetch.cpp @@ -31,6 +31,7 @@ nsCString MapInternalContentPolicyTypeToDest(nsContentPolicyType aType) { case nsIContentPolicy::TYPE_SCRIPT: return "script"_ns; case nsIContentPolicy::TYPE_INTERNAL_WORKER: + case nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE: return "worker"_ns; case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER: return "sharedworker"_ns; @@ -108,6 +109,7 @@ nsCString MapInternalContentPolicyTypeToDest(nsContentPolicyType aType) { return "empty"_ns; case nsIContentPolicy::TYPE_WEB_IDENTITY: return "webidentity"_ns; + case nsIContentPolicy::TYPE_END: case nsIContentPolicy::TYPE_INVALID: break; // Do not add default: so that compilers can catch the missing case. diff --git a/dom/security/nsCSPUtils.cpp b/dom/security/nsCSPUtils.cpp index cf97a50657b3..4a9eda24d2b7 100644 --- a/dom/security/nsCSPUtils.cpp +++ b/dom/security/nsCSPUtils.cpp @@ -304,6 +304,7 @@ CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType) { return nsIContentSecurityPolicy::WEB_MANIFEST_SRC_DIRECTIVE; case nsIContentPolicy::TYPE_INTERNAL_WORKER: + case nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE: case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER: case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER: return nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE; @@ -353,6 +354,7 @@ CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType) { // Fall through to error for all other directives // Note that we should never end up here for navigate-to case nsIContentPolicy::TYPE_INVALID: + case nsIContentPolicy::TYPE_END: MOZ_ASSERT(false, "Can not map nsContentPolicyType to CSPDirective"); // Do not add default: so that compilers can catch the missing case. } diff --git a/dom/security/nsMixedContentBlocker.cpp b/dom/security/nsMixedContentBlocker.cpp index 93a60de85a4e..6b050290a669 100644 --- a/dom/security/nsMixedContentBlocker.cpp +++ b/dom/security/nsMixedContentBlocker.cpp @@ -443,6 +443,8 @@ nsresult nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, // external type in all cases right now. bool isWorkerType = internalContentType == nsIContentPolicy::TYPE_INTERNAL_WORKER || + internalContentType == + nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE || internalContentType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER || internalContentType == nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER; ExtContentPolicyType contentType = diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp index 2491f4401abe..87b04823781b 100644 --- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -119,10 +119,9 @@ nsresult ChannelFromScriptURL( nsresult rv; nsCOMPtr uri = aScriptURL; - // If we have the document, use it. Unfortunately, for dedicated workers - // 'parentDoc' ends up being the parent document, which is not the document - // that we want to use. So make sure to avoid using 'parentDoc' in that - // situation. + // Only use the document when its principal matches the principal of the + // current request. This means scripts fetched using the Workers' own + // principal won't inherit properties of the document, in particular the CSP. if (parentDoc && parentDoc->NodePrincipal() != principal) { parentDoc = nullptr; } @@ -140,6 +139,7 @@ nsresult ChannelFromScriptURL( nsCOMPtr channel; if (parentDoc) { + // This is the path for top level dedicated worker scripts with a document rv = NS_NewChannel(getter_AddRefs(channel), uri, parentDoc, aSecFlags, aContentPolicyType, nullptr, // aPerformanceStorage @@ -148,6 +148,11 @@ nsresult ChannelFromScriptURL( aLoadFlags, ios); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR); } else { + // This branch is used in the following cases: + // * Shared and ServiceWorkers (who do not have a doc) + // * Static Module Imports + // * ImportScripts + // We must have a loadGroup with a load context for the principal to // traverse the channel correctly. MOZ_ASSERT(loadGroup); @@ -161,6 +166,8 @@ nsresult ChannelFromScriptURL( } if (aClientInfo.isSome()) { + // If we have an existing clientInfo (true for all modules and + // importScripts), we will use this branch rv = NS_NewChannel(getter_AddRefs(channel), uri, principal, aClientInfo.ref(), aController, aSecFlags, aContentPolicyType, aCookieJarSettings, @@ -544,6 +551,26 @@ nsTArray> WorkerScriptLoader::GetLoadingList() { return list; } +nsContentPolicyType WorkerScriptLoader::GetContentPolicyType( + ScriptLoadRequest* aRequest) { + if (aRequest->GetWorkerLoadContext()->IsTopLevel()) { + // Implements https://html.spec.whatwg.org/#worker-processing-model + // Step 13: Let destination be "sharedworker" if is shared is true, and + // "worker" otherwise. + return mWorkerRef->Private()->ContentPolicyType(); + } + if (aRequest->IsModuleRequest()) { + // Implements the destination for Step 14 in + // https://html.spec.whatwg.org/#worker-processing-model + // + // We need a special subresource type in order to correctly implement + // the graph fetch, where the destination is set to "worker" or + // "sharedworker". + return nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE; + } + return nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS; +} + already_AddRefed WorkerScriptLoader::CreateScriptLoadRequest( const nsString& aScriptURL, const mozilla::Encoding* aDocumentEncoding, bool aIsMainScript) { @@ -584,7 +611,7 @@ already_AddRefed WorkerScriptLoader::CreateScriptLoadRequest( } else { // Implements part of "To fetch a worklet/module worker script graph" // including, setting up the request with a credentials mode, - // destination (CSP, TODO). + // destination. // Step 1. Let options be a script fetch options. // We currently don't track credentials in our ScriptFetchOptions @@ -879,10 +906,7 @@ nsresult WorkerScriptLoader::LoadScript( return rv; } - nsContentPolicyType contentPolicyType = - loadContext->IsTopLevel() - ? mWorkerRef->Private()->ContentPolicyType() - : nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS; + nsContentPolicyType contentPolicyType = GetContentPolicyType(request); rv = ChannelFromScriptURL( principal, parentDoc, mWorkerRef->Private(), loadGroup, ios, secMan, diff --git a/dom/workers/ScriptLoader.h b/dom/workers/ScriptLoader.h index 4ab42a471284..ecc43be442e9 100644 --- a/dom/workers/ScriptLoader.h +++ b/dom/workers/ScriptLoader.h @@ -245,6 +245,8 @@ class WorkerScriptLoader : public JS::loader::ScriptLoaderInterface, nsTArray> GetLoadingList(); + nsContentPolicyType GetContentPolicyType(ScriptLoadRequest* aRequest); + bool EvaluateScript(JSContext* aCx, ScriptLoadRequest* aRequest); nsresult FillCompileOptionsForRequest( diff --git a/dom/workers/loader/WorkerModuleLoader.cpp b/dom/workers/loader/WorkerModuleLoader.cpp index fa0cbdae4201..1de3a22be853 100644 --- a/dom/workers/loader/WorkerModuleLoader.cpp +++ b/dom/workers/loader/WorkerModuleLoader.cpp @@ -34,6 +34,12 @@ WorkerModuleLoader::WorkerModuleLoader(WorkerScriptLoader* aScriptLoader, already_AddRefed WorkerModuleLoader::CreateStaticImport( nsIURI* aURI, ModuleLoadRequest* aParent) { + // We are intentionally deviating from the specification here and using the + // worker's CSP rather than the document CSP. The spec otherwise requires our + // service worker integration to be changed, and additionally the decision + // here did not make sense as we are treating static imports as different from + // other kinds of subresources. + // See Discussion in https://github.com/w3c/webappsec-csp/issues/336 Maybe clientInfo = GetGlobalObject()->GetClientInfo(); RefPtr loadContext = diff --git a/ipc/glue/IPCMessageUtilsSpecializations.h b/ipc/glue/IPCMessageUtilsSpecializations.h index 680859ffe538..5d71a2425b8e 100644 --- a/ipc/glue/IPCMessageUtilsSpecializations.h +++ b/ipc/glue/IPCMessageUtilsSpecializations.h @@ -328,9 +328,9 @@ struct ParamTraits { template <> struct ParamTraits - : public ContiguousEnumSerializerInclusive< - nsContentPolicyType, nsIContentPolicy::TYPE_INVALID, - nsIContentPolicy::TYPE_WEB_IDENTITY> {}; + : public ContiguousEnumSerializer {}; template <> struct ParamTraits { diff --git a/netwerk/base/nsINetworkInterceptController.idl b/netwerk/base/nsINetworkInterceptController.idl index fba1a8323116..d72dc570dc82 100644 --- a/netwerk/base/nsINetworkInterceptController.idl +++ b/netwerk/base/nsINetworkInterceptController.idl @@ -171,7 +171,8 @@ interface nsIInterceptedChannel : nsISupports case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD: case nsIContentPolicy::TYPE_INTERNAL_MODULE: case nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD: - case nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS: { + case nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS: + case nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE: { aKey = "subresource-script"_ns; break; } diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp index 4fc52663c49e..e54aec84242f 100644 --- a/netwerk/base/nsNetUtil.cpp +++ b/netwerk/base/nsNetUtil.cpp @@ -338,7 +338,8 @@ void AssertLoadingPrincipalAndClientInfoMatch( (aType == nsIContentPolicy::TYPE_INTERNAL_WORKER || aType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER || aType == nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER || - aType == nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS)) { + aType == nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS || + aType == nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE)) { return; } diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 40025e8e1d73..47e1d9b0caba 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -2806,6 +2806,7 @@ nsresult EnsureMIMEOfScript(HttpBaseChannel* aChannel, nsIURI* aURI, Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::script_load); break; case nsIContentPolicy::TYPE_INTERNAL_WORKER: + case nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE: case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER: AccumulateCategorical( Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::worker_load); @@ -2930,7 +2931,8 @@ nsresult EnsureMIMEOfScript(HttpBaseChannel* aChannel, nsIURI* aURI, // We restrict importScripts() in worker code to JavaScript MIME types. nsContentPolicyType internalType = aLoadInfo->InternalContentPolicyType(); - if (internalType == nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS) { + if (internalType == nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS || + internalType == nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE) { ReportMimeTypeMismatch(aChannel, "BlockImportScriptsWithWrongMimeType", aURI, contentType, Report::Error); return NS_ERROR_CORRUPTED_CONTENT; diff --git a/toolkit/components/extensions/webrequest/ChannelWrapper.cpp b/toolkit/components/extensions/webrequest/ChannelWrapper.cpp index ea3998bdbfe5..508302ff7bc1 100644 --- a/toolkit/components/extensions/webrequest/ChannelWrapper.cpp +++ b/toolkit/components/extensions/webrequest/ChannelWrapper.cpp @@ -487,7 +487,9 @@ bool ChannelWrapper::IsServiceWorkerScript(const nsCOMPtr& chan) { // Service worker import scripts load. if (loadInfo->InternalContentPolicyType() == - nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS) { + nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS || + loadInfo->InternalContentPolicyType() == + nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE) { nsLoadFlags loadFlags = 0; chan->GetLoadFlags(&loadFlags); return loadFlags & nsIChannel::LOAD_BYPASS_SERVICE_WORKER; diff --git a/toolkit/components/extensions/webrequest/WebRequest.jsm b/toolkit/components/extensions/webrequest/WebRequest.jsm index 65bea0be4bca..86a0b45e311c 100644 --- a/toolkit/components/extensions/webrequest/WebRequest.jsm +++ b/toolkit/components/extensions/webrequest/WebRequest.jsm @@ -116,8 +116,10 @@ function verifyRedirect(channel, redirectUri, finalUrl, addonId) { if ( isServiceWorkerScript && - channel.loadInfo?.internalContentPolicyType === - Ci.nsIContentPolicy.TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS && + (channel.loadInfo?.internalContentPolicyType === + Ci.nsIContentPolicy.TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS || + channel.loadInfo?.internalContentPolicyType === + Ci.nsIContentPolicy.TYPE_INTERNAL_WORKER_STATIC_MODULE) && !ALLOWED_SERVICEWORKER_SCHEMES.includes(redirectUri?.scheme) ) { throw new Error(