REGRESSION (r197724): <object>/<embed> with no URL does not match source *
[WebKit-https.git] / Source / WebCore / page / csp / ContentSecurityPolicy.cpp
1 /*
2  * Copyright (C) 2011 Google, Inc. All rights reserved.
3  * Copyright (C) 2013, 2016 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 "ContentSecurityPolicyDirective.h"
31 #include "ContentSecurityPolicyDirectiveList.h"
32 #include "ContentSecurityPolicyDirectiveNames.h"
33 #include "ContentSecurityPolicyHash.h"
34 #include "ContentSecurityPolicySource.h"
35 #include "ContentSecurityPolicySourceList.h"
36 #include "CryptoDigest.h"
37 #include "DOMStringList.h"
38 #include "Document.h"
39 #include "DocumentLoader.h"
40 #include "FormData.h"
41 #include "FormDataList.h"
42 #include "Frame.h"
43 #include "HTMLParserIdioms.h"
44 #include "InspectorInstrumentation.h"
45 #include "JSDOMWindowShell.h"
46 #include "JSMainThreadExecState.h"
47 #include "ParsingUtilities.h"
48 #include "PingLoader.h"
49 #include "RuntimeEnabledFeatures.h"
50 #include "SchemeRegistry.h"
51 #include "SecurityOrigin.h"
52 #include "SecurityPolicyViolationEvent.h"
53 #include "TextEncoding.h"
54 #include <inspector/InspectorValues.h>
55 #include <inspector/ScriptCallStack.h>
56 #include <inspector/ScriptCallStackFactory.h>
57 #include <wtf/TemporaryChange.h>
58 #include <wtf/text/StringBuilder.h>
59 #include <wtf/text/TextPosition.h>
60
61 using namespace Inspector;
62
63 namespace WebCore {
64
65 static String consoleMessageForViolation(const char* effectiveViolatedDirective, const ContentSecurityPolicyDirective& violatedDirective, const URL& blockedURL, const char* prefix, const char* subject = "it")
66 {
67     StringBuilder result;
68     if (violatedDirective.directiveList().isReportOnly())
69         result.appendLiteral("[Report Only] ");
70     result.append(prefix);
71     if (!blockedURL.isEmpty()) {
72         result.append(' ');
73         result.append(blockedURL.stringCenterEllipsizedToLength());
74     }
75     result.appendLiteral(" because ");
76     result.append(subject);
77     if (violatedDirective.isDefaultSrc()) {
78         result.appendLiteral(" appears in neither the ");
79         result.append(effectiveViolatedDirective);
80         result.appendLiteral(" directive nor the default-src directive of the Content Security Policy.");
81     } else {
82         result.appendLiteral(" does not appear in the ");
83         result.append(effectiveViolatedDirective);
84         result.appendLiteral(" directive of the Content Security Policy.");
85     }
86     return result.toString();
87 }
88
89 ContentSecurityPolicy::ContentSecurityPolicy(ScriptExecutionContext& scriptExecutionContext)
90     : m_scriptExecutionContext(&scriptExecutionContext)
91     , m_sandboxFlags(SandboxNone)
92 {
93     ASSERT(scriptExecutionContext.securityOrigin());
94     auto& securityOrigin = *scriptExecutionContext.securityOrigin();
95     m_selfSourceProtocol = securityOrigin.protocol();
96     m_selfSource = std::make_unique<ContentSecurityPolicySource>(*this, m_selfSourceProtocol, securityOrigin.host(), securityOrigin.port(), emptyString(), false, false);
97 }
98
99 ContentSecurityPolicy::ContentSecurityPolicy(const SecurityOrigin& securityOrigin, const Frame* frame)
100     : m_frame(frame)
101     , m_sandboxFlags(SandboxNone)
102 {
103     m_selfSourceProtocol = securityOrigin.protocol();
104     m_selfSource = std::make_unique<ContentSecurityPolicySource>(*this, m_selfSourceProtocol, securityOrigin.host(), securityOrigin.port(), emptyString(), false, false);
105 }
106
107 ContentSecurityPolicy::~ContentSecurityPolicy()
108 {
109 }
110
111 void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other) 
112 {
113     ASSERT(m_policies.isEmpty());
114     for (auto& policy : other->m_policies)
115         didReceiveHeader(policy->header(), policy->headerType(), ContentSecurityPolicy::PolicyFrom::Inherited);
116 }
117
118 void ContentSecurityPolicy::didCreateWindowShell(JSDOMWindowShell& windowShell) const
119 {
120     JSDOMWindow* window = windowShell.window();
121     ASSERT(window);
122     ASSERT(window->scriptExecutionContext());
123     ASSERT(window->scriptExecutionContext()->contentSecurityPolicy() == this);
124     if (!windowShell.world().isNormal()) {
125         window->setEvalEnabled(true);
126         return;
127     }
128     window->setEvalEnabled(m_lastPolicyEvalDisabledErrorMessage.isNull(), m_lastPolicyEvalDisabledErrorMessage);
129 }
130
131 ContentSecurityPolicyResponseHeaders ContentSecurityPolicy::responseHeaders() const
132 {
133     ContentSecurityPolicyResponseHeaders result;
134     result.m_headers.reserveInitialCapacity(m_policies.size());
135     for (auto& policy : m_policies)
136         result.m_headers.uncheckedAppend({ policy->header(), policy->headerType() });
137     return result;
138 }
139
140 void ContentSecurityPolicy::didReceiveHeaders(const ContentSecurityPolicyResponseHeaders& headers, ReportParsingErrors reportParsingErrors)
141 {
142     TemporaryChange<bool> isReportingEnabled(m_isReportingEnabled, reportParsingErrors == ReportParsingErrors::Yes);
143     for (auto& header : headers.m_headers)
144         didReceiveHeader(header.first, header.second, ContentSecurityPolicy::PolicyFrom::HTTPHeader);
145 }
146
147 void ContentSecurityPolicy::didReceiveHeader(const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicy::PolicyFrom policyFrom)
148 {
149     // RFC2616, section 4.2 specifies that headers appearing multiple times can
150     // be combined with a comma. Walk the header string, and parse each comma
151     // separated chunk as a separate header.
152     auto characters = StringView(header).upconvertedCharacters();
153     const UChar* begin = characters;
154     const UChar* position = begin;
155     const UChar* end = begin + header.length();
156     while (position < end) {
157         skipUntil<UChar>(position, end, ',');
158
159         // header1,header2 OR header1
160         //        ^                  ^
161         std::unique_ptr<ContentSecurityPolicyDirectiveList> policy = ContentSecurityPolicyDirectiveList::create(*this, String(begin, position - begin), type, policyFrom);
162         const ContentSecurityPolicyDirective* violatedDirective = policy->violatedDirectiveForUnsafeEval();
163         if (violatedDirective && !violatedDirective->directiveList().isReportOnly())
164             m_lastPolicyEvalDisabledErrorMessage = policy->evalDisabledErrorMessage();
165
166         m_policies.append(policy.release());
167
168         // Skip the comma, and begin the next header from the current position.
169         ASSERT(position == end || *position == ',');
170         skipExactly<UChar>(position, end, ',');
171         begin = position;
172     }
173
174     if (m_scriptExecutionContext)
175         applyPolicyToScriptExecutionContext();
176 }
177
178 void ContentSecurityPolicy::applyPolicyToScriptExecutionContext()
179 {
180     ASSERT(m_scriptExecutionContext);
181     if (!m_lastPolicyEvalDisabledErrorMessage.isNull())
182         m_scriptExecutionContext->disableEval(m_lastPolicyEvalDisabledErrorMessage);
183     if (m_sandboxFlags != SandboxNone && is<Document>(m_scriptExecutionContext))
184         m_scriptExecutionContext->enforceSandboxFlags(m_sandboxFlags);
185 }
186
187 void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value)
188 {
189     m_overrideInlineStyleAllowed = value;
190 }
191
192 bool ContentSecurityPolicy::urlMatchesSelf(const URL& url) const
193 {
194     return m_selfSource->matches(url);
195 }
196
197 bool ContentSecurityPolicy::protocolMatchesSelf(const URL& url) const
198 {
199     if (equalLettersIgnoringASCIICase(m_selfSourceProtocol, "http"))
200         return url.protocolIsInHTTPFamily();
201     return equalIgnoringASCIICase(url.protocol(), m_selfSourceProtocol);
202 }
203
204 static CryptoDigest::Algorithm toCryptoDigestAlgorithm(ContentSecurityPolicyHashAlgorithm algorithm)
205 {
206     switch (algorithm) {
207     case ContentSecurityPolicyHashAlgorithm::SHA_256:
208         return CryptoDigest::Algorithm::SHA_256;
209     case ContentSecurityPolicyHashAlgorithm::SHA_384:
210         return CryptoDigest::Algorithm::SHA_384;
211     case ContentSecurityPolicyHashAlgorithm::SHA_512:
212         return CryptoDigest::Algorithm::SHA_512;
213     }
214     ASSERT_NOT_REACHED();
215     return CryptoDigest::Algorithm::SHA_512;
216 }
217
218 template<typename Predicate>
219 bool ContentSecurityPolicy::foundHashOfContentInAllPolicies(Predicate&& predicate, const String& content, OptionSet<ContentSecurityPolicyHashAlgorithm> algorithms) const
220 {
221     if (algorithms.isEmpty() || content.isEmpty())
222         return false;
223
224     // FIXME: We should compute the document encoding once and cache it instead of computing it on each invocation.
225     TextEncoding documentEncoding;
226     if (is<Document>(m_scriptExecutionContext))
227         documentEncoding = downcast<Document>(*m_scriptExecutionContext).textEncoding();
228     const TextEncoding& encodingToUse = documentEncoding.isValid() ? documentEncoding : UTF8Encoding();
229
230     // FIXME: Compute the digest with respect to the raw bytes received from the page.
231     // See <https://bugs.webkit.org/show_bug.cgi?id=155184>.
232     CString contentCString = encodingToUse.encode(content, EntitiesForUnencodables);
233     for (auto algorithm : algorithms) {
234         auto cryptoDigest = CryptoDigest::create(toCryptoDigestAlgorithm(algorithm));
235         cryptoDigest->addBytes(contentCString.data(), contentCString.length());
236         Vector<uint8_t> digest = cryptoDigest->computeHash();
237         if (!violatedDirectiveInAnyPolicy(std::forward<Predicate>(predicate), std::make_pair(algorithm, digest)))
238             return true;
239     }
240     return false;
241 }
242
243 bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy) const
244 {
245     if (overrideContentSecurityPolicy)
246         return true;
247     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript);
248     if (!violatedDirective)
249         return true;
250     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, *violatedDirective, URL(), "Refused to execute a script", "its hash, its nonce, or 'unsafe-inline'");
251     reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, *violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
252     bool isReportOnly = violatedDirective->directiveList().isReportOnly();
253     if (!isReportOnly)
254         reportBlockedScriptExecutionToInspector(violatedDirective->text());
255     return isReportOnly;
256 }
257
258 bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy) const
259 {
260     if (overrideContentSecurityPolicy)
261         return true;
262     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript);
263     if (!violatedDirective)
264         return true;
265     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, *violatedDirective, URL(), "Refused to execute a script for an inline event handler", "'unsafe-inline'");
266     reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, *violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
267     bool isReportOnly = violatedDirective->directiveList().isReportOnly();
268     if (!isReportOnly)
269         reportBlockedScriptExecutionToInspector(violatedDirective->text());
270     return isReportOnly;
271 }
272
273 bool ContentSecurityPolicy::allowScriptWithNonce(const String& nonce, bool overrideContentSecurityPolicy) const
274 {
275     if (overrideContentSecurityPolicy)
276         return true;
277     String strippedNonce = stripLeadingAndTrailingHTMLSpaces(nonce);
278     if (strippedNonce.isEmpty())
279         return false;
280     return !violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForScriptNonce, strippedNonce);
281 }
282
283 bool ContentSecurityPolicy::allowStyleWithNonce(const String& nonce, bool overrideContentSecurityPolicy) const
284 {
285     if (overrideContentSecurityPolicy)
286         return true;
287     String strippedNonce = stripLeadingAndTrailingHTMLSpaces(nonce);
288     if (strippedNonce.isEmpty())
289         return false;
290     return !violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForStyleNonce, strippedNonce);
291 }
292
293 bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& scriptContent, bool overrideContentSecurityPolicy) const
294 {
295     if (overrideContentSecurityPolicy)
296         return true;
297     if (foundHashOfContentInAllPolicies(&ContentSecurityPolicyDirectiveList::violatedDirectiveForScriptHash, scriptContent, m_hashAlgorithmsForInlineScripts))
298         return true;
299     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript);
300     if (!violatedDirective)
301         return true;
302     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, *violatedDirective, URL(), "Refused to execute a script", "its hash, its nonce, or 'unsafe-inline'");
303     reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, *violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
304     bool isReportOnly = violatedDirective->directiveList().isReportOnly();
305     if (!isReportOnly)
306         reportBlockedScriptExecutionToInspector(violatedDirective->text());
307     return isReportOnly;
308 }
309
310 bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& styleContent, bool overrideContentSecurityPolicy) const
311 {
312     if (overrideContentSecurityPolicy)
313         return true;
314     if (m_overrideInlineStyleAllowed)
315         return true;
316     if (foundHashOfContentInAllPolicies(&ContentSecurityPolicyDirectiveList::violatedDirectiveForStyleHash, styleContent, m_hashAlgorithmsForInlineStylesheets))
317         return true;
318     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineStyle);
319     if (!violatedDirective)
320         return true;
321     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::styleSrc, *violatedDirective, URL(), "Refused to apply a stylesheet", "its hash, its nonce, or 'unsafe-inline'");
322     reportViolation(ContentSecurityPolicyDirectiveNames::styleSrc, *violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
323     return violatedDirective->directiveList().isReportOnly();
324 }
325
326 bool ContentSecurityPolicy::allowEval(JSC::ExecState* state, bool overrideContentSecurityPolicy) const
327 {
328     if (overrideContentSecurityPolicy)
329         return true;
330     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeEval);
331     if (!violatedDirective)
332         return true;
333     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, *violatedDirective, URL(), "Refused to execute a script", "'unsafe-eval'");
334     reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, *violatedDirective, URL(), consoleMessage, state);
335     bool isReportOnly = violatedDirective->directiveList().isReportOnly();
336     if (!isReportOnly)
337         reportBlockedScriptExecutionToInspector(violatedDirective->text());
338     return isReportOnly;
339 }
340
341 bool ContentSecurityPolicy::allowFrameAncestors(const Frame& frame, const URL& url, bool overrideContentSecurityPolicy) const
342 {
343     if (overrideContentSecurityPolicy)
344         return true;
345     Frame& topFrame = frame.tree().top();
346     if (&frame == &topFrame)
347         return true;
348     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForFrameAncestor, frame);
349     if (!violatedDirective)
350         return true;
351     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::frameAncestors, *violatedDirective, url, "Refused to load");
352     reportViolation(ContentSecurityPolicyDirectiveNames::frameAncestors, *violatedDirective, url, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()));
353     return violatedDirective->directiveList().isReportOnly();
354 }
355
356 bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const URL& url, bool overrideContentSecurityPolicy) const
357 {
358     if (overrideContentSecurityPolicy)
359         return true;
360     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForPluginType, type, typeAttribute);
361     if (!violatedDirective)
362         return true;
363     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::pluginTypes, *violatedDirective, url, "Refused to load", "its MIME type");
364     reportViolation(ContentSecurityPolicyDirectiveNames::pluginTypes, *violatedDirective, url, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()));
365     return violatedDirective->directiveList().isReportOnly();
366 }
367
368 bool ContentSecurityPolicy::allowScriptFromSource(const URL& url, bool overrideContentSecurityPolicy) const
369 {
370     if (overrideContentSecurityPolicy)
371         return true;
372     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
373         return true;
374     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForScript, url);
375     if (!violatedDirective)
376         return true;
377     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, *violatedDirective, url, "Refused to load");
378     reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, *violatedDirective, url, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()));
379     return violatedDirective->directiveList().isReportOnly();
380 }
381
382 bool ContentSecurityPolicy::allowObjectFromSource(const URL& url, bool overrideContentSecurityPolicy) const
383 {
384     if (overrideContentSecurityPolicy)
385         return true;
386     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
387         return true;
388     // 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),
389     // "If plugin content is loaded without an associated URL (perhaps an object element lacks a data attribute, but loads some default plugin based
390     // on the specified type), it MUST be blocked if object-src's value is 'none', but will otherwise be allowed".
391     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForObjectSource, url, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone::Yes);
392     if (!violatedDirective)
393         return true;
394     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::objectSrc, *violatedDirective, url, "Refused to load");
395     reportViolation(ContentSecurityPolicyDirectiveNames::objectSrc, *violatedDirective, url, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()));
396     return violatedDirective->directiveList().isReportOnly();
397 }
398
399 bool ContentSecurityPolicy::allowChildFrameFromSource(const URL& url, bool overrideContentSecurityPolicy) const
400 {
401     if (overrideContentSecurityPolicy)
402         return true;
403     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
404         return true;
405     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForFrame, url);
406     if (!violatedDirective)
407         return true;
408     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::frameSrc, *violatedDirective, url, "Refused to load");
409     reportViolation(ContentSecurityPolicyDirectiveNames::frameSrc, *violatedDirective, url, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()));
410     return violatedDirective->directiveList().isReportOnly();
411 }
412
413 bool ContentSecurityPolicy::allowChildContextFromSource(const URL& url, bool overrideContentSecurityPolicy) const
414 {
415     if (overrideContentSecurityPolicy)
416         return true;
417     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
418         return true;
419     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForChildContext, url);
420     if (!violatedDirective)
421         return true;
422     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::childSrc, *violatedDirective, url, "Refused to load");
423     reportViolation(ContentSecurityPolicyDirectiveNames::childSrc, *violatedDirective, url, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()));
424     return violatedDirective->directiveList().isReportOnly();
425 }
426
427 bool ContentSecurityPolicy::allowImageFromSource(const URL& url, bool overrideContentSecurityPolicy) const
428 {
429     if (overrideContentSecurityPolicy)
430         return true;
431     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
432         return true;
433     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForImage, url);
434     if (!violatedDirective)
435         return true;
436     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::imgSrc, *violatedDirective, url, "Refused to load");
437     reportViolation(ContentSecurityPolicyDirectiveNames::imgSrc, *violatedDirective, url, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()));
438     return violatedDirective->directiveList().isReportOnly();
439 }
440
441 bool ContentSecurityPolicy::allowStyleFromSource(const URL& url, bool overrideContentSecurityPolicy) const
442 {
443     if (overrideContentSecurityPolicy)
444         return true;
445     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
446         return true;
447     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForStyle, url);
448     if (!violatedDirective)
449         return true;
450     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::styleSrc, *violatedDirective, url, "Refused to load");
451     reportViolation(ContentSecurityPolicyDirectiveNames::styleSrc, *violatedDirective, url, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()));
452     return violatedDirective->directiveList().isReportOnly();
453 }
454
455 bool ContentSecurityPolicy::allowFontFromSource(const URL& url, bool overrideContentSecurityPolicy) const
456 {
457     if (overrideContentSecurityPolicy)
458         return true;
459     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
460         return true;
461     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForFont, url);
462     if (!violatedDirective)
463         return true;
464     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::fontSrc, *violatedDirective, url, "Refused to load");
465     reportViolation(ContentSecurityPolicyDirectiveNames::fontSrc, *violatedDirective, url, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()));
466     return violatedDirective->directiveList().isReportOnly();
467 }
468
469 bool ContentSecurityPolicy::allowMediaFromSource(const URL& url, bool overrideContentSecurityPolicy) const
470 {
471     if (overrideContentSecurityPolicy)
472         return true;
473     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
474         return true;
475     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForMedia, url);
476     if (!violatedDirective)
477         return true;
478     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::mediaSrc, *violatedDirective, url, "Refused to load");
479     reportViolation(ContentSecurityPolicyDirectiveNames::mediaSrc, *violatedDirective, url, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()));
480     return violatedDirective->directiveList().isReportOnly();
481 }
482
483 bool ContentSecurityPolicy::allowConnectToSource(const URL& url, bool overrideContentSecurityPolicy) const
484 {
485     if (overrideContentSecurityPolicy)
486         return true;
487     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
488         return true;
489     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForConnectSource, url);
490     if (!violatedDirective)
491         return true;
492     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::connectSrc, *violatedDirective, url, "Refused to connect to");
493     reportViolation(ContentSecurityPolicyDirectiveNames::connectSrc, *violatedDirective, url, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()));
494     return violatedDirective->directiveList().isReportOnly();
495 }
496
497 bool ContentSecurityPolicy::allowFormAction(const URL& url, bool overrideContentSecurityPolicy) const
498 {
499     if (overrideContentSecurityPolicy)
500         return true;
501     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
502         return true;
503     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForFormAction, url);
504     if (!violatedDirective)
505         return true;
506     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::formAction, *violatedDirective, url, "Refused to load");
507     reportViolation(ContentSecurityPolicyDirectiveNames::formAction, *violatedDirective, url, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()));
508     return violatedDirective->directiveList().isReportOnly();
509 }
510
511 bool ContentSecurityPolicy::allowBaseURI(const URL& url, bool overrideContentSecurityPolicy) const
512 {
513     if (overrideContentSecurityPolicy)
514         return true;
515     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
516         return true;
517     const ContentSecurityPolicyDirective* violatedDirective = violatedDirectiveInAnyPolicy(&ContentSecurityPolicyDirectiveList::violatedDirectiveForBaseURI, url);
518     if (!violatedDirective)
519         return true;
520     String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::baseURI, *violatedDirective, url, "Refused to change the document base URL to");
521     reportViolation(ContentSecurityPolicyDirectiveNames::baseURI, *violatedDirective, url, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()));
522     return violatedDirective->directiveList().isReportOnly();
523 }
524
525 ContentSecurityPolicy::ReflectedXSSDisposition ContentSecurityPolicy::reflectedXSSDisposition() const
526 {
527     ReflectedXSSDisposition disposition = ReflectedXSSUnset;
528     for (auto& policy : m_policies) {
529         if (policy->reflectedXSSDisposition() > disposition)
530             disposition = std::max(disposition, policy->reflectedXSSDisposition());
531     }
532     return disposition;
533 }
534
535 static String stripURLForUseInReport(Document& document, const URL& url)
536 {
537     if (!url.isValid())
538         return String();
539     if (!url.isHierarchical() || url.protocolIs("file"))
540         return url.protocol();
541     return document.securityOrigin()->canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url).get().toString();
542 }
543
544 void ContentSecurityPolicy::reportViolation(const String& violatedDirective, const ContentSecurityPolicyDirective& effectiveViolatedDirective, const URL& blockedURL, const String& consoleMessage, JSC::ExecState* state) const
545 {
546     // FIXME: Extract source file and source position from JSC::ExecState.
547     return reportViolation(violatedDirective, effectiveViolatedDirective, blockedURL, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber::beforeFirst()), state);
548 }
549
550 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
551 {
552     logToConsole(consoleMessage, sourceURL, sourcePosition.m_line, state);
553
554     if (!m_isReportingEnabled)
555         return;
556
557     // FIXME: Support sending reports from worker.
558     if (!is<Document>(m_scriptExecutionContext) && !m_frame)
559         return;
560
561     ASSERT(!m_frame || effectiveViolatedDirective == ContentSecurityPolicyDirectiveNames::frameAncestors);
562
563     Document& document = is<Document>(m_scriptExecutionContext) ? downcast<Document>(*m_scriptExecutionContext) : *m_frame->document();
564     Frame* frame = document.frame();
565     ASSERT(!m_frame || m_frame == frame);
566     if (!frame)
567         return;
568
569     String documentURI;
570     String blockedURI;
571     if (is<Document>(m_scriptExecutionContext)) {
572         documentURI = document.url().strippedForUseAsReferrer();
573         blockedURI = stripURLForUseInReport(document, blockedURL);
574     } else {
575         // The URL of |document| may not have been initialized (say, when reporting a frame-ancestors violation).
576         // So, we use the URL of the blocked document for the protected document URL.
577         documentURI = blockedURL;
578         blockedURI = blockedURL;
579     }
580     String violatedDirectiveText = violatedDirective.text();
581     String originalPolicy = violatedDirective.directiveList().header();
582     String referrer = document.referrer();
583     ASSERT(document.loader());
584     unsigned short statusCode = document.url().protocolIs("http") && document.loader() ? document.loader()->response().httpStatusCode() : 0;
585
586     String sourceFile;
587     int lineNumber = 0;
588     int columnNumber = 0;
589     RefPtr<ScriptCallStack> stack = createScriptCallStack(JSMainThreadExecState::currentState(), 2);
590     const ScriptCallFrame* callFrame = stack->firstNonNativeCallFrame();
591     if (callFrame && callFrame->lineNumber()) {
592         sourceFile = stripURLForUseInReport(document, URL(URL(), callFrame->sourceURL()));
593         lineNumber = callFrame->lineNumber();
594         columnNumber = callFrame->columnNumber();
595     }
596
597     // 1. Dispatch violation event.
598     bool canBubble = false;
599     bool cancelable = false;
600     document.enqueueDocumentEvent(SecurityPolicyViolationEvent::create(eventNames().securitypolicyviolationEvent, canBubble, cancelable, documentURI, referrer, blockedURI, violatedDirectiveText, effectiveViolatedDirective, originalPolicy, sourceFile, statusCode, lineNumber, columnNumber));
601
602     // 2. Send violation report (if applicable).
603     const Vector<String>& reportURIs = violatedDirective.directiveList().reportURIs();
604     if (reportURIs.isEmpty())
605         return;
606
607     // We need to be careful here when deciding what information to send to the
608     // report-uri. Currently, we send only the current document's URL and the
609     // directive that was violated. The document's URL is safe to send because
610     // it's the document itself that's requesting that it be sent. You could
611     // make an argument that we shouldn't send HTTPS document URLs to HTTP
612     // report-uris (for the same reasons that we suppress the Referer in that
613     // case), but the Referer is sent implicitly whereas this request is only
614     // sent explicitly. As for which directive was violated, that's pretty
615     // harmless information.
616
617     RefPtr<InspectorObject> cspReport = InspectorObject::create();
618     cspReport->setString(ASCIILiteral("document-uri"), documentURI);
619     cspReport->setString(ASCIILiteral("referrer"), referrer);
620     cspReport->setString(ASCIILiteral("violated-directive"), violatedDirectiveText);
621     cspReport->setString(ASCIILiteral("effective-directive"), effectiveViolatedDirective);
622     cspReport->setString(ASCIILiteral("original-policy"), originalPolicy);
623     cspReport->setString(ASCIILiteral("blocked-uri"), blockedURI);
624     cspReport->setInteger(ASCIILiteral("status-code"), statusCode);
625     if (!sourceFile.isNull()) {
626         cspReport->setString(ASCIILiteral("source-file"), sourceFile);
627         cspReport->setInteger(ASCIILiteral("line-number"), lineNumber);
628         cspReport->setInteger(ASCIILiteral("column-number"), columnNumber);
629     }
630
631     RefPtr<InspectorObject> reportObject = InspectorObject::create();
632     reportObject->setObject(ASCIILiteral("csp-report"), cspReport.release());
633
634     RefPtr<FormData> report = FormData::create(reportObject->toJSONString().utf8());
635     for (const auto& url : reportURIs)
636         PingLoader::sendViolationReport(*frame, document.completeURL(url), report.copyRef(), ViolationReportType::ContentSecurityPolicy);
637 }
638
639 void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) const
640 {
641     String message;
642     if (equalLettersIgnoringASCIICase(name, "allow"))
643         message = ASCIILiteral("The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect.");
644     else if (equalLettersIgnoringASCIICase(name, "options"))
645         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.");
646     else if (equalLettersIgnoringASCIICase(name, "policy-uri"))
647         message = ASCIILiteral("The 'policy-uri' directive has been removed from the specification. Please specify a complete policy via the Content-Security-Policy header.");
648     else
649         message = makeString("Unrecognized Content-Security-Policy directive '", name, "'.\n"); // FIXME: Why does this include a newline?
650
651     logToConsole(message);
652 }
653
654 void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const
655 {
656     logToConsole("The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?");
657 }
658
659 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const
660 {
661     logToConsole(makeString("Ignoring duplicate Content-Security-Policy directive '", name, "'.\n"));
662 }
663
664 void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) const
665 {
666     String message;
667     if (pluginType.isNull())
668         message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n";
669     else
670         message = makeString("Invalid plugin type in 'plugin-types' Content Security Policy directive: '", pluginType, "'.\n");
671     logToConsole(message);
672 }
673
674 void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags) const
675 {
676     logToConsole("Error while parsing the 'sandbox' Content Security Policy directive: " + invalidFlags);
677 }
678
679 void ContentSecurityPolicy::reportInvalidReflectedXSS(const String& invalidValue) const
680 {
681     logToConsole("The 'reflected-xss' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Value values are \"allow\", \"filter\", and \"block\".");
682 }
683
684 void ContentSecurityPolicy::reportInvalidDirectiveInReportOnlyMode(const String& directiveName) const
685 {
686     logToConsole("The Content Security Policy directive '" + directiveName + "' is ignored when delivered in a report-only policy.");
687 }
688
689 void ContentSecurityPolicy::reportInvalidDirectiveInHTTPEquivMeta(const String& directiveName) const
690 {
691     logToConsole("The Content Security Policy directive '" + directiveName + "' is ignored when delivered via an HTML meta element.");
692 }
693
694 void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) const
695 {
696     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.");
697     logToConsole(message);
698 }
699
700 void ContentSecurityPolicy::reportInvalidPathCharacter(const String& directiveName, const String& value, const char invalidChar) const
701 {
702     ASSERT(invalidChar == '#' || invalidChar == '?');
703
704     String ignoring;
705     if (invalidChar == '?')
706         ignoring = "The query component, including the '?', will be ignored.";
707     else
708         ignoring = "The fragment identifier, including the '#', will be ignored.";
709
710     String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains a source with an invalid path: '", value, "'. ", ignoring);
711     logToConsole(message);
712 }
713
714 void ContentSecurityPolicy::reportInvalidSourceExpression(const String& directiveName, const String& source) const
715 {
716     String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains an invalid source: '", source, "'. It will be ignored.");
717     if (equalLettersIgnoringASCIICase(source, "'none'"))
718         message = makeString(message, " Note that 'none' has no effect unless it is the only expression in the source list.");
719     logToConsole(message);
720 }
721
722 void ContentSecurityPolicy::reportMissingReportURI(const String& policy) const
723 {
724     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.");
725 }
726
727 void ContentSecurityPolicy::logToConsole(const String& message, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
728 {
729     if (!m_isReportingEnabled)
730         return;
731
732     // FIXME: <http://webkit.org/b/114317> ContentSecurityPolicy::logToConsole should include a column number
733     if (m_scriptExecutionContext)
734         m_scriptExecutionContext->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, contextURL, contextLine.oneBasedInt(), 0, state);
735     else if (m_frame && m_frame->document())
736         static_cast<ScriptExecutionContext*>(m_frame->document())->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, contextURL, contextLine.oneBasedInt(), 0, state);
737 }
738
739 void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String& directiveText) const
740 {
741     if (m_scriptExecutionContext)
742         InspectorInstrumentation::scriptExecutionBlockedByCSP(m_scriptExecutionContext, directiveText);
743 }
744
745 bool ContentSecurityPolicy::experimentalFeaturesEnabled() const
746 {
747 #if ENABLE(CSP_NEXT)
748     return RuntimeEnabledFeatures::sharedFeatures().experimentalContentSecurityPolicyFeaturesEnabled();
749 #else
750     return false;
751 #endif
752 }
753     
754 }