CSP: Extract helper classes into their own files
[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::allowImageFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
261 {
262     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowImageFromSource>(m_policies, url, reportingStatus);
263 }
264
265 bool ContentSecurityPolicy::allowStyleFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
266 {
267     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowStyleFromSource>(m_policies, url, reportingStatus);
268 }
269
270 bool ContentSecurityPolicy::allowFontFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
271 {
272     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowFontFromSource>(m_policies, url, reportingStatus);
273 }
274
275 bool ContentSecurityPolicy::allowMediaFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
276 {
277     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowMediaFromSource>(m_policies, url, reportingStatus);
278 }
279
280 bool ContentSecurityPolicy::allowConnectToSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
281 {
282     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowConnectToSource>(m_policies, url, reportingStatus);
283 }
284
285 bool ContentSecurityPolicy::allowFormAction(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
286 {
287     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowFormAction>(m_policies, url, reportingStatus);
288 }
289
290 bool ContentSecurityPolicy::allowBaseURI(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
291 {
292     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowBaseURI>(m_policies, url, reportingStatus);
293 }
294
295 bool ContentSecurityPolicy::isActive() const
296 {
297     return !m_policies.isEmpty();
298 }
299
300 ContentSecurityPolicy::ReflectedXSSDisposition ContentSecurityPolicy::reflectedXSSDisposition() const
301 {
302     ReflectedXSSDisposition disposition = ReflectedXSSUnset;
303     for (auto& policy : m_policies) {
304         if (policy->reflectedXSSDisposition() > disposition)
305             disposition = std::max(disposition, policy->reflectedXSSDisposition());
306     }
307     return disposition;
308 }
309
310 void ContentSecurityPolicy::gatherReportURIs(DOMStringList& list) const
311 {
312     ASSERT(m_scriptExecutionContext);
313     for (auto& policy : m_policies) {
314         for (auto& url : policy->reportURIs())
315             list.append(m_scriptExecutionContext->completeURL(url).string());
316     }
317 }
318
319 static String stripURLForUseInReport(Document& document, const URL& url)
320 {
321     if (!url.isValid())
322         return String();
323     if (!url.isHierarchical() || url.protocolIs("file"))
324         return url.protocol();
325     return document.securityOrigin()->canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url).get().toString();
326 }
327
328 #if ENABLE(CSP_NEXT)
329 static void gatherSecurityPolicyViolationEventData(SecurityPolicyViolationEventInit& init, Document& document, const String& directiveText, const String& effectiveDirective, const URL& blockedURL, const String& header)
330 {
331     init.documentURI = document.url().string();
332     init.referrer = document.referrer();
333     init.blockedURI = stripURLForUseInReport(document, blockedURL);
334     init.violatedDirective = directiveText;
335     init.effectiveDirective = effectiveDirective;
336     init.originalPolicy = header;
337     init.sourceFile = String();
338     init.lineNumber = 0;
339
340     RefPtr<ScriptCallStack> stack = createScriptCallStack(JSMainThreadExecState::currentState(), 2);
341     const ScriptCallFrame* callFrame = stack->firstNonNativeCallFrame();
342     if (callFrame && callFrame->lineNumber()) {
343         URL source = URL(URL(), callFrame->sourceURL());
344         init.sourceFile = stripURLForUseInReport(document, source);
345         init.lineNumber = callFrame->lineNumber();
346     }
347 }
348 #endif
349
350 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
351 {
352     logToConsole(consoleMessage, contextURL, contextLine, state);
353
354     // FIXME: Support sending reports from worker.
355     if (!is<Document>(m_scriptExecutionContext))
356         return;
357
358     Document& document = downcast<Document>(*m_scriptExecutionContext);
359     Frame* frame = document.frame();
360     if (!frame)
361         return;
362
363 #if ENABLE(CSP_NEXT)
364     if (experimentalFeaturesEnabled()) {
365         // 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.
366         SecurityPolicyViolationEventInit init;
367         gatherSecurityPolicyViolationEventData(init, document, directiveText, effectiveDirective, blockedURL, header);
368         document.enqueueDocumentEvent(SecurityPolicyViolationEvent::create(eventNames().securitypolicyviolationEvent, init));
369     }
370 #endif
371
372     if (reportURIs.isEmpty())
373         return;
374
375     // We need to be careful here when deciding what information to send to the
376     // report-uri. Currently, we send only the current document's URL and the
377     // directive that was violated. The document's URL is safe to send because
378     // it's the document itself that's requesting that it be sent. You could
379     // make an argument that we shouldn't send HTTPS document URLs to HTTP
380     // report-uris (for the same reasons that we suppress the Referer in that
381     // case), but the Referer is sent implicitly whereas this request is only
382     // sent explicitly. As for which directive was violated, that's pretty
383     // harmless information.
384
385     RefPtr<InspectorObject> cspReport = InspectorObject::create();
386     cspReport->setString(ASCIILiteral("document-uri"), document.url().strippedForUseAsReferrer());
387     cspReport->setString(ASCIILiteral("referrer"), document.referrer());
388     cspReport->setString(ASCIILiteral("violated-directive"), directiveText);
389 #if ENABLE(CSP_NEXT)
390     if (experimentalFeaturesEnabled())
391         cspReport->setString(ASCIILiteral("effective-directive"), effectiveDirective);
392 #else
393     UNUSED_PARAM(effectiveDirective);
394 #endif
395     cspReport->setString(ASCIILiteral("original-policy"), header);
396     cspReport->setString(ASCIILiteral("blocked-uri"), stripURLForUseInReport(document, blockedURL));
397
398     RefPtr<ScriptCallStack> stack = createScriptCallStack(JSMainThreadExecState::currentState(), 2);
399     const ScriptCallFrame* callFrame = stack->firstNonNativeCallFrame();
400     if (callFrame && callFrame->lineNumber()) {
401         URL source = URL(URL(), callFrame->sourceURL());
402         cspReport->setString(ASCIILiteral("source-file"), stripURLForUseInReport(document, source));
403         cspReport->setInteger(ASCIILiteral("line-number"), callFrame->lineNumber());
404     }
405
406     RefPtr<InspectorObject> reportObject = InspectorObject::create();
407     reportObject->setObject(ASCIILiteral("csp-report"), cspReport.release());
408
409     RefPtr<FormData> report = FormData::create(reportObject->toJSONString().utf8());
410
411     for (const auto& url : reportURIs)
412         PingLoader::sendViolationReport(*frame, document.completeURL(url), report.copyRef());
413 }
414
415 void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) const
416 {
417     String message;
418     if (equalLettersIgnoringASCIICase(name, "allow"))
419         message = ASCIILiteral("The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect.");
420     else if (equalLettersIgnoringASCIICase(name, "options"))
421         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.");
422     else if (equalLettersIgnoringASCIICase(name, "policy-uri"))
423         message = ASCIILiteral("The 'policy-uri' directive has been removed from the specification. Please specify a complete policy via the Content-Security-Policy header.");
424     else
425         message = makeString("Unrecognized Content-Security-Policy directive '", name, "'.\n"); // FIXME: Why does this include a newline?
426
427     logToConsole(message);
428 }
429
430 void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const
431 {
432     logToConsole("The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?");
433 }
434
435 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const
436 {
437     logToConsole(makeString("Ignoring duplicate Content-Security-Policy directive '", name, "'.\n"));
438 }
439
440 void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) const
441 {
442     String message;
443     if (pluginType.isNull())
444         message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n";
445     else
446         message = makeString("Invalid plugin type in 'plugin-types' Content Security Policy directive: '", pluginType, "'.\n");
447     logToConsole(message);
448 }
449
450 void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags) const
451 {
452     logToConsole("Error while parsing the 'sandbox' Content Security Policy directive: " + invalidFlags);
453 }
454
455 void ContentSecurityPolicy::reportInvalidReflectedXSS(const String& invalidValue) const
456 {
457     logToConsole("The 'reflected-xss' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Value values are \"allow\", \"filter\", and \"block\".");
458 }
459
460 void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) const
461 {
462     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.");
463     logToConsole(message);
464 }
465
466 void ContentSecurityPolicy::reportInvalidPathCharacter(const String& directiveName, const String& value, const char invalidChar) const
467 {
468     ASSERT(invalidChar == '#' || invalidChar == '?');
469
470     String ignoring;
471     if (invalidChar == '?')
472         ignoring = "The query component, including the '?', will be ignored.";
473     else
474         ignoring = "The fragment identifier, including the '#', will be ignored.";
475
476     String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains a source with an invalid path: '", value, "'. ", ignoring);
477     logToConsole(message);
478 }
479
480 void ContentSecurityPolicy::reportInvalidSourceExpression(const String& directiveName, const String& source) const
481 {
482     String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains an invalid source: '", source, "'. It will be ignored.");
483     if (equalLettersIgnoringASCIICase(source, "'none'"))
484         message = makeString(message, " Note that 'none' has no effect unless it is the only expression in the source list.");
485     logToConsole(message);
486 }
487
488 void ContentSecurityPolicy::reportMissingReportURI(const String& policy) const
489 {
490     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.");
491 }
492
493 void ContentSecurityPolicy::logToConsole(const String& message, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
494 {
495     // FIXME: <http://webkit.org/b/114317> ContentSecurityPolicy::logToConsole should include a column number
496     if (m_scriptExecutionContext)
497         m_scriptExecutionContext->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, contextURL, contextLine.oneBasedInt(), 0, state);
498 }
499
500 void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String& directiveText) const
501 {
502     if (m_scriptExecutionContext)
503         InspectorInstrumentation::scriptExecutionBlockedByCSP(m_scriptExecutionContext, directiveText);
504 }
505
506 bool ContentSecurityPolicy::experimentalFeaturesEnabled() const
507 {
508 #if ENABLE(CSP_NEXT)
509     return RuntimeEnabledFeatures::sharedFeatures().experimentalContentSecurityPolicyFeaturesEnabled();
510 #else
511     return false;
512 #endif
513 }
514     
515 }