зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 6 changesets (bug 1313937, bug 1843066, bug 1843002) for causing build bustages in ScriptLoadRequest.h. CLOSED TREE
Backed out changeset 84c2d1c04aa2 (bug 1313937) Backed out changeset 7cab9a1ea25f (bug 1313937) Backed out changeset affc7d1f130d (bug 1313937) Backed out changeset e13aacfe7944 (bug 1843066) Backed out changeset 21a9bee8f772 (bug 1843002) Backed out changeset d381b6365111 (bug 1843002)
This commit is contained in:
Родитель
bb40430223
Коммит
70ab9b117e
|
@ -1174,7 +1174,6 @@ nsresult EventListenerManager::CompileEventHandlerInternal(
|
|||
RefPtr<JS::loader::ScriptFetchOptions> fetchOptions =
|
||||
new JS::loader::ScriptFetchOptions(
|
||||
CORS_NONE, aElement->OwnerDoc()->GetReferrerPolicy(),
|
||||
/* aNonce = */ u""_ns, JS::loader::ParserMetadata::NotParserInserted,
|
||||
aElement->OwnerDoc()->NodePrincipal());
|
||||
|
||||
RefPtr<JS::loader::EventScript> eventScript =
|
||||
|
|
|
@ -271,9 +271,6 @@ already_AddRefed<ModuleLoadRequest> ModuleLoader::CreateDynamicImport(
|
|||
RefPtr<ScriptLoadContext> context = new ScriptLoadContext();
|
||||
|
||||
if (aMaybeActiveScript) {
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#hostloadimportedmodule
|
||||
// Step 6.3. Set fetchOptions to the new descendant script fetch options for
|
||||
// referencingScript's fetch options.
|
||||
options = aMaybeActiveScript->GetFetchOptions();
|
||||
baseURL = aMaybeActiveScript->BaseURL();
|
||||
} else {
|
||||
|
@ -288,19 +285,8 @@ already_AddRefed<ModuleLoadRequest> ModuleLoader::CreateDynamicImport(
|
|||
BasePrincipal::Cast(principal)->ContentScriptAddonPolicy());
|
||||
MOZ_ASSERT_IF(GetKind() == Normal, principal == document->NodePrincipal());
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#hostloadimportedmodule
|
||||
// Step 4. Let fetchOptions be the default classic script fetch options.
|
||||
//
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#default-classic-script-fetch-options
|
||||
// The default classic script fetch options are a script fetch options whose
|
||||
// cryptographic nonce is the empty string, integrity metadata is the empty
|
||||
// string, parser metadata is "not-parser-inserted", credentials mode is
|
||||
// "same-origin", referrer policy is the empty string, and fetch priority is
|
||||
// "auto".
|
||||
options = new ScriptFetchOptions(
|
||||
mozilla::CORS_NONE, document->GetReferrerPolicy(),
|
||||
/* aNonce = */ u""_ns, ParserMetadata::NotParserInserted, principal,
|
||||
nullptr);
|
||||
mozilla::CORS_NONE, document->GetReferrerPolicy(), principal, nullptr);
|
||||
baseURL = document->GetDocBaseURI();
|
||||
}
|
||||
|
||||
|
|
|
@ -396,30 +396,38 @@ nsContentPolicyType ScriptLoadRequestToContentPolicyType(
|
|||
}
|
||||
|
||||
nsresult ScriptLoader::CheckContentPolicy(Document* aDocument,
|
||||
nsIScriptElement* aElement,
|
||||
nsISupports* aContext,
|
||||
const nsAString& aType,
|
||||
const nsAString& aNonce,
|
||||
ScriptLoadRequest* aRequest) {
|
||||
nsContentPolicyType contentPolicyType =
|
||||
ScriptLoadRequestToContentPolicyType(aRequest);
|
||||
|
||||
nsCOMPtr<nsINode> requestingNode = do_QueryInterface(aElement);
|
||||
nsCOMPtr<nsINode> requestingNode = do_QueryInterface(aContext);
|
||||
nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo(
|
||||
aDocument->NodePrincipal(), // loading principal
|
||||
aDocument->NodePrincipal(), // triggering principal
|
||||
requestingNode, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
|
||||
contentPolicyType);
|
||||
secCheckLoadInfo->SetParserCreatedScript(aElement->GetParserCreated() !=
|
||||
mozilla::dom::NOT_FROM_PARSER);
|
||||
// Use nonce of the current element, instead of the preload, because those
|
||||
// are allowed to differ.
|
||||
secCheckLoadInfo->SetCspNonce(aNonce);
|
||||
|
||||
if (aRequest->mIntegrity.IsValid()) {
|
||||
MOZ_ASSERT(!aRequest->mIntegrity.IsEmpty());
|
||||
secCheckLoadInfo->SetIntegrityMetadata(
|
||||
aRequest->mIntegrity.GetIntegrityString());
|
||||
}
|
||||
|
||||
// snapshot the nonce at load start time for performing CSP checks
|
||||
if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT ||
|
||||
contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_MODULE) {
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(aContext);
|
||||
if (node) {
|
||||
nsString* cspNonce =
|
||||
static_cast<nsString*>(node->GetProperty(nsGkAtoms::nonce));
|
||||
if (cspNonce) {
|
||||
secCheckLoadInfo->SetCspNonce(*cspNonce);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
|
||||
nsresult rv = NS_CheckContentLoadPolicy(
|
||||
aRequest->mURI, secCheckLoadInfo, NS_LossyConvertUTF16toASCII(aType),
|
||||
|
@ -623,14 +631,23 @@ nsresult ScriptLoader::StartLoadInternal(
|
|||
}
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
|
||||
loadInfo->SetParserCreatedScript(aRequest->ParserMetadata() ==
|
||||
ParserMetadata::ParserInserted);
|
||||
loadInfo->SetCspNonce(aRequest->Nonce());
|
||||
if (aRequest->mIntegrity.IsValid()) {
|
||||
MOZ_ASSERT(!aRequest->mIntegrity.IsEmpty());
|
||||
loadInfo->SetIntegrityMetadata(aRequest->mIntegrity.GetIntegrityString());
|
||||
}
|
||||
|
||||
// snapshot the nonce at load start time for performing CSP checks
|
||||
if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT ||
|
||||
contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_MODULE) {
|
||||
if (context) {
|
||||
nsString* cspNonce =
|
||||
static_cast<nsString*>(context->GetProperty(nsGkAtoms::nonce));
|
||||
if (cspNonce) {
|
||||
loadInfo->SetCspNonce(*cspNonce);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIScriptGlobalObject> scriptGlobal = GetScriptGlobalObject();
|
||||
if (!scriptGlobal) {
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -814,7 +831,6 @@ bool ScriptLoader::PreloadURIComparator::Equals(const PreloadInfo& aPi,
|
|||
}
|
||||
|
||||
static bool CSPAllowsInlineScript(nsIScriptElement* aElement,
|
||||
const nsAString& aNonce,
|
||||
Document* aDocument) {
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
|
||||
if (!csp) {
|
||||
|
@ -822,14 +838,24 @@ static bool CSPAllowsInlineScript(nsIScriptElement* aElement,
|
|||
return true;
|
||||
}
|
||||
|
||||
// query the nonce
|
||||
nsCOMPtr<Element> scriptContent = do_QueryInterface(aElement);
|
||||
nsAutoString nonce;
|
||||
if (scriptContent) {
|
||||
nsString* cspNonce =
|
||||
static_cast<nsString*>(scriptContent->GetProperty(nsGkAtoms::nonce));
|
||||
if (cspNonce) {
|
||||
nonce = *cspNonce;
|
||||
}
|
||||
}
|
||||
|
||||
bool parserCreated =
|
||||
aElement->GetParserCreated() != mozilla::dom::NOT_FROM_PARSER;
|
||||
nsCOMPtr<Element> element = do_QueryInterface(aElement);
|
||||
|
||||
bool allowInlineScript = false;
|
||||
nsresult rv = csp->GetAllowsInline(
|
||||
nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE,
|
||||
false /* aHasUnsafeHash */, aNonce, parserCreated, element,
|
||||
false /* aHasUnsafeHash */, nonce, parserCreated, scriptContent,
|
||||
nullptr /* nsICSPEventListener */, u""_ns,
|
||||
aElement->GetScriptLineNumber(), aElement->GetScriptColumnNumber(),
|
||||
&allowInlineScript);
|
||||
|
@ -839,13 +865,11 @@ static bool CSPAllowsInlineScript(nsIScriptElement* aElement,
|
|||
already_AddRefed<ScriptLoadRequest> ScriptLoader::CreateLoadRequest(
|
||||
ScriptKind aKind, nsIURI* aURI, nsIScriptElement* aElement,
|
||||
nsIPrincipal* aTriggeringPrincipal, CORSMode aCORSMode,
|
||||
const nsAString& aNonce, const SRIMetadata& aIntegrity,
|
||||
ReferrerPolicy aReferrerPolicy, ParserMetadata aParserMetadata) {
|
||||
const SRIMetadata& aIntegrity, ReferrerPolicy aReferrerPolicy) {
|
||||
nsIURI* referrer = mDocument->GetDocumentURIAsReferrer();
|
||||
nsCOMPtr<Element> domElement = do_QueryInterface(aElement);
|
||||
RefPtr<ScriptFetchOptions> fetchOptions =
|
||||
new ScriptFetchOptions(aCORSMode, aReferrerPolicy, aNonce,
|
||||
aParserMetadata, aTriggeringPrincipal, domElement);
|
||||
RefPtr<ScriptFetchOptions> fetchOptions = new ScriptFetchOptions(
|
||||
aCORSMode, aReferrerPolicy, aTriggeringPrincipal, domElement);
|
||||
RefPtr<ScriptLoadContext> context = new ScriptLoadContext();
|
||||
|
||||
if (aKind == ScriptKind::eClassic || aKind == ScriptKind::eImportMap) {
|
||||
|
@ -907,12 +931,6 @@ bool ScriptLoader::ProcessScriptElement(nsIScriptElement* aElement,
|
|||
return ProcessInlineScript(aElement, scriptKind);
|
||||
}
|
||||
|
||||
static ParserMetadata GetParserMetadata(nsIScriptElement* aElement) {
|
||||
return aElement->GetParserCreated() == mozilla::dom::NOT_FROM_PARSER
|
||||
? ParserMetadata::NotParserInserted
|
||||
: ParserMetadata::ParserInserted;
|
||||
}
|
||||
|
||||
bool ScriptLoader::ProcessExternalScript(nsIScriptElement* aElement,
|
||||
ScriptKind aScriptKind,
|
||||
const nsAutoString& aTypeAttr,
|
||||
|
@ -943,12 +961,6 @@ bool ScriptLoader::ProcessExternalScript(nsIScriptElement* aElement,
|
|||
return false;
|
||||
}
|
||||
|
||||
nsAutoString nonce;
|
||||
if (nsString* cspNonce = static_cast<nsString*>(
|
||||
aScriptContent->GetProperty(nsGkAtoms::nonce))) {
|
||||
nonce = *cspNonce;
|
||||
}
|
||||
|
||||
SRIMetadata sriMetadata;
|
||||
{
|
||||
nsAutoString integrity;
|
||||
|
@ -959,8 +971,8 @@ bool ScriptLoader::ProcessExternalScript(nsIScriptElement* aElement,
|
|||
RefPtr<ScriptLoadRequest> request =
|
||||
LookupPreloadRequest(aElement, aScriptKind, sriMetadata);
|
||||
|
||||
if (request && NS_FAILED(CheckContentPolicy(mDocument, aElement, aTypeAttr,
|
||||
nonce, request))) {
|
||||
if (request &&
|
||||
NS_FAILED(CheckContentPolicy(mDocument, aElement, aTypeAttr, request))) {
|
||||
LOG(("ScriptLoader (%p): content policy check failed for preload", this));
|
||||
|
||||
// Probably plans have changed; even though the preload was allowed seems
|
||||
|
@ -1021,11 +1033,9 @@ bool ScriptLoader::ProcessExternalScript(nsIScriptElement* aElement,
|
|||
|
||||
CORSMode ourCORSMode = aElement->GetCORSMode();
|
||||
ReferrerPolicy referrerPolicy = GetReferrerPolicy(aElement);
|
||||
ParserMetadata parserMetadata = GetParserMetadata(aElement);
|
||||
|
||||
request = CreateLoadRequest(aScriptKind, scriptURI, aElement, principal,
|
||||
ourCORSMode, nonce, sriMetadata, referrerPolicy,
|
||||
parserMetadata);
|
||||
ourCORSMode, sriMetadata, referrerPolicy);
|
||||
request->GetScriptLoadContext()->mIsInline = false;
|
||||
request->GetScriptLoadContext()->SetScriptMode(
|
||||
aElement->GetScriptDeferred(), aElement->GetScriptAsync(), false);
|
||||
|
@ -1152,15 +1162,8 @@ bool ScriptLoader::ProcessInlineScript(nsIScriptElement* aElement,
|
|||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(aElement);
|
||||
nsAutoString nonce;
|
||||
if (nsString* cspNonce =
|
||||
static_cast<nsString*>(node->GetProperty(nsGkAtoms::nonce))) {
|
||||
nonce = *cspNonce;
|
||||
}
|
||||
|
||||
// Does CSP allow this inline script to run?
|
||||
if (!CSPAllowsInlineScript(aElement, nonce, mDocument)) {
|
||||
if (!CSPAllowsInlineScript(aElement, mDocument)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1192,14 +1195,13 @@ bool ScriptLoader::ProcessInlineScript(nsIScriptElement* aElement,
|
|||
if (aScriptKind == ScriptKind::eModule) {
|
||||
corsMode = aElement->GetCORSMode();
|
||||
}
|
||||
ReferrerPolicy referrerPolicy = GetReferrerPolicy(aElement);
|
||||
ParserMetadata parserMetadata = GetParserMetadata(aElement);
|
||||
|
||||
ReferrerPolicy referrerPolicy = GetReferrerPolicy(aElement);
|
||||
RefPtr<ScriptLoadRequest> request =
|
||||
CreateLoadRequest(aScriptKind, mDocument->GetDocumentURI(), aElement,
|
||||
mDocument->NodePrincipal(), corsMode, nonce,
|
||||
mDocument->NodePrincipal(), corsMode,
|
||||
SRIMetadata(), // SRI doesn't apply
|
||||
referrerPolicy, parserMetadata);
|
||||
referrerPolicy);
|
||||
request->GetScriptLoadContext()->mIsInline = true;
|
||||
request->GetScriptLoadContext()->mLineNo = aElement->GetScriptLineNumber();
|
||||
request->GetScriptLoadContext()->mColumnNo =
|
||||
|
@ -3542,12 +3544,14 @@ void ScriptLoader::ParsingComplete(bool aTerminated) {
|
|||
}
|
||||
}
|
||||
|
||||
void ScriptLoader::PreloadURI(
|
||||
nsIURI* aURI, const nsAString& aCharset, const nsAString& aType,
|
||||
const nsAString& aCrossOrigin, const nsAString& aNonce,
|
||||
const nsAString& aIntegrity, bool aScriptFromHead, bool aAsync, bool aDefer,
|
||||
bool aNoModule, bool aLinkPreload, const ReferrerPolicy aReferrerPolicy,
|
||||
uint64_t aEarlyHintPreloaderId) {
|
||||
void ScriptLoader::PreloadURI(nsIURI* aURI, const nsAString& aCharset,
|
||||
const nsAString& aType,
|
||||
const nsAString& aCrossOrigin,
|
||||
const nsAString& aIntegrity, bool aScriptFromHead,
|
||||
bool aAsync, bool aDefer, bool aNoModule,
|
||||
bool aLinkPreload,
|
||||
const ReferrerPolicy aReferrerPolicy,
|
||||
uint64_t aEarlyHintPreloaderId) {
|
||||
NS_ENSURE_TRUE_VOID(mDocument);
|
||||
// Check to see if scripts has been turned off.
|
||||
if (!mEnabled || !mDocument->IsScriptEnabled()) {
|
||||
|
@ -3578,22 +3582,9 @@ void ScriptLoader::PreloadURI(
|
|||
SRIMetadata sriMetadata;
|
||||
GetSRIMetadata(aIntegrity, &sriMetadata);
|
||||
|
||||
// For link type "modulepreload":
|
||||
// https://html.spec.whatwg.org/multipage/links.html#link-type-modulepreload
|
||||
// Step 11. Let options be a script fetch options whose cryptographic nonce is
|
||||
// cryptographic nonce, integrity metadata is integrity metadata, parser
|
||||
// metadata is "not-parser-inserted", credentials mode is credentials mode,
|
||||
// referrer policy is referrer policy, and fetch priority is fetch priority.
|
||||
//
|
||||
// We treat speculative <script> loads as parser-inserted, because they
|
||||
// come from a parser. This will also match how they should be treated
|
||||
// as a normal load.
|
||||
RefPtr<ScriptLoadRequest> request =
|
||||
CreateLoadRequest(scriptKind, aURI, nullptr, mDocument->NodePrincipal(),
|
||||
Element::StringToCORSMode(aCrossOrigin), aNonce,
|
||||
sriMetadata, aReferrerPolicy,
|
||||
aLinkPreload ? ParserMetadata::NotParserInserted
|
||||
: ParserMetadata::ParserInserted);
|
||||
RefPtr<ScriptLoadRequest> request = CreateLoadRequest(
|
||||
scriptKind, aURI, nullptr, mDocument->NodePrincipal(),
|
||||
Element::StringToCORSMode(aCrossOrigin), sriMetadata, aReferrerPolicy);
|
||||
request->GetScriptLoadContext()->mIsInline = false;
|
||||
request->GetScriptLoadContext()->mScriptFromHead = aScriptFromHead;
|
||||
request->GetScriptLoadContext()->SetScriptMode(aDefer, aAsync, aLinkPreload);
|
||||
|
|
|
@ -55,8 +55,6 @@ class ModuleScript;
|
|||
class ScriptLoadRequest;
|
||||
class ScriptLoadRequestList;
|
||||
|
||||
enum class ParserMetadata;
|
||||
|
||||
} // namespace loader
|
||||
} // namespace JS
|
||||
|
||||
|
@ -378,9 +376,9 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
|
|||
*/
|
||||
virtual void PreloadURI(nsIURI* aURI, const nsAString& aCharset,
|
||||
const nsAString& aType, const nsAString& aCrossOrigin,
|
||||
const nsAString& aNonce, const nsAString& aIntegrity,
|
||||
bool aScriptFromHead, bool aAsync, bool aDefer,
|
||||
bool aNoModule, bool aLinkPreload,
|
||||
const nsAString& aIntegrity, bool aScriptFromHead,
|
||||
bool aAsync, bool aDefer, bool aNoModule,
|
||||
bool aLinkPreload,
|
||||
const ReferrerPolicy aReferrerPolicy,
|
||||
uint64_t aEarlyHintPreloaderId);
|
||||
|
||||
|
@ -429,9 +427,7 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
|
|||
already_AddRefed<ScriptLoadRequest> CreateLoadRequest(
|
||||
ScriptKind aKind, nsIURI* aURI, nsIScriptElement* aElement,
|
||||
nsIPrincipal* aTriggeringPrincipal, mozilla::CORSMode aCORSMode,
|
||||
const nsAString& aNonce, const SRIMetadata& aIntegrity,
|
||||
ReferrerPolicy aReferrerPolicy,
|
||||
JS::loader::ParserMetadata aParserMetadata);
|
||||
const SRIMetadata& aIntegrity, ReferrerPolicy aReferrerPolicy);
|
||||
|
||||
/**
|
||||
* Unblocks the creator parser of the parser-blocking scripts.
|
||||
|
@ -465,10 +461,8 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
|
|||
/**
|
||||
* Helper function to check the content policy for a given request.
|
||||
*/
|
||||
static nsresult CheckContentPolicy(Document* aDocument,
|
||||
nsIScriptElement* aElement,
|
||||
static nsresult CheckContentPolicy(Document* aDocument, nsISupports* aContext,
|
||||
const nsAString& aType,
|
||||
const nsAString& aNonce,
|
||||
ScriptLoadRequest* aRequest);
|
||||
|
||||
/**
|
||||
|
|
|
@ -481,7 +481,8 @@ nsCSPContext::GetAllowsEval(bool* outShouldReportViolation,
|
|||
*outAllowsEval = true;
|
||||
|
||||
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
|
||||
if (!mPolicies[i]->allows(SCRIPT_SRC_DIRECTIVE, CSP_UNSAFE_EVAL, u""_ns)) {
|
||||
if (!mPolicies[i]->allows(SCRIPT_SRC_DIRECTIVE, CSP_UNSAFE_EVAL, u""_ns,
|
||||
false)) {
|
||||
// policy is violated: must report the violation and allow the inline
|
||||
// script if the policy is report-only.
|
||||
*outShouldReportViolation = true;
|
||||
|
@ -508,8 +509,9 @@ nsCSPContext::GetAllowsWasmEval(bool* outShouldReportViolation,
|
|||
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
|
||||
// Either 'unsafe-eval' or 'wasm-unsafe-eval' can allow this
|
||||
if (!mPolicies[i]->allows(SCRIPT_SRC_DIRECTIVE, CSP_WASM_UNSAFE_EVAL,
|
||||
u""_ns) &&
|
||||
!mPolicies[i]->allows(SCRIPT_SRC_DIRECTIVE, CSP_UNSAFE_EVAL, u""_ns)) {
|
||||
u""_ns, false) &&
|
||||
!mPolicies[i]->allows(SCRIPT_SRC_DIRECTIVE, CSP_UNSAFE_EVAL, u""_ns,
|
||||
false)) {
|
||||
// policy is violated: must report the violation and allow the inline
|
||||
// script if the policy is report-only.
|
||||
*outShouldReportViolation = true;
|
||||
|
@ -606,17 +608,15 @@ nsCSPContext::GetAllowsInline(CSPDirective aDirective, bool aHasUnsafeHash,
|
|||
|
||||
// always iterate all policies, otherwise we might not send out all reports
|
||||
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
|
||||
// https://w3c.github.io/webappsec-csp/#match-element-to-source-list
|
||||
// Step 1. If §6.7.3.2 Does a source list allow all inline behavior for
|
||||
// type? returns "Allows" given list and type, return "Matches".
|
||||
if (mPolicies[i]->allowsAllInlineBehavior(aDirective)) {
|
||||
continue;
|
||||
}
|
||||
bool allowed =
|
||||
mPolicies[i]->allows(aDirective, CSP_UNSAFE_INLINE, u""_ns,
|
||||
aParserCreated) ||
|
||||
mPolicies[i]->allows(aDirective, CSP_NONCE, aNonce, aParserCreated);
|
||||
|
||||
// Step 2. If type is "script" or "style", and §6.7.3.1 Is element
|
||||
// nonceable? returns "Nonceable" when executed upon element: [...]
|
||||
// TODO(Bug 1397308) Implement "is element nonceable?" CSP checks
|
||||
if (mPolicies[i]->allows(aDirective, CSP_NONCE, aNonce)) {
|
||||
// If the inlined script or style is allowed by either unsafe-inline or the
|
||||
// nonce, go ahead and shortcut this loop so we can avoid allocating
|
||||
// unecessary strings
|
||||
if (allowed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -634,28 +634,15 @@ nsCSPContext::GetAllowsInline(CSPDirective aDirective, bool aHasUnsafeHash,
|
|||
content = aContentOfPseudoScript;
|
||||
}
|
||||
|
||||
// Step 3. Let unsafe-hashes flag be false.
|
||||
// Step 4. For each expression of list: [...]
|
||||
bool unsafeHashesFlag =
|
||||
mPolicies[i]->allows(aDirective, CSP_UNSAFE_HASHES, u""_ns);
|
||||
|
||||
// Per https://w3c.github.io/webappsec-csp/#match-element-to-source-list
|
||||
// Step 5. If type is "script" or "style", or unsafe-hashes flag is true:
|
||||
//
|
||||
// aHasUnsafeHash is true for event handlers (type "script attribute"),
|
||||
// style= attributes (type "style attribute") and the javascript: protocol.
|
||||
if (!aHasUnsafeHash || unsafeHashesFlag) {
|
||||
if (mPolicies[i]->allows(aDirective, CSP_HASH, content)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(Bug 1844290): Figure out how/if strict-dynamic for inline scripts is
|
||||
// specified
|
||||
bool allowed = false;
|
||||
if ((aDirective == SCRIPT_SRC_ELEM_DIRECTIVE ||
|
||||
aDirective == SCRIPT_SRC_ATTR_DIRECTIVE) &&
|
||||
mPolicies[i]->allows(aDirective, CSP_STRICT_DYNAMIC, u""_ns)) {
|
||||
allowed = !aParserCreated;
|
||||
if (!aHasUnsafeHash || mPolicies[i]->allows(aDirective, CSP_UNSAFE_HASHES,
|
||||
u""_ns, aParserCreated)) {
|
||||
allowed =
|
||||
mPolicies[i]->allows(aDirective, CSP_HASH, content, aParserCreated);
|
||||
}
|
||||
|
||||
if (!allowed) {
|
||||
|
@ -822,7 +809,7 @@ nsCSPContext::LogViolationDetails(
|
|||
for (uint32_t p = 0; p < mPolicies.Length(); p++) {
|
||||
NS_ASSERTION(mPolicies[p], "null pointer in nsTArray<nsCSPPolicy>");
|
||||
|
||||
if (mPolicies[p]->allows(SCRIPT_SRC_DIRECTIVE, keyword, u""_ns)) {
|
||||
if (mPolicies[p]->allows(SCRIPT_SRC_DIRECTIVE, keyword, u""_ns, false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -414,14 +414,13 @@ nsCSPBaseSrc* nsCSPParser::keywordSource() {
|
|||
!CSP_IsDirective(mCurDir[0],
|
||||
nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) &&
|
||||
!CSP_IsDirective(mCurDir[0],
|
||||
nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE) &&
|
||||
!CSP_IsDirective(mCurDir[0],
|
||||
nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE)) {
|
||||
nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE)) {
|
||||
// Todo: Enforce 'strict-dynamic' within default-src; see Bug 1313937
|
||||
AutoTArray<nsString, 1> params = {u"strict-dynamic"_ns};
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag,
|
||||
"ignoringStrictDynamic", params);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mStrictDynamic = true;
|
||||
return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
|
||||
}
|
||||
|
@ -1074,26 +1073,40 @@ void nsCSPParser::directive() {
|
|||
srcs.InsertElementAt(0, keyword);
|
||||
}
|
||||
|
||||
// If policy contains 'strict-dynamic' warn about ignored sources.
|
||||
if (mStrictDynamic &&
|
||||
!CSP_IsDirective(mCurDir[0],
|
||||
nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE)) {
|
||||
// If policy contains 'strict-dynamic' invalidate all srcs within script-src.
|
||||
if (mStrictDynamic) {
|
||||
MOZ_ASSERT(
|
||||
cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) ||
|
||||
cspDir->equals(
|
||||
nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) ||
|
||||
cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE),
|
||||
"strict-dynamic only allowed within script-src(-elem|attr)");
|
||||
for (uint32_t i = 0; i < srcs.Length(); i++) {
|
||||
// Please note that nsCSPNonceSrc as well as nsCSPHashSrc overwrite
|
||||
// invalidate(), so it's fine to just call invalidate() on all srcs.
|
||||
// Please also note that nsCSPKeywordSrc() can not be invalidated and
|
||||
// always returns false unless the keyword is 'strict-dynamic' in which
|
||||
// case we allow the load if the script is not parser created!
|
||||
srcs[i]->invalidate();
|
||||
// Log a message to the console that src will be ignored.
|
||||
nsAutoString srcStr;
|
||||
srcs[i]->toString(srcStr);
|
||||
// Hashes and nonces continue to apply with 'strict-dynamic', as well as
|
||||
// 'unsafe-eval', 'wasm-unsafe-eval' and 'unsafe-hashes'.
|
||||
if (!srcs[i]->isKeyword(CSP_STRICT_DYNAMIC) &&
|
||||
!srcs[i]->isKeyword(CSP_UNSAFE_EVAL) &&
|
||||
!srcs[i]->isKeyword(CSP_WASM_UNSAFE_EVAL) &&
|
||||
!srcs[i]->isKeyword(CSP_UNSAFE_HASHES) && !srcs[i]->isNonce() &&
|
||||
!srcs[i]->isHash()) {
|
||||
// Even though we invalidate all of the srcs internally, we don't want to
|
||||
// log messages for the srcs: 'strict-dynamic', 'unsafe-inline',
|
||||
// 'unsafe-hashes', nonces, and hashes, because those still apply even
|
||||
// with 'strict-dynamic'.
|
||||
// TODO the comment seems wrong 'unsafe-eval' vs 'unsafe-inline'.
|
||||
if (!srcStr.EqualsASCII(CSP_EnumToUTF8Keyword(CSP_STRICT_DYNAMIC)) &&
|
||||
!srcStr.EqualsASCII(CSP_EnumToUTF8Keyword(CSP_UNSAFE_EVAL)) &&
|
||||
!srcStr.EqualsASCII(CSP_EnumToUTF8Keyword(CSP_UNSAFE_HASHES)) &&
|
||||
!StringBeginsWith(
|
||||
srcStr, nsDependentString(CSP_EnumToUTF16Keyword(CSP_NONCE))) &&
|
||||
!StringBeginsWith(srcStr, u"'sha"_ns)) {
|
||||
AutoTArray<nsString, 2> params = {srcStr, mCurDir[0]};
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag,
|
||||
"ignoringScriptSrcForStrictDynamic", params);
|
||||
}
|
||||
}
|
||||
|
||||
// Log a warning that all scripts might be blocked because the policy
|
||||
// contains 'strict-dynamic' but no valid nonce or hash.
|
||||
if (!mHasHashOrNonce) {
|
||||
|
@ -1114,6 +1127,8 @@ void nsCSPParser::directive() {
|
|||
cspDir->equals(nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE) ||
|
||||
cspDir->equals(nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE) ||
|
||||
cspDir->equals(nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE))) {
|
||||
mUnsafeInlineKeywordSrc->invalidate();
|
||||
|
||||
// Log to the console that unsafe-inline will be ignored.
|
||||
AutoTArray<nsString, 2> params = {u"'unsafe-inline'"_ns, mCurDir[0]};
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag,
|
||||
|
|
|
@ -525,15 +525,16 @@ nsresult CSP_AppendCSPFromHeader(nsIContentSecurityPolicy* aCsp,
|
|||
|
||||
/* ===== nsCSPSrc ============================ */
|
||||
|
||||
nsCSPBaseSrc::nsCSPBaseSrc() {}
|
||||
nsCSPBaseSrc::nsCSPBaseSrc() : mInvalidated(false) {}
|
||||
|
||||
nsCSPBaseSrc::~nsCSPBaseSrc() = default;
|
||||
|
||||
// ::permits is only called for external load requests, therefore:
|
||||
// nsCSPKeywordSrc and nsCSPHashSource fall back to this base class
|
||||
// implementation which will never allow the load.
|
||||
bool nsCSPBaseSrc::permits(nsIURI* aUri, bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure) const {
|
||||
bool nsCSPBaseSrc::permits(nsIURI* aUri, const nsAString& aNonce,
|
||||
bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure, bool aParserCreated) const {
|
||||
if (CSPUTILSLOGENABLED()) {
|
||||
CSPUTILSLOG(
|
||||
("nsCSPBaseSrc::permits, aUri: %s", aUri->GetSpecOrDefault().get()));
|
||||
|
@ -545,7 +546,8 @@ bool nsCSPBaseSrc::permits(nsIURI* aUri, bool aWasRedirected, bool aReportOnly,
|
|||
// nsCSPSchemeSrc, nsCSPHostSrc fall back
|
||||
// to this base class implementation which will never allow the load.
|
||||
bool nsCSPBaseSrc::allows(enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const {
|
||||
const nsAString& aHashOrNonce,
|
||||
bool aParserCreated) const {
|
||||
CSPUTILSLOG(("nsCSPBaseSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
|
||||
aKeyword == CSP_HASH ? "hash" : CSP_EnumToUTF8Keyword(aKeyword),
|
||||
NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
|
||||
|
@ -560,13 +562,17 @@ nsCSPSchemeSrc::nsCSPSchemeSrc(const nsAString& aScheme) : mScheme(aScheme) {
|
|||
|
||||
nsCSPSchemeSrc::~nsCSPSchemeSrc() = default;
|
||||
|
||||
bool nsCSPSchemeSrc::permits(nsIURI* aUri, bool aWasRedirected,
|
||||
bool aReportOnly, bool aUpgradeInsecure) const {
|
||||
bool nsCSPSchemeSrc::permits(nsIURI* aUri, const nsAString& aNonce,
|
||||
bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure, bool aParserCreated) const {
|
||||
if (CSPUTILSLOGENABLED()) {
|
||||
CSPUTILSLOG(
|
||||
("nsCSPSchemeSrc::permits, aUri: %s", aUri->GetSpecOrDefault().get()));
|
||||
}
|
||||
MOZ_ASSERT((!mScheme.EqualsASCII("")), "scheme can not be the empty string");
|
||||
if (mInvalidated) {
|
||||
return false;
|
||||
}
|
||||
return permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure, false);
|
||||
}
|
||||
|
||||
|
@ -684,14 +690,15 @@ bool permitsPort(const nsAString& aEnforcementScheme,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool nsCSPHostSrc::permits(nsIURI* aUri, bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure) const {
|
||||
bool nsCSPHostSrc::permits(nsIURI* aUri, const nsAString& aNonce,
|
||||
bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure, bool aParserCreated) const {
|
||||
if (CSPUTILSLOGENABLED()) {
|
||||
CSPUTILSLOG(
|
||||
("nsCSPHostSrc::permits, aUri: %s", aUri->GetSpecOrDefault().get()));
|
||||
}
|
||||
|
||||
if (mIsUniqueOrigin) {
|
||||
if (mInvalidated || mIsUniqueOrigin) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -857,12 +864,42 @@ nsCSPKeywordSrc::nsCSPKeywordSrc(enum CSPKeyword aKeyword)
|
|||
|
||||
nsCSPKeywordSrc::~nsCSPKeywordSrc() = default;
|
||||
|
||||
bool nsCSPKeywordSrc::permits(nsIURI* aUri, const nsAString& aNonce,
|
||||
bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure,
|
||||
bool aParserCreated) const {
|
||||
// no need to check for invalidated, this will always return false unless
|
||||
// it is an nsCSPKeywordSrc for 'strict-dynamic', which should allow non
|
||||
// parser created scripts.
|
||||
return ((mKeyword == CSP_STRICT_DYNAMIC) && !aParserCreated);
|
||||
}
|
||||
|
||||
bool nsCSPKeywordSrc::allows(enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const {
|
||||
CSPUTILSLOG(("nsCSPKeywordSrc::allows, aKeyWord: %s, aHashOrNonce: %s",
|
||||
CSP_EnumToUTF8Keyword(aKeyword),
|
||||
NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
|
||||
return mKeyword == aKeyword;
|
||||
const nsAString& aHashOrNonce,
|
||||
bool aParserCreated) const {
|
||||
CSPUTILSLOG(
|
||||
("nsCSPKeywordSrc::allows, aKeyWord: %s, aHashOrNonce: %s, mInvalidated: "
|
||||
"%s",
|
||||
CSP_EnumToUTF8Keyword(aKeyword),
|
||||
NS_ConvertUTF16toUTF8(aHashOrNonce).get(),
|
||||
mInvalidated ? "true" : "false"));
|
||||
|
||||
if (mInvalidated) {
|
||||
// only 'self', 'report-sample' and 'unsafe-inline' are keywords that can be
|
||||
// ignored. Please note that the parser already translates 'self' into a uri
|
||||
// (see assertion in constructor).
|
||||
MOZ_ASSERT(mKeyword == CSP_UNSAFE_INLINE || mKeyword == CSP_REPORT_SAMPLE,
|
||||
"should only invalidate unsafe-inline");
|
||||
return false;
|
||||
}
|
||||
// either the keyword allows the load or the policy contains 'strict-dynamic',
|
||||
// in which case we have to make sure the script is not parser created before
|
||||
// allowing the load and also eval & wasm-eval should be blocked even if
|
||||
// 'strict-dynamic' is present. Should be allowed only if 'unsafe-eval' is
|
||||
// present.
|
||||
return ((mKeyword == aKeyword) ||
|
||||
((mKeyword == CSP_STRICT_DYNAMIC) && !aParserCreated &&
|
||||
aKeyword != CSP_UNSAFE_EVAL && aKeyword != CSP_WASM_UNSAFE_EVAL));
|
||||
}
|
||||
|
||||
bool nsCSPKeywordSrc::visit(nsCSPSrcVisitor* aVisitor) const {
|
||||
|
@ -879,8 +916,41 @@ nsCSPNonceSrc::nsCSPNonceSrc(const nsAString& aNonce) : mNonce(aNonce) {}
|
|||
|
||||
nsCSPNonceSrc::~nsCSPNonceSrc() = default;
|
||||
|
||||
bool nsCSPNonceSrc::permits(nsIURI* aUri, const nsAString& aNonce,
|
||||
bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure, bool aParserCreated) const {
|
||||
if (CSPUTILSLOGENABLED()) {
|
||||
CSPUTILSLOG(("nsCSPNonceSrc::permits, aUri: %s, aNonce: %s",
|
||||
aUri->GetSpecOrDefault().get(),
|
||||
NS_ConvertUTF16toUTF8(aNonce).get()));
|
||||
}
|
||||
|
||||
if (aReportOnly && aWasRedirected && aNonce.IsEmpty()) {
|
||||
/* Fix for Bug 1505412
|
||||
* If we land here, we're currently handling a script-preload which got
|
||||
* redirected. Preloads do not have any info about the nonce assiociated.
|
||||
* Because of Report-Only the preload passes the 1st CSP-check so the
|
||||
* preload does not get retried with a nonce attached.
|
||||
* Currently we're relying on the script-manager to
|
||||
* provide a fake loadinfo to check the preloads against csp.
|
||||
* So during HTTPChannel->OnRedirect we cant check csp for this case.
|
||||
* But as the script-manager already checked the csp,
|
||||
* a report would already have been send,
|
||||
* if the nonce didnt match.
|
||||
* So we can pass the check here for Report-Only Cases.
|
||||
*/
|
||||
MOZ_ASSERT(aParserCreated == false,
|
||||
"Skipping nonce-check is only allowed for Preloads");
|
||||
return true;
|
||||
}
|
||||
|
||||
// nonces can not be invalidated by strict-dynamic
|
||||
return mNonce.Equals(aNonce);
|
||||
}
|
||||
|
||||
bool nsCSPNonceSrc::allows(enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const {
|
||||
const nsAString& aHashOrNonce,
|
||||
bool aParserCreated) const {
|
||||
CSPUTILSLOG(("nsCSPNonceSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
|
||||
CSP_EnumToUTF8Keyword(aKeyword),
|
||||
NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
|
||||
|
@ -926,7 +996,8 @@ nsCSPHashSrc::nsCSPHashSrc(const nsAString& aAlgo, const nsAString& aHash)
|
|||
nsCSPHashSrc::~nsCSPHashSrc() = default;
|
||||
|
||||
bool nsCSPHashSrc::allows(enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const {
|
||||
const nsAString& aHashOrNonce,
|
||||
bool aParserCreated) const {
|
||||
CSPUTILSLOG(("nsCSPHashSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
|
||||
CSP_EnumToUTF8Keyword(aKeyword),
|
||||
NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
|
||||
|
@ -1010,36 +1081,20 @@ nsCSPDirective::~nsCSPDirective() {
|
|||
}
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webappsec-csp/#match-nonce-to-source-list
|
||||
static bool DoesNonceMatchSourceList(nsILoadInfo* aLoadInfo,
|
||||
const nsTArray<nsCSPBaseSrc*>& aSrcs) {
|
||||
// Step 1. Assert: source list is not null. (implicit)
|
||||
|
||||
// Note: For code-reuse we do "request’s cryptographic nonce metadata" here
|
||||
// instead of the caller.
|
||||
nsAutoString nonce;
|
||||
MOZ_ALWAYS_SUCCEEDS(aLoadInfo->GetCspNonce(nonce));
|
||||
|
||||
// Step 2. If nonce is the empty string, return "Does Not Match".
|
||||
if (nonce.IsEmpty()) {
|
||||
return false;
|
||||
// This check only considers types "script-like"
|
||||
// (https://fetch.spec.whatwg.org/#request-destination-script-like) that can
|
||||
// also have integrity metadata.
|
||||
static bool IsScriptLikeWithIntegrity(nsContentPolicyType aType) {
|
||||
switch (aType) {
|
||||
case nsIContentPolicy::TYPE_SCRIPT:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SCRIPT:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_MODULE:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 3. For each expression of source list:
|
||||
for (nsCSPBaseSrc* src : aSrcs) {
|
||||
// Step 3.1. If expression matches the nonce-source grammar, and nonce is
|
||||
// identical to expression’s base64-value part, return "Matches".
|
||||
if (src->isNonce()) {
|
||||
nsAutoString srcNonce;
|
||||
static_cast<nsCSPNonceSrc*>(src)->getNonce(srcNonce);
|
||||
if (srcNonce == nonce) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4. Return "Does Not Match".
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/SRI/#parse-metadata
|
||||
|
@ -1081,8 +1136,9 @@ static nsTArray<SRIMetadata> ParseSRIMetadata(const nsAString& aMetadata) {
|
|||
}
|
||||
|
||||
bool nsCSPDirective::permits(CSPDirective aDirective, nsILoadInfo* aLoadInfo,
|
||||
nsIURI* aUri, bool aWasRedirected,
|
||||
bool aReportOnly, bool aUpgradeInsecure) const {
|
||||
nsIURI* aUri, const nsAString& aNonce,
|
||||
bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure, bool aParserCreated) const {
|
||||
MOZ_ASSERT(equals(aDirective) || isDefaultDirective());
|
||||
|
||||
if (CSPUTILSLOGENABLED()) {
|
||||
|
@ -1090,45 +1146,25 @@ bool nsCSPDirective::permits(CSPDirective aDirective, nsILoadInfo* aLoadInfo,
|
|||
("nsCSPDirective::permits, aUri: %s", aUri->GetSpecOrDefault().get()));
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webappsec-csp/#script-pre-request
|
||||
if (aLoadInfo) {
|
||||
// https://w3c.github.io/webappsec-csp/#style-src-elem-pre-request
|
||||
if (aDirective == CSPDirective::STYLE_SRC_ELEM_DIRECTIVE) {
|
||||
// Step 3. If the result of executing §6.7.2.3 Does nonce match source
|
||||
// list? on request’s cryptographic nonce metadata and this directive’s
|
||||
// value is "Matches", return "Allowed".
|
||||
if (DoesNonceMatchSourceList(aLoadInfo, mSrcs)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webappsec-csp/#script-pre-request
|
||||
// Step 1. If request’s destination is script-like:
|
||||
else if (aDirective == CSPDirective::SCRIPT_SRC_ELEM_DIRECTIVE ||
|
||||
aDirective == CSPDirective::WORKER_SRC_DIRECTIVE) {
|
||||
// Step 1.1. If the result of executing §6.7.2.3 Does nonce match source
|
||||
// list? on request’s cryptographic nonce metadata and this directive’s
|
||||
// value is "Matches", return "Allowed".
|
||||
if (DoesNonceMatchSourceList(aLoadInfo, mSrcs)) {
|
||||
return true;
|
||||
}
|
||||
if (IsScriptLikeWithIntegrity(aLoadInfo->InternalContentPolicyType()) &&
|
||||
StaticPrefs::security_csp_external_hashes_enabled()) {
|
||||
MOZ_ASSERT(aDirective == CSPDirective::SCRIPT_SRC_ELEM_DIRECTIVE);
|
||||
|
||||
// Step 1.2. Let integrity expressions be the set of source expressions in
|
||||
// directive’s value that match the hash-source grammar.
|
||||
nsTArray<nsCSPHashSrc*> integrityExpressions;
|
||||
bool hasStrictDynamicKeyword =
|
||||
false; // Optimization to reduce number of iterations.
|
||||
for (uint32_t i = 0; i < mSrcs.Length(); i++) {
|
||||
if (mSrcs[i]->isHash()) {
|
||||
integrityExpressions.AppendElement(
|
||||
static_cast<nsCSPHashSrc*>(mSrcs[i]));
|
||||
} else if (mSrcs[i]->isKeyword(CSP_STRICT_DYNAMIC)) {
|
||||
hasStrictDynamicKeyword = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1.3. If integrity expressions is not empty:
|
||||
if (!integrityExpressions.IsEmpty() &&
|
||||
StaticPrefs::security_csp_external_hashes_enabled()) {
|
||||
if (!integrityExpressions.IsEmpty()) {
|
||||
// Step 1.3.1. Let integrity sources be the result of executing the
|
||||
// algorithm defined in [SRI 3.3.3 Parse metadata] on request’s
|
||||
// integrity metadata.
|
||||
|
@ -1194,23 +1230,12 @@ bool nsCSPDirective::permits(CSPDirective aDirective, nsILoadInfo* aLoadInfo,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1.4. If directive’s value contains a source expression that is an
|
||||
// ASCII case-insensitive match for the "'strict-dynamic'" keyword-source:
|
||||
|
||||
// XXX I don't think we should apply strict-dynamic to XSLT.
|
||||
if (hasStrictDynamicKeyword && aLoadInfo->InternalContentPolicyType() !=
|
||||
nsIContentPolicy::TYPE_XSLT) {
|
||||
// Step 1.4.1 If the request’s parser metadata is "parser-inserted",
|
||||
// return "Blocked". Otherwise, return "Allowed".
|
||||
return !aLoadInfo->GetParserCreatedScript();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < mSrcs.Length(); i++) {
|
||||
if (mSrcs[i]->permits(aUri, aWasRedirected, aReportOnly,
|
||||
aUpgradeInsecure)) {
|
||||
if (mSrcs[i]->permits(aUri, aNonce, aWasRedirected, aReportOnly,
|
||||
aUpgradeInsecure, aParserCreated)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1218,53 +1243,20 @@ bool nsCSPDirective::permits(CSPDirective aDirective, nsILoadInfo* aLoadInfo,
|
|||
}
|
||||
|
||||
bool nsCSPDirective::allows(enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const {
|
||||
const nsAString& aHashOrNonce,
|
||||
bool aParserCreated) const {
|
||||
CSPUTILSLOG(("nsCSPDirective::allows, aKeyWord: %s, a HashOrNonce: %s",
|
||||
CSP_EnumToUTF8Keyword(aKeyword),
|
||||
NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
|
||||
|
||||
for (uint32_t i = 0; i < mSrcs.Length(); i++) {
|
||||
if (mSrcs[i]->allows(aKeyword, aHashOrNonce)) {
|
||||
if (mSrcs[i]->allows(aKeyword, aHashOrNonce, aParserCreated)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webappsec-csp/#allow-all-inline
|
||||
bool nsCSPDirective::allowsAllInlineBehavior(CSPDirective aDir) const {
|
||||
// Step 1. Let allow all inline be false.
|
||||
bool allowAll = false;
|
||||
|
||||
// Step 2. For each expression of list:
|
||||
for (nsCSPBaseSrc* src : mSrcs) {
|
||||
// Step 2.1. If expression matches the nonce-source or hash-source grammar,
|
||||
// return "Does Not Allow".
|
||||
if (src->isNonce() || src->isHash()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2.2. If type is "script", "script attribute" or "navigation" and
|
||||
// expression matches the keyword-source "'strict-dynamic'", return "Does
|
||||
// Not Allow".
|
||||
if ((aDir == nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE ||
|
||||
aDir == nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE) &&
|
||||
src->isKeyword(CSP_STRICT_DYNAMIC)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2.3. If expression is an ASCII case-insensitive match for the
|
||||
// keyword-source "'unsafe-inline'", set allow all inline to true.
|
||||
if (src->isKeyword(CSP_UNSAFE_INLINE)) {
|
||||
allowAll = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3. If allow all inline is true, return "Allows". Otherwise, return
|
||||
// "Does Not Allow".
|
||||
return allowAll;
|
||||
}
|
||||
|
||||
void nsCSPDirective::toString(nsAString& outStr) const {
|
||||
// Append directive name
|
||||
outStr.AppendASCII(CSP_CSPDirectiveToString(mDirective));
|
||||
|
@ -1568,6 +1560,13 @@ bool nsCSPPolicy::permits(CSPDirective aDir, nsILoadInfo* aLoadInfo,
|
|||
NS_ASSERTION(aUri, "permits needs an uri to perform the check!");
|
||||
outViolatedDirective.Truncate();
|
||||
|
||||
bool parserCreated = false;
|
||||
nsAutoString nonce;
|
||||
if (aLoadInfo) {
|
||||
parserCreated = aLoadInfo->GetParserCreatedScript();
|
||||
MOZ_ALWAYS_SUCCEEDS(aLoadInfo->GetCspNonce(nonce));
|
||||
}
|
||||
|
||||
nsCSPDirective* defaultDir = nullptr;
|
||||
|
||||
// Try to find a relevant directive
|
||||
|
@ -1575,8 +1574,9 @@ bool nsCSPPolicy::permits(CSPDirective aDir, nsILoadInfo* aLoadInfo,
|
|||
// hashtable.
|
||||
for (uint32_t i = 0; i < mDirectives.Length(); i++) {
|
||||
if (mDirectives[i]->equals(aDir)) {
|
||||
if (!mDirectives[i]->permits(aDir, aLoadInfo, aUri, aWasRedirected,
|
||||
mReportOnly, mUpgradeInsecDir)) {
|
||||
if (!mDirectives[i]->permits(aDir, aLoadInfo, aUri, nonce, aWasRedirected,
|
||||
mReportOnly, mUpgradeInsecDir,
|
||||
parserCreated)) {
|
||||
mDirectives[i]->getDirName(outViolatedDirective);
|
||||
return false;
|
||||
}
|
||||
|
@ -1590,8 +1590,8 @@ bool nsCSPPolicy::permits(CSPDirective aDir, nsILoadInfo* aLoadInfo,
|
|||
// If the above loop runs through, we haven't found a matching directive.
|
||||
// Avoid relooping, just store the result of default-src while looping.
|
||||
if (!aSpecific && defaultDir) {
|
||||
if (!defaultDir->permits(aDir, aLoadInfo, aUri, aWasRedirected, mReportOnly,
|
||||
mUpgradeInsecDir)) {
|
||||
if (!defaultDir->permits(aDir, aLoadInfo, aUri, nonce, aWasRedirected,
|
||||
mReportOnly, mUpgradeInsecDir, parserCreated)) {
|
||||
defaultDir->getDirName(outViolatedDirective);
|
||||
return false;
|
||||
}
|
||||
|
@ -1604,26 +1604,12 @@ bool nsCSPPolicy::permits(CSPDirective aDir, nsILoadInfo* aLoadInfo,
|
|||
}
|
||||
|
||||
bool nsCSPPolicy::allows(CSPDirective aDirective, enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const {
|
||||
const nsAString& aHashOrNonce,
|
||||
bool aParserCreated) const {
|
||||
CSPUTILSLOG(("nsCSPPolicy::allows, aKeyWord: %s, a HashOrNonce: %s",
|
||||
CSP_EnumToUTF8Keyword(aKeyword),
|
||||
NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
|
||||
|
||||
if (nsCSPDirective* directive = matchingOrDefaultDirective(aDirective)) {
|
||||
return directive->allows(aKeyword, aHashOrNonce);
|
||||
}
|
||||
|
||||
// No matching directive or default directive as fallback found, thus
|
||||
// allowing the load; see Bug 885433
|
||||
// a) inline scripts (also unsafe eval) should only be blocked
|
||||
// if there is a [script-src] or [default-src]
|
||||
// b) inline styles should only be blocked
|
||||
// if there is a [style-src] or [default-src]
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCSPDirective* nsCSPPolicy::matchingOrDefaultDirective(
|
||||
CSPDirective aDirective) const {
|
||||
nsCSPDirective* defaultDir = nullptr;
|
||||
|
||||
// Try to find a matching directive
|
||||
|
@ -1633,11 +1619,25 @@ nsCSPDirective* nsCSPPolicy::matchingOrDefaultDirective(
|
|||
continue;
|
||||
}
|
||||
if (mDirectives[i]->equals(aDirective)) {
|
||||
return mDirectives[i];
|
||||
if (mDirectives[i]->allows(aKeyword, aHashOrNonce, aParserCreated)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultDir;
|
||||
// If the above loop runs through, we haven't found a matching directive.
|
||||
// Avoid relooping, just store the result of default-src while looping.
|
||||
if (defaultDir) {
|
||||
return defaultDir->allows(aKeyword, aHashOrNonce, aParserCreated);
|
||||
}
|
||||
|
||||
// Allowing the load; see Bug 885433
|
||||
// a) inline scripts (also unsafe eval) should only be blocked
|
||||
// if there is a [script-src] or [default-src]
|
||||
// b) inline styles should only be blocked
|
||||
// if there is a [style-src] or [default-src]
|
||||
return true;
|
||||
}
|
||||
|
||||
void nsCSPPolicy::toString(nsAString& outStr) const {
|
||||
|
@ -1674,13 +1674,13 @@ bool nsCSPPolicy::allowsNavigateTo(nsIURI* aURI, bool aWasRedirected,
|
|||
// Early return if we can skip the allowlist AND 'unsafe-allow-redirects'
|
||||
// is present.
|
||||
if (!aEnforceAllowlist &&
|
||||
mDirectives[i]->allows(CSP_UNSAFE_ALLOW_REDIRECTS, u""_ns)) {
|
||||
mDirectives[i]->allows(CSP_UNSAFE_ALLOW_REDIRECTS, u""_ns, false)) {
|
||||
return true;
|
||||
}
|
||||
// Otherwise, check against the allowlist.
|
||||
if (!mDirectives[i]->permits(
|
||||
nsIContentSecurityPolicy::NAVIGATE_TO_DIRECTIVE, nullptr, aURI,
|
||||
aWasRedirected, false, false)) {
|
||||
u""_ns, aWasRedirected, false, false, false)) {
|
||||
allowsNavigateTo = false;
|
||||
}
|
||||
}
|
||||
|
@ -1689,17 +1689,6 @@ bool nsCSPPolicy::allowsNavigateTo(nsIURI* aURI, bool aWasRedirected,
|
|||
return allowsNavigateTo;
|
||||
}
|
||||
|
||||
bool nsCSPPolicy::allowsAllInlineBehavior(CSPDirective aDir) const {
|
||||
nsCSPDirective* directive = matchingOrDefaultDirective(aDir);
|
||||
if (!directive) {
|
||||
// No matching or default directive found thus allow the all inline
|
||||
// scripts or styles. (See nsCSPPolicy::allows)
|
||||
return true;
|
||||
}
|
||||
|
||||
return directive->allowsAllInlineBehavior(aDir);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use this function only after ::allows() returned 'false'. Most and
|
||||
* foremost it's used to get the violated directive before sending reports.
|
||||
|
|
|
@ -225,18 +225,24 @@ class nsCSPBaseSrc {
|
|||
nsCSPBaseSrc();
|
||||
virtual ~nsCSPBaseSrc();
|
||||
|
||||
virtual bool permits(nsIURI* aUri, bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure) const;
|
||||
virtual bool allows(enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const;
|
||||
virtual bool permits(nsIURI* aUri, const nsAString& aNonce,
|
||||
bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure, bool aParserCreated) const;
|
||||
virtual bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
|
||||
bool aParserCreated) const;
|
||||
virtual bool visit(nsCSPSrcVisitor* aVisitor) const = 0;
|
||||
virtual void toString(nsAString& outStr) const = 0;
|
||||
|
||||
virtual void invalidate() const { mInvalidated = true; }
|
||||
|
||||
virtual bool isReportSample() const { return false; }
|
||||
|
||||
virtual bool isHash() const { return false; }
|
||||
virtual bool isNonce() const { return false; }
|
||||
virtual bool isKeyword(CSPKeyword aKeyword) const { return false; }
|
||||
|
||||
protected:
|
||||
// invalidate srcs if 'script-dynamic' is present or also invalidate
|
||||
// unsafe-inline' if nonce- or hash-source specified
|
||||
mutable bool mInvalidated;
|
||||
};
|
||||
|
||||
/* =============== nsCSPSchemeSrc ============ */
|
||||
|
@ -246,8 +252,9 @@ class nsCSPSchemeSrc : public nsCSPBaseSrc {
|
|||
explicit nsCSPSchemeSrc(const nsAString& aScheme);
|
||||
virtual ~nsCSPSchemeSrc();
|
||||
|
||||
bool permits(nsIURI* aUri, bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure) const override;
|
||||
bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
|
||||
bool aReportOnly, bool aUpgradeInsecure,
|
||||
bool aParserCreated) const override;
|
||||
bool visit(nsCSPSrcVisitor* aVisitor) const override;
|
||||
void toString(nsAString& outStr) const override;
|
||||
|
||||
|
@ -264,8 +271,9 @@ class nsCSPHostSrc : public nsCSPBaseSrc {
|
|||
explicit nsCSPHostSrc(const nsAString& aHost);
|
||||
virtual ~nsCSPHostSrc();
|
||||
|
||||
bool permits(nsIURI* aUri, bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure) const override;
|
||||
bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
|
||||
bool aReportOnly, bool aUpgradeInsecure,
|
||||
bool aParserCreated) const override;
|
||||
bool visit(nsCSPSrcVisitor* aVisitor) const override;
|
||||
void toString(nsAString& outStr) const override;
|
||||
|
||||
|
@ -308,19 +316,26 @@ class nsCSPKeywordSrc : public nsCSPBaseSrc {
|
|||
explicit nsCSPKeywordSrc(CSPKeyword aKeyword);
|
||||
virtual ~nsCSPKeywordSrc();
|
||||
|
||||
bool allows(enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const override;
|
||||
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
|
||||
bool aParserCreated) const override;
|
||||
bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
|
||||
bool aReportOnly, bool aUpgradeInsecure,
|
||||
bool aParserCreated) const override;
|
||||
bool visit(nsCSPSrcVisitor* aVisitor) const override;
|
||||
void toString(nsAString& outStr) const override;
|
||||
|
||||
inline CSPKeyword getKeyword() const { return mKeyword; };
|
||||
|
||||
bool isReportSample() const override { return mKeyword == CSP_REPORT_SAMPLE; }
|
||||
|
||||
bool isKeyword(CSPKeyword aKeyword) const final {
|
||||
return mKeyword == aKeyword;
|
||||
inline void invalidate() const override {
|
||||
// keywords that need to invalidated
|
||||
if (mKeyword == CSP_SELF || mKeyword == CSP_UNSAFE_INLINE ||
|
||||
mKeyword == CSP_REPORT_SAMPLE) {
|
||||
mInvalidated = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool isReportSample() const override { return mKeyword == CSP_REPORT_SAMPLE; }
|
||||
|
||||
private:
|
||||
CSPKeyword mKeyword;
|
||||
};
|
||||
|
@ -332,14 +347,21 @@ class nsCSPNonceSrc : public nsCSPBaseSrc {
|
|||
explicit nsCSPNonceSrc(const nsAString& aNonce);
|
||||
virtual ~nsCSPNonceSrc();
|
||||
|
||||
bool allows(enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const override;
|
||||
bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
|
||||
bool aReportOnly, bool aUpgradeInsecure,
|
||||
bool aParserCreated) const override;
|
||||
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
|
||||
bool aParserCreated) const override;
|
||||
bool visit(nsCSPSrcVisitor* aVisitor) const override;
|
||||
void toString(nsAString& outStr) const override;
|
||||
|
||||
inline void getNonce(nsAString& outStr) const { outStr.Assign(mNonce); };
|
||||
|
||||
bool isNonce() const final { return true; }
|
||||
inline void invalidate() const override {
|
||||
// overwrite nsCSPBaseSRC::invalidate() and explicitily
|
||||
// do *not* invalidate, because 'strict-dynamic' should
|
||||
// not invalidate nonces.
|
||||
}
|
||||
|
||||
private:
|
||||
nsString mNonce;
|
||||
|
@ -352,8 +374,8 @@ class nsCSPHashSrc : public nsCSPBaseSrc {
|
|||
nsCSPHashSrc(const nsAString& algo, const nsAString& hash);
|
||||
virtual ~nsCSPHashSrc();
|
||||
|
||||
bool allows(enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const override;
|
||||
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
|
||||
bool aParserCreated) const override;
|
||||
void toString(nsAString& outStr) const override;
|
||||
bool visit(nsCSPSrcVisitor* aVisitor) const override;
|
||||
|
||||
|
@ -363,6 +385,12 @@ class nsCSPHashSrc : public nsCSPBaseSrc {
|
|||
|
||||
inline void getHash(nsAString& outStr) const { outStr.Assign(mHash); };
|
||||
|
||||
inline void invalidate() const override {
|
||||
// overwrite nsCSPBaseSRC::invalidate() and explicitily
|
||||
// do *not* invalidate, because 'strict-dynamic' should
|
||||
// not invalidate hashes.
|
||||
}
|
||||
|
||||
bool isHash() const final { return true; }
|
||||
|
||||
private:
|
||||
|
@ -425,11 +453,11 @@ class nsCSPDirective {
|
|||
virtual ~nsCSPDirective();
|
||||
|
||||
virtual bool permits(CSPDirective aDirective, nsILoadInfo* aLoadInfo,
|
||||
nsIURI* aUri, bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure) const;
|
||||
virtual bool allows(enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const;
|
||||
bool allowsAllInlineBehavior(CSPDirective aDir) const;
|
||||
nsIURI* aUri, const nsAString& aNonce,
|
||||
bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure, bool aParserCreated) const;
|
||||
virtual bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
|
||||
bool aParserCreated) const;
|
||||
virtual void toString(nsAString& outStr) const;
|
||||
void toDomCSPStruct(mozilla::dom::CSP& outCSP) const;
|
||||
|
||||
|
@ -533,15 +561,15 @@ class nsBlockAllMixedContentDirective : public nsCSPDirective {
|
|||
~nsBlockAllMixedContentDirective();
|
||||
|
||||
bool permits(CSPDirective aDirective, nsILoadInfo* aLoadInfo, nsIURI* aUri,
|
||||
bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure) const override {
|
||||
const nsAString& aNonce, bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure, bool aParserCreated) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool permits(nsIURI* aUri) const { return false; }
|
||||
|
||||
bool allows(enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const override {
|
||||
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
|
||||
bool aParserCreated) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -591,15 +619,15 @@ class nsUpgradeInsecureDirective : public nsCSPDirective {
|
|||
~nsUpgradeInsecureDirective();
|
||||
|
||||
bool permits(CSPDirective aDirective, nsILoadInfo* aLoadInfo, nsIURI* aUri,
|
||||
bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure) const override {
|
||||
const nsAString& aNonce, bool aWasRedirected, bool aReportOnly,
|
||||
bool aUpgradeInsecure, bool aParserCreated) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool permits(nsIURI* aUri) const { return false; }
|
||||
|
||||
bool allows(enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const override {
|
||||
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
|
||||
bool aParserCreated) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -623,7 +651,7 @@ class nsCSPPolicy {
|
|||
bool aWasRedirected, bool aSpecific,
|
||||
nsAString& outViolatedDirective) const;
|
||||
bool allows(CSPDirective aDirective, enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const;
|
||||
const nsAString& aHashOrNonce, bool aParserCreated) const;
|
||||
void toString(nsAString& outStr) const;
|
||||
void toDomCSPStruct(mozilla::dom::CSP& outCSP) const;
|
||||
|
||||
|
@ -667,11 +695,7 @@ class nsCSPPolicy {
|
|||
bool allowsNavigateTo(nsIURI* aURI, bool aWasRedirected,
|
||||
bool aEnforceAllowlist) const;
|
||||
|
||||
bool allowsAllInlineBehavior(CSPDirective aDir) const;
|
||||
|
||||
private:
|
||||
nsCSPDirective* matchingOrDefaultDirective(CSPDirective aDirective) const;
|
||||
|
||||
nsUpgradeInsecureDirective* mUpgradeInsecDir;
|
||||
nsTArray<nsCSPDirective*> mDirectives;
|
||||
bool mReportOnly;
|
||||
|
|
|
@ -217,7 +217,7 @@ TEST(CSPParser, Directives)
|
|||
{ "script-src 'nonce-foo' 'strict-dynamic' 'unsafe-inline' 'report-sample' https: ",
|
||||
"script-src 'nonce-foo' 'strict-dynamic' 'unsafe-inline' 'report-sample' https:" },
|
||||
{ "default-src 'sha256-siVR8' 'strict-dynamic' 'unsafe-inline' https: ",
|
||||
"default-src 'sha256-siVR8' 'strict-dynamic' 'unsafe-inline' https:" },
|
||||
"default-src 'sha256-siVR8' 'unsafe-inline' https:" },
|
||||
{ "worker-src https://example.com",
|
||||
"worker-src https://example.com" },
|
||||
{ "worker-src http://worker.com; frame-src http://frame.com; child-src http://child.com",
|
||||
|
|
|
@ -79,7 +79,6 @@
|
|||
|
||||
#define MAX_CONCURRENT_SCRIPTS 1000
|
||||
|
||||
using JS::loader::ParserMetadata;
|
||||
using JS::loader::ScriptKind;
|
||||
using JS::loader::ScriptLoadRequest;
|
||||
using mozilla::ipc::PrincipalInfo;
|
||||
|
@ -642,18 +641,8 @@ already_AddRefed<ScriptLoadRequest> WorkerScriptLoader::CreateScriptLoadRequest(
|
|||
loadContext->mLoadResult = rv;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-worker-script
|
||||
// Step 2.5. Let script be the result [...] and the default classic script
|
||||
// fetch options.
|
||||
//
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-worklet/module-worker-script-graph
|
||||
// Step 1. Let options be a script fetch options whose cryptographic nonce is
|
||||
// the empty string, integrity metadata is the empty string, parser metadata
|
||||
// is "not-parser-inserted", credentials mode is credentials mode, referrer
|
||||
// policy is the empty string, and fetch priority is "auto".
|
||||
RefPtr<ScriptFetchOptions> fetchOptions = new ScriptFetchOptions(
|
||||
CORSMode::CORS_NONE, referrerPolicy, /* aNonce = */ u""_ns,
|
||||
ParserMetadata::NotParserInserted, nullptr);
|
||||
RefPtr<ScriptFetchOptions> fetchOptions =
|
||||
new ScriptFetchOptions(CORSMode::CORS_NONE, referrerPolicy, nullptr);
|
||||
|
||||
RefPtr<ScriptLoadRequest> request = nullptr;
|
||||
// Bug 1817259 - For now the debugger scripts are always loaded a Classic.
|
||||
|
|
|
@ -99,26 +99,12 @@ already_AddRefed<ModuleLoadRequest> WorkerModuleLoader::CreateDynamicImport(
|
|||
RefPtr<ScriptFetchOptions> options;
|
||||
nsIURI* baseURL = nullptr;
|
||||
if (aMaybeActiveScript) {
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#hostloadimportedmodule
|
||||
// Step 6.3. Set fetchOptions to the new descendant script fetch options for
|
||||
// referencingScript's fetch options.
|
||||
options = aMaybeActiveScript->GetFetchOptions();
|
||||
baseURL = aMaybeActiveScript->BaseURL();
|
||||
} else {
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#hostloadimportedmodule
|
||||
// Step 4. Let fetchOptions be the default classic script fetch options.
|
||||
//
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#default-classic-script-fetch-options
|
||||
// The default classic script fetch options are a script fetch options whose
|
||||
// cryptographic nonce is the empty string, integrity metadata is the empty
|
||||
// string, parser metadata is "not-parser-inserted", credentials mode is
|
||||
// "same-origin", referrer policy is the empty string, and fetch priority is
|
||||
// "auto".
|
||||
ReferrerPolicy referrerPolicy = workerPrivate->GetReferrerPolicy();
|
||||
options = new ScriptFetchOptions(
|
||||
CORSMode::CORS_NONE, referrerPolicy,
|
||||
/* aNonce = */ u""_ns, JS::loader::ParserMetadata::NotParserInserted,
|
||||
nullptr);
|
||||
options =
|
||||
new ScriptFetchOptions(CORSMode::CORS_NONE, referrerPolicy, nullptr);
|
||||
baseURL = GetBaseURI();
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "nsIThreadRetargetableRequest.h"
|
||||
|
||||
using JS::loader::ModuleLoadRequest;
|
||||
using JS::loader::ParserMetadata;
|
||||
using JS::loader::ScriptFetchOptions;
|
||||
using mozilla::dom::loader::WorkletModuleLoader;
|
||||
|
||||
|
@ -88,14 +87,11 @@ NS_IMETHODIMP StartModuleLoadRunnable::RunOnWorkletThread() {
|
|||
|
||||
// To fetch a worklet/module worker script graph:
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-worklet/module-worker-script-graph
|
||||
// Step 1. Let options be a script fetch options whose cryptographic nonce is
|
||||
// the empty string, integrity metadata is the empty string, parser metadata
|
||||
// is "not-parser-inserted", credentials mode is credentials mode, referrer
|
||||
// policy is the empty string, and fetch priority is "auto".
|
||||
// Step 1. Let options be a script fetch options. And referrer policy is the
|
||||
// empty string.
|
||||
ReferrerPolicy referrerPolicy = ReferrerPolicy::_empty;
|
||||
RefPtr<ScriptFetchOptions> fetchOptions = new ScriptFetchOptions(
|
||||
CORSMode::CORS_NONE, ReferrerPolicy::_empty, /* aNonce = */ u""_ns,
|
||||
ParserMetadata::NotParserInserted,
|
||||
/*triggeringPrincipal*/ nullptr);
|
||||
CORSMode::CORS_NONE, referrerPolicy, /*triggeringPrincipal*/ nullptr);
|
||||
|
||||
WorkletModuleLoader* moduleLoader =
|
||||
static_cast<WorkletModuleLoader*>(globalScope->GetModuleLoader());
|
||||
|
|
|
@ -37,12 +37,9 @@ NS_IMPL_CYCLE_COLLECTION(ScriptFetchOptions, mTriggeringPrincipal, mElement)
|
|||
|
||||
ScriptFetchOptions::ScriptFetchOptions(
|
||||
mozilla::CORSMode aCORSMode, mozilla::dom::ReferrerPolicy aReferrerPolicy,
|
||||
const nsAString& aNonce, const ParserMetadata aParserMetadata,
|
||||
nsIPrincipal* aTriggeringPrincipal, mozilla::dom::Element* aElement)
|
||||
: mCORSMode(aCORSMode),
|
||||
mReferrerPolicy(aReferrerPolicy),
|
||||
mNonce(aNonce),
|
||||
mParserMetadata(aParserMetadata),
|
||||
mTriggeringPrincipal(aTriggeringPrincipal),
|
||||
mElement(aElement) {}
|
||||
|
||||
|
|
|
@ -55,18 +55,22 @@ class LoadContextBase;
|
|||
class ModuleLoadRequest;
|
||||
class ScriptLoadRequestList;
|
||||
|
||||
// https://fetch.spec.whatwg.org/#concept-request-parser-metadata
|
||||
// All scripts are either "parser-inserted" or "not-parser-inserted", so
|
||||
// the empty string is not necessary.
|
||||
enum class ParserMetadata {
|
||||
NotParserInserted,
|
||||
ParserInserted,
|
||||
};
|
||||
|
||||
/*
|
||||
* ScriptFetchOptions loosely corresponds to HTML's "script fetch options",
|
||||
* https://html.spec.whatwg.org/multipage/webappapis.html#script-fetch-options
|
||||
* with the exception of the following properties:
|
||||
* cryptographic nonce
|
||||
* The cryptographic nonce metadata used for the initial fetch and for
|
||||
* fetching any imported modules. As this is populated by a DOM element,
|
||||
* this is implemented via mozilla::dom::Element as the field
|
||||
* mElement. The default value is an empty string, and is indicated
|
||||
* when this field is a nullptr. Nonce is not represented on the dom
|
||||
* side as per bug 1374612.
|
||||
* parser metadata
|
||||
* The parser metadata used for the initial fetch and for fetching any
|
||||
* imported modules. This is populated from a mozilla::dom::Element and is
|
||||
* handled by the field mElement. The default value is an empty string,
|
||||
* and is indicated when this field is a nullptr.
|
||||
* integrity metadata
|
||||
* The integrity metadata used for the initial fetch. This is
|
||||
* implemented in ScriptLoadRequest, as it changes for every
|
||||
|
@ -87,8 +91,6 @@ class ScriptFetchOptions {
|
|||
|
||||
ScriptFetchOptions(mozilla::CORSMode aCORSMode,
|
||||
enum mozilla::dom::ReferrerPolicy aReferrerPolicy,
|
||||
const nsAString& aNonce,
|
||||
const ParserMetadata aParserMetadata,
|
||||
nsIPrincipal* aTriggeringPrincipal,
|
||||
mozilla::dom::Element* aElement = nullptr);
|
||||
|
||||
|
@ -105,18 +107,6 @@ class ScriptFetchOptions {
|
|||
*/
|
||||
const enum mozilla::dom::ReferrerPolicy mReferrerPolicy;
|
||||
|
||||
/*
|
||||
* The cryptographic nonce metadata used for the initial fetch and for
|
||||
* fetching any imported modules.
|
||||
*/
|
||||
const nsString mNonce;
|
||||
|
||||
/*
|
||||
* The parser metadata used for the initial fetch and for fetching any
|
||||
* imported modules
|
||||
*/
|
||||
const ParserMetadata mParserMetadata;
|
||||
|
||||
/*
|
||||
* Used to determine CSP and if we are on the About page.
|
||||
* Only used in DOM content scripts.
|
||||
|
@ -297,12 +287,6 @@ class ScriptLoadRequest
|
|||
return mFetchOptions->mReferrerPolicy;
|
||||
}
|
||||
|
||||
ParserMetadata ParserMetadata() const {
|
||||
return mFetchOptions->mParserMetadata;
|
||||
}
|
||||
|
||||
const nsString& Nonce() const { return mFetchOptions->mNonce; }
|
||||
|
||||
nsIPrincipal* TriggeringPrincipal() const {
|
||||
return mFetchOptions->mTriggeringPrincipal;
|
||||
}
|
||||
|
|
|
@ -1805,8 +1805,7 @@ nsresult mozJSModuleLoader::ImportESModule(
|
|||
MOZ_ASSERT(principal);
|
||||
|
||||
RefPtr<ScriptFetchOptions> options = new ScriptFetchOptions(
|
||||
CORS_NONE, dom::ReferrerPolicy::No_referrer,
|
||||
/* aNonce = */ u""_ns, ParserMetadata::NotParserInserted, principal);
|
||||
CORS_NONE, dom::ReferrerPolicy::No_referrer, principal);
|
||||
|
||||
RefPtr<ComponentLoadContext> context = new ComponentLoadContext();
|
||||
context->mSkipCheck = aSkipCheck;
|
||||
|
|
|
@ -308,6 +308,15 @@ LoadInfo::LoadInfo(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in case this is a loadinfo for a parser generated script, then we store
|
||||
// that bit of information so CSP strict-dynamic can query it.
|
||||
if (!nsContentUtils::IsPreloadType(mInternalContentPolicyType)) {
|
||||
nsCOMPtr<nsIScriptElement> script = do_QueryInterface(aLoadingContext);
|
||||
if (script && script->GetParserCreated() != mozilla::dom::NOT_FROM_PARSER) {
|
||||
mParserCreatedScript = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Constructor takes an outer window, but no loadingNode or loadingPrincipal.
|
||||
|
|
|
@ -63,7 +63,7 @@ void nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor) {
|
|||
aExecutor->PreloadScript(
|
||||
mUrlOrSizes, mCharsetOrSrcset,
|
||||
mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity,
|
||||
mCrossOrigin, mMedia, mNonce, mReferrerPolicyOrIntegrity,
|
||||
mCrossOrigin, mMedia, mReferrerPolicyOrIntegrity,
|
||||
mScriptReferrerPolicy, false, mIsAsync, mIsDefer, false,
|
||||
mIsLinkPreload);
|
||||
break;
|
||||
|
@ -71,7 +71,7 @@ void nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor) {
|
|||
aExecutor->PreloadScript(
|
||||
mUrlOrSizes, mCharsetOrSrcset,
|
||||
mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity,
|
||||
mCrossOrigin, mMedia, mNonce, mReferrerPolicyOrIntegrity,
|
||||
mCrossOrigin, mMedia, mReferrerPolicyOrIntegrity,
|
||||
mScriptReferrerPolicy, true, mIsAsync, mIsDefer, false,
|
||||
mIsLinkPreload);
|
||||
break;
|
||||
|
@ -79,7 +79,7 @@ void nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor) {
|
|||
aExecutor->PreloadScript(
|
||||
mUrlOrSizes, mCharsetOrSrcset,
|
||||
mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity,
|
||||
mCrossOrigin, mMedia, mNonce, mReferrerPolicyOrIntegrity,
|
||||
mCrossOrigin, mMedia, mReferrerPolicyOrIntegrity,
|
||||
mScriptReferrerPolicy, false, mIsAsync, mIsDefer, true,
|
||||
mIsLinkPreload);
|
||||
break;
|
||||
|
@ -87,7 +87,7 @@ void nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor) {
|
|||
aExecutor->PreloadScript(
|
||||
mUrlOrSizes, mCharsetOrSrcset,
|
||||
mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity,
|
||||
mCrossOrigin, mMedia, mNonce, mReferrerPolicyOrIntegrity,
|
||||
mCrossOrigin, mMedia, mReferrerPolicyOrIntegrity,
|
||||
mScriptReferrerPolicy, true, mIsAsync, mIsDefer, true,
|
||||
mIsLinkPreload);
|
||||
break;
|
||||
|
|
|
@ -168,8 +168,7 @@ class nsHtml5SpeculativeLoad {
|
|||
|
||||
inline void InitScript(nsHtml5String aUrl, nsHtml5String aCharset,
|
||||
nsHtml5String aType, nsHtml5String aCrossOrigin,
|
||||
nsHtml5String aMedia, nsHtml5String aNonce,
|
||||
nsHtml5String aIntegrity,
|
||||
nsHtml5String aMedia, nsHtml5String aIntegrity,
|
||||
nsHtml5String aReferrerPolicy, bool aParserInHead,
|
||||
bool aAsync, bool aDefer, bool aNoModule,
|
||||
bool aLinkPreload) {
|
||||
|
@ -188,7 +187,6 @@ class nsHtml5SpeculativeLoad {
|
|||
mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity);
|
||||
aCrossOrigin.ToString(mCrossOrigin);
|
||||
aMedia.ToString(mMedia);
|
||||
aNonce.ToString(mNonce);
|
||||
aIntegrity.ToString(mReferrerPolicyOrIntegrity);
|
||||
nsAutoString referrerPolicy;
|
||||
aReferrerPolicy.ToString(referrerPolicy);
|
||||
|
@ -408,11 +406,6 @@ class nsHtml5SpeculativeLoad {
|
|||
* will be a void string.
|
||||
*/
|
||||
nsString mMedia;
|
||||
/**
|
||||
* If mOpCode is eSpeculativeLoadScript[FromHead] this represents the value
|
||||
* of the "nonce" attribute.
|
||||
*/
|
||||
nsString mNonce;
|
||||
/**
|
||||
* If mOpCode is eSpeculativeLoadScript[FromHead] this represents the value
|
||||
* of the "referrerpolicy" attribute. This field holds one of the values
|
||||
|
|
|
@ -249,8 +249,6 @@ nsIContentHandle* nsHtml5TreeBuilder::createElement(
|
|||
aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
|
||||
nsHtml5String crossOrigin =
|
||||
aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
|
||||
nsHtml5String nonce =
|
||||
aAttributes->getValue(nsHtml5AttributeName::ATTR_NONCE);
|
||||
nsHtml5String integrity =
|
||||
aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
|
||||
nsHtml5String referrerPolicy = aAttributes->getValue(
|
||||
|
@ -262,7 +260,7 @@ nsIContentHandle* nsHtml5TreeBuilder::createElement(
|
|||
bool noModule =
|
||||
aAttributes->contains(nsHtml5AttributeName::ATTR_NOMODULE);
|
||||
mSpeculativeLoadQueue.AppendElement()->InitScript(
|
||||
url, charset, type, crossOrigin, /* aMedia = */ nullptr, nonce,
|
||||
url, charset, type, crossOrigin, /* aMedia = */ nullptr,
|
||||
integrity, referrerPolicy, mode == nsHtml5TreeBuilder::IN_HEAD,
|
||||
async, defer, noModule, false);
|
||||
mCurrentHtmlScriptIsAsyncOrDefer = async || defer;
|
||||
|
@ -329,10 +327,9 @@ nsIContentHandle* nsHtml5TreeBuilder::createElement(
|
|||
nsHtml5String type =
|
||||
aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
|
||||
mSpeculativeLoadQueue.AppendElement()->InitScript(
|
||||
url, charset, type, crossOrigin, media,
|
||||
/* aNonce */ nullptr, integrity, referrerPolicy,
|
||||
mode == nsHtml5TreeBuilder::IN_HEAD, false, false, false,
|
||||
true);
|
||||
url, charset, type, crossOrigin, media, integrity,
|
||||
referrerPolicy, mode == nsHtml5TreeBuilder::IN_HEAD,
|
||||
false, false, false, true);
|
||||
} else if (as.LowerCaseEqualsASCII("style")) {
|
||||
mSpeculativeLoadQueue.AppendElement()->InitStyle(
|
||||
url, charset, crossOrigin, media, referrerPolicy,
|
||||
|
@ -379,10 +376,9 @@ nsIContentHandle* nsHtml5TreeBuilder::createElement(
|
|||
nsHtml5String referrerPolicy = aAttributes->getValue(
|
||||
nsHtml5AttributeName::ATTR_REFERRERPOLICY);
|
||||
mSpeculativeLoadQueue.AppendElement()->InitScript(
|
||||
url, charset, type, crossOrigin, media,
|
||||
/* aNonce */ nullptr, integrity, referrerPolicy,
|
||||
mode == nsHtml5TreeBuilder::IN_HEAD, false, false, false,
|
||||
true);
|
||||
url, charset, type, crossOrigin, media, integrity,
|
||||
referrerPolicy, mode == nsHtml5TreeBuilder::IN_HEAD,
|
||||
false, false, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -473,14 +469,12 @@ nsIContentHandle* nsHtml5TreeBuilder::createElement(
|
|||
aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
|
||||
nsHtml5String crossOrigin =
|
||||
aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
|
||||
nsHtml5String nonce =
|
||||
aAttributes->getValue(nsHtml5AttributeName::ATTR_NONCE);
|
||||
nsHtml5String integrity =
|
||||
aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
|
||||
nsHtml5String referrerPolicy = aAttributes->getValue(
|
||||
nsHtml5AttributeName::ATTR_REFERRERPOLICY);
|
||||
mSpeculativeLoadQueue.AppendElement()->InitScript(
|
||||
url, nullptr, type, crossOrigin, /* aMedia = */ nullptr, nonce,
|
||||
url, nullptr, type, crossOrigin, /* aMedia = */ nullptr,
|
||||
integrity, referrerPolicy, mode == nsHtml5TreeBuilder::IN_HEAD,
|
||||
false, false, false, false);
|
||||
}
|
||||
|
|
|
@ -1205,9 +1205,9 @@ dom::ReferrerPolicy nsHtml5TreeOpExecutor::GetPreloadReferrerPolicy(
|
|||
void nsHtml5TreeOpExecutor::PreloadScript(
|
||||
const nsAString& aURL, const nsAString& aCharset, const nsAString& aType,
|
||||
const nsAString& aCrossOrigin, const nsAString& aMedia,
|
||||
const nsAString& aNonce, const nsAString& aIntegrity,
|
||||
dom::ReferrerPolicy aReferrerPolicy, bool aScriptFromHead, bool aAsync,
|
||||
bool aDefer, bool aNoModule, bool aLinkPreload) {
|
||||
const nsAString& aIntegrity, dom::ReferrerPolicy aReferrerPolicy,
|
||||
bool aScriptFromHead, bool aAsync, bool aDefer, bool aNoModule,
|
||||
bool aLinkPreload) {
|
||||
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYetAndMediaApplies(aURL, aMedia);
|
||||
if (!uri) {
|
||||
return;
|
||||
|
@ -1217,8 +1217,8 @@ void nsHtml5TreeOpExecutor::PreloadScript(
|
|||
return;
|
||||
}
|
||||
mDocument->ScriptLoader()->PreloadURI(
|
||||
uri, aCharset, aType, aCrossOrigin, aNonce, aIntegrity, aScriptFromHead,
|
||||
aAsync, aDefer, aNoModule, aLinkPreload,
|
||||
uri, aCharset, aType, aCrossOrigin, aIntegrity, aScriptFromHead, aAsync,
|
||||
aDefer, aNoModule, aLinkPreload,
|
||||
GetPreloadReferrerPolicy(aReferrerPolicy), 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -245,8 +245,7 @@ class nsHtml5TreeOpExecutor final
|
|||
|
||||
void PreloadScript(const nsAString& aURL, const nsAString& aCharset,
|
||||
const nsAString& aType, const nsAString& aCrossOrigin,
|
||||
const nsAString& aMedia, const nsAString& aNonce,
|
||||
const nsAString& aIntegrity,
|
||||
const nsAString& aMedia, const nsAString& aIntegrity,
|
||||
ReferrerPolicy aReferrerPolicy, bool aScriptFromHead,
|
||||
bool aAsync, bool aDefer, bool aNoModule,
|
||||
bool aLinkPreload);
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
[default-src-strict_dynamic_and_unsafe_inline.html]
|
||||
expected: ERROR
|
||||
[Should fire a security policy violation for the inline block]
|
||||
expected: NOTRUN
|
|
@ -211,8 +211,8 @@ void PreloadService::PreloadScript(nsIURI* aURI, const nsAString& aType,
|
|||
bool aScriptFromHead,
|
||||
uint64_t aEarlyHintPreloaderId) {
|
||||
mDocument->ScriptLoader()->PreloadURI(
|
||||
aURI, aCharset, aType, aCrossOrigin, u""_ns, aIntegrity, aScriptFromHead,
|
||||
false, false, false, true, PreloadReferrerPolicy(aReferrerPolicy),
|
||||
aURI, aCharset, aType, aCrossOrigin, aIntegrity, aScriptFromHead, false,
|
||||
false, false, true, PreloadReferrerPolicy(aReferrerPolicy),
|
||||
aEarlyHintPreloaderId);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче