Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(402)

Unified Diff: third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp

Issue 2449873004: Removing CSPSourceList level up to SourceListDirective. (Closed)
Patch Set: Moving CSPSourceList logic to SourceListDirective Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp
diff --git a/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp b/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp
index 4d2cd4a8b7b26f4217594f9cc663c5dd2c914826..adf39a29dd9327244ebba2c0baa2d5ad010dc2ac 100644
--- a/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp
@@ -4,10 +4,13 @@
#include "core/frame/csp/SourceListDirective.h"
-#include "core/frame/csp/CSPSourceList.h"
#include "core/frame/csp/ContentSecurityPolicy.h"
#include "platform/network/ContentSecurityPolicyParsers.h"
#include "platform/weborigin/KURL.h"
+#include "platform/weborigin/SecurityOrigin.h"
+#include "wtf/text/Base64.h"
+#include "wtf/text/ParsingUtilities.h"
+#include "wtf/text/StringToNumber.h"
#include "wtf/text/WTFString.h"
namespace blink {
@@ -15,53 +18,558 @@ namespace blink {
SourceListDirective::SourceListDirective(const String& name,
const String& value,
ContentSecurityPolicy* policy)
- : CSPDirective(name, value, policy), m_sourceList(policy, name) {
+ : CSPDirective(name, value, policy),
+ m_policy(policy),
+ m_directiveName(name),
+ m_allowSelf(false),
+ m_allowStar(false),
+ m_allowInline(false),
+ m_allowEval(false),
+ m_allowDynamic(false),
+ m_allowHashedAttributes(false),
+ m_hashAlgorithmsUsed(0) {
Vector<UChar> characters;
value.appendTo(characters);
+ parse(characters.data(), characters.data() + characters.size());
+}
+
+static bool isSourceListNone(const UChar* begin, const UChar* end) {
+ skipWhile<UChar, isASCIISpace>(begin, end);
+
+ const UChar* position = begin;
+ skipWhile<UChar, isSourceCharacter>(position, end);
+ if (!equalIgnoringCase("'none'", StringView(begin, position - begin)))
+ return false;
+
+ skipWhile<UChar, isASCIISpace>(position, end);
+ if (position != end)
+ return false;
- m_sourceList.parse(characters.data(), characters.data() + characters.size());
+ return true;
}
bool SourceListDirective::allows(
const KURL& url,
ResourceRequest::RedirectStatus redirectStatus) const {
- return m_sourceList.matches(url, redirectStatus);
+ // Wildcards match network schemes ('http', 'https', 'ftp', 'ws', 'wss'), and
+ // the scheme of the protected resource:
+ // https://w3c.github.io/webappsec-csp/#match-url-to-source-expression. Other
+ // schemes, including custom schemes, must be explicitly listed in a source
+ // list.
+ if (m_allowStar) {
+ if (url.protocolIsInHTTPFamily() || url.protocolIs("ftp") ||
+ url.protocolIs("ws") || url.protocolIs("wss") ||
+ m_policy->protocolMatchesSelf(url))
+ return true;
+
+ return hasSourceMatchInList(url, redirectStatus);
+ }
+
+ KURL effectiveURL =
+ m_policy->selfMatchesInnerURL() && SecurityOrigin::shouldUseInnerURL(url)
+ ? SecurityOrigin::extractInnerURL(url)
+ : url;
+
+ if (m_allowSelf && m_policy->urlMatchesSelf(effectiveURL))
+ return true;
+
+ return hasSourceMatchInList(effectiveURL, redirectStatus);
}
bool SourceListDirective::allowInline() const {
- return m_sourceList.allowInline();
+ return m_allowInline;
}
bool SourceListDirective::allowEval() const {
- return m_sourceList.allowEval();
+ return m_allowEval;
}
bool SourceListDirective::allowDynamic() const {
- return m_sourceList.allowDynamic();
+ return m_allowDynamic;
}
-bool SourceListDirective::allowNonce(const String& nonce) const {
- return m_sourceList.allowNonce(nonce.stripWhiteSpace());
+bool SourceListDirective::allowNonce(String nonce) const {
+ nonce = nonce.stripWhiteSpace();
+ return !nonce.isNull() && m_nonces.contains(nonce);
}
bool SourceListDirective::allowHash(const CSPHashValue& hashValue) const {
- return m_sourceList.allowHash(hashValue);
+ return m_hashes.contains(hashValue);
}
bool SourceListDirective::allowHashedAttributes() const {
- return m_sourceList.allowHashedAttributes();
+ return m_allowHashedAttributes;
+}
+
+uint8_t SourceListDirective::hashAlgorithmsUsed() const {
+ return m_hashAlgorithmsUsed;
}
bool SourceListDirective::isHashOrNoncePresent() const {
- return m_sourceList.isHashOrNoncePresent();
+ return !m_nonces.isEmpty() ||
+ m_hashAlgorithmsUsed != ContentSecurityPolicyHashAlgorithmNone;
}
-uint8_t SourceListDirective::hashAlgorithmsUsed() const {
- return m_sourceList.hashAlgorithmsUsed();
+// source-list = *WSP [ source *( 1*WSP source ) *WSP ]
+// / *WSP "'none'" *WSP
+//
+void SourceListDirective::parse(const UChar* begin, const UChar* end) {
+ // We represent 'none' as an empty m_list.
+ if (isSourceListNone(begin, end))
+ return;
+
+ const UChar* position = begin;
+ while (position < end) {
+ skipWhile<UChar, isASCIISpace>(position, end);
+ if (position == end)
+ return;
+
+ const UChar* beginSource = position;
+ skipWhile<UChar, isSourceCharacter>(position, end);
+
+ String scheme, host, path;
+ int port = 0;
+ CSPSource::WildcardDisposition hostWildcard = CSPSource::NoWildcard;
+ CSPSource::WildcardDisposition portWildcard = CSPSource::NoWildcard;
+
+ if (parseSource(beginSource, position, scheme, host, port, path,
+ hostWildcard, portWildcard)) {
+ // Wildcard hosts and keyword sources ('self', 'unsafe-inline',
+ // etc.) aren't stored in m_list, but as attributes on the source
+ // list itself.
+ if (scheme.isEmpty() && host.isEmpty())
+ continue;
+ if (m_policy->isDirectiveName(host))
+ m_policy->reportDirectiveAsSourceExpression(m_directiveName, host);
+ m_list.append(new CSPSource(m_policy, scheme, host, port, path,
+ hostWildcard, portWildcard));
+ } else {
+ m_policy->reportInvalidSourceExpression(
+ m_directiveName, String(beginSource, position - beginSource));
+ }
+
+ DCHECK(position == end || isASCIISpace(*position));
+ }
+}
+
+// source = scheme ":"
+// / ( [ scheme "://" ] host [ port ] [ path ] )
+// / "'self'"
+bool SourceListDirective::parseSource(
+ const UChar* begin,
+ const UChar* end,
+ String& scheme,
+ String& host,
+ int& port,
+ String& path,
+ CSPSource::WildcardDisposition& hostWildcard,
+ CSPSource::WildcardDisposition& portWildcard) {
+ if (begin == end)
+ return false;
+
+ StringView token(begin, end - begin);
+
+ if (equalIgnoringCase("'none'", token))
+ return false;
+
+ if (end - begin == 1 && *begin == '*') {
+ addSourceStar();
+ return true;
+ }
+
+ if (equalIgnoringCase("'self'", token)) {
+ addSourceSelf();
+ return true;
+ }
+
+ if (equalIgnoringCase("'unsafe-inline'", token)) {
+ addSourceUnsafeInline();
+ return true;
+ }
+
+ if (equalIgnoringCase("'unsafe-eval'", token)) {
+ addSourceUnsafeEval();
+ return true;
+ }
+
+ if (equalIgnoringCase("'strict-dynamic'", token)) {
+ addSourceStrictDynamic();
+ return true;
+ }
+
+ if (equalIgnoringCase("'unsafe-hashed-attributes'", token)) {
+ addSourceUnsafeHashedAttributes();
+ return true;
+ }
+
+ String nonce;
+ if (!parseNonce(begin, end, nonce))
+ return false;
+
+ if (!nonce.isNull()) {
+ addSourceNonce(nonce);
+ return true;
+ }
+
+ DigestValue hash;
+ ContentSecurityPolicyHashAlgorithm algorithm =
+ ContentSecurityPolicyHashAlgorithmNone;
+ if (!parseHash(begin, end, hash, algorithm))
+ return false;
+
+ if (hash.size() > 0) {
+ addSourceHash(algorithm, hash);
+ return true;
+ }
+
+ const UChar* position = begin;
+ const UChar* beginHost = begin;
+ const UChar* beginPath = end;
+ const UChar* beginPort = 0;
+
+ skipWhile<UChar, isNotColonOrSlash>(position, end);
+
+ if (position == end) {
+ // host
+ // ^
+ return parseHost(beginHost, position, host, hostWildcard);
+ }
+
+ if (position < end && *position == '/') {
+ // host/path || host/ || /
+ // ^ ^ ^
+ return parseHost(beginHost, position, host, hostWildcard) &&
+ parsePath(position, end, path);
+ }
+
+ if (position < end && *position == ':') {
+ if (end - position == 1) {
+ // scheme:
+ // ^
+ return parseScheme(begin, position, scheme);
+ }
+
+ if (position[1] == '/') {
+ // scheme://host || scheme://
+ // ^ ^
+ if (!parseScheme(begin, position, scheme) ||
+ !skipExactly<UChar>(position, end, ':') ||
+ !skipExactly<UChar>(position, end, '/') ||
+ !skipExactly<UChar>(position, end, '/'))
+ return false;
+ if (position == end)
+ return false;
+ beginHost = position;
+ skipWhile<UChar, isNotColonOrSlash>(position, end);
+ }
+
+ if (position < end && *position == ':') {
+ // host:port || scheme://host:port
+ // ^ ^
+ beginPort = position;
+ skipUntil<UChar>(position, end, '/');
+ }
+ }
+
+ if (position < end && *position == '/') {
+ // scheme://host/path || scheme://host:port/path
+ // ^ ^
+ if (position == beginHost)
+ return false;
+ beginPath = position;
+ }
+
+ if (!parseHost(beginHost, beginPort ? beginPort : beginPath, host,
+ hostWildcard))
+ return false;
+
+ if (beginPort) {
+ if (!parsePort(beginPort, beginPath, port, portWildcard))
+ return false;
+ } else {
+ port = 0;
+ }
+
+ if (beginPath != end) {
+ if (!parsePath(beginPath, end, path))
+ return false;
+ }
+
+ return true;
+}
+
+// nonce-source = "'nonce-" nonce-value "'"
+// nonce-value = 1*( ALPHA / DIGIT / "+" / "/" / "=" )
+//
+bool SourceListDirective::parseNonce(const UChar* begin,
+ const UChar* end,
+ String& nonce) {
+ size_t nonceLength = end - begin;
+ StringView prefix("'nonce-");
+
+ // TODO(esprehn): Should be StringView(begin, nonceLength).startsWith(prefix).
+ if (nonceLength <= prefix.length() ||
+ !equalIgnoringCase(prefix, StringView(begin, prefix.length())))
+ return true;
+
+ const UChar* position = begin + prefix.length();
+ const UChar* nonceBegin = position;
+
+ DCHECK(position < end);
+ skipWhile<UChar, isNonceCharacter>(position, end);
+ DCHECK(nonceBegin <= position);
+
+ if (position + 1 != end || *position != '\'' || position == nonceBegin)
+ return false;
+
+ nonce = String(nonceBegin, position - nonceBegin);
+ return true;
+}
+
+// hash-source = "'" hash-algorithm "-" hash-value "'"
+// hash-algorithm = "sha1" / "sha256" / "sha384" / "sha512"
+// hash-value = 1*( ALPHA / DIGIT / "+" / "/" / "=" )
+//
+bool SourceListDirective::parseHash(
+ const UChar* begin,
+ const UChar* end,
+ DigestValue& hash,
+ ContentSecurityPolicyHashAlgorithm& hashAlgorithm) {
+ // Any additions or subtractions from this struct should also modify the
+ // respective entries in the kAlgorithmMap array in checkDigest().
+ static const struct {
+ const char* prefix;
+ ContentSecurityPolicyHashAlgorithm type;
+ } kSupportedPrefixes[] = {
+ // FIXME: Drop support for SHA-1. It's not in the spec.
+ {"'sha1-", ContentSecurityPolicyHashAlgorithmSha1},
+ {"'sha256-", ContentSecurityPolicyHashAlgorithmSha256},
+ {"'sha384-", ContentSecurityPolicyHashAlgorithmSha384},
+ {"'sha512-", ContentSecurityPolicyHashAlgorithmSha512},
+ {"'sha-256-", ContentSecurityPolicyHashAlgorithmSha256},
+ {"'sha-384-", ContentSecurityPolicyHashAlgorithmSha384},
+ {"'sha-512-", ContentSecurityPolicyHashAlgorithmSha512}};
+
+ StringView prefix;
+ hashAlgorithm = ContentSecurityPolicyHashAlgorithmNone;
+ size_t hashLength = end - begin;
+
+ for (const auto& algorithm : kSupportedPrefixes) {
+ prefix = algorithm.prefix;
+ // TODO(esprehn): Should be StringView(begin, end -
+ // begin).startsWith(prefix).
+ if (hashLength > prefix.length() &&
+ equalIgnoringCase(prefix, StringView(begin, prefix.length()))) {
+ hashAlgorithm = algorithm.type;
+ break;
+ }
+ }
+
+ if (hashAlgorithm == ContentSecurityPolicyHashAlgorithmNone)
+ return true;
+
+ const UChar* position = begin + prefix.length();
+ const UChar* hashBegin = position;
+
+ DCHECK(position < end);
+ skipWhile<UChar, isBase64EncodedCharacter>(position, end);
+ DCHECK(hashBegin <= position);
+
+ // Base64 encodings may end with exactly one or two '=' characters
+ if (position < end)
+ skipExactly<UChar>(position, position + 1, '=');
+ if (position < end)
+ skipExactly<UChar>(position, position + 1, '=');
+
+ if (position + 1 != end || *position != '\'' || position == hashBegin)
+ return false;
+
+ Vector<char> hashVector;
+ // We accept base64url-encoded data here by normalizing it to base64.
+ base64Decode(normalizeToBase64(String(hashBegin, position - hashBegin)),
+ hashVector);
+ if (hashVector.size() > kMaxDigestSize)
+ return false;
+ hash.append(reinterpret_cast<uint8_t*>(hashVector.data()), hashVector.size());
+ return true;
+}
+
+// ; <scheme> production from RFC 3986
+// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+//
+bool SourceListDirective::parseScheme(const UChar* begin,
+ const UChar* end,
+ String& scheme) {
+ DCHECK(begin <= end);
+ DCHECK(scheme.isEmpty());
+
+ if (begin == end)
+ return false;
+
+ const UChar* position = begin;
+
+ if (!skipExactly<UChar, isASCIIAlpha>(position, end))
+ return false;
+
+ skipWhile<UChar, isSchemeContinuationCharacter>(position, end);
+
+ if (position != end)
+ return false;
+
+ scheme = String(begin, end - begin);
+ return true;
+}
+
+// host = [ "*." ] 1*host-char *( "." 1*host-char )
+// / "*"
+// host-char = ALPHA / DIGIT / "-"
+//
+bool SourceListDirective::parseHost(
+ const UChar* begin,
+ const UChar* end,
+ String& host,
+ CSPSource::WildcardDisposition& hostWildcard) {
+ DCHECK(begin <= end);
+ DCHECK(host.isEmpty());
+ DCHECK(hostWildcard == CSPSource::NoWildcard);
+
+ if (begin == end)
+ return false;
+
+ const UChar* position = begin;
+
+ if (skipExactly<UChar>(position, end, '*')) {
+ hostWildcard = CSPSource::HasWildcard;
+
+ if (position == end)
+ return true;
+
+ if (!skipExactly<UChar>(position, end, '.'))
+ return false;
+ }
+
+ const UChar* hostBegin = position;
+
+ while (position < end) {
+ if (!skipExactly<UChar, isHostCharacter>(position, end))
+ return false;
+
+ skipWhile<UChar, isHostCharacter>(position, end);
+
+ if (position < end && !skipExactly<UChar>(position, end, '.'))
+ return false;
+ }
+
+ DCHECK(position == end);
+ host = String(hostBegin, end - hostBegin);
+ return true;
+}
+
+bool SourceListDirective::parsePath(const UChar* begin,
+ const UChar* end,
+ String& path) {
+ DCHECK(begin <= end);
+ DCHECK(path.isEmpty());
+
+ const UChar* position = begin;
+ skipWhile<UChar, isPathComponentCharacter>(position, end);
+ // path/to/file.js?query=string || path/to/file.js#anchor
+ // ^ ^
+ if (position < end) {
+ m_policy->reportInvalidPathCharacter(m_directiveName,
+ String(begin, end - begin), *position);
+ }
+
+ path = decodeURLEscapeSequences(String(begin, position - begin));
+
+ DCHECK(position <= end);
+ DCHECK(position == end || (*position == '#' || *position == '?'));
+ return true;
+}
+
+// port = ":" ( 1*DIGIT / "*" )
+//
+bool SourceListDirective::parsePort(
+ const UChar* begin,
+ const UChar* end,
+ int& port,
+ CSPSource::WildcardDisposition& portWildcard) {
+ DCHECK(begin <= end);
+ DCHECK(!port);
+ DCHECK(portWildcard == CSPSource::NoWildcard);
+
+ if (!skipExactly<UChar>(begin, end, ':'))
+ NOTREACHED();
+
+ if (begin == end)
+ return false;
+
+ if (end - begin == 1 && *begin == '*') {
+ port = 0;
+ portWildcard = CSPSource::HasWildcard;
+ return true;
+ }
+
+ const UChar* position = begin;
+ skipWhile<UChar, isASCIIDigit>(position, end);
+
+ if (position != end)
+ return false;
+
+ bool ok;
+ port = charactersToIntStrict(begin, end - begin, &ok);
+ return ok;
+}
+
+void SourceListDirective::addSourceSelf() {
+ m_allowSelf = true;
+}
+
+void SourceListDirective::addSourceStar() {
+ m_allowStar = true;
+}
+
+void SourceListDirective::addSourceUnsafeInline() {
+ m_allowInline = true;
+}
+
+void SourceListDirective::addSourceUnsafeEval() {
+ m_allowEval = true;
+}
+
+void SourceListDirective::addSourceStrictDynamic() {
+ m_allowDynamic = true;
+}
+
+void SourceListDirective::addSourceUnsafeHashedAttributes() {
+ m_allowHashedAttributes = true;
+}
+
+void SourceListDirective::addSourceNonce(const String& nonce) {
+ m_nonces.add(nonce);
+}
+
+void SourceListDirective::addSourceHash(
+ const ContentSecurityPolicyHashAlgorithm& algorithm,
+ const DigestValue& hash) {
+ m_hashes.add(CSPHashValue(algorithm, hash));
+ m_hashAlgorithmsUsed |= algorithm;
+}
+
+bool SourceListDirective::hasSourceMatchInList(
+ const KURL& url,
+ ResourceRequest::RedirectStatus redirectStatus) const {
+ for (size_t i = 0; i < m_list.size(); ++i) {
+ if (m_list[i]->matches(url, redirectStatus))
+ return true;
+ }
+
+ return false;
}
DEFINE_TRACE(SourceListDirective) {
- visitor->trace(m_sourceList);
+ visitor->trace(m_policy);
+ visitor->trace(m_list);
CSPDirective::trace(visitor);
}

Powered by Google App Engine
This is Rietveld 408576698