summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp')
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp110
1 files changed, 110 insertions, 0 deletions
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp b/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp
index 408b40e2b..8044ae8bf 100644
--- a/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp
+++ b/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp
@@ -33,6 +33,8 @@
#include "SecurityOrigin.h"
#include "URL.h"
#include <wtf/ASCIICType.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/text/Base64.h>
namespace WebCore {
@@ -125,6 +127,16 @@ bool ContentSecurityPolicySourceList::matches(const URL& url)
return false;
}
+bool ContentSecurityPolicySourceList::matches(const ContentSecurityPolicyHash& hash) const
+{
+ return m_hashes.contains(hash);
+}
+
+bool ContentSecurityPolicySourceList::matches(const String& nonce) const
+{
+ return m_nonces.contains(nonce);
+}
+
// source-list = *WSP [ source *( 1*WSP source ) *WSP ]
// / *WSP "'none'" *WSP
//
@@ -145,6 +157,12 @@ void ContentSecurityPolicySourceList::parse(const UChar* begin, const UChar* end
bool hostHasWildcard = false;
bool portHasWildcard = false;
+ if (parseNonceSource(beginSource, position))
+ continue;
+
+ if (parseHashSource(beginSource, position))
+ continue;
+
if (parseSource(beginSource, position, scheme, host, port, path, hostHasWildcard, portHasWildcard)) {
// Wildcard hosts and keyword sources ('self', 'unsafe-inline',
// etc.) aren't stored in m_list, but as attributes on the source
@@ -385,4 +403,96 @@ bool ContentSecurityPolicySourceList::parsePort(const UChar* begin, const UChar*
return ok;
}
+static bool isBase64Character(UChar c)
+{
+ return isASCIIAlphanumeric(c) || c == '+' || c == '/' || c == '-' || c == '_';
+}
+
+// Match Blink's behavior of allowing an equal sign to appear anywhere in the value of the nonce
+// even though this does not match the behavior of Content Security Policy Level 3 spec.,
+// <https://w3c.github.io/webappsec-csp/> (29 February 2016).
+static bool isNonceCharacter(UChar c)
+{
+ return isBase64Character(c) || c == '=';
+}
+
+// nonce-source = "'nonce-" nonce-value "'"
+// nonce-value = base64-value
+bool ContentSecurityPolicySourceList::parseNonceSource(const UChar* begin, const UChar* end)
+{
+ static NeverDestroyed<String> noncePrefix("'nonce-", String::ConstructFromLiteral);
+ if (!StringView(begin, end - begin).startsWithIgnoringASCIICase(noncePrefix.get()))
+ return false;
+ const UChar* position = begin + noncePrefix.get().length();
+ const UChar* beginNonceValue = position;
+ skipWhile<UChar, isNonceCharacter>(position, end);
+ if (position >= end || position == beginNonceValue || *position != '\'')
+ return false;
+ m_nonces.add(String(beginNonceValue, position - beginNonceValue));
+ return true;
+}
+
+static bool parseHashAlgorithmAdvancingPosition(const UChar*& position, size_t length, ContentSecurityPolicyHashAlgorithm& algorithm)
+{
+ static struct {
+ NeverDestroyed<String> label;
+ ContentSecurityPolicyHashAlgorithm algorithm;
+ } labelToHashAlgorithmTable[] {
+ { ASCIILiteral("sha256"), ContentSecurityPolicyHashAlgorithm::SHA_256 },
+ { ASCIILiteral("sha384"), ContentSecurityPolicyHashAlgorithm::SHA_384 },
+ { ASCIILiteral("sha512"), ContentSecurityPolicyHashAlgorithm::SHA_512 },
+ };
+
+ StringView stringView(position, length);
+ for (auto& entry : labelToHashAlgorithmTable) {
+ String& label = entry.label.get();
+ if (!stringView.startsWithIgnoringASCIICase(label))
+ continue;
+ position += label.length();
+ algorithm = entry.algorithm;
+ return true;
+ }
+ return false;
+}
+
+// hash-source = "'" hash-algorithm "-" base64-value "'"
+// hash-algorithm = "sha256" / "sha384" / "sha512"
+// base64-value = 1*( ALPHA / DIGIT / "+" / "/" / "-" / "_" )*2( "=" )
+bool ContentSecurityPolicySourceList::parseHashSource(const UChar* begin, const UChar* end)
+{
+ if (begin == end)
+ return false;
+
+ const UChar* position = begin;
+ if (!skipExactly<UChar>(position, end, '\''))
+ return false;
+
+ ContentSecurityPolicyHashAlgorithm algorithm;
+ if (!parseHashAlgorithmAdvancingPosition(position, end - position, algorithm))
+ return false;
+
+ if (!skipExactly<UChar>(position, end, '-'))
+ return false;
+
+ const UChar* beginHashValue = position;
+ skipWhile<UChar, isBase64Character>(position, end);
+ skipExactly<UChar>(position, end, '=');
+ skipExactly<UChar>(position, end, '=');
+ if (position >= end || position == beginHashValue || *position != '\'')
+ return false;
+ Vector<uint8_t> digest;
+ StringView hashValue(beginHashValue, position - beginHashValue); // base64url or base64 encoded
+ // FIXME: Normalize Base64URL to Base64 instead of decoding twice. See <https://bugs.webkit.org/show_bug.cgi?id=155186>.
+ if (!base64Decode(hashValue.toStringWithoutCopying(), digest, Base64ValidatePadding)) {
+ if (!base64URLDecode(hashValue.toStringWithoutCopying(), digest))
+ return false;
+ }
+ if (digest.size() > maximumContentSecurityPolicyDigestLength)
+ return false;
+
+ m_hashes.add(std::make_pair(algorithm, digest));
+ m_hashAlgorithmsUsed |= algorithm;
+ return true;
+}
+
} // namespace WebCore