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