Bug 1247687 - Implement csp for Module Workers; r=evilpie,asuth,rpl,ckerschb

Depends on D155691

Differential Revision: https://phabricator.services.mozilla.com/D156102
This commit is contained in:
Yulia Startsev 2023-01-18 13:46:33 +00:00
Родитель 55f1e53d70
Коммит 1c5ad3e91d
18 изменённых файлов: 87 добавлений и 20 удалений

Просмотреть файл

@ -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.

Просмотреть файл

@ -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:

Просмотреть файл

@ -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

4
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 {

Просмотреть файл

@ -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.
}

Просмотреть файл

@ -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.
*

Просмотреть файл

@ -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.

Просмотреть файл

@ -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.
}

Просмотреть файл

@ -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 =

Просмотреть файл

@ -119,10 +119,9 @@ nsresult ChannelFromScriptURL(
nsresult rv;
nsCOMPtr<nsIURI> 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<nsIChannel> 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<RefPtr<ThreadSafeRequestHandle>> 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<ScriptLoadRequest> WorkerScriptLoader::CreateScriptLoadRequest(
const nsString& aScriptURL, const mozilla::Encoding* aDocumentEncoding,
bool aIsMainScript) {
@ -584,7 +611,7 @@ already_AddRefed<ScriptLoadRequest> 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,

Просмотреть файл

@ -245,6 +245,8 @@ class WorkerScriptLoader : public JS::loader::ScriptLoaderInterface,
nsTArray<RefPtr<ThreadSafeRequestHandle>> GetLoadingList();
nsContentPolicyType GetContentPolicyType(ScriptLoadRequest* aRequest);
bool EvaluateScript(JSContext* aCx, ScriptLoadRequest* aRequest);
nsresult FillCompileOptionsForRequest(

Просмотреть файл

@ -34,6 +34,12 @@ WorkerModuleLoader::WorkerModuleLoader(WorkerScriptLoader* aScriptLoader,
already_AddRefed<ModuleLoadRequest> 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> clientInfo = GetGlobalObject()->GetClientInfo();
RefPtr<WorkerLoadContext> loadContext =

Просмотреть файл

@ -328,9 +328,9 @@ struct ParamTraits<nsID> {
template <>
struct ParamTraits<nsContentPolicyType>
: public ContiguousEnumSerializerInclusive<
nsContentPolicyType, nsIContentPolicy::TYPE_INVALID,
nsIContentPolicy::TYPE_WEB_IDENTITY> {};
: public ContiguousEnumSerializer<nsContentPolicyType,
nsIContentPolicy::TYPE_INVALID,
nsIContentPolicy::TYPE_END> {};
template <>
struct ParamTraits<mozilla::TimeDuration> {

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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;

Просмотреть файл

@ -487,7 +487,9 @@ bool ChannelWrapper::IsServiceWorkerScript(const nsCOMPtr<nsIChannel>& 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;

Просмотреть файл

@ -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(