summaryrefslogtreecommitdiff
path: root/Source/WebCore/page/csp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/page/csp')
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicy.cpp881
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicy.h220
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicyDirective.cpp38
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicyDirective.h57
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp511
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h132
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.cpp54
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.h54
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicyHash.h66
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicyMediaListDirective.cpp118
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicyMediaListDirective.h49
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.cpp62
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.h57
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicySource.cpp113
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicySource.h60
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp520
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicySourceList.h86
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp60
-rw-r--r--Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.h53
19 files changed, 3191 insertions, 0 deletions
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicy.cpp b/Source/WebCore/page/csp/ContentSecurityPolicy.cpp
new file mode 100644
index 000000000..475087f0e
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicy.cpp
@@ -0,0 +1,881 @@
+/*
+ * Copyright (C) 2011 Google, Inc. All rights reserved.
+ * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ContentSecurityPolicy.h"
+
+#include "ContentSecurityPolicyDirective.h"
+#include "ContentSecurityPolicyDirectiveList.h"
+#include "ContentSecurityPolicyDirectiveNames.h"
+#include "ContentSecurityPolicyHash.h"
+#include "ContentSecurityPolicySource.h"
+#include "ContentSecurityPolicySourceList.h"
+#include "DOMStringList.h"
+#include "Document.h"
+#include "DocumentLoader.h"
+#include "EventNames.h"
+#include "FormData.h"
+#include "FormDataList.h"
+#include "Frame.h"
+#include "HTMLParserIdioms.h"
+#include "InspectorInstrumentation.h"
+#include "JSDOMWindowShell.h"
+#include "JSMainThreadExecState.h"
+#include "ParsingUtilities.h"
+#include "PingLoader.h"
+#include "ResourceRequest.h"
+#include "RuntimeEnabledFeatures.h"
+#include "SchemeRegistry.h"
+#include "SecurityOrigin.h"
+#include "SecurityPolicyViolationEvent.h"
+#include "Settings.h"
+#include "TextEncoding.h"
+#include <inspector/InspectorValues.h>
+#include <inspector/ScriptCallStack.h>
+#include <inspector/ScriptCallStackFactory.h>
+#include <pal/crypto/CryptoDigest.h>
+#include <wtf/SetForScope.h>
+#include <wtf/text/StringBuilder.h>
+#include <wtf/text/TextPosition.h>
+
+using namespace Inspector;
+
+namespace WebCore {
+
+static String consoleMessageForViolation(const char* effectiveViolatedDirective, const ContentSecurityPolicyDirective& violatedDirective, const URL& blockedURL, const char* prefix, const char* subject = "it")
+{
+ StringBuilder result;
+ if (violatedDirective.directiveList().isReportOnly())
+ result.appendLiteral("[Report Only] ");
+ result.append(prefix);
+ if (!blockedURL.isEmpty()) {
+ result.append(' ');
+ result.append(blockedURL.stringCenterEllipsizedToLength());
+ }
+ result.appendLiteral(" because ");
+ result.append(subject);
+ if (violatedDirective.isDefaultSrc()) {
+ result.appendLiteral(" appears in neither the ");
+ result.append(effectiveViolatedDirective);
+ result.appendLiteral(" directive nor the default-src directive of the Content Security Policy.");
+ } else {
+ result.appendLiteral(" does not appear in the ");
+ result.append(effectiveViolatedDirective);
+ result.appendLiteral(" directive of the Content Security Policy.");
+ }
+ return result.toString();
+}
+
+ContentSecurityPolicy::ContentSecurityPolicy(ScriptExecutionContext& scriptExecutionContext)
+ : m_scriptExecutionContext(&scriptExecutionContext)
+ , m_sandboxFlags(SandboxNone)
+{
+ ASSERT(scriptExecutionContext.securityOrigin());
+ updateSourceSelf(*scriptExecutionContext.securityOrigin());
+}
+
+ContentSecurityPolicy::ContentSecurityPolicy(const SecurityOrigin& securityOrigin, const Frame* frame)
+ : m_frame(frame)
+ , m_sandboxFlags(SandboxNone)
+{
+ updateSourceSelf(securityOrigin);
+}
+
+ContentSecurityPolicy::~ContentSecurityPolicy()
+{
+}
+
+void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other)
+{
+ if (m_hasAPIPolicy)
+ return;
+ ASSERT(m_policies.isEmpty());
+ for (auto& policy : other->m_policies)
+ didReceiveHeader(policy->header(), policy->headerType(), ContentSecurityPolicy::PolicyFrom::Inherited);
+}
+
+void ContentSecurityPolicy::copyUpgradeInsecureRequestStateFrom(const ContentSecurityPolicy& other)
+{
+ m_upgradeInsecureRequests = other.m_upgradeInsecureRequests;
+ m_insecureNavigationRequestsToUpgrade.add(other.m_insecureNavigationRequestsToUpgrade.begin(), other.m_insecureNavigationRequestsToUpgrade.end());
+}
+
+bool ContentSecurityPolicy::allowRunningOrDisplayingInsecureContent(const URL& url)
+{
+ bool allow = true;
+ bool isReportOnly = false;
+ for (auto& policy : m_policies) {
+ if (!policy->hasBlockAllMixedContentDirective())
+ continue;
+
+ isReportOnly = policy->isReportOnly();
+
+ StringBuilder consoleMessage;
+ if (isReportOnly)
+ consoleMessage.appendLiteral("[Report Only] ");
+ consoleMessage.append("Blocked mixed content ");
+ consoleMessage.append(url.stringCenterEllipsizedToLength());
+ consoleMessage.appendLiteral(" because ");
+ consoleMessage.append("'block-all-mixed-content' appears in the Content Security Policy.");
+ reportViolation(ContentSecurityPolicyDirectiveNames::blockAllMixedContent, ContentSecurityPolicyDirectiveNames::blockAllMixedContent, *policy, url, consoleMessage.toString());
+
+ if (!isReportOnly)
+ allow = false;
+ }
+ return allow;
+}
+
+void ContentSecurityPolicy::didCreateWindowShell(JSDOMWindowShell& windowShell) const
+{
+ JSDOMWindow* window = windowShell.window();
+ ASSERT(window);
+ ASSERT(window->scriptExecutionContext());
+ ASSERT(window->scriptExecutionContext()->contentSecurityPolicy() == this);
+ if (!windowShell.world().isNormal()) {
+ window->setEvalEnabled(true);
+ return;
+ }
+ window->setEvalEnabled(m_lastPolicyEvalDisabledErrorMessage.isNull(), m_lastPolicyEvalDisabledErrorMessage);
+}
+
+ContentSecurityPolicyResponseHeaders ContentSecurityPolicy::responseHeaders() const
+{
+ ContentSecurityPolicyResponseHeaders result;
+ result.m_headers.reserveInitialCapacity(m_policies.size());
+ for (auto& policy : m_policies)
+ result.m_headers.uncheckedAppend({ policy->header(), policy->headerType() });
+ return result;
+}
+
+void ContentSecurityPolicy::didReceiveHeaders(const ContentSecurityPolicyResponseHeaders& headers, ReportParsingErrors reportParsingErrors)
+{
+ SetForScope<bool> isReportingEnabled(m_isReportingEnabled, reportParsingErrors == ReportParsingErrors::Yes);
+ for (auto& header : headers.m_headers)
+ didReceiveHeader(header.first, header.second, ContentSecurityPolicy::PolicyFrom::HTTPHeader);
+}
+
+void ContentSecurityPolicy::didReceiveHeader(const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicy::PolicyFrom policyFrom)
+{
+ if (m_hasAPIPolicy)
+ return;
+
+ if (policyFrom == PolicyFrom::API) {
+ ASSERT(m_policies.isEmpty());
+ m_hasAPIPolicy = true;
+ }
+
+ // RFC2616, section 4.2 specifies that headers appearing multiple times can
+ // be combined with a comma. Walk the header string, and parse each comma
+ // separated chunk as a separate header.
+ auto characters = StringView(header).upconvertedCharacters();
+ const UChar* begin = characters;
+ const UChar* position = begin;
+ const UChar* end = begin + header.length();
+ while (position < end) {
+ skipUntil<UChar>(position, end, ',');
+
+ // header1,header2 OR header1
+ // ^ ^
+ m_policies.append(ContentSecurityPolicyDirectiveList::create(*this, String(begin, position - begin), type, policyFrom));
+
+ // Skip the comma, and begin the next header from the current position.
+ ASSERT(position == end || *position == ',');
+ skipExactly<UChar>(position, end, ',');
+ begin = position;
+ }
+
+ if (m_scriptExecutionContext)
+ applyPolicyToScriptExecutionContext();
+}
+
+void ContentSecurityPolicy::updateSourceSelf(const SecurityOrigin& securityOrigin)
+{
+ m_selfSourceProtocol = securityOrigin.protocol();
+ m_selfSource = std::make_unique<ContentSecurityPolicySource>(*this, m_selfSourceProtocol, securityOrigin.host(), securityOrigin.port(), emptyString(), false, false);
+}
+
+void ContentSecurityPolicy::applyPolicyToScriptExecutionContext()
+{
+ ASSERT(m_scriptExecutionContext);
+
+ // Update source self as the security origin may have changed between the time we were created and now.
+ // For instance, we may have been initially created for an about:blank iframe that later inherited the
+ // security origin of its owner document.
+ ASSERT(m_scriptExecutionContext->securityOrigin());
+ updateSourceSelf(*m_scriptExecutionContext->securityOrigin());
+
+ bool enableStrictMixedContentMode = false;
+ for (auto& policy : m_policies) {
+ const ContentSecurityPolicyDirective* violatedDirective = policy->violatedDirectiveForUnsafeEval();
+ if (violatedDirective && !violatedDirective->directiveList().isReportOnly())
+ m_lastPolicyEvalDisabledErrorMessage = policy->evalDisabledErrorMessage();
+ if (policy->hasBlockAllMixedContentDirective() && !policy->isReportOnly())
+ enableStrictMixedContentMode = true;
+ }
+
+ if (!m_lastPolicyEvalDisabledErrorMessage.isNull())
+ m_scriptExecutionContext->disableEval(m_lastPolicyEvalDisabledErrorMessage);
+ if (m_sandboxFlags != SandboxNone && is<Document>(m_scriptExecutionContext))
+ m_scriptExecutionContext->enforceSandboxFlags(m_sandboxFlags);
+ if (enableStrictMixedContentMode)
+ m_scriptExecutionContext->setStrictMixedContentMode(true);
+}
+
+void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value)
+{
+ m_overrideInlineStyleAllowed = value;
+}
+
+bool ContentSecurityPolicy::urlMatchesSelf(const URL& url) const
+{
+ return m_selfSource->matches(url);
+}
+
+bool ContentSecurityPolicy::allowContentSecurityPolicySourceStarToMatchAnyProtocol() const
+{
+ if (is<Document>(m_scriptExecutionContext))
+ return downcast<Document>(*m_scriptExecutionContext).settings().allowContentSecurityPolicySourceStarToMatchAnyProtocol();
+ return false;
+}
+
+bool ContentSecurityPolicy::protocolMatchesSelf(const URL& url) const
+{
+ if (equalLettersIgnoringASCIICase(m_selfSourceProtocol, "http"))
+ return url.protocolIsInHTTPFamily();
+ return equalIgnoringASCIICase(url.protocol(), m_selfSourceProtocol);
+}
+
+template<typename Predicate, typename... Args>
+typename std::enable_if<!std::is_convertible<Predicate, ContentSecurityPolicy::ViolatedDirectiveCallback>::value, bool>::type ContentSecurityPolicy::allPoliciesWithDispositionAllow(Disposition disposition, Predicate&& predicate, Args&&... args) const
+{
+ bool isReportOnly = disposition == ContentSecurityPolicy::Disposition::ReportOnly;
+ for (auto& policy : m_policies) {
+ if (policy->isReportOnly() != isReportOnly)
+ continue;
+ if ((policy.get()->*predicate)(std::forward<Args>(args)...))
+ return false;
+ }
+ return true;
+}
+
+template<typename Predicate, typename... Args>
+bool ContentSecurityPolicy::allPoliciesWithDispositionAllow(Disposition disposition, ViolatedDirectiveCallback&& callback, Predicate&& predicate, Args&&... args) const
+{
+ bool isReportOnly = disposition == ContentSecurityPolicy::Disposition::ReportOnly;
+ bool isAllowed = true;
+ for (auto& policy : m_policies) {
+ if (policy->isReportOnly() != isReportOnly)
+ continue;
+ if (const ContentSecurityPolicyDirective* violatedDirective = (policy.get()->*predicate)(std::forward<Args>(args)...)) {
+ isAllowed = false;
+ callback(*violatedDirective);
+ }
+ }
+ return isAllowed;
+}
+
+template<typename Predicate, typename... Args>
+bool ContentSecurityPolicy::allPoliciesAllow(ViolatedDirectiveCallback&& callback, Predicate&& predicate, Args&&... args) const
+{
+ bool isAllowed = true;
+ for (auto& policy : m_policies) {
+ if (const ContentSecurityPolicyDirective* violatedDirective = (policy.get()->*predicate)(std::forward<Args>(args)...)) {
+ if (!violatedDirective->directiveList().isReportOnly())
+ isAllowed = false;
+ callback(*violatedDirective);
+ }
+ }
+ return isAllowed;
+}
+
+static PAL::CryptoDigest::Algorithm toCryptoDigestAlgorithm(ContentSecurityPolicyHashAlgorithm algorithm)
+{
+ switch (algorithm) {
+ case ContentSecurityPolicyHashAlgorithm::SHA_256:
+ return PAL::CryptoDigest::Algorithm::SHA_256;
+ case ContentSecurityPolicyHashAlgorithm::SHA_384:
+ return PAL::CryptoDigest::Algorithm::SHA_384;
+ case ContentSecurityPolicyHashAlgorithm::SHA_512:
+ return PAL::CryptoDigest::Algorithm::SHA_512;
+ }
+ ASSERT_NOT_REACHED();
+ return PAL::CryptoDigest::Algorithm::SHA_512;
+}
+
+template<typename Predicate>
+ContentSecurityPolicy::HashInEnforcedAndReportOnlyPoliciesPair ContentSecurityPolicy::findHashOfContentInPolicies(Predicate&& predicate, const String& content, OptionSet<ContentSecurityPolicyHashAlgorithm> algorithms) const
+{
+ if (algorithms.isEmpty() || content.isEmpty())
+ return { false, false };
+
+ // FIXME: We should compute the document encoding once and cache it instead of computing it on each invocation.
+ TextEncoding documentEncoding;
+ if (is<Document>(m_scriptExecutionContext))
+ documentEncoding = downcast<Document>(*m_scriptExecutionContext).textEncoding();
+ const TextEncoding& encodingToUse = documentEncoding.isValid() ? documentEncoding : UTF8Encoding();
+
+ // FIXME: Compute the digest with respect to the raw bytes received from the page.
+ // See <https://bugs.webkit.org/show_bug.cgi?id=155184>.
+ CString contentCString = encodingToUse.encode(content, EntitiesForUnencodables);
+ bool foundHashInEnforcedPolicies = false;
+ bool foundHashInReportOnlyPolicies = false;
+ for (auto algorithm : algorithms) {
+ auto cryptoDigest = PAL::CryptoDigest::create(toCryptoDigestAlgorithm(algorithm));
+ cryptoDigest->addBytes(contentCString.data(), contentCString.length());
+ ContentSecurityPolicyHash hash = { algorithm, cryptoDigest->computeHash() };
+ if (!foundHashInEnforcedPolicies && allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, std::forward<Predicate>(predicate), hash))
+ foundHashInEnforcedPolicies = true;
+ if (!foundHashInReportOnlyPolicies && allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::ReportOnly, std::forward<Predicate>(predicate), hash))
+ foundHashInReportOnlyPolicies = true;
+ if (foundHashInEnforcedPolicies && foundHashInReportOnlyPolicies)
+ break;
+ }
+ return { foundHashInEnforcedPolicies, foundHashInReportOnlyPolicies };
+}
+
+bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy) const
+{
+ if (overrideContentSecurityPolicy)
+ return true;
+ auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
+ String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script", "its hash, its nonce, or 'unsafe-inline'");
+ reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
+ if (!violatedDirective.directiveList().isReportOnly())
+ reportBlockedScriptExecutionToInspector(violatedDirective.text());
+ };
+ return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript);
+}
+
+bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy) const
+{
+ if (overrideContentSecurityPolicy)
+ return true;
+ auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
+ String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script for an inline event handler", "'unsafe-inline'");
+ reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
+ if (!violatedDirective.directiveList().isReportOnly())
+ reportBlockedScriptExecutionToInspector(violatedDirective.text());
+ };
+ return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript);
+}
+
+bool ContentSecurityPolicy::allowScriptWithNonce(const String& nonce, bool overrideContentSecurityPolicy) const
+{
+ if (overrideContentSecurityPolicy)
+ return true;
+ String strippedNonce = stripLeadingAndTrailingHTMLSpaces(nonce);
+ if (strippedNonce.isEmpty())
+ return false;
+ // FIXME: We need to report violations in report-only policies. See <https://bugs.webkit.org/show_bug.cgi?id=159830>.
+ return allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, &ContentSecurityPolicyDirectiveList::violatedDirectiveForScriptNonce, strippedNonce);
+}
+
+bool ContentSecurityPolicy::allowStyleWithNonce(const String& nonce, bool overrideContentSecurityPolicy) const
+{
+ if (overrideContentSecurityPolicy)
+ return true;
+ String strippedNonce = stripLeadingAndTrailingHTMLSpaces(nonce);
+ if (strippedNonce.isEmpty())
+ return false;
+ // FIXME: We need to report violations in report-only policies. See <https://bugs.webkit.org/show_bug.cgi?id=159830>.
+ return allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, &ContentSecurityPolicyDirectiveList::violatedDirectiveForStyleNonce, strippedNonce);
+}
+
+bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& scriptContent, bool overrideContentSecurityPolicy) const
+{
+ if (overrideContentSecurityPolicy)
+ return true;
+ bool foundHashInEnforcedPolicies;
+ bool foundHashInReportOnlyPolicies;
+ std::tie(foundHashInEnforcedPolicies, foundHashInReportOnlyPolicies) = findHashOfContentInPolicies(&ContentSecurityPolicyDirectiveList::violatedDirectiveForScriptHash, scriptContent, m_hashAlgorithmsForInlineScripts);
+ if (foundHashInEnforcedPolicies && foundHashInReportOnlyPolicies)
+ return true;
+ auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
+ String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script", "its hash, its nonce, or 'unsafe-inline'");
+ reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
+ if (!violatedDirective.directiveList().isReportOnly())
+ reportBlockedScriptExecutionToInspector(violatedDirective.text());
+ };
+ // FIXME: We should not report that the inline script violated a policy when its hash matched a source
+ // expression in the policy and the page has more than one policy. See <https://bugs.webkit.org/show_bug.cgi?id=159832>.
+ if (!foundHashInReportOnlyPolicies)
+ allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::ReportOnly, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript);
+ return foundHashInEnforcedPolicies || allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript);
+}
+
+bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& styleContent, bool overrideContentSecurityPolicy) const
+{
+ if (overrideContentSecurityPolicy)
+ return true;
+ if (m_overrideInlineStyleAllowed)
+ return true;
+ bool foundHashInEnforcedPolicies;
+ bool foundHashInReportOnlyPolicies;
+ std::tie(foundHashInEnforcedPolicies, foundHashInReportOnlyPolicies) = findHashOfContentInPolicies(&ContentSecurityPolicyDirectiveList::violatedDirectiveForStyleHash, styleContent, m_hashAlgorithmsForInlineStylesheets);
+ if (foundHashInEnforcedPolicies && foundHashInReportOnlyPolicies)
+ return true;
+ auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
+ String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::styleSrc, violatedDirective, URL(), "Refused to apply a stylesheet", "its hash, its nonce, or 'unsafe-inline'");
+ reportViolation(ContentSecurityPolicyDirectiveNames::styleSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
+ };
+ // FIXME: We should not report that the inline stylesheet violated a policy when its hash matched a source
+ // expression in the policy and the page has more than one policy. See <https://bugs.webkit.org/show_bug.cgi?id=159832>.
+ if (!foundHashInReportOnlyPolicies)
+ allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::ReportOnly, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineStyle);
+ return foundHashInEnforcedPolicies || allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineStyle);
+}
+
+bool ContentSecurityPolicy::allowEval(JSC::ExecState* state, bool overrideContentSecurityPolicy) const
+{
+ if (overrideContentSecurityPolicy)
+ return true;
+ auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
+ String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script", "'unsafe-eval'");
+ reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, state);
+ if (!violatedDirective.directiveList().isReportOnly())
+ reportBlockedScriptExecutionToInspector(violatedDirective.text());
+ };
+ return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeEval);
+}
+
+bool ContentSecurityPolicy::allowFrameAncestors(const Frame& frame, const URL& url, bool overrideContentSecurityPolicy) const
+{
+ if (overrideContentSecurityPolicy)
+ return true;
+ Frame& topFrame = frame.tree().top();
+ if (&frame == &topFrame)
+ return true;
+ String sourceURL;
+ TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
+ auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
+ String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::frameAncestors, violatedDirective, url, "Refused to load");
+ reportViolation(ContentSecurityPolicyDirectiveNames::frameAncestors, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
+ };
+ return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForFrameAncestor, frame);
+}
+
+bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const URL& url, bool overrideContentSecurityPolicy) const
+{
+ if (overrideContentSecurityPolicy)
+ return true;
+ String sourceURL;
+ TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
+ auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
+ String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::pluginTypes, violatedDirective, url, "Refused to load", "its MIME type");
+ reportViolation(ContentSecurityPolicyDirectiveNames::pluginTypes, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
+ };
+ return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForPluginType, type, typeAttribute);
+}
+
+bool ContentSecurityPolicy::allowObjectFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
+{
+ if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
+ return true;
+ // As per section object-src of the Content Security Policy Level 3 spec., <http://w3c.github.io/webappsec-csp> (Editor's Draft, 29 February 2016),
+ // "If plugin content is loaded without an associated URL (perhaps an object element lacks a data attribute, but loads some default plugin based
+ // on the specified type), it MUST be blocked if object-src's value is 'none', but will otherwise be allowed".
+ String sourceURL;
+ TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
+ auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
+ String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::objectSrc, violatedDirective, url, "Refused to load");
+ reportViolation(ContentSecurityPolicyDirectiveNames::objectSrc, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
+ };
+ return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForObjectSource, url, redirectResponseReceived == RedirectResponseReceived::Yes, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone::Yes);
+}
+
+bool ContentSecurityPolicy::allowChildFrameFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
+{
+ if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
+ return true;
+ String sourceURL;
+ TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
+ auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
+ const char* effectiveViolatedDirective = violatedDirective.name() == ContentSecurityPolicyDirectiveNames::frameSrc ? ContentSecurityPolicyDirectiveNames::frameSrc : ContentSecurityPolicyDirectiveNames::childSrc;
+ String consoleMessage = consoleMessageForViolation(effectiveViolatedDirective, violatedDirective, url, "Refused to load");
+ reportViolation(effectiveViolatedDirective, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
+ };
+ return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForFrame, url, redirectResponseReceived == RedirectResponseReceived::Yes);
+}
+
+bool ContentSecurityPolicy::allowResourceFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived, const char* name, ResourcePredicate resourcePredicate) const
+{
+ if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
+ return true;
+ String sourceURL;
+ TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
+ auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
+ String consoleMessage = consoleMessageForViolation(name, violatedDirective, url, "Refused to load");
+ reportViolation(name, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
+ };
+ return allPoliciesAllow(WTFMove(handleViolatedDirective), resourcePredicate, url, redirectResponseReceived == RedirectResponseReceived::Yes);
+}
+
+bool ContentSecurityPolicy::allowChildContextFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
+{
+ return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::childSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForChildContext);
+}
+
+bool ContentSecurityPolicy::allowScriptFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
+{
+ return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::scriptSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForScript);
+}
+
+bool ContentSecurityPolicy::allowImageFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
+{
+ return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::imgSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForImage);
+}
+
+bool ContentSecurityPolicy::allowStyleFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
+{
+ return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::styleSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForStyle);
+}
+
+bool ContentSecurityPolicy::allowFontFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
+{
+ return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::fontSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForFont);
+}
+
+bool ContentSecurityPolicy::allowMediaFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
+{
+ return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::mediaSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForMedia);
+}
+
+bool ContentSecurityPolicy::allowConnectToSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
+{
+ if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
+ return true;
+ String sourceURL;
+ TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
+ auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
+ String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::connectSrc, violatedDirective, url, "Refused to connect to");
+ reportViolation(ContentSecurityPolicyDirectiveNames::connectSrc, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
+ };
+ return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForConnectSource, url, redirectResponseReceived == RedirectResponseReceived::Yes);
+}
+
+bool ContentSecurityPolicy::allowFormAction(const URL& url, RedirectResponseReceived redirectResponseReceived) const
+{
+ return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::formAction, &ContentSecurityPolicyDirectiveList::violatedDirectiveForFormAction);
+}
+
+bool ContentSecurityPolicy::allowBaseURI(const URL& url, bool overrideContentSecurityPolicy) const
+{
+ if (overrideContentSecurityPolicy)
+ return true;
+ if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
+ return true;
+ String sourceURL;
+ TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
+ auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
+ String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::baseURI, violatedDirective, url, "Refused to change the document base URL to");
+ reportViolation(ContentSecurityPolicyDirectiveNames::baseURI, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
+ };
+ return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForBaseURI, url);
+}
+
+static String stripURLForUseInReport(Document& document, const URL& url)
+{
+ if (!url.isValid())
+ return String();
+ if (!url.isHierarchical() || url.protocolIs("file"))
+ return url.protocol().toString();
+ return document.securityOrigin().canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url).get().toString();
+}
+
+void ContentSecurityPolicy::reportViolation(const String& violatedDirective, const ContentSecurityPolicyDirective& effectiveViolatedDirective, const URL& blockedURL, const String& consoleMessage, JSC::ExecState* state) const
+{
+ // FIXME: Extract source file and source position from JSC::ExecState.
+ return reportViolation(violatedDirective, effectiveViolatedDirective.text(), effectiveViolatedDirective.directiveList(), blockedURL, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber::beforeFirst()), state);
+}
+
+void ContentSecurityPolicy::reportViolation(const String& effectiveViolatedDirective, const String& violatedDirective, const ContentSecurityPolicyDirectiveList& violatedDirectiveList, const URL& blockedURL, const String& consoleMessage, JSC::ExecState* state) const
+{
+ // FIXME: Extract source file and source position from JSC::ExecState.
+ return reportViolation(effectiveViolatedDirective, violatedDirective, violatedDirectiveList, blockedURL, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber::beforeFirst()), state);
+}
+
+void ContentSecurityPolicy::reportViolation(const String& effectiveViolatedDirective, const ContentSecurityPolicyDirective& violatedDirective, const URL& blockedURL, const String& consoleMessage, const String& sourceURL, const TextPosition& sourcePosition, JSC::ExecState* state) const
+{
+ return reportViolation(effectiveViolatedDirective, violatedDirective.text(), violatedDirective.directiveList(), blockedURL, consoleMessage, sourceURL, sourcePosition, state);
+}
+
+void ContentSecurityPolicy::reportViolation(const String& effectiveViolatedDirective, const String& violatedDirective, const ContentSecurityPolicyDirectiveList& violatedDirectiveList, const URL& blockedURL, const String& consoleMessage, const String& sourceURL, const TextPosition& sourcePosition, JSC::ExecState* state) const
+{
+ logToConsole(consoleMessage, sourceURL, sourcePosition.m_line, state);
+
+ if (!m_isReportingEnabled)
+ return;
+
+ // FIXME: Support sending reports from worker.
+ if (!is<Document>(m_scriptExecutionContext) && !m_frame)
+ return;
+
+ ASSERT(!m_frame || effectiveViolatedDirective == ContentSecurityPolicyDirectiveNames::frameAncestors);
+
+ auto& document = is<Document>(m_scriptExecutionContext) ? downcast<Document>(*m_scriptExecutionContext) : *m_frame->document();
+ auto* frame = document.frame();
+ ASSERT(!m_frame || m_frame == frame);
+ if (!frame)
+ return;
+
+ String documentURI;
+ String blockedURI;
+ if (is<Document>(m_scriptExecutionContext)) {
+ documentURI = document.url().strippedForUseAsReferrer();
+ blockedURI = stripURLForUseInReport(document, blockedURL);
+ } else {
+ // The URL of |document| may not have been initialized (say, when reporting a frame-ancestors violation).
+ // So, we use the URL of the blocked document for the protected document URL.
+ documentURI = blockedURL;
+ blockedURI = blockedURL;
+ }
+ String violatedDirectiveText = violatedDirective;
+ String originalPolicy = violatedDirectiveList.header();
+ String referrer = document.referrer();
+ ASSERT(document.loader());
+ // FIXME: Is it policy to not use the status code for HTTPS, or is that a bug?
+ unsigned short statusCode = document.url().protocolIs("http") && document.loader() ? document.loader()->response().httpStatusCode() : 0;
+
+ String sourceFile;
+ int lineNumber = 0;
+ int columnNumber = 0;
+ auto stack = createScriptCallStack(JSMainThreadExecState::currentState(), 2);
+ auto* callFrame = stack->firstNonNativeCallFrame();
+ if (callFrame && callFrame->lineNumber()) {
+ sourceFile = stripURLForUseInReport(document, URL(URL(), callFrame->sourceURL()));
+ lineNumber = callFrame->lineNumber();
+ columnNumber = callFrame->columnNumber();
+ }
+
+ // 1. Dispatch violation event.
+ bool canBubble = false;
+ bool cancelable = false;
+ document.enqueueDocumentEvent(SecurityPolicyViolationEvent::create(eventNames().securitypolicyviolationEvent, canBubble, cancelable, documentURI, referrer, blockedURI, violatedDirectiveText, effectiveViolatedDirective, originalPolicy, sourceFile, statusCode, lineNumber, columnNumber));
+
+ // 2. Send violation report (if applicable).
+ auto& reportURIs = violatedDirectiveList.reportURIs();
+ if (reportURIs.isEmpty())
+ return;
+
+ // We need to be careful here when deciding what information to send to the
+ // report-uri. Currently, we send only the current document's URL and the
+ // directive that was violated. The document's URL is safe to send because
+ // it's the document itself that's requesting that it be sent. You could
+ // make an argument that we shouldn't send HTTPS document URLs to HTTP
+ // report-uris (for the same reasons that we suppress the Referer in that
+ // case), but the Referer is sent implicitly whereas this request is only
+ // sent explicitly. As for which directive was violated, that's pretty
+ // harmless information.
+
+ auto cspReport = InspectorObject::create();
+ cspReport->setString(ASCIILiteral("document-uri"), documentURI);
+ cspReport->setString(ASCIILiteral("referrer"), referrer);
+ cspReport->setString(ASCIILiteral("violated-directive"), violatedDirectiveText);
+ cspReport->setString(ASCIILiteral("effective-directive"), effectiveViolatedDirective);
+ cspReport->setString(ASCIILiteral("original-policy"), originalPolicy);
+ cspReport->setString(ASCIILiteral("blocked-uri"), blockedURI);
+ cspReport->setInteger(ASCIILiteral("status-code"), statusCode);
+ if (!sourceFile.isNull()) {
+ cspReport->setString(ASCIILiteral("source-file"), sourceFile);
+ cspReport->setInteger(ASCIILiteral("line-number"), lineNumber);
+ cspReport->setInteger(ASCIILiteral("column-number"), columnNumber);
+ }
+
+ auto reportObject = InspectorObject::create();
+ reportObject->setObject(ASCIILiteral("csp-report"), WTFMove(cspReport));
+
+ auto report = FormData::create(reportObject->toJSONString().utf8());
+ for (const auto& url : reportURIs)
+ PingLoader::sendViolationReport(*frame, is<Document>(m_scriptExecutionContext) ? document.completeURL(url) : document.completeURL(url, blockedURL), report.copyRef(), ViolationReportType::ContentSecurityPolicy);
+}
+
+void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) const
+{
+ String message;
+ if (equalLettersIgnoringASCIICase(name, "allow"))
+ message = ASCIILiteral("The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect.");
+ else if (equalLettersIgnoringASCIICase(name, "options"))
+ message = ASCIILiteral("The 'options' directive has been replaced with 'unsafe-inline' and 'unsafe-eval' source expressions for the 'script-src' and 'style-src' directives. Please use those directives instead, as 'options' has no effect.");
+ else if (equalLettersIgnoringASCIICase(name, "policy-uri"))
+ message = ASCIILiteral("The 'policy-uri' directive has been removed from the specification. Please specify a complete policy via the Content-Security-Policy header.");
+ else
+ message = makeString("Unrecognized Content-Security-Policy directive '", name, "'.\n"); // FIXME: Why does this include a newline?
+
+ logToConsole(message);
+}
+
+void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const
+{
+ logToConsole("The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?");
+}
+
+void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const
+{
+ logToConsole(makeString("Ignoring duplicate Content-Security-Policy directive '", name, "'.\n"));
+}
+
+void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) const
+{
+ String message;
+ if (pluginType.isNull())
+ message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n";
+ else
+ message = makeString("Invalid plugin type in 'plugin-types' Content Security Policy directive: '", pluginType, "'.\n");
+ logToConsole(message);
+}
+
+void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags) const
+{
+ logToConsole("Error while parsing the 'sandbox' Content Security Policy directive: " + invalidFlags);
+}
+
+void ContentSecurityPolicy::reportInvalidDirectiveInReportOnlyMode(const String& directiveName) const
+{
+ logToConsole("The Content Security Policy directive '" + directiveName + "' is ignored when delivered in a report-only policy.");
+}
+
+void ContentSecurityPolicy::reportInvalidDirectiveInHTTPEquivMeta(const String& directiveName) const
+{
+ logToConsole("The Content Security Policy directive '" + directiveName + "' is ignored when delivered via an HTML meta element.");
+}
+
+void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) const
+{
+ String message = makeString("The value for Content Security Policy directive '", directiveName, "' contains an invalid character: '", value, "'. Non-whitespace characters outside ASCII 0x21-0x7E must be percent-encoded, as described in RFC 3986, section 2.1: http://tools.ietf.org/html/rfc3986#section-2.1.");
+ logToConsole(message);
+}
+
+void ContentSecurityPolicy::reportInvalidPathCharacter(const String& directiveName, const String& value, const char invalidChar) const
+{
+ ASSERT(invalidChar == '#' || invalidChar == '?');
+
+ String ignoring;
+ if (invalidChar == '?')
+ ignoring = "The query component, including the '?', will be ignored.";
+ else
+ ignoring = "The fragment identifier, including the '#', will be ignored.";
+
+ String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains a source with an invalid path: '", value, "'. ", ignoring);
+ logToConsole(message);
+}
+
+void ContentSecurityPolicy::reportInvalidSourceExpression(const String& directiveName, const String& source) const
+{
+ String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains an invalid source: '", source, "'. It will be ignored.");
+ if (equalLettersIgnoringASCIICase(source, "'none'"))
+ message = makeString(message, " Note that 'none' has no effect unless it is the only expression in the source list.");
+ logToConsole(message);
+}
+
+void ContentSecurityPolicy::reportMissingReportURI(const String& policy) const
+{
+ logToConsole("The Content Security Policy '" + policy + "' was delivered in report-only mode, but does not specify a 'report-uri'; the policy will have no effect. Please either add a 'report-uri' directive, or deliver the policy via the 'Content-Security-Policy' header.");
+}
+
+void ContentSecurityPolicy::logToConsole(const String& message, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
+{
+ if (!m_isReportingEnabled)
+ return;
+
+ // FIXME: <http://webkit.org/b/114317> ContentSecurityPolicy::logToConsole should include a column number
+ if (m_scriptExecutionContext)
+ m_scriptExecutionContext->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, contextURL, contextLine.oneBasedInt(), 0, state);
+ else if (m_frame && m_frame->document())
+ static_cast<ScriptExecutionContext*>(m_frame->document())->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, contextURL, contextLine.oneBasedInt(), 0, state);
+}
+
+void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String& directiveText) const
+{
+ if (m_scriptExecutionContext)
+ InspectorInstrumentation::scriptExecutionBlockedByCSP(m_scriptExecutionContext, directiveText);
+}
+
+void ContentSecurityPolicy::upgradeInsecureRequestIfNeeded(ResourceRequest& request, InsecureRequestType requestType) const
+{
+ URL url = request.url();
+ upgradeInsecureRequestIfNeeded(url, requestType);
+ request.setURL(url);
+}
+
+void ContentSecurityPolicy::upgradeInsecureRequestIfNeeded(URL& url, InsecureRequestType requestType) const
+{
+ if (!url.protocolIs("http") && !url.protocolIs("ws"))
+ return;
+
+ bool upgradeRequest = m_insecureNavigationRequestsToUpgrade.contains(SecurityOrigin::create(url));
+ if (requestType == InsecureRequestType::Load || requestType == InsecureRequestType::FormSubmission)
+ upgradeRequest |= m_upgradeInsecureRequests;
+
+ if (!upgradeRequest)
+ return;
+
+ if (url.protocolIs("http"))
+ url.setProtocol("https");
+ else if (url.protocolIs("ws"))
+ url.setProtocol("wss");
+ else
+ return;
+
+ if (url.port() && url.port().value() == 80)
+ url.setPort(443);
+}
+
+void ContentSecurityPolicy::setUpgradeInsecureRequests(bool upgradeInsecureRequests)
+{
+ m_upgradeInsecureRequests = upgradeInsecureRequests;
+ if (!m_upgradeInsecureRequests)
+ return;
+
+ if (!m_scriptExecutionContext)
+ return;
+
+ // Store the upgrade domain as an 'insecure' protocol so we can quickly identify
+ // origins we should upgrade.
+ URL upgradeURL = m_scriptExecutionContext->url();
+ if (upgradeURL.protocolIs("https"))
+ upgradeURL.setProtocol("http");
+ else if (upgradeURL.protocolIs("wss"))
+ upgradeURL.setProtocol("ws");
+
+ m_insecureNavigationRequestsToUpgrade.add(SecurityOrigin::create(upgradeURL));
+}
+
+void ContentSecurityPolicy::inheritInsecureNavigationRequestsToUpgradeFromOpener(const ContentSecurityPolicy& other)
+{
+ m_insecureNavigationRequestsToUpgrade.add(other.m_insecureNavigationRequestsToUpgrade.begin(), other.m_insecureNavigationRequestsToUpgrade.end());
+}
+
+HashSet<RefPtr<SecurityOrigin>>&& ContentSecurityPolicy::takeNavigationRequestsToUpgrade()
+{
+ return WTFMove(m_insecureNavigationRequestsToUpgrade);
+}
+
+void ContentSecurityPolicy::setInsecureNavigationRequestsToUpgrade(HashSet<RefPtr<SecurityOrigin>>&& insecureNavigationRequests)
+{
+ m_insecureNavigationRequestsToUpgrade = WTFMove(insecureNavigationRequests);
+}
+
+}
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicy.h b/Source/WebCore/page/csp/ContentSecurityPolicy.h
new file mode 100644
index 000000000..5286a86a6
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicy.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2011 Google, Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ContentSecurityPolicyResponseHeaders.h"
+#include "SecurityOrigin.h"
+#include "SecurityOriginHash.h"
+#include <wtf/HashSet.h>
+#include <wtf/OptionSet.h>
+#include <wtf/Vector.h>
+#include <wtf/text/TextPosition.h>
+
+namespace JSC {
+class ExecState;
+}
+
+namespace WTF {
+class OrdinalNumber;
+}
+
+namespace WebCore {
+
+class ContentSecurityPolicyDirective;
+class ContentSecurityPolicyDirectiveList;
+class ContentSecurityPolicySource;
+class DOMStringList;
+class Frame;
+class JSDOMWindowShell;
+class ResourceRequest;
+class ScriptExecutionContext;
+class SecurityOrigin;
+class TextEncoding;
+class URL;
+
+enum class ContentSecurityPolicyHashAlgorithm;
+
+typedef Vector<std::unique_ptr<ContentSecurityPolicyDirectiveList>> CSPDirectiveListVector;
+typedef int SandboxFlags;
+
+class ContentSecurityPolicy {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ explicit ContentSecurityPolicy(ScriptExecutionContext&);
+ explicit ContentSecurityPolicy(const SecurityOrigin&, const Frame* = nullptr);
+ ~ContentSecurityPolicy();
+
+ void copyStateFrom(const ContentSecurityPolicy*);
+ void copyUpgradeInsecureRequestStateFrom(const ContentSecurityPolicy&);
+
+ void didCreateWindowShell(JSDOMWindowShell&) const;
+
+ enum class PolicyFrom {
+ API,
+ HTTPEquivMeta,
+ HTTPHeader,
+ Inherited,
+ };
+ ContentSecurityPolicyResponseHeaders responseHeaders() const;
+ enum ReportParsingErrors { No, Yes };
+ void didReceiveHeaders(const ContentSecurityPolicyResponseHeaders&, ReportParsingErrors = ReportParsingErrors::Yes);
+ void didReceiveHeader(const String&, ContentSecurityPolicyHeaderType, ContentSecurityPolicy::PolicyFrom);
+
+ bool allowScriptWithNonce(const String& nonce, bool overrideContentSecurityPolicy = false) const;
+ bool allowStyleWithNonce(const String& nonce, bool overrideContentSecurityPolicy = false) const;
+
+ bool allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy = false) const;
+ bool allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy = false) const;
+ bool allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& scriptContent, bool overrideContentSecurityPolicy = false) const;
+ bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& styleContent, bool overrideContentSecurityPolicy = false) const;
+
+ bool allowEval(JSC::ExecState*, bool overrideContentSecurityPolicy = false) const;
+
+ bool allowPluginType(const String& type, const String& typeAttribute, const URL&, bool overrideContentSecurityPolicy = false) const;
+
+ bool allowFrameAncestors(const Frame&, const URL&, bool overrideContentSecurityPolicy = false) const;
+
+ enum class RedirectResponseReceived { No, Yes };
+ bool allowScriptFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const;
+ bool allowImageFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const;
+ bool allowStyleFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const;
+ bool allowFontFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const;
+ bool allowMediaFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const;
+
+ bool allowChildFrameFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const;
+ bool allowChildContextFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const;
+ bool allowConnectToSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const;
+ bool allowFormAction(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const;
+
+ bool allowObjectFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const;
+ bool allowBaseURI(const URL&, bool overrideContentSecurityPolicy = false) const;
+
+ void setOverrideAllowInlineStyle(bool);
+
+ void gatherReportURIs(DOMStringList&) const;
+
+ bool allowRunningOrDisplayingInsecureContent(const URL&);
+
+ // The following functions are used by internal data structures to call back into this object when parsing, validating,
+ // and applying a Content Security Policy.
+ // FIXME: We should make the various directives serve only as state stores for the parsed policy and remove these functions.
+ // This class should traverse the directives, validating the policy, and applying it to the script execution context.
+
+ // Used by ContentSecurityPolicyMediaListDirective
+ void reportInvalidPluginTypes(const String&) const;
+
+ // Used by ContentSecurityPolicySourceList
+ void reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const;
+ void reportInvalidPathCharacter(const String& directiveName, const String& value, const char) const;
+ void reportInvalidSourceExpression(const String& directiveName, const String& source) const;
+ bool urlMatchesSelf(const URL&) const;
+ bool allowContentSecurityPolicySourceStarToMatchAnyProtocol() const;
+
+ // Used by ContentSecurityPolicyDirectiveList
+ void reportDuplicateDirective(const String&) const;
+ void reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) const;
+ void reportInvalidSandboxFlags(const String&) const;
+ void reportInvalidDirectiveInReportOnlyMode(const String&) const;
+ void reportInvalidDirectiveInHTTPEquivMeta(const String&) const;
+ void reportMissingReportURI(const String&) const;
+ void reportUnsupportedDirective(const String&) const;
+ void enforceSandboxFlags(SandboxFlags sandboxFlags) { m_sandboxFlags |= sandboxFlags; }
+ void addHashAlgorithmsForInlineScripts(OptionSet<ContentSecurityPolicyHashAlgorithm> hashAlgorithmsForInlineScripts)
+ {
+ m_hashAlgorithmsForInlineScripts |= hashAlgorithmsForInlineScripts;
+ }
+ void addHashAlgorithmsForInlineStylesheets(OptionSet<ContentSecurityPolicyHashAlgorithm> hashAlgorithmsForInlineStylesheets)
+ {
+ m_hashAlgorithmsForInlineStylesheets |= hashAlgorithmsForInlineStylesheets;
+ }
+
+ // Used by ContentSecurityPolicySource
+ bool protocolMatchesSelf(const URL&) const;
+
+ void setUpgradeInsecureRequests(bool);
+ bool upgradeInsecureRequests() const { return m_upgradeInsecureRequests; }
+ enum class InsecureRequestType { Load, FormSubmission, Navigation };
+ void upgradeInsecureRequestIfNeeded(ResourceRequest&, InsecureRequestType) const;
+ void upgradeInsecureRequestIfNeeded(URL&, InsecureRequestType) const;
+
+ HashSet<RefPtr<SecurityOrigin>>&& takeNavigationRequestsToUpgrade();
+ void inheritInsecureNavigationRequestsToUpgradeFromOpener(const ContentSecurityPolicy&);
+ void setInsecureNavigationRequestsToUpgrade(HashSet<RefPtr<SecurityOrigin>>&&);
+
+private:
+ void logToConsole(const String& message, const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), JSC::ExecState* = nullptr) const;
+ void updateSourceSelf(const SecurityOrigin&);
+ void applyPolicyToScriptExecutionContext();
+
+ const TextEncoding& documentEncoding() const;
+
+ enum class Disposition {
+ Enforce,
+ ReportOnly,
+ };
+
+ using ViolatedDirectiveCallback = std::function<void (const ContentSecurityPolicyDirective&)>;
+
+ template<typename Predicate, typename... Args>
+ typename std::enable_if<!std::is_convertible<Predicate, ViolatedDirectiveCallback>::value, bool>::type allPoliciesWithDispositionAllow(Disposition, Predicate&&, Args&&...) const;
+
+ template<typename Predicate, typename... Args>
+ bool allPoliciesWithDispositionAllow(Disposition, ViolatedDirectiveCallback&&, Predicate&&, Args&&...) const;
+
+ template<typename Predicate, typename... Args>
+ bool allPoliciesAllow(ViolatedDirectiveCallback&&, Predicate&&, Args&&...) const WARN_UNUSED_RETURN;
+
+ using ResourcePredicate = const ContentSecurityPolicyDirective *(ContentSecurityPolicyDirectiveList::*)(const URL &, bool) const;
+ bool allowResourceFromSource(const URL&, RedirectResponseReceived, const char*, ResourcePredicate) const;
+
+ using HashInEnforcedAndReportOnlyPoliciesPair = std::pair<bool, bool>;
+ template<typename Predicate> HashInEnforcedAndReportOnlyPoliciesPair findHashOfContentInPolicies(Predicate&&, const String& content, OptionSet<ContentSecurityPolicyHashAlgorithm>) const WARN_UNUSED_RETURN;
+
+ void reportViolation(const String& effectiveViolatedDirective, const ContentSecurityPolicyDirective& violatedDirective, const URL& blockedURL, const String& consoleMessage, JSC::ExecState*) const;
+ void reportViolation(const String& effectiveViolatedDirective, const String& violatedDirective, const ContentSecurityPolicyDirectiveList&, const URL& blockedURL, const String& consoleMessage, JSC::ExecState* = nullptr) const;
+ void reportViolation(const String& effectiveViolatedDirective, const ContentSecurityPolicyDirective& violatedDirective, const URL& blockedURL, const String& consoleMessage, const String& sourceURL, const TextPosition& sourcePosition, JSC::ExecState* = nullptr) const;
+ void reportViolation(const String& effectiveViolatedDirective, const String& violatedDirective, const ContentSecurityPolicyDirectiveList& violatedDirectiveList, const URL& blockedURL, const String& consoleMessage, const String& sourceURL, const TextPosition& sourcePosition, JSC::ExecState*) const;
+ void reportBlockedScriptExecutionToInspector(const String& directiveText) const;
+
+ // We can never have both a script execution context and a frame.
+ ScriptExecutionContext* m_scriptExecutionContext { nullptr };
+ const Frame* m_frame { nullptr };
+ std::unique_ptr<ContentSecurityPolicySource> m_selfSource;
+ String m_selfSourceProtocol;
+ CSPDirectiveListVector m_policies;
+ String m_lastPolicyEvalDisabledErrorMessage;
+ SandboxFlags m_sandboxFlags;
+ bool m_overrideInlineStyleAllowed { false };
+ bool m_isReportingEnabled { true };
+ bool m_upgradeInsecureRequests { false };
+ bool m_hasAPIPolicy { false };
+ OptionSet<ContentSecurityPolicyHashAlgorithm> m_hashAlgorithmsForInlineScripts;
+ OptionSet<ContentSecurityPolicyHashAlgorithm> m_hashAlgorithmsForInlineStylesheets;
+ HashSet<RefPtr<SecurityOrigin>> m_insecureNavigationRequestsToUpgrade;
+};
+
+}
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirective.cpp b/Source/WebCore/page/csp/ContentSecurityPolicyDirective.cpp
new file mode 100644
index 000000000..980c72fd4
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirective.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ContentSecurityPolicyDirective.h"
+
+#include "ContentSecurityPolicyDirectiveList.h"
+
+namespace WebCore {
+
+bool ContentSecurityPolicyDirective::isDefaultSrc() const
+{
+ return this == m_directiveList.defaultSrc();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirective.h b/Source/WebCore/page/csp/ContentSecurityPolicyDirective.h
new file mode 100644
index 000000000..a8f1d4f0b
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirective.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 Google, Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class ContentSecurityPolicyDirectiveList;
+
+class ContentSecurityPolicyDirective {
+public:
+ ContentSecurityPolicyDirective(const ContentSecurityPolicyDirectiveList& directiveList, const String& name, const String& value)
+ : m_name(name)
+ , m_text(name + ' ' + value)
+ , m_directiveList(directiveList)
+ {
+ }
+
+ const String& name() const { return m_name; }
+ const String& text() const { return m_text; }
+
+ const ContentSecurityPolicyDirectiveList& directiveList() const { return m_directiveList; }
+
+ bool isDefaultSrc() const;
+
+private:
+ String m_name;
+ String m_text;
+ const ContentSecurityPolicyDirectiveList& m_directiveList;
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp
new file mode 100644
index 000000000..4bdc6f1d2
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2011 Google, Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ContentSecurityPolicyDirectiveList.h"
+
+#include "ContentSecurityPolicyDirectiveNames.h"
+#include "Document.h"
+#include "Frame.h"
+#include "ParsingUtilities.h"
+#include "SecurityContext.h"
+
+namespace WebCore {
+
+static bool isDirectiveNameCharacter(UChar c)
+{
+ return isASCIIAlphanumeric(c) || c == '-';
+}
+
+static bool isDirectiveValueCharacter(UChar c)
+{
+ return isASCIISpace(c) || (c >= 0x21 && c <= 0x7e); // Whitespace + VCHAR
+}
+
+static bool isNotASCIISpace(UChar c)
+{
+ return !isASCIISpace(c);
+}
+
+static inline bool checkEval(ContentSecurityPolicySourceListDirective* directive)
+{
+ return !directive || directive->allowEval();
+}
+
+static inline bool checkInline(ContentSecurityPolicySourceListDirective* directive)
+{
+ return !directive || directive->allowInline();
+}
+
+static inline bool checkSource(ContentSecurityPolicySourceListDirective* directive, const URL& url, bool didReceiveRedirectResponse = false, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone shouldAllowEmptyURLIfSourceListEmpty = ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone::No)
+{
+ return !directive || directive->allows(url, didReceiveRedirectResponse, shouldAllowEmptyURLIfSourceListEmpty);
+}
+
+static inline bool checkHash(ContentSecurityPolicySourceListDirective* directive, const ContentSecurityPolicyHash& hash)
+{
+ return !directive || directive->allows(hash);
+}
+
+static inline bool checkNonce(ContentSecurityPolicySourceListDirective* directive, const String& nonce)
+{
+ return !directive || directive->allows(nonce);
+}
+
+static inline bool checkFrameAncestors(ContentSecurityPolicySourceListDirective* directive, const Frame& frame)
+{
+ if (!directive)
+ return true;
+ bool didReceiveRedirectResponse = false;
+ for (Frame* current = frame.tree().parent(); current; current = current->tree().parent()) {
+ if (!directive->allows(current->document()->url(), didReceiveRedirectResponse, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone::No))
+ return false;
+ }
+ return true;
+}
+
+static inline bool checkMediaType(ContentSecurityPolicyMediaListDirective* directive, const String& type, const String& typeAttribute)
+{
+ if (!directive)
+ return true;
+ if (typeAttribute.isEmpty() || typeAttribute.stripWhiteSpace() != type)
+ return false;
+ return directive->allows(type);
+}
+
+ContentSecurityPolicyDirectiveList::ContentSecurityPolicyDirectiveList(ContentSecurityPolicy& policy, ContentSecurityPolicyHeaderType type)
+ : m_policy(policy)
+ , m_headerType(type)
+{
+ m_reportOnly = (type == ContentSecurityPolicyHeaderType::Report || type == ContentSecurityPolicyHeaderType::PrefixedReport);
+}
+
+std::unique_ptr<ContentSecurityPolicyDirectiveList> ContentSecurityPolicyDirectiveList::create(ContentSecurityPolicy& policy, const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicy::PolicyFrom from)
+{
+ auto directives = std::make_unique<ContentSecurityPolicyDirectiveList>(policy, type);
+ directives->parse(header, from);
+
+ if (!checkEval(directives->operativeDirective(directives->m_scriptSrc.get()))) {
+ String message = makeString("Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: \"", directives->operativeDirective(directives->m_scriptSrc.get())->text(), "\".\n");
+ directives->setEvalDisabledErrorMessage(message);
+ }
+
+ if (directives->isReportOnly() && directives->reportURIs().isEmpty())
+ policy.reportMissingReportURI(header);
+
+ return directives;
+}
+
+ContentSecurityPolicySourceListDirective* ContentSecurityPolicyDirectiveList::operativeDirective(ContentSecurityPolicySourceListDirective* directive) const
+{
+ return directive ? directive : m_defaultSrc.get();
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeEval() const
+{
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_scriptSrc.get());
+ if (checkEval(operativeDirective))
+ return nullptr;
+ return operativeDirective;
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript() const
+{
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_scriptSrc.get());
+ if (checkInline(operativeDirective))
+ return nullptr;
+ return operativeDirective;
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineStyle() const
+{
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_styleSrc.get());
+ if (checkInline(operativeDirective))
+ return nullptr;
+ return operativeDirective;
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForScriptHash(const ContentSecurityPolicyHash& hash) const
+{
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_scriptSrc.get());
+ if (checkHash(operativeDirective, hash))
+ return nullptr;
+ return operativeDirective;
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForStyleHash(const ContentSecurityPolicyHash& hash) const
+{
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_styleSrc.get());
+ if (checkHash(operativeDirective, hash))
+ return nullptr;
+ return operativeDirective;
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForScriptNonce(const String& nonce) const
+{
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_scriptSrc.get());
+ if (checkNonce(operativeDirective, nonce))
+ return nullptr;
+ return operativeDirective;
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForStyleNonce(const String& nonce) const
+{
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_styleSrc.get());
+ if (checkNonce(operativeDirective, nonce))
+ return nullptr;
+ return operativeDirective;
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForBaseURI(const URL& url) const
+{
+ if (checkSource(m_baseURI.get(), url))
+ return nullptr;
+ return m_baseURI.get();
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForChildContext(const URL& url, bool didReceiveRedirectResponse) const
+{
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_childSrc.get());
+ if (checkSource(operativeDirective, url, didReceiveRedirectResponse))
+ return nullptr;
+ return operativeDirective;
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForConnectSource(const URL& url, bool didReceiveRedirectResponse) const
+{
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_connectSrc.get());
+ if (checkSource(operativeDirective, url, didReceiveRedirectResponse))
+ return nullptr;
+ return operativeDirective;
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForFont(const URL& url, bool didReceiveRedirectResponse) const
+{
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_fontSrc.get());
+ if (checkSource(operativeDirective, url, didReceiveRedirectResponse))
+ return nullptr;
+ return operativeDirective;
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForFormAction(const URL& url, bool didReceiveRedirectResponse) const
+{
+ if (checkSource(m_formAction.get(), url, didReceiveRedirectResponse))
+ return nullptr;
+ return m_formAction.get();
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForFrame(const URL& url, bool didReceiveRedirectResponse) const
+{
+ if (url.isBlankURL())
+ return nullptr;
+
+ // We must enforce the frame-src directive (if specified) before enforcing the child-src directive for a nested browsing
+ // context by <https://w3c.github.io/webappsec-csp/2/#directive-child-src-nested> (29 August 2015).
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_frameSrc ? m_frameSrc.get() : m_childSrc.get());
+ if (checkSource(operativeDirective, url, didReceiveRedirectResponse))
+ return nullptr;
+ return operativeDirective;
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForFrameAncestor(const Frame& frame) const
+{
+ if (checkFrameAncestors(m_frameAncestors.get(), frame))
+ return nullptr;
+ return m_frameAncestors.get();
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForImage(const URL& url, bool didReceiveRedirectResponse) const
+{
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_imgSrc.get());
+ if (checkSource(operativeDirective, url, didReceiveRedirectResponse))
+ return nullptr;
+ return operativeDirective;
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForMedia(const URL& url, bool didReceiveRedirectResponse) const
+{
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_mediaSrc.get());
+ if (checkSource(operativeDirective, url, didReceiveRedirectResponse))
+ return nullptr;
+ return operativeDirective;
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForObjectSource(const URL& url, bool didReceiveRedirectResponse, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone shouldAllowEmptyURLIfSourceListEmpty) const
+{
+ if (url.isBlankURL())
+ return nullptr;
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_objectSrc.get());
+ if (checkSource(operativeDirective, url, didReceiveRedirectResponse, shouldAllowEmptyURLIfSourceListEmpty))
+ return nullptr;
+ return operativeDirective;
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForPluginType(const String& type, const String& typeAttribute) const
+{
+ if (checkMediaType(m_pluginTypes.get(), type, typeAttribute))
+ return nullptr;
+ return m_pluginTypes.get();
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForScript(const URL& url, bool didReceiveRedirectResponse) const
+{
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_scriptSrc.get());
+ if (checkSource(operativeDirective, url, didReceiveRedirectResponse))
+ return nullptr;
+ return operativeDirective;
+}
+
+const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForStyle(const URL& url, bool didReceiveRedirectResponse) const
+{
+ ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_styleSrc.get());
+ if (checkSource(operativeDirective, url, didReceiveRedirectResponse))
+ return nullptr;
+ return operativeDirective;
+}
+
+// policy = directive-list
+// directive-list = [ directive *( ";" [ directive ] ) ]
+//
+void ContentSecurityPolicyDirectiveList::parse(const String& policy, ContentSecurityPolicy::PolicyFrom policyFrom)
+{
+ m_header = policy;
+ if (policy.isEmpty())
+ return;
+
+ auto characters = StringView(policy).upconvertedCharacters();
+ const UChar* position = characters;
+ const UChar* end = position + policy.length();
+
+ while (position < end) {
+ const UChar* directiveBegin = position;
+ skipUntil<UChar>(position, end, ';');
+
+ String name, value;
+ if (parseDirective(directiveBegin, position, name, value)) {
+ ASSERT(!name.isEmpty());
+ if (policyFrom == ContentSecurityPolicy::PolicyFrom::Inherited) {
+ if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::upgradeInsecureRequests))
+ continue;
+ } else if (policyFrom == ContentSecurityPolicy::PolicyFrom::HTTPEquivMeta) {
+ if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::sandbox)
+ || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::reportURI)
+ || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::frameAncestors)) {
+ m_policy.reportInvalidDirectiveInHTTPEquivMeta(name);
+ continue;
+ }
+ }
+ addDirective(name, value);
+ }
+
+ ASSERT(position == end || *position == ';');
+ skipExactly<UChar>(position, end, ';');
+ }
+}
+
+// directive = *WSP [ directive-name [ WSP directive-value ] ]
+// directive-name = 1*( ALPHA / DIGIT / "-" )
+// directive-value = *( WSP / <VCHAR except ";"> )
+//
+bool ContentSecurityPolicyDirectiveList::parseDirective(const UChar* begin, const UChar* end, String& name, String& value)
+{
+ ASSERT(name.isEmpty());
+ ASSERT(value.isEmpty());
+
+ const UChar* position = begin;
+ skipWhile<UChar, isASCIISpace>(position, end);
+
+ // Empty directive (e.g. ";;;"). Exit early.
+ if (position == end)
+ return false;
+
+ const UChar* nameBegin = position;
+ skipWhile<UChar, isDirectiveNameCharacter>(position, end);
+
+ // The directive-name must be non-empty.
+ if (nameBegin == position) {
+ skipWhile<UChar, isNotASCIISpace>(position, end);
+ m_policy.reportUnsupportedDirective(String(nameBegin, position - nameBegin));
+ return false;
+ }
+
+ name = String(nameBegin, position - nameBegin);
+
+ if (position == end)
+ return true;
+
+ if (!skipExactly<UChar, isASCIISpace>(position, end)) {
+ skipWhile<UChar, isNotASCIISpace>(position, end);
+ m_policy.reportUnsupportedDirective(String(nameBegin, position - nameBegin));
+ return false;
+ }
+
+ skipWhile<UChar, isASCIISpace>(position, end);
+
+ const UChar* valueBegin = position;
+ skipWhile<UChar, isDirectiveValueCharacter>(position, end);
+
+ if (position != end) {
+ m_policy.reportInvalidDirectiveValueCharacter(name, String(valueBegin, end - valueBegin));
+ return false;
+ }
+
+ // The directive-value may be empty.
+ if (valueBegin == position)
+ return true;
+
+ value = String(valueBegin, position - valueBegin);
+ return true;
+}
+
+void ContentSecurityPolicyDirectiveList::parseReportURI(const String& name, const String& value)
+{
+ if (!m_reportURIs.isEmpty()) {
+ m_policy.reportDuplicateDirective(name);
+ return;
+ }
+
+ auto characters = StringView(value).upconvertedCharacters();
+ const UChar* position = characters;
+ const UChar* end = position + value.length();
+
+ while (position < end) {
+ skipWhile<UChar, isASCIISpace>(position, end);
+
+ const UChar* urlBegin = position;
+ skipWhile<UChar, isNotASCIISpace>(position, end);
+
+ if (urlBegin < position)
+ m_reportURIs.append(value.substring(urlBegin - characters, position - urlBegin));
+ }
+}
+
+
+template<class CSPDirectiveType>
+void ContentSecurityPolicyDirectiveList::setCSPDirective(const String& name, const String& value, std::unique_ptr<CSPDirectiveType>& directive)
+{
+ if (directive) {
+ m_policy.reportDuplicateDirective(name);
+ return;
+ }
+ directive = std::make_unique<CSPDirectiveType>(*this, name, value);
+}
+
+void ContentSecurityPolicyDirectiveList::applySandboxPolicy(const String& name, const String& sandboxPolicy)
+{
+ if (m_reportOnly) {
+ m_policy.reportInvalidDirectiveInReportOnlyMode(name);
+ return;
+ }
+ if (m_haveSandboxPolicy) {
+ m_policy.reportDuplicateDirective(name);
+ return;
+ }
+ m_haveSandboxPolicy = true;
+ String invalidTokens;
+ m_policy.enforceSandboxFlags(SecurityContext::parseSandboxPolicy(sandboxPolicy, invalidTokens));
+ if (!invalidTokens.isNull())
+ m_policy.reportInvalidSandboxFlags(invalidTokens);
+}
+
+void ContentSecurityPolicyDirectiveList::setUpgradeInsecureRequests(const String& name)
+{
+ if (m_reportOnly) {
+ m_policy.reportInvalidDirectiveInReportOnlyMode(name);
+ return;
+ }
+ if (m_upgradeInsecureRequests) {
+ m_policy.reportDuplicateDirective(name);
+ return;
+ }
+ m_upgradeInsecureRequests = true;
+ m_policy.setUpgradeInsecureRequests(true);
+}
+
+void ContentSecurityPolicyDirectiveList::setBlockAllMixedContentEnabled(const String& name)
+{
+ if (m_hasBlockAllMixedContentDirective) {
+ m_policy.reportDuplicateDirective(name);
+ return;
+ }
+ m_hasBlockAllMixedContentDirective = true;
+}
+
+void ContentSecurityPolicyDirectiveList::addDirective(const String& name, const String& value)
+{
+ ASSERT(!name.isEmpty());
+
+ if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::defaultSrc)) {
+ setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_defaultSrc);
+ m_policy.addHashAlgorithmsForInlineScripts(m_defaultSrc->hashAlgorithmsUsed());
+ m_policy.addHashAlgorithmsForInlineStylesheets(m_defaultSrc->hashAlgorithmsUsed());
+ } else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::scriptSrc)) {
+ setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_scriptSrc);
+ m_policy.addHashAlgorithmsForInlineScripts(m_scriptSrc->hashAlgorithmsUsed());
+ } else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::styleSrc)) {
+ setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_styleSrc);
+ m_policy.addHashAlgorithmsForInlineStylesheets(m_styleSrc->hashAlgorithmsUsed());
+ } else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::objectSrc))
+ setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_objectSrc);
+ else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::frameSrc)) {
+ // FIXME: Log to console "The frame-src directive is deprecated. Use the child-src directive instead."
+ // See <https://bugs.webkit.org/show_bug.cgi?id=155773>.
+ setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_frameSrc);
+ } else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::imgSrc))
+ setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_imgSrc);
+ else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::fontSrc))
+ setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_fontSrc);
+ else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::mediaSrc))
+ setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_mediaSrc);
+ else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::connectSrc))
+ setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_connectSrc);
+ else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::childSrc))
+ setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_childSrc);
+ else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::formAction))
+ setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_formAction);
+ else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::baseURI))
+ setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_baseURI);
+ else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::frameAncestors)) {
+ if (m_reportOnly) {
+ m_policy.reportInvalidDirectiveInReportOnlyMode(name);
+ return;
+ }
+ setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_frameAncestors);
+ } else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::pluginTypes))
+ setCSPDirective<ContentSecurityPolicyMediaListDirective>(name, value, m_pluginTypes);
+ else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::sandbox))
+ applySandboxPolicy(name, value);
+ else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::reportURI))
+ parseReportURI(name, value);
+ else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::upgradeInsecureRequests))
+ setUpgradeInsecureRequests(name);
+ else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::blockAllMixedContent))
+ setBlockAllMixedContentEnabled(name);
+ else
+ m_policy.reportUnsupportedDirective(name);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h
new file mode 100644
index 000000000..3adb22fe8
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2011 Google, Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ContentSecurityPolicy.h"
+#include "ContentSecurityPolicyHash.h"
+#include "ContentSecurityPolicyMediaListDirective.h"
+#include "ContentSecurityPolicySourceListDirective.h"
+#include "URL.h"
+
+namespace WebCore {
+
+class Frame;
+
+class ContentSecurityPolicyDirectiveList {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ static std::unique_ptr<ContentSecurityPolicyDirectiveList> create(ContentSecurityPolicy&, const String&, ContentSecurityPolicyHeaderType, ContentSecurityPolicy::PolicyFrom);
+ ContentSecurityPolicyDirectiveList(ContentSecurityPolicy&, ContentSecurityPolicyHeaderType);
+
+ const String& header() const { return m_header; }
+ ContentSecurityPolicyHeaderType headerType() const { return m_headerType; }
+
+ const ContentSecurityPolicyDirective* violatedDirectiveForUnsafeEval() const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForUnsafeInlineScript() const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForUnsafeInlineStyle() const;
+
+ const ContentSecurityPolicyDirective* violatedDirectiveForScriptHash(const ContentSecurityPolicyHash&) const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForStyleHash(const ContentSecurityPolicyHash&) const;
+
+ const ContentSecurityPolicyDirective* violatedDirectiveForScriptNonce(const String&) const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForStyleNonce(const String&) const;
+
+ const ContentSecurityPolicyDirective* violatedDirectiveForBaseURI(const URL&) const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForChildContext(const URL&, bool didReceiveRedirectResponse) const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForConnectSource(const URL&, bool didReceiveRedirectResponse) const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForFont(const URL&, bool didReceiveRedirectResponse) const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForFormAction(const URL&, bool didReceiveRedirectResponse) const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForFrame(const URL&, bool didReceiveRedirectResponse) const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForFrameAncestor(const Frame&) const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForImage(const URL&, bool didReceiveRedirectResponse) const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForMedia(const URL&, bool didReceiveRedirectResponse) const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForObjectSource(const URL&, bool didReceiveRedirectResponse, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone) const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForPluginType(const String& type, const String& typeAttribute) const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForScript(const URL&, bool didReceiveRedirectResponse) const;
+ const ContentSecurityPolicyDirective* violatedDirectiveForStyle(const URL&, bool didReceiveRedirectResponse) const;
+
+ const ContentSecurityPolicyDirective* defaultSrc() const { return m_defaultSrc.get(); }
+
+ bool hasBlockAllMixedContentDirective() const { return m_hasBlockAllMixedContentDirective; }
+
+ const String& evalDisabledErrorMessage() const { return m_evalDisabledErrorMessage; }
+ bool isReportOnly() const { return m_reportOnly; }
+ const Vector<String>& reportURIs() const { return m_reportURIs; }
+
+ // FIXME: Remove this once we teach ContentSecurityPolicyDirectiveList how to log an arbitrary console message.
+ const ContentSecurityPolicy& policy() const { return m_policy; }
+
+private:
+ void parse(const String&, ContentSecurityPolicy::PolicyFrom);
+
+ bool parseDirective(const UChar* begin, const UChar* end, String& name, String& value);
+ void parseReportURI(const String& name, const String& value);
+ void parsePluginTypes(const String& name, const String& value);
+ void addDirective(const String& name, const String& value);
+ void applySandboxPolicy(const String& name, const String& sandboxPolicy);
+ void setUpgradeInsecureRequests(const String& name);
+ void setBlockAllMixedContentEnabled(const String& name);
+
+ template <class CSPDirectiveType>
+ void setCSPDirective(const String& name, const String& value, std::unique_ptr<CSPDirectiveType>&);
+
+ ContentSecurityPolicySourceListDirective* operativeDirective(ContentSecurityPolicySourceListDirective*) const;
+
+ void setEvalDisabledErrorMessage(const String& errorMessage) { m_evalDisabledErrorMessage = errorMessage; }
+
+ // FIXME: Make this a const reference once we teach applySandboxPolicy() to store its policy as opposed to applying it directly onto ContentSecurityPolicy.
+ ContentSecurityPolicy& m_policy;
+
+ String m_header;
+ ContentSecurityPolicyHeaderType m_headerType;
+
+ bool m_reportOnly { false };
+ bool m_haveSandboxPolicy { false };
+ bool m_upgradeInsecureRequests { false };
+ bool m_hasBlockAllMixedContentDirective { false };
+
+ std::unique_ptr<ContentSecurityPolicyMediaListDirective> m_pluginTypes;
+ std::unique_ptr<ContentSecurityPolicySourceListDirective> m_baseURI;
+ std::unique_ptr<ContentSecurityPolicySourceListDirective> m_connectSrc;
+ std::unique_ptr<ContentSecurityPolicySourceListDirective> m_childSrc;
+ std::unique_ptr<ContentSecurityPolicySourceListDirective> m_defaultSrc;
+ std::unique_ptr<ContentSecurityPolicySourceListDirective> m_fontSrc;
+ std::unique_ptr<ContentSecurityPolicySourceListDirective> m_formAction;
+ std::unique_ptr<ContentSecurityPolicySourceListDirective> m_frameAncestors;
+ std::unique_ptr<ContentSecurityPolicySourceListDirective> m_frameSrc;
+ std::unique_ptr<ContentSecurityPolicySourceListDirective> m_imgSrc;
+ std::unique_ptr<ContentSecurityPolicySourceListDirective> m_mediaSrc;
+ std::unique_ptr<ContentSecurityPolicySourceListDirective> m_objectSrc;
+ std::unique_ptr<ContentSecurityPolicySourceListDirective> m_scriptSrc;
+ std::unique_ptr<ContentSecurityPolicySourceListDirective> m_styleSrc;
+
+ Vector<String> m_reportURIs;
+
+ String m_evalDisabledErrorMessage;
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.cpp b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.cpp
new file mode 100644
index 000000000..261f741f6
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ContentSecurityPolicyDirectiveNames.h"
+
+namespace WebCore {
+
+namespace ContentSecurityPolicyDirectiveNames {
+
+const char* const baseURI = "base-uri";
+const char* const childSrc = "child-src";
+const char* const connectSrc = "connect-src";
+const char* const defaultSrc = "default-src";
+const char* const fontSrc = "font-src";
+const char* const formAction = "form-action";
+const char* const frameAncestors = "frame-ancestors";
+const char* const frameSrc = "frame-src";
+const char* const imgSrc = "img-src";
+const char* const mediaSrc = "media-src";
+const char* const objectSrc = "object-src";
+const char* const pluginTypes = "plugin-types";
+const char* const reportURI = "report-uri";
+const char* const sandbox = "sandbox";
+const char* const scriptSrc = "script-src";
+const char* const styleSrc = "style-src";
+const char* const upgradeInsecureRequests = "upgrade-insecure-requests";
+const char* const blockAllMixedContent = "block-all-mixed-content";
+
+} // namespace ContentSecurityPolicyDirectiveNames
+
+} // namespace WebCore
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.h b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.h
new file mode 100644
index 000000000..f13f04aca
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace WebCore {
+
+namespace ContentSecurityPolicyDirectiveNames {
+
+extern const char* const baseURI;
+extern const char* const childSrc;
+extern const char* const connectSrc;
+extern const char* const defaultSrc;
+extern const char* const fontSrc;
+extern const char* const formAction;
+extern const char* const frameAncestors;
+extern const char* const frameSrc;
+extern const char* const imgSrc;
+extern const char* const mediaSrc;
+extern const char* const objectSrc;
+extern const char* const pluginTypes;
+extern const char* const reportURI;
+extern const char* const sandbox;
+extern const char* const scriptSrc;
+extern const char* const styleSrc;
+extern const char* const upgradeInsecureRequests;
+extern const char* const blockAllMixedContent;
+
+} // namespace ContentSecurityPolicyDirectiveNames
+
+} // namespace WebCore
+
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyHash.h b/Source/WebCore/page/csp/ContentSecurityPolicyHash.h
new file mode 100644
index 000000000..83c1ec22e
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicyHash.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/HashTraits.h>
+#include <wtf/Hasher.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+// Keep this synchronized with the constant maximumContentSecurityPolicyDigestLength below.
+enum class ContentSecurityPolicyHashAlgorithm {
+ SHA_256 = 1 << 0,
+ SHA_384 = 1 << 1,
+ SHA_512 = 1 << 2,
+};
+
+const size_t maximumContentSecurityPolicyDigestLength = 64; // bytes to hold SHA-512 digest
+
+typedef Vector<uint8_t> ContentSecurityPolicyDigest;
+typedef std::pair<ContentSecurityPolicyHashAlgorithm, ContentSecurityPolicyDigest> ContentSecurityPolicyHash;
+
+} // namespace WebCore
+
+namespace WTF {
+
+template<> struct DefaultHash<WebCore::ContentSecurityPolicyHashAlgorithm> { typedef IntHash<WebCore::ContentSecurityPolicyHashAlgorithm> Hash; };
+template<> struct HashTraits<WebCore::ContentSecurityPolicyHashAlgorithm> : StrongEnumHashTraits<WebCore::ContentSecurityPolicyHashAlgorithm> { };
+template<> struct DefaultHash<WebCore::ContentSecurityPolicyDigest> {
+ struct Hash {
+ static unsigned hash(const WebCore::ContentSecurityPolicyDigest& digest)
+ {
+ return StringHasher::computeHashAndMaskTop8Bits(digest.data(), digest.size());
+ }
+ static bool equal(const WebCore::ContentSecurityPolicyDigest& a, const WebCore::ContentSecurityPolicyDigest& b)
+ {
+ return a == b;
+ }
+ static const bool safeToCompareToEmptyOrDeleted = true;
+ };
+};
+
+} // namespace WTF
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyMediaListDirective.cpp b/Source/WebCore/page/csp/ContentSecurityPolicyMediaListDirective.cpp
new file mode 100644
index 000000000..3d3ed4d0d
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicyMediaListDirective.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 Google, Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ContentSecurityPolicyMediaListDirective.h"
+
+#include "ContentSecurityPolicy.h"
+#include "ContentSecurityPolicyDirectiveList.h"
+#include "ParsingUtilities.h"
+#include <wtf/text/StringHash.h>
+
+namespace WebCore {
+
+static bool isMediaTypeCharacter(UChar c)
+{
+ return !isASCIISpace(c) && c != '/';
+}
+
+static bool isNotASCIISpace(UChar c)
+{
+ return !isASCIISpace(c);
+}
+
+ContentSecurityPolicyMediaListDirective::ContentSecurityPolicyMediaListDirective(const ContentSecurityPolicyDirectiveList& directiveList, const String& name, const String& value)
+ : ContentSecurityPolicyDirective(directiveList, name, value)
+{
+ parse(value);
+}
+
+bool ContentSecurityPolicyMediaListDirective::allows(const String& type) const
+{
+ return m_pluginTypes.contains(type);
+}
+
+void ContentSecurityPolicyMediaListDirective::parse(const String& value)
+{
+ auto characters = StringView(value).upconvertedCharacters();
+ const UChar* begin = characters;
+ const UChar* position = begin;
+ const UChar* end = begin + value.length();
+
+ // 'plugin-types ____;' OR 'plugin-types;'
+ if (value.isEmpty()) {
+ directiveList().policy().reportInvalidPluginTypes(value);
+ return;
+ }
+
+ while (position < end) {
+ // _____ OR _____mime1/mime1
+ // ^ ^
+ skipWhile<UChar, isASCIISpace>(position, end);
+ if (position == end)
+ return;
+
+ // mime1/mime1 mime2/mime2
+ // ^
+ begin = position;
+ if (!skipExactly<UChar, isMediaTypeCharacter>(position, end)) {
+ skipWhile<UChar, isNotASCIISpace>(position, end);
+ directiveList().policy().reportInvalidPluginTypes(String(begin, position - begin));
+ continue;
+ }
+ skipWhile<UChar, isMediaTypeCharacter>(position, end);
+
+ // mime1/mime1 mime2/mime2
+ // ^
+ if (!skipExactly<UChar>(position, end, '/')) {
+ skipWhile<UChar, isNotASCIISpace>(position, end);
+ directiveList().policy().reportInvalidPluginTypes(String(begin, position - begin));
+ continue;
+ }
+
+ // mime1/mime1 mime2/mime2
+ // ^
+ if (!skipExactly<UChar, isMediaTypeCharacter>(position, end)) {
+ skipWhile<UChar, isNotASCIISpace>(position, end);
+ directiveList().policy().reportInvalidPluginTypes(String(begin, position - begin));
+ continue;
+ }
+ skipWhile<UChar, isMediaTypeCharacter>(position, end);
+
+ // mime1/mime1 mime2/mime2 OR mime1/mime1 OR mime1/mime1/error
+ // ^ ^ ^
+ if (position < end && isNotASCIISpace(*position)) {
+ skipWhile<UChar, isNotASCIISpace>(position, end);
+ directiveList().policy().reportInvalidPluginTypes(String(begin, position - begin));
+ continue;
+ }
+ m_pluginTypes.add(String(begin, position - begin));
+
+ ASSERT(position == end || isASCIISpace(*position));
+ }
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyMediaListDirective.h b/Source/WebCore/page/csp/ContentSecurityPolicyMediaListDirective.h
new file mode 100644
index 000000000..d991481fa
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicyMediaListDirective.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 Google, Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ContentSecurityPolicyDirective.h"
+#include <wtf/HashSet.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class ContentSecurityPolicyDirectiveList;
+
+class ContentSecurityPolicyMediaListDirective : public ContentSecurityPolicyDirective {
+public:
+ ContentSecurityPolicyMediaListDirective(const ContentSecurityPolicyDirectiveList&, const String& name, const String& value);
+
+ bool allows(const String& type) const;
+
+private:
+ void parse(const String&);
+
+ HashSet<String> m_pluginTypes;
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.cpp b/Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.cpp
new file mode 100644
index 000000000..0b67b91d4
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ContentSecurityPolicyResponseHeaders.h"
+
+#include "HTTPHeaderNames.h"
+#include "ResourceResponse.h"
+
+namespace WebCore {
+
+ContentSecurityPolicyResponseHeaders::ContentSecurityPolicyResponseHeaders(const ResourceResponse& response)
+{
+ String policyValue = response.httpHeaderField(HTTPHeaderName::ContentSecurityPolicy);
+ if (!policyValue.isEmpty())
+ m_headers.append({ policyValue, ContentSecurityPolicyHeaderType::Enforce });
+
+ policyValue = response.httpHeaderField(HTTPHeaderName::ContentSecurityPolicyReportOnly);
+ if (!policyValue.isEmpty())
+ m_headers.append({ policyValue, ContentSecurityPolicyHeaderType::Report });
+
+ policyValue = response.httpHeaderField(HTTPHeaderName::XWebKitCSP);
+ if (!policyValue.isEmpty())
+ m_headers.append({ policyValue, ContentSecurityPolicyHeaderType::PrefixedEnforce });
+
+ policyValue = response.httpHeaderField(HTTPHeaderName::XWebKitCSPReportOnly);
+ if (!policyValue.isEmpty())
+ m_headers.append({ policyValue, ContentSecurityPolicyHeaderType::PrefixedReport });
+}
+
+ContentSecurityPolicyResponseHeaders ContentSecurityPolicyResponseHeaders::isolatedCopy() const
+{
+ ContentSecurityPolicyResponseHeaders isolatedCopy;
+ isolatedCopy.m_headers.reserveInitialCapacity(m_headers.size());
+ for (auto& header : m_headers)
+ isolatedCopy.m_headers.uncheckedAppend({ header.first.isolatedCopy(), header.second });
+ return isolatedCopy;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.h b/Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.h
new file mode 100644
index 000000000..b154613c3
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class ContentSecurityPolicy;
+class ResourceResponse;
+
+enum class ContentSecurityPolicyHeaderType {
+ Report,
+ Enforce,
+ PrefixedReport,
+ PrefixedEnforce,
+};
+
+class ContentSecurityPolicyResponseHeaders {
+public:
+ explicit ContentSecurityPolicyResponseHeaders(const ResourceResponse&);
+
+ ContentSecurityPolicyResponseHeaders isolatedCopy() const;
+
+private:
+ friend class ContentSecurityPolicy;
+
+ ContentSecurityPolicyResponseHeaders() = default;
+
+ Vector<std::pair<String, ContentSecurityPolicyHeaderType>> m_headers;
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySource.cpp b/Source/WebCore/page/csp/ContentSecurityPolicySource.cpp
new file mode 100644
index 000000000..59604c67a
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicySource.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2011 Google, Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ContentSecurityPolicySource.h"
+
+#include "ContentSecurityPolicy.h"
+#include "URL.h"
+
+namespace WebCore {
+
+ContentSecurityPolicySource::ContentSecurityPolicySource(const ContentSecurityPolicy& policy, const String& scheme, const String& host, std::optional<uint16_t> port, const String& path, bool hostHasWildcard, bool portHasWildcard)
+ : m_policy(policy)
+ , m_scheme(scheme)
+ , m_host(host)
+ , m_port(port)
+ , m_path(path)
+ , m_hostHasWildcard(hostHasWildcard)
+ , m_portHasWildcard(portHasWildcard)
+{
+}
+
+bool ContentSecurityPolicySource::matches(const URL& url, bool didReceiveRedirectResponse) const
+{
+ if (!schemeMatches(url))
+ return false;
+ if (isSchemeOnly())
+ return true;
+ return hostMatches(url) && portMatches(url) && (didReceiveRedirectResponse || pathMatches(url));
+}
+
+bool ContentSecurityPolicySource::schemeMatches(const URL& url) const
+{
+ if (m_scheme.isEmpty())
+ return m_policy.protocolMatchesSelf(url);
+ if (equalLettersIgnoringASCIICase(m_scheme, "http"))
+ return url.protocolIsInHTTPFamily();
+ return equalIgnoringASCIICase(url.protocol(), m_scheme);
+}
+
+bool ContentSecurityPolicySource::hostMatches(const URL& url) const
+{
+ const String& host = url.host();
+ if (equalIgnoringASCIICase(host, m_host))
+ return true;
+ return m_hostHasWildcard && host.endsWith("." + m_host, false);
+
+}
+
+bool ContentSecurityPolicySource::pathMatches(const URL& url) const
+{
+ if (m_path.isEmpty())
+ return true;
+
+ String path = decodeURLEscapeSequences(url.path());
+
+ if (m_path.endsWith("/"))
+ return path.startsWith(m_path);
+
+ return path == m_path;
+}
+
+bool ContentSecurityPolicySource::portMatches(const URL& url) const
+{
+ if (m_portHasWildcard)
+ return true;
+
+ std::optional<uint16_t> port = url.port();
+
+ if (port == m_port)
+ return true;
+
+ if (isDefaultPortForProtocol(m_port.value(), "http") && ((!port && url.protocolIs("https")) || isDefaultPortForProtocol(port.value(), "https")))
+ return true;
+
+ if (!port)
+ return isDefaultPortForProtocol(m_port.value(), url.protocol());
+
+ if (!m_port)
+ return isDefaultPortForProtocol(port.value(), url.protocol());
+
+ return false;
+}
+
+bool ContentSecurityPolicySource::isSchemeOnly() const
+{
+ return m_host.isEmpty();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySource.h b/Source/WebCore/page/csp/ContentSecurityPolicySource.h
new file mode 100644
index 000000000..28a1a6830
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicySource.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 Google, Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class ContentSecurityPolicy;
+class URL;
+
+class ContentSecurityPolicySource {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ ContentSecurityPolicySource(const ContentSecurityPolicy&, const String& scheme, const String& host, std::optional<uint16_t> port, const String& path, bool hostHasWildcard, bool portHasWildcard);
+
+ bool matches(const URL&, bool didReceiveRedirectResponse = false) const;
+
+private:
+ bool schemeMatches(const URL&) const;
+ bool hostMatches(const URL&) const;
+ bool pathMatches(const URL&) const;
+ bool portMatches(const URL&) const;
+ bool isSchemeOnly() const;
+
+ const ContentSecurityPolicy& m_policy;
+ String m_scheme;
+ String m_host;
+ std::optional<uint16_t> m_port;
+ String m_path;
+
+ bool m_hostHasWildcard;
+ bool m_portHasWildcard;
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp b/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp
new file mode 100644
index 000000000..b9e8c1141
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2011 Google, Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ContentSecurityPolicySourceList.h"
+
+#include "ContentSecurityPolicy.h"
+#include "ContentSecurityPolicyDirectiveNames.h"
+#include "ParsingUtilities.h"
+#include "URL.h"
+#include <wtf/ASCIICType.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/text/Base64.h>
+
+namespace WebCore {
+
+static bool isCSPDirectiveName(const String& name)
+{
+ return equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::baseURI)
+ || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::connectSrc)
+ || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::defaultSrc)
+ || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::fontSrc)
+ || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::formAction)
+ || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::frameSrc)
+ || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::imgSrc)
+ || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::mediaSrc)
+ || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::objectSrc)
+ || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::pluginTypes)
+ || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::reportURI)
+ || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::sandbox)
+ || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::scriptSrc)
+ || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::styleSrc);
+}
+
+static bool isSourceCharacter(UChar c)
+{
+ return !isASCIISpace(c);
+}
+
+static bool isHostCharacter(UChar c)
+{
+ return isASCIIAlphanumeric(c) || c == '-';
+}
+
+static bool isPathComponentCharacter(UChar c)
+{
+ return c != '?' && c != '#';
+}
+
+static bool isSchemeContinuationCharacter(UChar c)
+{
+ return isASCIIAlphanumeric(c) || c == '+' || c == '-' || c == '.';
+}
+
+static bool isNotColonOrSlash(UChar c)
+{
+ return c != ':' && c != '/';
+}
+
+static bool isSourceListNone(const String& value)
+{
+ auto characters = StringView(value).upconvertedCharacters();
+ const UChar* begin = characters;
+ const UChar* end = characters + value.length();
+ skipWhile<UChar, isASCIISpace>(begin, end);
+
+ const UChar* position = begin;
+ skipWhile<UChar, isSourceCharacter>(position, end);
+ if (!equalLettersIgnoringASCIICase(begin, position - begin, "'none'"))
+ return false;
+
+ skipWhile<UChar, isASCIISpace>(position, end);
+ if (position != end)
+ return false;
+
+ return true;
+}
+
+ContentSecurityPolicySourceList::ContentSecurityPolicySourceList(const ContentSecurityPolicy& policy, const String& directiveName)
+ : m_policy(policy)
+ , m_directiveName(directiveName)
+{
+}
+
+void ContentSecurityPolicySourceList::parse(const String& value)
+{
+ if (isSourceListNone(value)) {
+ m_isNone = true;
+ return;
+ }
+ auto characters = StringView(value).upconvertedCharacters();
+ parse(characters, characters + value.length());
+}
+
+bool ContentSecurityPolicySourceList::isProtocolAllowedByStar(const URL& url) const
+{
+ if (m_policy.allowContentSecurityPolicySourceStarToMatchAnyProtocol())
+ return true;
+
+ // Although not allowed by the Content Security Policy Level 3 spec., we allow a data URL to match
+ // "img-src *" and either a data URL or blob URL to match "media-src *" for web compatibility.
+ bool isAllowed = url.protocolIsInHTTPFamily() || url.protocolIs("ws") || url.protocolIs("wss") || m_policy.protocolMatchesSelf(url);
+ if (equalIgnoringASCIICase(m_directiveName, ContentSecurityPolicyDirectiveNames::imgSrc))
+ isAllowed |= url.protocolIsData();
+ else if (equalIgnoringASCIICase(m_directiveName, ContentSecurityPolicyDirectiveNames::mediaSrc))
+ isAllowed |= url.protocolIsData() || url.protocolIsBlob();
+ return isAllowed;
+}
+
+bool ContentSecurityPolicySourceList::matches(const URL& url, bool didReceiveRedirectResponse)
+{
+ if (m_allowStar && isProtocolAllowedByStar(url))
+ return true;
+
+ if (m_allowSelf && m_policy.urlMatchesSelf(url))
+ return true;
+
+ for (auto& entry : m_list) {
+ if (entry.matches(url, didReceiveRedirectResponse))
+ return true;
+ }
+
+ 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
+//
+void ContentSecurityPolicySourceList::parse(const UChar* begin, const UChar* end)
+{
+ 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;
+ std::optional<uint16_t> port;
+ 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
+ // list itself.
+ if (scheme.isEmpty() && host.isEmpty())
+ continue;
+ if (isCSPDirectiveName(host))
+ m_policy.reportDirectiveAsSourceExpression(m_directiveName, host);
+ m_list.append(ContentSecurityPolicySource(m_policy, scheme, host, port, path, hostHasWildcard, portHasWildcard));
+ } else
+ m_policy.reportInvalidSourceExpression(m_directiveName, String(beginSource, position - beginSource));
+
+ ASSERT(position == end || isASCIISpace(*position));
+ }
+}
+
+// source = scheme ":"
+// / ( [ scheme "://" ] host [ port ] [ path ] )
+// / "'self'"
+//
+bool ContentSecurityPolicySourceList::parseSource(const UChar* begin, const UChar* end, String& scheme, String& host, std::optional<uint16_t>& port, String& path, bool& hostHasWildcard, bool& portHasWildcard)
+{
+ if (begin == end)
+ return false;
+
+ if (equalLettersIgnoringASCIICase(begin, end - begin, "'none'"))
+ return false;
+
+ if (end - begin == 1 && *begin == '*') {
+ m_allowStar = true;
+ return true;
+ }
+
+ if (equalLettersIgnoringASCIICase(begin, end - begin, "'self'")) {
+ m_allowSelf = true;
+ return true;
+ }
+
+ if (equalLettersIgnoringASCIICase(begin, end - begin, "'unsafe-inline'")) {
+ m_allowInline = true;
+ return true;
+ }
+
+ if (equalLettersIgnoringASCIICase(begin, end - begin, "'unsafe-eval'")) {
+ m_allowEval = true;
+ return true;
+ }
+
+ const UChar* position = begin;
+ const UChar* beginHost = begin;
+ const UChar* beginPath = end;
+ const UChar* beginPort = nullptr;
+
+ skipWhile<UChar, isNotColonOrSlash>(position, end);
+
+ if (position == end) {
+ // host
+ // ^
+ return parseHost(beginHost, position, host, hostHasWildcard);
+ }
+
+ if (position < end && *position == '/') {
+ // host/path || host/ || /
+ // ^ ^ ^
+ return parseHost(beginHost, position, host, hostHasWildcard) && 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, hostHasWildcard))
+ return false;
+
+ if (!beginPort)
+ port = std::nullopt;
+ else {
+ if (!parsePort(beginPort, beginPath, port, portHasWildcard))
+ return false;
+ }
+
+ if (beginPath != end) {
+ if (!parsePath(beginPath, end, path))
+ return false;
+ }
+
+ return true;
+}
+
+// ; <scheme> production from RFC 3986
+// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+//
+bool ContentSecurityPolicySourceList::parseScheme(const UChar* begin, const UChar* end, String& scheme)
+{
+ ASSERT(begin <= end);
+ ASSERT(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 ContentSecurityPolicySourceList::parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard)
+{
+ ASSERT(begin <= end);
+ ASSERT(host.isEmpty());
+ ASSERT(!hostHasWildcard);
+
+ if (begin == end)
+ return false;
+
+ const UChar* position = begin;
+
+ if (skipExactly<UChar>(position, end, '*')) {
+ hostHasWildcard = true;
+
+ 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;
+ }
+
+ ASSERT(position == end);
+ host = String(hostBegin, end - hostBegin);
+ return true;
+}
+
+bool ContentSecurityPolicySourceList::parsePath(const UChar* begin, const UChar* end, String& path)
+{
+ ASSERT(begin <= end);
+ ASSERT(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));
+
+ ASSERT(position <= end);
+ ASSERT(position == end || (*position == '#' || *position == '?'));
+ return true;
+}
+
+// port = ":" ( 1*DIGIT / "*" )
+//
+bool ContentSecurityPolicySourceList::parsePort(const UChar* begin, const UChar* end, std::optional<uint16_t>& port, bool& portHasWildcard)
+{
+ ASSERT(begin <= end);
+ ASSERT(!port);
+ ASSERT(!portHasWildcard);
+
+ if (!skipExactly<UChar>(begin, end, ':'))
+ ASSERT_NOT_REACHED();
+
+ if (begin == end)
+ return false;
+
+ if (end - begin == 1 && *begin == '*') {
+ port = std::nullopt;
+ portHasWildcard = true;
+ return true;
+ }
+
+ const UChar* position = begin;
+ skipWhile<UChar, isASCIIDigit>(position, end);
+
+ if (position != end)
+ return false;
+
+ bool ok;
+ int portInt = charactersToIntStrict(begin, end - begin, &ok);
+ if (portInt < 0 || portInt > std::numeric_limits<uint16_t>::max())
+ return false;
+ port = portInt;
+ 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
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySourceList.h b/Source/WebCore/page/csp/ContentSecurityPolicySourceList.h
new file mode 100644
index 000000000..04dc29c33
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicySourceList.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2011 Google, Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ContentSecurityPolicyHash.h"
+#include "ContentSecurityPolicySource.h"
+#include <wtf/HashSet.h>
+#include <wtf/OptionSet.h>
+#include <wtf/text/StringHash.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class ContentSecurityPolicy;
+class URL;
+
+class ContentSecurityPolicySourceList {
+public:
+ ContentSecurityPolicySourceList(const ContentSecurityPolicy&, const String& directiveName);
+
+ void parse(const String&);
+
+ bool matches(const URL&, bool didReceiveRedirectResponse);
+ bool matches(const ContentSecurityPolicyHash&) const;
+ bool matches(const String& nonce) const;
+
+ OptionSet<ContentSecurityPolicyHashAlgorithm> hashAlgorithmsUsed() const { return m_hashAlgorithmsUsed; }
+
+ bool allowInline() const { return m_allowInline && m_hashes.isEmpty() && m_nonces.isEmpty(); }
+ bool allowEval() const { return m_allowEval; }
+ bool allowSelf() const { return m_allowSelf; }
+ bool isNone() const { return m_isNone; }
+
+private:
+ void parse(const UChar* begin, const UChar* end);
+
+ bool parseSource(const UChar* begin, const UChar* end, String& scheme, String& host, std::optional<uint16_t>& port, String& path, bool& hostHasWildcard, bool& portHasWildcard);
+ bool parseScheme(const UChar* begin, const UChar* end, String& scheme);
+ bool parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard);
+ bool parsePort(const UChar* begin, const UChar* end, std::optional<uint16_t>& port, bool& portHasWildcard);
+ bool parsePath(const UChar* begin, const UChar* end, String& path);
+
+ bool parseNonceSource(const UChar* begin, const UChar* end);
+
+ bool isProtocolAllowedByStar(const URL&) const;
+
+ bool parseHashSource(const UChar* begin, const UChar* end);
+
+ const ContentSecurityPolicy& m_policy;
+ Vector<ContentSecurityPolicySource> m_list;
+ HashSet<String> m_nonces;
+ HashSet<ContentSecurityPolicyHash> m_hashes;
+ OptionSet<ContentSecurityPolicyHashAlgorithm> m_hashAlgorithmsUsed;
+ String m_directiveName;
+ bool m_allowSelf { false };
+ bool m_allowStar { false };
+ bool m_allowInline { false };
+ bool m_allowEval { false };
+ bool m_isNone { false };
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp b/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp
new file mode 100644
index 000000000..c2bf1d45a
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 Google, Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ContentSecurityPolicySourceListDirective.h"
+
+#include "ContentSecurityPolicy.h"
+#include "ContentSecurityPolicyDirectiveList.h"
+#include "URL.h"
+
+namespace WebCore {
+
+ContentSecurityPolicySourceListDirective::ContentSecurityPolicySourceListDirective(const ContentSecurityPolicyDirectiveList& directiveList, const String& name, const String& value)
+ : ContentSecurityPolicyDirective(directiveList, name, value)
+ , m_sourceList(directiveList.policy(), name)
+{
+ m_sourceList.parse(value);
+}
+
+bool ContentSecurityPolicySourceListDirective::allows(const URL& url, bool didReceiveRedirectResponse, ShouldAllowEmptyURLIfSourceListIsNotNone shouldAllowEmptyURLIfSourceListEmpty)
+{
+ if (url.isEmpty())
+ return shouldAllowEmptyURLIfSourceListEmpty == ShouldAllowEmptyURLIfSourceListIsNotNone::Yes && !m_sourceList.isNone();
+ return m_sourceList.matches(url, didReceiveRedirectResponse);
+}
+
+bool ContentSecurityPolicySourceListDirective::allows(const String& nonce) const
+{
+ return m_sourceList.matches(nonce);
+}
+
+bool ContentSecurityPolicySourceListDirective::allows(const ContentSecurityPolicyHash& hash) const
+{
+ return m_sourceList.matches(hash);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.h b/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.h
new file mode 100644
index 000000000..afb2ed44f
--- /dev/null
+++ b/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 Google, Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ContentSecurityPolicyDirective.h"
+#include "ContentSecurityPolicySourceList.h"
+
+namespace WebCore {
+
+class ContentSecurityPolicyDirectiveList;
+
+class ContentSecurityPolicySourceListDirective : public ContentSecurityPolicyDirective {
+public:
+ ContentSecurityPolicySourceListDirective(const ContentSecurityPolicyDirectiveList&, const String& name, const String& value);
+
+ enum class ShouldAllowEmptyURLIfSourceListIsNotNone { No, Yes };
+ bool allows(const URL&, bool didReceiveRedirectResponse, ShouldAllowEmptyURLIfSourceListIsNotNone);
+ bool allows(const ContentSecurityPolicyHash&) const;
+ bool allows(const String& nonce) const;
+ bool allowInline() const { return m_sourceList.allowInline(); }
+ bool allowEval() const { return m_sourceList.allowEval(); }
+
+ OptionSet<ContentSecurityPolicyHashAlgorithm> hashAlgorithmsUsed() const { return m_sourceList.hashAlgorithmsUsed(); }
+
+private:
+ ContentSecurityPolicySourceList m_sourceList;
+};
+
+} // namespace WebCore