6136e4281753f649d4881f0a057929028f4d8f68
[WebKit-https.git] / Source / WebCore / page / csp / ContentSecurityPolicy.cpp
1 /*
2  * Copyright (C) 2011 Google, Inc. All rights reserved.
3  * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "ContentSecurityPolicy.h"
29
30 #include "ContentSecurityPolicyClient.h"
31 #include "ContentSecurityPolicyDirective.h"
32 #include "ContentSecurityPolicyDirectiveList.h"
33 #include "ContentSecurityPolicyDirectiveNames.h"
34 #include "ContentSecurityPolicyHash.h"
35 #include "ContentSecurityPolicySource.h"
36 #include "ContentSecurityPolicySourceList.h"
37 #include "DOMStringList.h"
38 #include "Document.h"
39 #include "DocumentLoader.h"
40 #include "EventNames.h"
41 #include "FormData.h"
42 #include "Frame.h"
43 #include "HTMLParserIdioms.h"
44 #include "InspectorInstrumentation.h"
45 #include "JSExecState.h"
46 #include "JSWindowProxy.h"
47 #include "ParsingUtilities.h"
48 #include "PingLoader.h"
49 #include "ResourceRequest.h"
50 #include "RuntimeEnabledFeatures.h"
51 #include "SchemeRegistry.h"
52 #include "SecurityOrigin.h"
53 #include "SecurityPolicyViolationEvent.h"
54 #include "Settings.h"
55 #include "TextEncoding.h"
56 #include <JavaScriptCore/ScriptCallStack.h>
57 #include <JavaScriptCore/ScriptCallStackFactory.h>
58 #include <pal/crypto/CryptoDigest.h>
59 #include <wtf/JSONValues.h>
60 #include <wtf/SetForScope.h>
61 #include <wtf/text/StringBuilder.h>
62 #include <wtf/text/TextPosition.h>
63
64
65 namespace WebCore {
66 using namespace Inspector;
67
68 static String consoleMessageForViolation(const char* effectiveViolatedDirective, const ContentSecurityPolicyDirective& violatedDirective, const URL& blockedURL, const char* prefix, const char* subject = "it")
69 {
70     StringBuilder result;
71     if (violatedDirective.directiveList().isReportOnly())
72         result.appendLiteral("[Report Only] ");
73     result.append(prefix);
74     if (!blockedURL.isEmpty()) {
75         result.append(' ');
76         result.append(blockedURL.stringCenterEllipsizedToLength());
77     }
78     result.appendLiteral(" because ");
79     result.append(subject);
80     if (violatedDirective.isDefaultSrc()) {
81         result.appendLiteral(" appears in neither the ");
82         result.append(effectiveViolatedDirective);
83         result.appendLiteral(" directive nor the default-src directive of the Content Security Policy.");
84     } else {
85         result.appendLiteral(" does not appear in the ");
86         result.append(effectiveViolatedDirective);
87         result.appendLiteral(" directive of the Content Security Policy.");
88     }
89     return result.toString();
90 }
91
92 ContentSecurityPolicy::ContentSecurityPolicy(URL&& protectedURL, ContentSecurityPolicyClient* client)
93     : m_client { client }
94     , m_protectedURL { WTFMove(protectedURL) }
95 {
96     updateSourceSelf(SecurityOrigin::create(m_protectedURL).get());
97 }
98
99 ContentSecurityPolicy::ContentSecurityPolicy(URL&& protectedURL, ScriptExecutionContext& scriptExecutionContext)
100     : m_scriptExecutionContext(&scriptExecutionContext)
101     , m_protectedURL { WTFMove(protectedURL) }
102 {
103     ASSERT(scriptExecutionContext.securityOrigin());
104     updateSourceSelf(*scriptExecutionContext.securityOrigin());
105 }
106
107 ContentSecurityPolicy::~ContentSecurityPolicy() = default;
108
109 void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other) 
110 {
111     if (m_hasAPIPolicy)
112         return;
113     ASSERT(m_policies.isEmpty());
114     for (auto& policy : other->m_policies)
115         didReceiveHeader(policy->header(), policy->headerType(), ContentSecurityPolicy::PolicyFrom::Inherited, String { });
116     m_referrer = other->m_referrer;
117     m_httpStatusCode = other->m_httpStatusCode;
118 }
119
120 void ContentSecurityPolicy::createPolicyForPluginDocumentFrom(const ContentSecurityPolicy& other)
121 {
122     if (m_hasAPIPolicy)
123         return;
124     ASSERT(m_policies.isEmpty());
125     for (auto& policy : other.m_policies)
126         didReceiveHeader(policy->header(), policy->headerType(), ContentSecurityPolicy::PolicyFrom::InheritedForPluginDocument, String { });
127     m_referrer = other.m_referrer;
128     m_httpStatusCode = other.m_httpStatusCode;
129 }
130
131 void ContentSecurityPolicy::copyUpgradeInsecureRequestStateFrom(const ContentSecurityPolicy& other)
132 {
133     m_upgradeInsecureRequests = other.m_upgradeInsecureRequests;
134     m_insecureNavigationRequestsToUpgrade.add(other.m_insecureNavigationRequestsToUpgrade.begin(), other.m_insecureNavigationRequestsToUpgrade.end());
135 }
136
137 bool ContentSecurityPolicy::allowRunningOrDisplayingInsecureContent(const URL& url)
138 {
139     bool allow = true;
140     bool isReportOnly = false;
141     for (auto& policy : m_policies) {
142         if (!policy->hasBlockAllMixedContentDirective())
143             continue;
144
145         isReportOnly = policy->isReportOnly();
146
147         StringBuilder consoleMessage;
148         if (isReportOnly)
149             consoleMessage.appendLiteral("[Report Only] ");
150         consoleMessage.append("Blocked mixed content ");
151         consoleMessage.append(url.stringCenterEllipsizedToLength());
152         consoleMessage.appendLiteral(" because ");
153         consoleMessage.append("'block-all-mixed-content' appears in the Content Security Policy.");
154         reportViolation(ContentSecurityPolicyDirectiveNames::blockAllMixedContent, ContentSecurityPolicyDirectiveNames::blockAllMixedContent, *policy, url, consoleMessage.toString());
155
156         if (!isReportOnly)
157             allow = false;
158     }
159     return allow;
160 }
161
162 void ContentSecurityPolicy::didCreateWindowProxy(JSWindowProxy& windowProxy) const
163 {
164     auto* window = windowProxy.window();
165     ASSERT(window);
166     ASSERT(window->scriptExecutionContext());
167     ASSERT(window->scriptExecutionContext()->contentSecurityPolicy() == this);
168     if (!windowProxy.world().isNormal()) {
169         window->setEvalEnabled(true);
170         return;
171     }
172     window->setEvalEnabled(m_lastPolicyEvalDisabledErrorMessage.isNull(), m_lastPolicyEvalDisabledErrorMessage);
173     window->setWebAssemblyEnabled(m_lastPolicyWebAssemblyDisabledErrorMessage.isNull(), m_lastPolicyWebAssemblyDisabledErrorMessage);
174 }
175
176 ContentSecurityPolicyResponseHeaders ContentSecurityPolicy::responseHeaders() const
177 {
178     if (!m_cachedResponseHeaders) {
179         ContentSecurityPolicyResponseHeaders result;
180         result.m_headers.reserveInitialCapacity(m_policies.size());
181         for (auto& policy : m_policies)
182             result.m_headers.uncheckedAppend({ policy->header(), policy->headerType() });
183         result.m_httpStatusCode = m_httpStatusCode;
184         m_cachedResponseHeaders = WTFMove(result);
185     }
186     return *m_cachedResponseHeaders;
187 }
188
189 void ContentSecurityPolicy::didReceiveHeaders(const ContentSecurityPolicyResponseHeaders& headers, String&& referrer, ReportParsingErrors reportParsingErrors)
190 {
191     SetForScope<bool> isReportingEnabled(m_isReportingEnabled, reportParsingErrors == ReportParsingErrors::Yes);
192     for (auto& header : headers.m_headers)
193         didReceiveHeader(header.first, header.second, ContentSecurityPolicy::PolicyFrom::HTTPHeader, String { });
194     m_referrer = WTFMove(referrer);
195     m_httpStatusCode = headers.m_httpStatusCode;
196 }
197
198 void ContentSecurityPolicy::didReceiveHeader(const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicy::PolicyFrom policyFrom, String&& referrer, int httpStatusCode)
199 {
200     if (m_hasAPIPolicy)
201         return;
202
203     m_referrer = WTFMove(referrer);
204     m_httpStatusCode = httpStatusCode;
205
206     if (policyFrom == PolicyFrom::API) {
207         ASSERT(m_policies.isEmpty());
208         m_hasAPIPolicy = true;
209     }
210
211     m_cachedResponseHeaders = WTF::nullopt;
212
213     // RFC2616, section 4.2 specifies that headers appearing multiple times can
214     // be combined with a comma. Walk the header string, and parse each comma
215     // separated chunk as a separate header.
216     auto characters = StringView(header).upconvertedCharacters();
217     const UChar* begin = characters;
218     const UChar* position = begin;
219     const UChar* end = begin + header.length();
220     while (position < end) {
221         skipUntil<UChar>(position, end, ',');
222
223         // header1,header2 OR header1
224         //        ^                  ^
225         m_policies.append(ContentSecurityPolicyDirectiveList::create(*this, String(begin, position - begin), type, policyFrom));
226
227         // Skip the comma, and begin the next header from the current position.
228         ASSERT(position == end || *position == ',');
229         skipExactly<UChar>(position, end, ',');
230         begin = position;
231     }
232
233     if (m_scriptExecutionContext)
234         applyPolicyToScriptExecutionContext();
235 }
236
237 void ContentSecurityPolicy::updateSourceSelf(const SecurityOrigin& securityOrigin)
238 {
239     m_selfSourceProtocol = securityOrigin.protocol();
240     m_selfSource = std::make_unique<ContentSecurityPolicySource>(*this, m_selfSourceProtocol, securityOrigin.host(), securityOrigin.port(), emptyString(), false, false);
241 }
242
243 void ContentSecurityPolicy::applyPolicyToScriptExecutionContext()
244 {
245     ASSERT(m_scriptExecutionContext);
246
247     // Update source self as the security origin may have changed between the time we were created and now.
248     // For instance, we may have been initially created for an about:blank iframe that later inherited the
249     // security origin of its owner document.
250     ASSERT(m_scriptExecutionContext->securityOrigin());
251     updateSourceSelf(*m_scriptExecutionContext->securityOrigin());
252
253     bool enableStrictMixedContentMode = false;
254     for (auto& policy : m_policies) {
255         const ContentSecurityPolicyDirective* violatedDirective = policy->violatedDirectiveForUnsafeEval();
256         if (violatedDirective && !violatedDirective->directiveList().isReportOnly()) {
257             m_lastPolicyEvalDisabledErrorMessage = policy->evalDisabledErrorMessage();
258             m_lastPolicyWebAssemblyDisabledErrorMessage = policy->webAssemblyDisabledErrorMessage();
259         }
260         if (policy->hasBlockAllMixedContentDirective() && !policy->isReportOnly())
261             enableStrictMixedContentMode = true;
262     }
263
264     if (!m_lastPolicyEvalDisabledErrorMessage.isNull())
265         m_scriptExecutionContext->disableEval(m_lastPolicyEvalDisabledErrorMessage);
266     if (!m_lastPolicyWebAssemblyDisabledErrorMessage.isNull())
267         m_scriptExecutionContext->disableWebAssembly(m_lastPolicyWebAssemblyDisabledErrorMessage);
268     if (m_sandboxFlags != SandboxNone && is<Document>(m_scriptExecutionContext))
269         m_scriptExecutionContext->enforceSandboxFlags(m_sandboxFlags);
270     if (enableStrictMixedContentMode)
271         m_scriptExecutionContext->setStrictMixedContentMode(true);
272 }
273
274 void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value)
275 {
276     m_overrideInlineStyleAllowed = value;
277 }
278
279 bool ContentSecurityPolicy::urlMatchesSelf(const URL& url) const
280 {
281     return m_selfSource->matches(url);
282 }
283
284 bool ContentSecurityPolicy::allowContentSecurityPolicySourceStarToMatchAnyProtocol() const
285 {
286     if (is<Document>(m_scriptExecutionContext))
287         return downcast<Document>(*m_scriptExecutionContext).settings().allowContentSecurityPolicySourceStarToMatchAnyProtocol();
288     return false;
289 }
290
291 bool ContentSecurityPolicy::protocolMatchesSelf(const URL& url) const
292 {
293     if (equalLettersIgnoringASCIICase(m_selfSourceProtocol, "http"))
294         return url.protocolIsInHTTPFamily();
295     return equalIgnoringASCIICase(url.protocol(), m_selfSourceProtocol);
296 }
297
298 template<typename Predicate, typename... Args>
299 typename std::enable_if<!std::is_convertible<Predicate, ContentSecurityPolicy::ViolatedDirectiveCallback>::value, bool>::type ContentSecurityPolicy::allPoliciesWithDispositionAllow(Disposition disposition, Predicate&& predicate, Args&&... args) const
300 {
301     bool isReportOnly = disposition == ContentSecurityPolicy::Disposition::ReportOnly;
302     for (auto& policy : m_policies) {
303         if (policy->isReportOnly() != isReportOnly)
304             continue;
305         if ((policy.get()->*predicate)(std::forward<Args>(args)...))
306             return false;
307     }
308     return true;
309 }
310
311 template<typename Predicate, typename... Args>
312 bool ContentSecurityPolicy::allPoliciesWithDispositionAllow(Disposition disposition, ViolatedDirectiveCallback&& callback, Predicate&& predicate, Args&&... args) const
313 {
314     bool isReportOnly = disposition == ContentSecurityPolicy::Disposition::ReportOnly;
315     bool isAllowed = true;
316     for (auto& policy : m_policies) {
317         if (policy->isReportOnly() != isReportOnly)
318             continue;
319         if (const ContentSecurityPolicyDirective* violatedDirective = (policy.get()->*predicate)(std::forward<Args>(args)...)) {
320             isAllowed = false;
321             callback(*violatedDirective);
322         }
323     }
324     return isAllowed;
325 }
326
327 template<typename Predicate, typename... Args>
328 bool ContentSecurityPolicy::allPoliciesAllow(ViolatedDirectiveCallback&& callback, Predicate&& predicate, Args&&... args) const
329 {
330     bool isAllowed = true;
331     for (auto& policy : m_policies) {
332         if (const ContentSecurityPolicyDirective* violatedDirective = (policy.get()->*predicate)(std::forward<Args>(args)...)) {
333             if (!violatedDirective->directiveList().isReportOnly())
334                 isAllowed = false;
335             callback(*violatedDirective);
336         }
337     }
338     return isAllowed;
339 }
340
341 template<typename Predicate>
342 ContentSecurityPolicy::HashInEnforcedAndReportOnlyPoliciesPair ContentSecurityPolicy::findHashOfContentInPolicies(Predicate&& predicate, const String& content, OptionSet<ContentSecurityPolicyHashAlgorithm> algorithms) const
343 {
344     if (algorithms.isEmpty() || content.isEmpty())
345         return { false, false };
346
347     // FIXME: We should compute the document encoding once and cache it instead of computing it on each invocation.
348     TextEncoding documentEncoding;
349     if (is<Document>(m_scriptExecutionContext))
350         documentEncoding = downcast<Document>(*m_scriptExecutionContext).textEncoding();
351     const TextEncoding& encodingToUse = documentEncoding.isValid() ? documentEncoding : UTF8Encoding();
352
353     // FIXME: Compute the digest with respect to the raw bytes received from the page.
354     // See <https://bugs.webkit.org/show_bug.cgi?id=155184>.
355     auto encodedContent = encodingToUse.encode(content, UnencodableHandling::Entities);
356     bool foundHashInEnforcedPolicies = false;
357     bool foundHashInReportOnlyPolicies = false;
358     for (auto algorithm : algorithms) {
359         ContentSecurityPolicyHash hash = cryptographicDigestForBytes(algorithm, encodedContent.data(), encodedContent.size());
360         if (!foundHashInEnforcedPolicies && allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, std::forward<Predicate>(predicate), hash))
361             foundHashInEnforcedPolicies = true;
362         if (!foundHashInReportOnlyPolicies && allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::ReportOnly, std::forward<Predicate>(predicate), hash))
363             foundHashInReportOnlyPolicies = true;
364         if (foundHashInEnforcedPolicies && foundHashInReportOnlyPolicies)
365             break;
366     }
367     return { foundHashInEnforcedPolicies, foundHashInReportOnlyPolicies };
368 }
369
370 bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy) const
371 {
372     if (overrideContentSecurityPolicy)
373         return true;
374     bool didNotifyInspector = false;
375     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
376         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script", "its hash, its nonce, or 'unsafe-inline'");
377         reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
378         if (!didNotifyInspector && violatedDirective.directiveList().isReportOnly()) {
379             reportBlockedScriptExecutionToInspector(violatedDirective.text());
380             didNotifyInspector = true;
381         }
382     };
383     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript);
384 }
385
386 bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy) const
387 {
388     if (overrideContentSecurityPolicy)
389         return true;
390     bool didNotifyInspector = false;
391     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
392         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script for an inline event handler", "'unsafe-inline'");
393         reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
394         if (!didNotifyInspector && !violatedDirective.directiveList().isReportOnly()) {
395             reportBlockedScriptExecutionToInspector(violatedDirective.text());
396             didNotifyInspector = true;
397         }
398     };
399     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript);
400 }
401
402 bool ContentSecurityPolicy::allowScriptWithNonce(const String& nonce, bool overrideContentSecurityPolicy) const
403 {
404     if (overrideContentSecurityPolicy)
405         return true;
406     String strippedNonce = stripLeadingAndTrailingHTMLSpaces(nonce);
407     if (strippedNonce.isEmpty())
408         return false;
409     // FIXME: We need to report violations in report-only policies. See <https://bugs.webkit.org/show_bug.cgi?id=159830>.
410     return allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, &ContentSecurityPolicyDirectiveList::violatedDirectiveForScriptNonce, strippedNonce);
411 }
412
413 bool ContentSecurityPolicy::allowStyleWithNonce(const String& nonce, bool overrideContentSecurityPolicy) const
414 {
415     if (overrideContentSecurityPolicy)
416         return true;
417     String strippedNonce = stripLeadingAndTrailingHTMLSpaces(nonce);
418     if (strippedNonce.isEmpty())
419         return false;
420     // FIXME: We need to report violations in report-only policies. See <https://bugs.webkit.org/show_bug.cgi?id=159830>.
421     return allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, &ContentSecurityPolicyDirectiveList::violatedDirectiveForStyleNonce, strippedNonce);
422 }
423
424 bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& scriptContent, bool overrideContentSecurityPolicy) const
425 {
426     if (overrideContentSecurityPolicy)
427         return true;
428     bool didNotifyInspector = false;
429     bool foundHashInEnforcedPolicies;
430     bool foundHashInReportOnlyPolicies;
431     std::tie(foundHashInEnforcedPolicies, foundHashInReportOnlyPolicies) = findHashOfContentInPolicies(&ContentSecurityPolicyDirectiveList::violatedDirectiveForScriptHash, scriptContent, m_hashAlgorithmsForInlineScripts);
432     if (foundHashInEnforcedPolicies && foundHashInReportOnlyPolicies)
433         return true;
434     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
435         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script", "its hash, its nonce, or 'unsafe-inline'");
436         reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
437         if (!didNotifyInspector && !violatedDirective.directiveList().isReportOnly()) {
438             reportBlockedScriptExecutionToInspector(violatedDirective.text());
439             didNotifyInspector = true;
440         }
441     };
442     // FIXME: We should not report that the inline script violated a policy when its hash matched a source
443     // expression in the policy and the page has more than one policy. See <https://bugs.webkit.org/show_bug.cgi?id=159832>.
444     if (!foundHashInReportOnlyPolicies)
445         allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::ReportOnly, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript);
446     return foundHashInEnforcedPolicies || allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript);
447 }
448
449 bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& styleContent, bool overrideContentSecurityPolicy) const
450 {
451     if (overrideContentSecurityPolicy)
452         return true;
453     if (m_overrideInlineStyleAllowed)
454         return true;
455     bool foundHashInEnforcedPolicies;
456     bool foundHashInReportOnlyPolicies;
457     std::tie(foundHashInEnforcedPolicies, foundHashInReportOnlyPolicies) = findHashOfContentInPolicies(&ContentSecurityPolicyDirectiveList::violatedDirectiveForStyleHash, styleContent, m_hashAlgorithmsForInlineStylesheets);
458     if (foundHashInEnforcedPolicies && foundHashInReportOnlyPolicies)
459         return true;
460     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
461         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::styleSrc, violatedDirective, URL(), "Refused to apply a stylesheet", "its hash, its nonce, or 'unsafe-inline'");
462         reportViolation(ContentSecurityPolicyDirectiveNames::styleSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
463     };
464     // FIXME: We should not report that the inline stylesheet violated a policy when its hash matched a source
465     // expression in the policy and the page has more than one policy. See <https://bugs.webkit.org/show_bug.cgi?id=159832>.
466     if (!foundHashInReportOnlyPolicies)
467         allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::ReportOnly, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineStyle);
468     return foundHashInEnforcedPolicies || allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineStyle);
469 }
470
471 bool ContentSecurityPolicy::allowEval(JSC::ExecState* state, bool overrideContentSecurityPolicy) const
472 {
473     if (overrideContentSecurityPolicy)
474         return true;
475     bool didNotifyInspector = false;
476     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
477         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script", "'unsafe-eval'");
478         reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, state);
479         if (!didNotifyInspector && !violatedDirective.directiveList().isReportOnly()) {
480             reportBlockedScriptExecutionToInspector(violatedDirective.text());
481             didNotifyInspector = true;
482         }
483     };
484     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeEval);
485 }
486
487 bool ContentSecurityPolicy::allowFrameAncestors(const Frame& frame, const URL& url, bool overrideContentSecurityPolicy) const
488 {
489     if (overrideContentSecurityPolicy)
490         return true;
491     Frame& topFrame = frame.tree().top();
492     if (&frame == &topFrame)
493         return true;
494     String sourceURL;
495     TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
496     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
497         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::frameAncestors, violatedDirective, url, "Refused to load");
498         reportViolation(ContentSecurityPolicyDirectiveNames::frameAncestors, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
499     };
500     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForFrameAncestor, frame);
501 }
502
503 bool ContentSecurityPolicy::overridesXFrameOptions() const
504 {
505     // If a resource is delivered with an policy that includes a directive named frame-ancestors and whose disposition
506     // is "enforce", then the X-Frame-Options header MUST be ignored.
507     // https://www.w3.org/TR/CSP3/#frame-ancestors-and-frame-options
508     for (auto& policy : m_policies) {
509         if (!policy->isReportOnly() && policy->hasFrameAncestorsDirective())
510             return true;
511     }
512     return false;
513 }
514
515 bool ContentSecurityPolicy::allowFrameAncestors(const Vector<RefPtr<SecurityOrigin>>& ancestorOrigins, const URL& url, bool overrideContentSecurityPolicy) const
516 {
517     if (overrideContentSecurityPolicy)
518         return true;
519     bool isTopLevelFrame = ancestorOrigins.isEmpty();
520     if (isTopLevelFrame)
521         return true;
522     String sourceURL;
523     TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
524     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
525         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::frameAncestors, violatedDirective, url, "Refused to load");
526         reportViolation(ContentSecurityPolicyDirectiveNames::frameAncestors, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
527     };
528     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForFrameAncestorOrigins, ancestorOrigins);
529 }
530
531 bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const URL& url, bool overrideContentSecurityPolicy) const
532 {
533     if (overrideContentSecurityPolicy)
534         return true;
535     String sourceURL;
536     TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
537     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
538         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::pluginTypes, violatedDirective, url, "Refused to load", "its MIME type");
539         reportViolation(ContentSecurityPolicyDirectiveNames::pluginTypes, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
540     };
541     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForPluginType, type, typeAttribute);
542 }
543
544 bool ContentSecurityPolicy::allowObjectFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
545 {
546     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
547         return true;
548     // 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),
549     // "If plugin content is loaded without an associated URL (perhaps an object element lacks a data attribute, but loads some default plugin based
550     // on the specified type), it MUST be blocked if object-src's value is 'none', but will otherwise be allowed".
551     String sourceURL;
552     TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
553     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
554         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::objectSrc, violatedDirective, url, "Refused to load");
555         reportViolation(ContentSecurityPolicyDirectiveNames::objectSrc, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
556     };
557     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForObjectSource, url, redirectResponseReceived == RedirectResponseReceived::Yes, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone::Yes);
558 }
559
560 bool ContentSecurityPolicy::allowChildFrameFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
561 {
562     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
563         return true;
564     String sourceURL;
565     TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
566     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
567         const char* effectiveViolatedDirective = violatedDirective.name() == ContentSecurityPolicyDirectiveNames::frameSrc ? ContentSecurityPolicyDirectiveNames::frameSrc : ContentSecurityPolicyDirectiveNames::childSrc;
568         String consoleMessage = consoleMessageForViolation(effectiveViolatedDirective, violatedDirective, url, "Refused to load");
569         reportViolation(effectiveViolatedDirective, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
570     };
571     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForFrame, url, redirectResponseReceived == RedirectResponseReceived::Yes);
572 }
573
574 bool ContentSecurityPolicy::allowResourceFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived, const char* name, ResourcePredicate resourcePredicate) const
575 {
576     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
577         return true;
578     String sourceURL;
579     TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
580     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
581         String consoleMessage = consoleMessageForViolation(name, violatedDirective, url, "Refused to load");
582         reportViolation(name, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
583     };
584     return allPoliciesAllow(WTFMove(handleViolatedDirective), resourcePredicate, url, redirectResponseReceived == RedirectResponseReceived::Yes);
585 }
586
587 bool ContentSecurityPolicy::allowChildContextFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
588 {
589     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::childSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForChildContext);
590 }
591
592 bool ContentSecurityPolicy::allowScriptFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
593 {
594     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::scriptSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForScript);
595 }
596
597 bool ContentSecurityPolicy::allowImageFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
598 {
599     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::imgSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForImage);
600 }
601
602 bool ContentSecurityPolicy::allowStyleFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
603 {
604     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::styleSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForStyle);
605 }
606
607 bool ContentSecurityPolicy::allowFontFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
608 {
609     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::fontSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForFont);
610 }
611
612 #if ENABLE(APPLICATION_MANIFEST)
613 bool ContentSecurityPolicy::allowManifestFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
614 {
615     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::manifestSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForManifest);
616 }
617 #endif // ENABLE(APPLICATION_MANIFEST)
618
619 bool ContentSecurityPolicy::allowMediaFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
620 {
621     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::mediaSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForMedia);
622 }
623
624 bool ContentSecurityPolicy::allowConnectToSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
625 {
626     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
627         return true;
628     String sourceURL;
629     TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
630     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
631         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::connectSrc, violatedDirective, url, "Refused to connect to");
632         reportViolation(ContentSecurityPolicyDirectiveNames::connectSrc, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
633     };
634     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForConnectSource, url, redirectResponseReceived == RedirectResponseReceived::Yes);
635 }
636
637 bool ContentSecurityPolicy::allowFormAction(const URL& url, RedirectResponseReceived redirectResponseReceived) const
638 {
639     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::formAction, &ContentSecurityPolicyDirectiveList::violatedDirectiveForFormAction);
640 }
641
642 bool ContentSecurityPolicy::allowBaseURI(const URL& url, bool overrideContentSecurityPolicy) const
643 {
644     if (overrideContentSecurityPolicy)
645         return true;
646     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
647         return true;
648     String sourceURL;
649     TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
650     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
651         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::baseURI, violatedDirective, url, "Refused to change the document base URL to");
652         reportViolation(ContentSecurityPolicyDirectiveNames::baseURI, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
653     };
654     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForBaseURI, url);
655 }
656
657 String ContentSecurityPolicy::deprecatedURLForReporting(const URL& url) const
658 {
659     if (!url.isValid())
660         return { };
661     if (!url.isHierarchical() || url.protocolIs("file"))
662         return url.protocol().toString();
663     return static_cast<SecurityOriginData>(*m_selfSource).securityOrigin()->canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url)->toString();
664 }
665
666 void ContentSecurityPolicy::reportViolation(const String& violatedDirective, const ContentSecurityPolicyDirective& effectiveViolatedDirective, const URL& blockedURL, const String& consoleMessage, JSC::ExecState* state) const
667 {
668     // FIXME: Extract source file and source position from JSC::ExecState.
669     return reportViolation(violatedDirective, effectiveViolatedDirective.text(), effectiveViolatedDirective.directiveList(), blockedURL, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber::beforeFirst()), state);
670 }
671
672 void ContentSecurityPolicy::reportViolation(const String& effectiveViolatedDirective, const String& violatedDirective, const ContentSecurityPolicyDirectiveList& violatedDirectiveList, const URL& blockedURL, const String& consoleMessage, JSC::ExecState* state) const
673 {
674     // FIXME: Extract source file and source position from JSC::ExecState.
675     return reportViolation(effectiveViolatedDirective, violatedDirective, violatedDirectiveList, blockedURL, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber::beforeFirst()), state);
676 }
677
678 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
679 {
680     return reportViolation(effectiveViolatedDirective, violatedDirective.text(), violatedDirective.directiveList(), blockedURL, consoleMessage, sourceURL, sourcePosition, state);
681 }
682
683 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
684 {
685     logToConsole(consoleMessage, sourceURL, sourcePosition.m_line, sourcePosition.m_column, state);
686
687     if (!m_isReportingEnabled)
688         return;
689
690     // FIXME: Support sending reports from worker.
691     CSPInfo info;
692     info.documentURI = blockedURL;
693     if (m_client)
694         m_client->willSendCSPViolationReport(info);
695     else {
696         if (!is<Document>(m_scriptExecutionContext))
697             return;
698
699         auto& document = downcast<Document>(*m_scriptExecutionContext);
700         auto* frame = document.frame();
701         if (!frame)
702             return;
703
704         info.documentURI = document.url().strippedForUseAsReferrer();
705
706         auto stack = createScriptCallStack(JSExecState::currentState(), 2);
707         auto* callFrame = stack->firstNonNativeCallFrame();
708         if (callFrame && callFrame->lineNumber()) {
709             info.sourceFile = deprecatedURLForReporting(URL { URL { }, callFrame->sourceURL() });
710             info.lineNumber = callFrame->lineNumber();
711             info.columnNumber = callFrame->columnNumber();
712         }
713     }
714     ASSERT(m_client || is<Document>(m_scriptExecutionContext));
715
716     String blockedURI = deprecatedURLForReporting(blockedURL);
717     // FIXME: Is it policy to not use the status code for HTTPS, or is that a bug?
718     unsigned short httpStatusCode = m_selfSourceProtocol == "http" ? m_httpStatusCode : 0;
719
720     // 1. Dispatch violation event.
721     SecurityPolicyViolationEvent::Init violationEventInit;
722     violationEventInit.documentURI = info.documentURI;
723     violationEventInit.referrer = m_referrer;
724     violationEventInit.blockedURI = blockedURI;
725     violationEventInit.violatedDirective = violatedDirective;
726     violationEventInit.effectiveDirective = effectiveViolatedDirective;
727     violationEventInit.originalPolicy = violatedDirectiveList.header();
728     violationEventInit.sourceFile = info.sourceFile;
729     violationEventInit.statusCode = httpStatusCode;
730     violationEventInit.lineNumber =  info.lineNumber;
731     violationEventInit.columnNumber = info.columnNumber;
732     if (m_client)
733         m_client->enqueueSecurityPolicyViolationEvent(WTFMove(violationEventInit));
734     else
735         downcast<Document>(*m_scriptExecutionContext).enqueueSecurityPolicyViolationEvent(WTFMove(violationEventInit));
736
737     // 2. Send violation report (if applicable).
738     auto& reportURIs = violatedDirectiveList.reportURIs();
739     if (reportURIs.isEmpty())
740         return;
741
742     // We need to be careful here when deciding what information to send to the
743     // report-uri. Currently, we send only the current document's URL and the
744     // directive that was violated. The document's URL is safe to send because
745     // it's the document itself that's requesting that it be sent. You could
746     // make an argument that we shouldn't send HTTPS document URLs to HTTP
747     // report-uris (for the same reasons that we suppress the Referer in that
748     // case), but the Referer is sent implicitly whereas this request is only
749     // sent explicitly. As for which directive was violated, that's pretty
750     // harmless information.
751
752     auto cspReport = JSON::Object::create();
753     cspReport->setString("document-uri"_s, info.documentURI);
754     cspReport->setString("referrer"_s, m_referrer);
755     cspReport->setString("violated-directive"_s, violatedDirective);
756     cspReport->setString("effective-directive"_s, effectiveViolatedDirective);
757     cspReport->setString("original-policy"_s, violatedDirectiveList.header());
758     cspReport->setString("blocked-uri"_s, blockedURI);
759     cspReport->setInteger("status-code"_s, httpStatusCode);
760     if (!info.sourceFile.isNull()) {
761         cspReport->setString("source-file"_s, info.sourceFile);
762         cspReport->setInteger("line-number"_s, info.lineNumber);
763         cspReport->setInteger("column-number"_s, info.columnNumber);
764     }
765
766     auto reportObject = JSON::Object::create();
767     reportObject->setObject("csp-report"_s, WTFMove(cspReport));
768
769     auto report = FormData::create(reportObject->toJSONString().utf8());
770
771     if (m_client) {
772         for (const auto& url : reportURIs)
773             m_client->sendCSPViolationReport(URL { m_protectedURL, url }, report.copyRef());
774     } else {
775         auto& document = downcast<Document>(*m_scriptExecutionContext);
776         for (const auto& url : reportURIs)
777             PingLoader::sendViolationReport(*document.frame(), URL { m_protectedURL, url }, report.copyRef(), ViolationReportType::ContentSecurityPolicy);
778     }
779 }
780
781 void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) const
782 {
783     String message;
784     if (equalLettersIgnoringASCIICase(name, "allow"))
785         message = "The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect."_s;
786     else if (equalLettersIgnoringASCIICase(name, "options"))
787         message = "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."_s;
788     else if (equalLettersIgnoringASCIICase(name, "policy-uri"))
789         message = "The 'policy-uri' directive has been removed from the specification. Please specify a complete policy via the Content-Security-Policy header."_s;
790     else
791         message = makeString("Unrecognized Content-Security-Policy directive '", name, "'.\n"); // FIXME: Why does this include a newline?
792
793     logToConsole(message);
794 }
795
796 void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const
797 {
798     logToConsole("The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?");
799 }
800
801 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const
802 {
803     logToConsole(makeString("Ignoring duplicate Content-Security-Policy directive '", name, "'.\n"));
804 }
805
806 void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) const
807 {
808     String message;
809     if (pluginType.isNull())
810         message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n";
811     else
812         message = makeString("Invalid plugin type in 'plugin-types' Content Security Policy directive: '", pluginType, "'.\n");
813     logToConsole(message);
814 }
815
816 void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags) const
817 {
818     logToConsole("Error while parsing the 'sandbox' Content Security Policy directive: " + invalidFlags);
819 }
820
821 void ContentSecurityPolicy::reportInvalidDirectiveInReportOnlyMode(const String& directiveName) const
822 {
823     logToConsole("The Content Security Policy directive '" + directiveName + "' is ignored when delivered in a report-only policy.");
824 }
825
826 void ContentSecurityPolicy::reportInvalidDirectiveInHTTPEquivMeta(const String& directiveName) const
827 {
828     logToConsole("The Content Security Policy directive '" + directiveName + "' is ignored when delivered via an HTML meta element.");
829 }
830
831 void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) const
832 {
833     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.");
834     logToConsole(message);
835 }
836
837 void ContentSecurityPolicy::reportInvalidPathCharacter(const String& directiveName, const String& value, const char invalidChar) const
838 {
839     ASSERT(invalidChar == '#' || invalidChar == '?');
840
841     String ignoring;
842     if (invalidChar == '?')
843         ignoring = "The query component, including the '?', will be ignored.";
844     else
845         ignoring = "The fragment identifier, including the '#', will be ignored.";
846
847     String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains a source with an invalid path: '", value, "'. ", ignoring);
848     logToConsole(message);
849 }
850
851 void ContentSecurityPolicy::reportInvalidSourceExpression(const String& directiveName, const String& source) const
852 {
853     String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains an invalid source: '", source, "'. It will be ignored.");
854     if (equalLettersIgnoringASCIICase(source, "'none'"))
855         message = makeString(message, " Note that 'none' has no effect unless it is the only expression in the source list.");
856     logToConsole(message);
857 }
858
859 void ContentSecurityPolicy::reportMissingReportURI(const String& policy) const
860 {
861     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.");
862 }
863
864 void ContentSecurityPolicy::logToConsole(const String& message, const String& contextURL, const WTF::OrdinalNumber& contextLine, const WTF::OrdinalNumber& contextColumn, JSC::ExecState* state) const
865 {
866     if (!m_isReportingEnabled)
867         return;
868
869     if (m_client)
870         m_client->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, 0);
871     else if (m_scriptExecutionContext)
872         m_scriptExecutionContext->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, contextURL, contextLine.oneBasedInt(), contextColumn.oneBasedInt(), state);
873 }
874
875 void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String& directiveText) const
876 {
877     if (m_scriptExecutionContext)
878         InspectorInstrumentation::scriptExecutionBlockedByCSP(m_scriptExecutionContext, directiveText);
879 }
880
881 void ContentSecurityPolicy::upgradeInsecureRequestIfNeeded(ResourceRequest& request, InsecureRequestType requestType) const
882 {
883     URL url = request.url();
884     upgradeInsecureRequestIfNeeded(url, requestType);
885     request.setURL(url);
886 }
887
888 void ContentSecurityPolicy::upgradeInsecureRequestIfNeeded(URL& url, InsecureRequestType requestType) const
889 {
890     if (!url.protocolIs("http") && !url.protocolIs("ws"))
891         return;
892
893     bool upgradeRequest = m_insecureNavigationRequestsToUpgrade.contains(SecurityOriginData::fromURL(url));
894     if (requestType == InsecureRequestType::Load || requestType == InsecureRequestType::FormSubmission)
895         upgradeRequest |= m_upgradeInsecureRequests;
896
897     if (!upgradeRequest)
898         return;
899
900     if (url.protocolIs("http"))
901         url.setProtocol("https");
902     else {
903         ASSERT(url.protocolIs("ws"));
904         url.setProtocol("wss");
905     }
906
907     if (url.port() && url.port().value() == 80)
908         url.setPort(443);
909 }
910
911 void ContentSecurityPolicy::setUpgradeInsecureRequests(bool upgradeInsecureRequests)
912 {
913     m_upgradeInsecureRequests = upgradeInsecureRequests;
914     if (!m_upgradeInsecureRequests)
915         return;
916
917     if (!m_scriptExecutionContext)
918         return;
919
920     // Store the upgrade domain as an 'insecure' protocol so we can quickly identify
921     // origins we should upgrade.
922     URL upgradeURL = m_scriptExecutionContext->url();
923     if (upgradeURL.protocolIs("https"))
924         upgradeURL.setProtocol("http");
925     else if (upgradeURL.protocolIs("wss"))
926         upgradeURL.setProtocol("ws");
927     
928     m_insecureNavigationRequestsToUpgrade.add(SecurityOriginData::fromURL(upgradeURL));
929 }
930
931 void ContentSecurityPolicy::inheritInsecureNavigationRequestsToUpgradeFromOpener(const ContentSecurityPolicy& other)
932 {
933     m_insecureNavigationRequestsToUpgrade.add(other.m_insecureNavigationRequestsToUpgrade.begin(), other.m_insecureNavigationRequestsToUpgrade.end());
934 }
935
936 HashSet<SecurityOriginData> ContentSecurityPolicy::takeNavigationRequestsToUpgrade()
937 {
938     return WTFMove(m_insecureNavigationRequestsToUpgrade);
939 }
940
941 void ContentSecurityPolicy::setInsecureNavigationRequestsToUpgrade(HashSet<SecurityOriginData>&& insecureNavigationRequests)
942 {
943     m_insecureNavigationRequestsToUpgrade = WTFMove(insecureNavigationRequests);
944 }
945
946 }