CSP: Implement child-src directive
[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 "ContentSecurityPolicySource.h"
33 #include "ContentSecurityPolicySourceList.h"
34 #include "DOMStringList.h"
35 #include "Document.h"
36 #include "FormData.h"
37 #include "FormDataList.h"
38 #include "Frame.h"
39 #include "InspectorInstrumentation.h"
40 #include "JSMainThreadExecState.h"
41 #include "ParsingUtilities.h"
42 #include "PingLoader.h"
43 #include "RuntimeEnabledFeatures.h"
44 #include "SchemeRegistry.h"
45 #include "SecurityOrigin.h"
46 #include "SecurityPolicyViolationEvent.h"
47 #include <inspector/InspectorValues.h>
48 #include <inspector/ScriptCallStack.h>
49 #include <inspector/ScriptCallStackFactory.h>
50 #include <wtf/text/TextPosition.h>
51
52 using namespace Inspector;
53
54 namespace WebCore {
55
56 ContentSecurityPolicy::ContentSecurityPolicy(ScriptExecutionContext& scriptExecutionContext)
57     : m_scriptExecutionContext(&scriptExecutionContext)
58     , m_sandboxFlags(SandboxNone)
59 {
60     ASSERT(scriptExecutionContext.securityOrigin());
61     auto& securityOrigin = *scriptExecutionContext.securityOrigin();
62     m_selfSourceProtocol = securityOrigin.protocol();
63     m_selfSource = std::make_unique<ContentSecurityPolicySource>(*this, m_selfSourceProtocol, securityOrigin.host(), securityOrigin.port(), emptyString(), false, false);
64 }
65
66 ContentSecurityPolicy::ContentSecurityPolicy(const SecurityOrigin& securityOrigin)
67     : m_sandboxFlags(SandboxNone)
68 {
69     m_selfSourceProtocol = securityOrigin.protocol();
70     m_selfSource = std::make_unique<ContentSecurityPolicySource>(*this, m_selfSourceProtocol, securityOrigin.host(), securityOrigin.port(), emptyString(), false, false);
71 }
72
73 ContentSecurityPolicy::~ContentSecurityPolicy()
74 {
75 }
76
77 void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other) 
78 {
79     ASSERT(m_policies.isEmpty());
80     for (auto& policy : other->m_policies)
81         didReceiveHeader(policy->header(), policy->headerType());
82 }
83
84 ContentSecurityPolicyResponseHeaders ContentSecurityPolicy::responseHeaders() const
85 {
86     ContentSecurityPolicyResponseHeaders result;
87     result.m_headers.reserveInitialCapacity(m_policies.size());
88     for (auto& policy : m_policies)
89         result.m_headers.uncheckedAppend({ policy->header(), policy->headerType() });
90     return result;
91 }
92
93 void ContentSecurityPolicy::didReceiveHeaders(const ContentSecurityPolicyResponseHeaders& headers)
94 {
95     for (auto& header : headers.m_headers)
96         didReceiveHeader(header.first, header.second);
97 }
98
99 void ContentSecurityPolicy::didReceiveHeader(const String& header, ContentSecurityPolicyHeaderType type)
100 {
101     // RFC2616, section 4.2 specifies that headers appearing multiple times can
102     // be combined with a comma. Walk the header string, and parse each comma
103     // separated chunk as a separate header.
104     auto characters = StringView(header).upconvertedCharacters();
105     const UChar* begin = characters;
106     const UChar* position = begin;
107     const UChar* end = begin + header.length();
108     while (position < end) {
109         skipUntil<UChar>(position, end, ',');
110
111         // header1,header2 OR header1
112         //        ^                  ^
113         std::unique_ptr<ContentSecurityPolicyDirectiveList> policy = ContentSecurityPolicyDirectiveList::create(*this, String(begin, position - begin), type);
114         if (!policy->allowEval(0, ContentSecurityPolicy::ReportingStatus::SuppressReport))
115             m_lastPolicyEvalDisabledErrorMessage = policy->evalDisabledErrorMessage();
116
117         m_policies.append(policy.release());
118
119         // Skip the comma, and begin the next header from the current position.
120         ASSERT(position == end || *position == ',');
121         skipExactly<UChar>(position, end, ',');
122         begin = position;
123     }
124
125     if (m_scriptExecutionContext)
126         applyPolicyToScriptExecutionContext();
127 }
128
129 void ContentSecurityPolicy::applyPolicyToScriptExecutionContext()
130 {
131     ASSERT(m_scriptExecutionContext);
132     if (!m_lastPolicyEvalDisabledErrorMessage.isNull())
133         m_scriptExecutionContext->disableEval(m_lastPolicyEvalDisabledErrorMessage);
134     if (m_sandboxFlags != SandboxNone && is<Document>(m_scriptExecutionContext))
135         m_scriptExecutionContext->enforceSandboxFlags(m_sandboxFlags);
136 }
137
138 void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value)
139 {
140     m_overrideInlineStyleAllowed = value;
141 }
142
143 bool ContentSecurityPolicy::urlMatchesSelf(const URL& url) const
144 {
145     return m_selfSource->matches(url);
146 }
147
148 bool ContentSecurityPolicy::protocolMatchesSelf(const URL& url) const
149 {
150 #if ENABLE(CSP_NEXT)
151     if (equalLettersIgnoringASCIICase(m_selfSourceProtocol, "http"))
152         return url.protocolIsInHTTPFamily();
153 #endif
154     return equalIgnoringASCIICase(url.protocol(), m_selfSourceProtocol);
155 }
156
157 template<bool (ContentSecurityPolicyDirectiveList::*allowed)(ContentSecurityPolicy::ReportingStatus) const>
158 bool isAllowedByAll(const CSPDirectiveListVector& policies, ContentSecurityPolicy::ReportingStatus reportingStatus)
159 {
160     for (auto& policy : policies) {
161         if (!(policy.get()->*allowed)(reportingStatus))
162             return false;
163     }
164     return true;
165 }
166
167 template<bool (ContentSecurityPolicyDirectiveList::*allowed)(JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus) const>
168 bool isAllowedByAllWithState(const CSPDirectiveListVector& policies, JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus reportingStatus)
169 {
170     for (auto& policy : policies) {
171         if (!(policy.get()->*allowed)(state, reportingStatus))
172             return false;
173     }
174     return true;
175 }
176
177 template<bool (ContentSecurityPolicyDirectiveList::*allowed)(const String&, const WTF::OrdinalNumber&, ContentSecurityPolicy::ReportingStatus) const>
178 bool isAllowedByAllWithContext(const CSPDirectiveListVector& policies, const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus)
179 {
180     for (auto& policy : policies) {
181         if (!(policy.get()->*allowed)(contextURL, contextLine, reportingStatus))
182             return false;
183     }
184     return true;
185 }
186
187 template<bool (ContentSecurityPolicyDirectiveList::*allowFromURL)(const URL&, ContentSecurityPolicy::ReportingStatus) const>
188 bool isAllowedByAllWithURL(const CSPDirectiveListVector& policies, const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus)
189 {
190     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
191         return true;
192
193     for (auto& policy : policies) {
194         if (!(policy.get()->*allowFromURL)(url, reportingStatus))
195             return false;
196     }
197     return true;
198 }
199
200 bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
201 {
202     return overrideContentSecurityPolicy || isAllowedByAllWithContext<&ContentSecurityPolicyDirectiveList::allowJavaScriptURLs>(m_policies, contextURL, contextLine, reportingStatus);
203 }
204
205 bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
206 {
207     return overrideContentSecurityPolicy || isAllowedByAllWithContext<&ContentSecurityPolicyDirectiveList::allowInlineEventHandlers>(m_policies, contextURL, contextLine, reportingStatus);
208 }
209
210 bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
211 {
212     return overrideContentSecurityPolicy || isAllowedByAllWithContext<&ContentSecurityPolicyDirectiveList::allowInlineScript>(m_policies, contextURL, contextLine, reportingStatus);
213 }
214
215 bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
216 {
217     return overrideContentSecurityPolicy || m_overrideInlineStyleAllowed || isAllowedByAllWithContext<&ContentSecurityPolicyDirectiveList::allowInlineStyle>(m_policies, contextURL, contextLine, reportingStatus);
218 }
219
220 bool ContentSecurityPolicy::allowEval(JSC::ExecState* state, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
221 {
222     return overrideContentSecurityPolicy || isAllowedByAllWithState<&ContentSecurityPolicyDirectiveList::allowEval>(m_policies, state, reportingStatus);
223 }
224
225 String ContentSecurityPolicy::evalDisabledErrorMessage() const
226 {
227     for (auto& policy : m_policies) {
228         if (!policy->allowEval(0, ContentSecurityPolicy::ReportingStatus::SuppressReport))
229             return policy->evalDisabledErrorMessage();
230     }
231     return String();
232 }
233
234 bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
235 {
236     if (overrideContentSecurityPolicy)
237         return true;
238     for (auto& policy : m_policies) {
239         if (!policy->allowPluginType(type, typeAttribute, url, reportingStatus))
240             return false;
241     }
242     return true;
243 }
244
245 bool ContentSecurityPolicy::allowScriptFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
246 {
247     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus);
248 }
249
250 bool ContentSecurityPolicy::allowObjectFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
251 {
252     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowObjectFromSource>(m_policies, url, reportingStatus);
253 }
254
255 bool ContentSecurityPolicy::allowChildFrameFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
256 {
257     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowChildFrameFromSource>(m_policies, url, reportingStatus);
258 }
259
260 bool ContentSecurityPolicy::allowChildContextFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
261 {
262     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowChildContextFromSource>(m_policies, url, reportingStatus);
263 }
264
265 bool ContentSecurityPolicy::allowImageFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
266 {
267     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowImageFromSource>(m_policies, url, reportingStatus);
268 }
269
270 bool ContentSecurityPolicy::allowStyleFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
271 {
272     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowStyleFromSource>(m_policies, url, reportingStatus);
273 }
274
275 bool ContentSecurityPolicy::allowFontFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
276 {
277     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowFontFromSource>(m_policies, url, reportingStatus);
278 }
279
280 bool ContentSecurityPolicy::allowMediaFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
281 {
282     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowMediaFromSource>(m_policies, url, reportingStatus);
283 }
284
285 bool ContentSecurityPolicy::allowConnectToSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
286 {
287     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowConnectToSource>(m_policies, url, reportingStatus);
288 }
289
290 bool ContentSecurityPolicy::allowFormAction(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
291 {
292     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowFormAction>(m_policies, url, reportingStatus);
293 }
294
295 bool ContentSecurityPolicy::allowBaseURI(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
296 {
297     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowBaseURI>(m_policies, url, reportingStatus);
298 }
299
300 bool ContentSecurityPolicy::isActive() const
301 {
302     return !m_policies.isEmpty();
303 }
304
305 ContentSecurityPolicy::ReflectedXSSDisposition ContentSecurityPolicy::reflectedXSSDisposition() const
306 {
307     ReflectedXSSDisposition disposition = ReflectedXSSUnset;
308     for (auto& policy : m_policies) {
309         if (policy->reflectedXSSDisposition() > disposition)
310             disposition = std::max(disposition, policy->reflectedXSSDisposition());
311     }
312     return disposition;
313 }
314
315 void ContentSecurityPolicy::gatherReportURIs(DOMStringList& list) const
316 {
317     ASSERT(m_scriptExecutionContext);
318     for (auto& policy : m_policies) {
319         for (auto& url : policy->reportURIs())
320             list.append(m_scriptExecutionContext->completeURL(url).string());
321     }
322 }
323
324 static String stripURLForUseInReport(Document& document, const URL& url)
325 {
326     if (!url.isValid())
327         return String();
328     if (!url.isHierarchical() || url.protocolIs("file"))
329         return url.protocol();
330     return document.securityOrigin()->canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url).get().toString();
331 }
332
333 void ContentSecurityPolicy::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const URL& blockedURL, const Vector<String>& reportURIs, const String& header, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
334 {
335     logToConsole(consoleMessage, contextURL, contextLine, state);
336
337     // FIXME: Support sending reports from worker.
338     if (!is<Document>(m_scriptExecutionContext))
339         return;
340
341     Document& document = downcast<Document>(*m_scriptExecutionContext);
342     Frame* frame = document.frame();
343     if (!frame)
344         return;
345
346 #if ENABLE(CSP_NEXT)
347     if (experimentalFeaturesEnabled()) {
348         // FIXME: This code means that we're gathering information like line numbers twice. Once we can bring this out from behind the flag, we should reuse the data gathered here when generating the JSON report below.
349         String documentURI = document.url().string();
350         String referrer = document.referrer();
351         String blockedURI = stripURLForUseInReport(document, blockedURL);
352         String violatedDirective = directiveText;
353         String originalPolicy = header;
354         String sourceFile = String();
355         int lineNumber = 0;
356         
357         Ref<ScriptCallStack> stack = createScriptCallStack(JSMainThreadExecState::currentState(), 2);
358         const ScriptCallFrame* callFrame = stack->firstNonNativeCallFrame();
359         if (callFrame && callFrame->lineNumber()) {
360             URL source = URL(URL(), callFrame->sourceURL());
361             sourceFile = stripURLForUseInReport(document, source);
362             lineNumber = callFrame->lineNumber();
363         }
364
365         document.enqueueDocumentEvent(SecurityPolicyViolationEvent::create(eventNames().securitypolicyviolationEvent, false, false, documentURI, referrer, blockedURI, violatedDirective, effectiveDirective, originalPolicy, sourceFile, lineNumber));
366     }
367 #endif
368
369     if (reportURIs.isEmpty())
370         return;
371
372     // We need to be careful here when deciding what information to send to the
373     // report-uri. Currently, we send only the current document's URL and the
374     // directive that was violated. The document's URL is safe to send because
375     // it's the document itself that's requesting that it be sent. You could
376     // make an argument that we shouldn't send HTTPS document URLs to HTTP
377     // report-uris (for the same reasons that we suppress the Referer in that
378     // case), but the Referer is sent implicitly whereas this request is only
379     // sent explicitly. As for which directive was violated, that's pretty
380     // harmless information.
381
382     RefPtr<InspectorObject> cspReport = InspectorObject::create();
383     cspReport->setString(ASCIILiteral("document-uri"), document.url().strippedForUseAsReferrer());
384     cspReport->setString(ASCIILiteral("referrer"), document.referrer());
385     cspReport->setString(ASCIILiteral("violated-directive"), directiveText);
386 #if ENABLE(CSP_NEXT)
387     if (experimentalFeaturesEnabled())
388         cspReport->setString(ASCIILiteral("effective-directive"), effectiveDirective);
389 #else
390     UNUSED_PARAM(effectiveDirective);
391 #endif
392     cspReport->setString(ASCIILiteral("original-policy"), header);
393     cspReport->setString(ASCIILiteral("blocked-uri"), stripURLForUseInReport(document, blockedURL));
394
395     RefPtr<ScriptCallStack> stack = createScriptCallStack(JSMainThreadExecState::currentState(), 2);
396     const ScriptCallFrame* callFrame = stack->firstNonNativeCallFrame();
397     if (callFrame && callFrame->lineNumber()) {
398         URL source = URL(URL(), callFrame->sourceURL());
399         cspReport->setString(ASCIILiteral("source-file"), stripURLForUseInReport(document, source));
400         cspReport->setInteger(ASCIILiteral("line-number"), callFrame->lineNumber());
401     }
402
403     RefPtr<InspectorObject> reportObject = InspectorObject::create();
404     reportObject->setObject(ASCIILiteral("csp-report"), cspReport.release());
405
406     RefPtr<FormData> report = FormData::create(reportObject->toJSONString().utf8());
407
408     for (const auto& url : reportURIs)
409         PingLoader::sendViolationReport(*frame, document.completeURL(url), report.copyRef());
410 }
411
412 void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) const
413 {
414     String message;
415     if (equalLettersIgnoringASCIICase(name, "allow"))
416         message = ASCIILiteral("The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect.");
417     else if (equalLettersIgnoringASCIICase(name, "options"))
418         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.");
419     else if (equalLettersIgnoringASCIICase(name, "policy-uri"))
420         message = ASCIILiteral("The 'policy-uri' directive has been removed from the specification. Please specify a complete policy via the Content-Security-Policy header.");
421     else
422         message = makeString("Unrecognized Content-Security-Policy directive '", name, "'.\n"); // FIXME: Why does this include a newline?
423
424     logToConsole(message);
425 }
426
427 void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const
428 {
429     logToConsole("The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?");
430 }
431
432 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const
433 {
434     logToConsole(makeString("Ignoring duplicate Content-Security-Policy directive '", name, "'.\n"));
435 }
436
437 void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) const
438 {
439     String message;
440     if (pluginType.isNull())
441         message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n";
442     else
443         message = makeString("Invalid plugin type in 'plugin-types' Content Security Policy directive: '", pluginType, "'.\n");
444     logToConsole(message);
445 }
446
447 void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags) const
448 {
449     logToConsole("Error while parsing the 'sandbox' Content Security Policy directive: " + invalidFlags);
450 }
451
452 void ContentSecurityPolicy::reportInvalidReflectedXSS(const String& invalidValue) const
453 {
454     logToConsole("The 'reflected-xss' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Value values are \"allow\", \"filter\", and \"block\".");
455 }
456
457 void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) const
458 {
459     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.");
460     logToConsole(message);
461 }
462
463 void ContentSecurityPolicy::reportInvalidPathCharacter(const String& directiveName, const String& value, const char invalidChar) const
464 {
465     ASSERT(invalidChar == '#' || invalidChar == '?');
466
467     String ignoring;
468     if (invalidChar == '?')
469         ignoring = "The query component, including the '?', will be ignored.";
470     else
471         ignoring = "The fragment identifier, including the '#', will be ignored.";
472
473     String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains a source with an invalid path: '", value, "'. ", ignoring);
474     logToConsole(message);
475 }
476
477 void ContentSecurityPolicy::reportInvalidSourceExpression(const String& directiveName, const String& source) const
478 {
479     String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains an invalid source: '", source, "'. It will be ignored.");
480     if (equalLettersIgnoringASCIICase(source, "'none'"))
481         message = makeString(message, " Note that 'none' has no effect unless it is the only expression in the source list.");
482     logToConsole(message);
483 }
484
485 void ContentSecurityPolicy::reportMissingReportURI(const String& policy) const
486 {
487     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.");
488 }
489
490 void ContentSecurityPolicy::logToConsole(const String& message, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
491 {
492     // FIXME: <http://webkit.org/b/114317> ContentSecurityPolicy::logToConsole should include a column number
493     if (m_scriptExecutionContext)
494         m_scriptExecutionContext->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, contextURL, contextLine.oneBasedInt(), 0, state);
495 }
496
497 void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String& directiveText) const
498 {
499     if (m_scriptExecutionContext)
500         InspectorInstrumentation::scriptExecutionBlockedByCSP(m_scriptExecutionContext, directiveText);
501 }
502
503 bool ContentSecurityPolicy::experimentalFeaturesEnabled() const
504 {
505 #if ENABLE(CSP_NEXT)
506     return RuntimeEnabledFeatures::sharedFeatures().experimentalContentSecurityPolicyFeaturesEnabled();
507 #else
508     return false;
509 #endif
510 }
511     
512 }