/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// The path is terminated by the first question mark ("?") or // number sign ("#") character, or by the end of the URI. // http://tools.ietf.org/html/rfc3986#section-3.3 bool nsCSPParser::atEndOfPath() { return (atEnd() || peek(QUESTIONMARK) || peek(NUMBER_SIGN));
}
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")" // / "*" / "+" / "," / ";" / "=" // Please note that even though ',' and ';' appear to be // valid sub-delims according to the RFC production of paths, // both can not appear here by itself, they would need to be // pct-encoded in order to be part of the path. bool nsCSPParser::atValidSubDelimChar() { return (peek(EXCLAMATION) || peek(DOLLAR) || peek(AMPERSAND) ||
peek(SINGLEQUOTE) || peek(OPENBRACE) || peek(CLOSINGBRACE) ||
peek(WILDCARD) || peek(PLUS) || peek(EQUALS));
}
// send console messages off to the context and let the context // deal with it (potentially messages need to be queued up)
mCSPContext->logToConsole(aProperty, aParams, ""_ns, // aSourceName
u""_ns, // aSourceLine
0, // aLineNumber
1, // aColumnNumber
aSeverityFlag); // aFlags
}
// Consume the COLON we just peeked at in houstSource
accept(COLON);
// Resetting current value since we start to parse a port now. // e.g; "http://www.example.com:8888" then we have already parsed // everything up to (including) ":";
resetCurValue();
// Port might be "*" if (accept(WILDCARD)) { returntrue;
}
// Port must start with a number if (!accept(isNumberToken)) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParsePort",
params); returnfalse;
} // Consume more numbers and set parsed port to the nsCSPHost while (accept(isNumberToken)) { /* consume */
} returntrue;
}
// Emergency exit to avoid endless loops in case a path in a CSP policy // is longer than 512 characters, or also to avoid endless loops // in case we are parsing unrecognized characters in the following loop.
uint32_t charCounter = 0;
nsString pctDecodedSubPath;
while (!atEndOfPath()) { if (peek(SLASH)) { // before appendig any additional portion of a subpath we have to // pct-decode that portion of the subpath. atValidPathChar() already // verified a correct pct-encoding, now we can safely decode and append // the decoded-sub path.
CSP_PercentDecodeStr(mCurValue, pctDecodedSubPath);
aCspHost->appendPath(pctDecodedSubPath); // Resetting current value since we are appending parts of the path // to aCspHost, e.g; "http://www.example.com/path1/path2" then the // first part is "/path1", second part "/path2"
resetCurValue();
} elseif (!atValidPathChar()) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidSource", params); returnfalse;
} // potentially we have encountred a valid pct-encoded character in // atValidPathChar(); if so, we have to account for "% HEXDIG HEXDIG" and // advance the pointer past the pct-encoded char. if (peek(PERCENT_SIGN)) {
advance();
advance();
}
advance(); if (++charCounter > kSubHostPathCharacterCutoff) { returnfalse;
}
} // before appendig any additional portion of a subpath we have to pct-decode // that portion of the subpath. atValidPathChar() already verified a correct // pct-encoding, now we can safely decode and append the decoded-sub path.
CSP_PercentDecodeStr(mCurValue, pctDecodedSubPath);
aCspHost->appendPath(pctDecodedSubPath);
resetCurValue(); returntrue;
}
// Resetting current value and forgetting everything we have parsed so far // e.g. parsing "http://www.example.com/path1/path2", then // "http://www.example.com" has already been parsed so far // forget about it.
resetCurValue();
if (!accept(SLASH)) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidSource", params); returnfalse;
} if (atEndOfPath()) { // one slash right after host [port] is also considered a path, e.g. // www.example.com/ should result in www.example.com/ // please note that we do not have to perform any pct-decoding here // because we are just appending a '/' and not any actual chars.
aCspHost->appendPath(u"/"_ns); returntrue;
} // path can begin with "/" but not "//" // see http://tools.ietf.org/html/rfc3986#section-3.3 if (peek(SLASH)) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidSource", params); returnfalse;
} return subPath(aCspHost);
}
// Emergency exit to avoid endless loops in case a host in a CSP policy // is longer than 512 characters, or also to avoid endless loops // in case we are parsing unrecognized characters in the following loop.
uint32_t charCounter = 0;
while (!atEndOfPath() && !peek(COLON) && !peek(SLASH)) {
++charCounter; while (hostChar()) { /* consume */
++charCounter;
} if (accept(DOT) && !hostChar()) { returnfalse;
} if (charCounter > kSubHostPathCharacterCutoff) { returnfalse;
}
} returntrue;
}
// Check if the token starts with "*"; please remember that we handle // a single "*" as host in sourceExpression, but we still have to handle // the case where a scheme was defined, e.g., as: // "https://*", "*.example.com", "*:*", etc. if (accept(WILDCARD)) { // Might solely be the wildcard if (atEnd() || peek(COLON)) { returnnew nsCSPHostSrc(mCurValue);
} // If the token is not only the "*", a "." must follow right after if (!accept(DOT)) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidHost", params); return nullptr;
}
}
// Expecting at least one host-char if (!hostChar()) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidHost", params); return nullptr;
}
// There might be several sub hosts defined. if (!subHost()) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidHost", params); return nullptr;
}
// HostName might match a keyword, log to the console. if (CSP_IsQuotelessKeyword(mCurValue)) {
nsString keyword = mCurValue;
ToLowerCase(keyword);
AutoTArray<nsString, 2> params = {mCurToken, keyword};
logWarningErrorToConsole(nsIScriptError::warningFlag, "hostNameMightBeKeyword", params);
}
// Create a new nsCSPHostSrc with the parsed host. returnnew nsCSPHostSrc(mCurValue);
}
// Special case handling for 'self' which is not stored internally as a // keyword, but rather creates a nsCSPHostSrc using the selfURI if (CSP_IsKeyword(mCurToken, CSP_SELF)) { return CSP_CreateHostSrcFromSelfURI(mSelfURI);
}
if (CSP_IsKeyword(mCurToken, CSP_REPORT_SAMPLE)) { returnnew nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
}
if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_INLINE)) { // make sure script-src only contains 'unsafe-inline' once; // ignore duplicates and log warning if (mUnsafeInlineKeywordSrc) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringDuplicateSrc", params); return nullptr;
} // cache if we encounter 'unsafe-inline' so we can invalidate (ignore) it in // case that script-src directive also contains hash- or nonce-.
mUnsafeInlineKeywordSrc = new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken)); return mUnsafeInlineKeywordSrc;
}
if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_EVAL)) {
mHasAnyUnsafeEval = true; returnnew nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
}
if (CSP_IsKeyword(mCurToken, CSP_WASM_UNSAFE_EVAL)) {
mHasAnyUnsafeEval = true; returnnew nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
}
if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_HASHES)) { returnnew nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
}
nsCSPHostSrc* cspHost = host(); if (!cspHost) { // Error was reported in host() return nullptr;
}
// Calling port() to see if there is a port to parse, if an error // occurs, port() reports the error, if port() returns true; // we have a valid port, so we add it to cspHost. if (peek(COLON)) { if (!port()) { delete cspHost; return nullptr;
}
cspHost->setPort(mCurValue);
}
if (atEndOfPath()) { return cspHost;
}
// Calling path() to see if there is a path to parse, if an error // occurs, path() reports the error; handing cspHost as an argument // which simplifies parsing of several paths. if (!path(cspHost)) { // If the host [port] is followed by a path, it has to be a valid path, // otherwise we pass the nullptr, indicating an error, up the callstack. // see also http://www.w3.org/TR/CSP11/#source-list delete cspHost; return nullptr;
} return cspHost;
}
if (!accept(isCharacterToken)) { return nullptr;
} while (schemeChar()) { /* consume */
}
nsString scheme = mCurValue;
// If the potential scheme is not followed by ":" - it's not a scheme if (!accept(COLON)) { return nullptr;
}
// If the chraracter following the ":" is a number or the "*" // then we are not parsing a scheme; but rather a host; if (peek(isNumberToken) || peek(WILDCARD)) { return nullptr;
}
// Check if mCurToken begins with "'nonce-" and ends with "'" if (!StringBeginsWith(mCurToken,
nsDependentString(CSP_EnumToUTF16Keyword(CSP_NONCE)),
nsASCIICaseInsensitiveStringComparator) ||
mCurToken.Last() != SINGLEQUOTE) { return nullptr;
}
// Trim surrounding single quotes const nsAString& expr = Substring(mCurToken, 1, mCurToken.Length() - 2);
for (uint32_t i = 0; i < kHashSourceValidFnsLen; i++) { if (algo.LowerCaseEqualsASCII(kHashSourceValidFns[i])) { // cache if encountering hash or nonce to invalidate unsafe-inline
mHasHashOrNonce = true; returnnew nsCSPHashSrc(algo, hash);
}
} return nullptr;
}
// Check if it is a keyword if (nsCSPBaseSrc* cspKeyword = keywordSource()) { return cspKeyword;
}
// Check if it is a nonce-source if (nsCSPNonceSrc* cspNonce = nonceSource()) { return cspNonce;
}
// Check if it is a hash-source if (nsCSPHashSrc* cspHash = hashSource()) { return cspHash;
}
// We handle a single "*" as host here, to avoid any confusion when applying // the default scheme. However, we still would need to apply the default // scheme in case we would parse "*:80". if (mCurToken.EqualsASCII("*")) { returnnew nsCSPHostSrc(u"*"_ns);
}
// Calling resetCurChar allows us to use mCurChar and mEndChar // to parse mCurToken; e.g. mCurToken = "http://www.example.com", then // mCurChar = 'h' // mEndChar = points just after the last 'm' // mCurValue = ""
resetCurChar(mCurToken);
// Check if mCurToken starts with a scheme
nsAutoString parsedScheme; if (nsCSPSchemeSrc* cspScheme = schemeSource()) { // mCurToken might only enforce a specific scheme if (atEnd()) { return cspScheme;
} // If something follows the scheme, we do not create // a nsCSPSchemeSrc, but rather a nsCSPHostSrc, which // needs to know the scheme to enforce; remember the // scheme and delete cspScheme;
cspScheme->toString(parsedScheme);
parsedScheme.Trim(":", false, true); delete cspScheme;
// If mCurToken provides not only a scheme, but also a host, we have to // check if two slashes follow the scheme. if (!accept(SLASH) || !accept(SLASH)) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource", params); return nullptr;
}
}
// Calling resetCurValue allows us to keep pointers for mCurChar and mEndChar // alive, but resets mCurValue; e.g. mCurToken = "http://www.example.com", // then mCurChar = 'w' mEndChar = 'm' mCurValue = ""
resetCurValue();
// If mCurToken does not provide a scheme (scheme-less source), we apply the // scheme from selfURI if (parsedScheme.IsEmpty()) { // Resetting internal helpers, because we might already have parsed some of // the host when trying to parse a scheme.
resetCurChar(mCurToken);
nsAutoCString selfScheme;
mSelfURI->GetScheme(selfScheme);
parsedScheme.AssignASCII(selfScheme.get());
}
// At this point we are expecting a host to be parsed. // Trying to create a new nsCSPHost. if (nsCSPHostSrc* cspHost = hostSource()) { // Do not forget to set the parsed scheme.
cspHost->setScheme(parsedScheme);
cspHost->setWithinFrameAncestorsDir(mParsingFrameAncestorsDir); return cspHost;
} // Error was reported in hostSource() return nullptr;
}
// remember, srcs start at index 1 for (uint32_t i = 1; i < mCurDir.Length(); i++) { // mCurToken is only set here and remains the current token // to be processed, which avoid passing arguments between functions.
mCurToken = mCurDir[i];
resetCurValue();
// Special case handling for none: // Ignore 'none' if any other src is available. // (See http://www.w3.org/TR/CSP11/#parsing) if (CSP_IsKeyword(mCurToken, CSP_NONE)) {
isNone = true; continue;
} // Must be a regular source expression
nsCSPBaseSrc* src = sourceExpression(); if (src) {
outSrcs.AppendElement(src);
}
}
// Check if the directive contains a 'none' if (isNone) { // If the directive contains no other srcs, then we set the 'none' if (outSrcs.IsEmpty() ||
(outSrcs.Length() == 1 && outSrcs[0]->isReportSample())) {
nsCSPKeywordSrc* keyword = new nsCSPKeywordSrc(CSP_NONE);
outSrcs.InsertElementAt(0, keyword);
} // Otherwise, we ignore 'none' and report a warning else {
logWarningForIgnoringNoneKeywordToConsole();
}
}
}
// If creating the URI casued an error, skip this URI if (NS_FAILED(rv)) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldNotParseReportURI", params); continue;
}
// Create new nsCSPReportURI and append to the list.
nsCSPReportURI* reportURI = new nsCSPReportURI(uri);
srcs.AppendElement(reportURI);
}
nsCSPGroup* group = new nsCSPGroup(mCurToken);
srcs.AppendElement(group);
aDir->addSrcs(srcs);
mPolicy->addDirective(aDir);
aDir = nullptr;
};
/* Helper function for parsing sandbox flags. This function solely concatenates * all the source list tokens (the sandbox flags) so the attribute parser * (nsContentUtils::ParseSandboxAttributeToFlags) can parse them.
*/ void nsCSPParser::sandboxFlagList(nsCSPDirective* aDir) {
CSPPARSERLOG(("nsCSPParser::sandboxFlagList"));
nsAutoString flags;
// remember, srcs start at index 1 for (uint32_t i = 1; i < mCurDir.Length(); i++) {
mCurToken = mCurDir[i];
flags.Append(mCurToken); if (i != mCurDir.Length() - 1) {
flags.AppendLiteral(" ");
}
}
// Please note that the sandbox directive can exist // by itself (not containing any flags).
nsTArray<nsCSPBaseSrc*> srcs;
srcs.AppendElement(new nsCSPSandboxFlags(flags));
aDir->addSrcs(srcs);
mPolicy->addDirective(aDir);
}
// Check if it is a valid directive
CSPDirective directive = CSP_StringToCSPDirective(mCurToken); if (directive == nsIContentSecurityPolicy::NO_DIRECTIVE ||
(!StaticPrefs::dom_security_trusted_types_enabled() &&
(directive ==
nsIContentSecurityPolicy::REQUIRE_TRUSTED_TYPES_FOR_DIRECTIVE ||
directive == nsIContentSecurityPolicy::TRUSTED_TYPES_DIRECTIVE))) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldNotProcessUnknownDirective", params); return nullptr;
}
// The directive 'reflected-xss' is part of CSP 1.1, see: // http://www.w3.org/TR/2014/WD-CSP11-20140211/#reflected-xss // Currently we are not supporting that directive, hence we log a // warning to the console and ignore the directive including its values. if (directive == nsIContentSecurityPolicy::REFLECTED_XSS_DIRECTIVE) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag, "notSupportingDirective", params); return nullptr;
}
// Make sure the directive does not already exist // (see http://www.w3.org/TR/CSP11/#parsing) if (mPolicy->hasDirective(directive)) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag, "duplicateDirective",
params); return nullptr;
}
// CSP delivered via meta tag should ignore the following directives: // report-uri, frame-ancestors, and sandbox, see: // http://www.w3.org/TR/CSP11/#delivery-html-meta-element if (mDeliveredViaMetaTag &&
((directive == nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE) ||
(directive == nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE) ||
(directive == nsIContentSecurityPolicy::SANDBOX_DIRECTIVE))) { // log to the console to indicate that meta CSP is ignoring the directive
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringSrcFromMetaCSP", params); return nullptr;
}
// special case handling for block-all-mixed-content if (directive == nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT) { // If mixed content upgrade is enabled for all types block-all-mixed-content // is obsolete if (mozilla::StaticPrefs::
security_mixed_content_upgrade_display_content() &&
mozilla::StaticPrefs::
security_mixed_content_upgrade_display_content_image() &&
mozilla::StaticPrefs::
security_mixed_content_upgrade_display_content_audio() &&
mozilla::StaticPrefs::
security_mixed_content_upgrade_display_content_video()) { // log to the console that if mixed content display upgrading is enabled // block-all-mixed-content is obsolete.
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag, "obsoleteBlockAllMixedContent", params);
} returnnew nsBlockAllMixedContentDirective(directive);
}
// special case handling for upgrade-insecure-requests if (directive == nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE) { returnnew nsUpgradeInsecureDirective(directive);
}
// if we have a child-src, cache it as a fallback for // * workers (if worker-src is not explicitly specified) // * frames (if frame-src is not explicitly specified) if (directive == nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE) {
mChildSrc = new nsCSPChildSrcDirective(directive); return mChildSrc;
}
// if we have a frame-src, cache it so we can discard child-src for frames if (directive == nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE) {
mFrameSrc = new nsCSPDirective(directive); return mFrameSrc;
}
// if we have a worker-src, cache it so we can discard child-src for workers if (directive == nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE) {
mWorkerSrc = new nsCSPDirective(directive); return mWorkerSrc;
}
// if we have a script-src, cache it as a fallback for worker-src // in case child-src is not present. It is also used as a fallback for // script-src-elem and script-src-attr. if (directive == nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) {
mScriptSrc = new nsCSPScriptSrcDirective(directive); return mScriptSrc;
}
// If we have a style-src, cache it as a fallback for style-src-elem and // style-src-attr. if (directive == nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE) {
mStyleSrc = new nsCSPStyleSrcDirective(directive); return mStyleSrc;
}
returnnew nsCSPDirective(directive);
}
// directive = *WSP [ directive-name [ WSP directive-value ] ] void nsCSPParser::directive() { // Make sure that the directive-srcs-array contains at least // one directive. if (mCurDir.Length() == 0) {
AutoTArray<nsString, 1> params = {u"directive missing"_ns};
logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource", params); return;
}
// Set the directiveName to mCurToken // Remember, the directive name is stored at index 0
mCurToken = mCurDir[0];
if (CSP_IsEmptyDirective(mCurValue, mCurToken)) { return;
}
// Try to create a new CSPDirective
nsCSPDirective* cspDir = directiveName(); if (!cspDir) { // if we can not create a CSPDirective, we can skip parsing the srcs for // that array return;
}
// special case handling for block-all-mixed-content, which is only specified // by a directive name but does not include any srcs. if (cspDir->equals(nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)) { if (mCurDir.Length() > 1) {
AutoTArray<nsString, 1> params = {u"block-all-mixed-content"_ns};
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoreSrcForDirective", params);
} // add the directive and return
mPolicy->addDirective(cspDir); return;
}
// special case handling for upgrade-insecure-requests, which is only // specified by a directive name but does not include any srcs. if (cspDir->equals(nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) { if (mCurDir.Length() > 1) {
AutoTArray<nsString, 1> params = {u"upgrade-insecure-requests"_ns};
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoreSrcForDirective", params);
} // add the directive and return
mPolicy->addUpgradeInsecDir( static_cast<nsUpgradeInsecureDirective*>(cspDir)); return;
}
// special case handling for report-uri directive (since it doesn't contain // a valid source list but rather actual URIs) if (CSP_IsDirective(mCurDir[0],
nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
reportURIList(cspDir); return;
}
// special case handling for report-to directive (since it doesn't contain // a valid source list but rather an endpoint group) if (CSP_IsDirective(mCurDir[0],
nsIContentSecurityPolicy::REPORT_TO_DIRECTIVE)) {
reportGroup(cspDir); return;
}
// special case handling for sandbox directive (since it doe4sn't contain // a valid source list but rather special sandbox flags) if (CSP_IsDirective(mCurDir[0],
nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)) {
sandboxFlagList(cspDir); return;
}
// Special case handling since these directives don't contain source lists. if (CSP_IsDirective(
mCurDir[0],
nsIContentSecurityPolicy::REQUIRE_TRUSTED_TYPES_FOR_DIRECTIVE)) {
handleRequireTrustedTypesForDirective(cspDir); return;
}
if (cspDir->equals(nsIContentSecurityPolicy::TRUSTED_TYPES_DIRECTIVE)) {
handleTrustedTypesDirective(cspDir); return;
}
// make sure to reset cache variables when trying to invalidate unsafe-inline; // unsafe-inline might not only appear in script-src, but also in default-src
mHasHashOrNonce = false;
mHasAnyUnsafeEval = false;
mStrictDynamic = false;
mUnsafeInlineKeywordSrc = nullptr;
// Try to parse all the srcs by handing the array off to directiveValue
nsTArray<nsCSPBaseSrc*> srcs;
directiveValue(srcs);
// If we can not parse any srcs; we let the source expression be the empty set // ('none') see, http://www.w3.org/TR/CSP11/#source-list-parsing if (srcs.IsEmpty() || (srcs.Length() == 1 && srcs[0]->isReportSample())) {
nsCSPKeywordSrc* keyword = new nsCSPKeywordSrc(CSP_NONE);
srcs.InsertElementAt(0, keyword);
}
// Add the newly created srcs to the directive and add the directive to the // policy
cspDir->addSrcs(srcs);
mPolicy->addDirective(cspDir);
}
void nsCSPParser::MaybeWarnAboutIgnoredSources( const nsTArray<nsCSPBaseSrc*>& aSrcs) { // If policy contains 'strict-dynamic' warn about ignored sources. if (mStrictDynamic &&
!CSP_IsDirective(mCurDir[0],
nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE)) { for (uint32_t i = 0; i < aSrcs.Length(); i++) {
nsAutoString srcStr;
aSrcs[i]->toString(srcStr); // Hashes and nonces continue to apply with 'strict-dynamic', as well as // 'unsafe-eval', 'wasm-unsafe-eval' and 'unsafe-hashes'. if (!aSrcs[i]->isKeyword(CSP_STRICT_DYNAMIC) &&
!aSrcs[i]->isKeyword(CSP_UNSAFE_EVAL) &&
!aSrcs[i]->isKeyword(CSP_WASM_UNSAFE_EVAL) &&
!aSrcs[i]->isKeyword(CSP_UNSAFE_HASHES) && !aSrcs[i]->isNonce() &&
!aSrcs[i]->isHash()) {
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) {
AutoTArray<nsString, 1> params = {mCurDir[0]};
logWarningErrorToConsole(nsIScriptError::warningFlag, "strictDynamicButNoHashOrNonce", params);
}
}
}
void nsCSPParser::MaybeWarnAboutUnsafeInline(const nsCSPDirective& aDirective) { // From https://w3c.github.io/webappsec-csp/#allow-all-inline // follows that when either a hash or nonce is specified, 'unsafe-inline' // should not apply. if (mHasHashOrNonce && mUnsafeInlineKeywordSrc &&
(aDirective.isDefaultDirective() ||
aDirective.equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) ||
aDirective.equals(nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) ||
aDirective.equals(nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE) ||
aDirective.equals(nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE) ||
aDirective.equals(nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE) ||
aDirective.equals(nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE))) { // Log to the console that unsafe-inline will be ignored.
AutoTArray<nsString, 2> params = {u"'unsafe-inline'"_ns, mCurDir[0]};
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringSrcWithinNonceOrHashDirective", params);
}
}
void nsCSPParser::MaybeWarnAboutUnsafeEval(const nsCSPDirective& aDirective) { if (mHasAnyUnsafeEval &&
(aDirective.equals(nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) ||
aDirective.equals(
nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE))) { // Log to the console that (wasm-)unsafe-eval will be ignored.
AutoTArray<nsString, 1> params = {mCurDir[0]};
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringUnsafeEval",
params);
}
}
mPolicy = new nsCSPPolicy(); for (uint32_t i = 0; i < mTokens.Length(); i++) { // https://w3c.github.io/webappsec-csp/#parse-serialized-policy // Step 2.2. ..., or if token is not an ASCII string, continue. // // Note: In the spec the token isn't split by whitespace yet. bool isAscii = true; for (constauto& token : mTokens[i]) { if (!IsAscii(token)) {
AutoTArray<nsString, 1> params = {mTokens[i][0], token};
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringNonAsciiToken", params);
isAscii = false; break;
}
} if (!isAscii) { continue;
}
// All input is already tokenized; set one tokenized array in the form of // [ name, src, src, ... ] // to mCurDir and call directive which processes the current directive.
mCurDir = mTokens[i].Clone();
directive();
}
if (mChildSrc) { if (!mFrameSrc) { // if frame-src is specified explicitly for that policy than child-src // should not restrict frames; if not, than child-src needs to restrict // frames.
mChildSrc->setRestrictFrames();
} if (!mWorkerSrc) { // if worker-src is specified explicitly for that policy than child-src // should not restrict workers; if not, than child-src needs to restrict // workers.
mChildSrc->setRestrictWorkers();
}
}
// if script-src is specified, but not worker-src and also no child-src, then // script-src has to govern workers. if (mScriptSrc && !mWorkerSrc && !mChildSrc) {
mScriptSrc->setRestrictWorkers();
}
// If script-src is specified and script-src-elem is not specified, then // script-src has to govern script requests and script blocks. if (mScriptSrc && !mPolicy->hasDirective(
nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE)) {
mScriptSrc->setRestrictScriptElem();
}
// If script-src is specified and script-src-attr is not specified, then // script-src has to govern script attr (event handlers). if (mScriptSrc && !mPolicy->hasDirective(
nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE)) {
mScriptSrc->setRestrictScriptAttr();
}
// If style-src is specified and style-src-elem is not specified, then // style-src serves as a fallback. if (mStyleSrc && !mPolicy->hasDirective(
nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE)) {
mStyleSrc->setRestrictStyleElem();
}
// If style-src is specified and style-attr-elem is not specified, then // style-src serves as a fallback. if (mStyleSrc && !mPolicy->hasDirective(
nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE)) {
mStyleSrc->setRestrictStyleAttr();
}
NS_ASSERTION(aSelfURI, "Can not parseContentSecurityPolicy without aSelfURI");
// Separate all input into tokens and store them in the form of: // [ [ name, src, src, ... ], [ name, src, src, ... ], ... ] // The tokenizer itself can not fail; all eventual errors // are detected in the parser itself.
if (policy->getNumDirectives() == 0) { // Individual errors were already reported in the parser, but if // we do not have an enforcable directive at all, we return null. delete policy; return nullptr;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.