CSP: 'sandbox' should be ignored in report-only mode
[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 (equalLettersIgnoringASCIICase(m_selfSourceProtocol, "http"))
151         return url.protocolIsInHTTPFamily();
152     return equalIgnoringASCIICase(url.protocol(), m_selfSourceProtocol);
153 }
154
155 template<bool (ContentSecurityPolicyDirectiveList::*allowed)(ContentSecurityPolicy::ReportingStatus) const>
156 bool isAllowedByAll(const CSPDirectiveListVector& policies, ContentSecurityPolicy::ReportingStatus reportingStatus)
157 {
158     for (auto& policy : policies) {
159         if (!(policy.get()->*allowed)(reportingStatus))
160             return false;
161     }
162     return true;
163 }
164
165 template<bool (ContentSecurityPolicyDirectiveList::*allowed)(JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus) const>
166 bool isAllowedByAllWithState(const CSPDirectiveListVector& policies, JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus reportingStatus)
167 {
168     for (auto& policy : policies) {
169         if (!(policy.get()->*allowed)(state, reportingStatus))
170             return false;
171     }
172     return true;
173 }
174
175 template<bool (ContentSecurityPolicyDirectiveList::*allowed)(const String&, const WTF::OrdinalNumber&, ContentSecurityPolicy::ReportingStatus) const>
176 bool isAllowedByAllWithContext(const CSPDirectiveListVector& policies, const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus)
177 {
178     for (auto& policy : policies) {
179         if (!(policy.get()->*allowed)(contextURL, contextLine, reportingStatus))
180             return false;
181     }
182     return true;
183 }
184
185 template<bool (ContentSecurityPolicyDirectiveList::*allowFromURL)(const URL&, ContentSecurityPolicy::ReportingStatus) const>
186 bool isAllowedByAllWithURL(const CSPDirectiveListVector& policies, const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus)
187 {
188     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
189         return true;
190
191     for (auto& policy : policies) {
192         if (!(policy.get()->*allowFromURL)(url, reportingStatus))
193             return false;
194     }
195     return true;
196 }
197
198 bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
199 {
200     return overrideContentSecurityPolicy || isAllowedByAllWithContext<&ContentSecurityPolicyDirectiveList::allowJavaScriptURLs>(m_policies, contextURL, contextLine, reportingStatus);
201 }
202
203 bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
204 {
205     return overrideContentSecurityPolicy || isAllowedByAllWithContext<&ContentSecurityPolicyDirectiveList::allowInlineEventHandlers>(m_policies, contextURL, contextLine, reportingStatus);
206 }
207
208 bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
209 {
210     return overrideContentSecurityPolicy || isAllowedByAllWithContext<&ContentSecurityPolicyDirectiveList::allowInlineScript>(m_policies, contextURL, contextLine, reportingStatus);
211 }
212
213 bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
214 {
215     return overrideContentSecurityPolicy || m_overrideInlineStyleAllowed || isAllowedByAllWithContext<&ContentSecurityPolicyDirectiveList::allowInlineStyle>(m_policies, contextURL, contextLine, reportingStatus);
216 }
217
218 bool ContentSecurityPolicy::allowEval(JSC::ExecState* state, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
219 {
220     return overrideContentSecurityPolicy || isAllowedByAllWithState<&ContentSecurityPolicyDirectiveList::allowEval>(m_policies, state, reportingStatus);
221 }
222
223 String ContentSecurityPolicy::evalDisabledErrorMessage() const
224 {
225     for (auto& policy : m_policies) {
226         if (!policy->allowEval(0, ContentSecurityPolicy::ReportingStatus::SuppressReport))
227             return policy->evalDisabledErrorMessage();
228     }
229     return String();
230 }
231
232 bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
233 {
234     if (overrideContentSecurityPolicy)
235         return true;
236     for (auto& policy : m_policies) {
237         if (!policy->allowPluginType(type, typeAttribute, url, reportingStatus))
238             return false;
239     }
240     return true;
241 }
242
243 bool ContentSecurityPolicy::allowScriptFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
244 {
245     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus);
246 }
247
248 bool ContentSecurityPolicy::allowObjectFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
249 {
250     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowObjectFromSource>(m_policies, url, reportingStatus);
251 }
252
253 bool ContentSecurityPolicy::allowChildFrameFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
254 {
255     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowChildFrameFromSource>(m_policies, url, reportingStatus);
256 }
257
258 bool ContentSecurityPolicy::allowChildContextFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
259 {
260     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowChildContextFromSource>(m_policies, url, reportingStatus);
261 }
262
263 bool ContentSecurityPolicy::allowImageFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
264 {
265     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowImageFromSource>(m_policies, url, reportingStatus);
266 }
267
268 bool ContentSecurityPolicy::allowStyleFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
269 {
270     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowStyleFromSource>(m_policies, url, reportingStatus);
271 }
272
273 bool ContentSecurityPolicy::allowFontFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
274 {
275     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowFontFromSource>(m_policies, url, reportingStatus);
276 }
277
278 bool ContentSecurityPolicy::allowMediaFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
279 {
280     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowMediaFromSource>(m_policies, url, reportingStatus);
281 }
282
283 bool ContentSecurityPolicy::allowConnectToSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
284 {
285     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowConnectToSource>(m_policies, url, reportingStatus);
286 }
287
288 bool ContentSecurityPolicy::allowFormAction(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
289 {
290     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowFormAction>(m_policies, url, reportingStatus);
291 }
292
293 bool ContentSecurityPolicy::allowBaseURI(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
294 {
295     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&ContentSecurityPolicyDirectiveList::allowBaseURI>(m_policies, url, reportingStatus);
296 }
297
298 bool ContentSecurityPolicy::isActive() const
299 {
300     return !m_policies.isEmpty();
301 }
302
303 ContentSecurityPolicy::ReflectedXSSDisposition ContentSecurityPolicy::reflectedXSSDisposition() const
304 {
305     ReflectedXSSDisposition disposition = ReflectedXSSUnset;
306     for (auto& policy : m_policies) {
307         if (policy->reflectedXSSDisposition() > disposition)
308             disposition = std::max(disposition, policy->reflectedXSSDisposition());
309     }
310     return disposition;
311 }
312
313 void ContentSecurityPolicy::gatherReportURIs(DOMStringList& list) const
314 {
315     ASSERT(m_scriptExecutionContext);
316     for (auto& policy : m_policies) {
317         for (auto& url : policy->reportURIs())
318             list.append(m_scriptExecutionContext->completeURL(url).string());
319     }
320 }
321
322 static String stripURLForUseInReport(Document& document, const URL& url)
323 {
324     if (!url.isValid())
325         return String();
326     if (!url.isHierarchical() || url.protocolIs("file"))
327         return url.protocol();
328     return document.securityOrigin()->canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url).get().toString();
329 }
330
331 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
332 {
333     logToConsole(consoleMessage, contextURL, contextLine, state);
334
335     // FIXME: Support sending reports from worker.
336     if (!is<Document>(m_scriptExecutionContext))
337         return;
338
339     Document& document = downcast<Document>(*m_scriptExecutionContext);
340     Frame* frame = document.frame();
341     if (!frame)
342         return;
343
344 #if ENABLE(CSP_NEXT)
345     if (experimentalFeaturesEnabled()) {
346         // 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.
347         String documentURI = document.url().string();
348         String referrer = document.referrer();
349         String blockedURI = stripURLForUseInReport(document, blockedURL);
350         String violatedDirective = directiveText;
351         String originalPolicy = header;
352         String sourceFile = String();
353         int lineNumber = 0;
354         
355         Ref<ScriptCallStack> stack = createScriptCallStack(JSMainThreadExecState::currentState(), 2);
356         const ScriptCallFrame* callFrame = stack->firstNonNativeCallFrame();
357         if (callFrame && callFrame->lineNumber()) {
358             URL source = URL(URL(), callFrame->sourceURL());
359             sourceFile = stripURLForUseInReport(document, source);
360             lineNumber = callFrame->lineNumber();
361         }
362
363         document.enqueueDocumentEvent(SecurityPolicyViolationEvent::create(eventNames().securitypolicyviolationEvent, false, false, documentURI, referrer, blockedURI, violatedDirective, effectiveDirective, originalPolicy, sourceFile, lineNumber));
364     }
365 #endif
366
367     if (reportURIs.isEmpty())
368         return;
369
370     // We need to be careful here when deciding what information to send to the
371     // report-uri. Currently, we send only the current document's URL and the
372     // directive that was violated. The document's URL is safe to send because
373     // it's the document itself that's requesting that it be sent. You could
374     // make an argument that we shouldn't send HTTPS document URLs to HTTP
375     // report-uris (for the same reasons that we suppress the Referer in that
376     // case), but the Referer is sent implicitly whereas this request is only
377     // sent explicitly. As for which directive was violated, that's pretty
378     // harmless information.
379
380     RefPtr<InspectorObject> cspReport = InspectorObject::create();
381     cspReport->setString(ASCIILiteral("document-uri"), document.url().strippedForUseAsReferrer());
382     cspReport->setString(ASCIILiteral("referrer"), document.referrer());
383     cspReport->setString(ASCIILiteral("violated-directive"), directiveText);
384 #if ENABLE(CSP_NEXT)
385     if (experimentalFeaturesEnabled())
386         cspReport->setString(ASCIILiteral("effective-directive"), effectiveDirective);
387 #else
388     UNUSED_PARAM(effectiveDirective);
389 #endif
390     cspReport->setString(ASCIILiteral("original-policy"), header);
391     cspReport->setString(ASCIILiteral("blocked-uri"), stripURLForUseInReport(document, blockedURL));
392
393     RefPtr<ScriptCallStack> stack = createScriptCallStack(JSMainThreadExecState::currentState(), 2);
394     const ScriptCallFrame* callFrame = stack->firstNonNativeCallFrame();
395     if (callFrame && callFrame->lineNumber()) {
396         URL source = URL(URL(), callFrame->sourceURL());
397         cspReport->setString(ASCIILiteral("source-file"), stripURLForUseInReport(document, source));
398         cspReport->setInteger(ASCIILiteral("line-number"), callFrame->lineNumber());
399     }
400
401     RefPtr<InspectorObject> reportObject = InspectorObject::create();
402     reportObject->setObject(ASCIILiteral("csp-report"), cspReport.release());
403
404     RefPtr<FormData> report = FormData::create(reportObject->toJSONString().utf8());
405
406     for (const auto& url : reportURIs)
407         PingLoader::sendViolationReport(*frame, document.completeURL(url), report.copyRef());
408 }
409
410 void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) const
411 {
412     String message;
413     if (equalLettersIgnoringASCIICase(name, "allow"))
414         message = ASCIILiteral("The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect.");
415     else if (equalLettersIgnoringASCIICase(name, "options"))
416         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.");
417     else if (equalLettersIgnoringASCIICase(name, "policy-uri"))
418         message = ASCIILiteral("The 'policy-uri' directive has been removed from the specification. Please specify a complete policy via the Content-Security-Policy header.");
419     else
420         message = makeString("Unrecognized Content-Security-Policy directive '", name, "'.\n"); // FIXME: Why does this include a newline?
421
422     logToConsole(message);
423 }
424
425 void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const
426 {
427     logToConsole("The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?");
428 }
429
430 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const
431 {
432     logToConsole(makeString("Ignoring duplicate Content-Security-Policy directive '", name, "'.\n"));
433 }
434
435 void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) const
436 {
437     String message;
438     if (pluginType.isNull())
439         message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n";
440     else
441         message = makeString("Invalid plugin type in 'plugin-types' Content Security Policy directive: '", pluginType, "'.\n");
442     logToConsole(message);
443 }
444
445 void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags) const
446 {
447     logToConsole("Error while parsing the 'sandbox' Content Security Policy directive: " + invalidFlags);
448 }
449
450 void ContentSecurityPolicy::reportInvalidReflectedXSS(const String& invalidValue) const
451 {
452     logToConsole("The 'reflected-xss' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Value values are \"allow\", \"filter\", and \"block\".");
453 }
454
455 void ContentSecurityPolicy::reportInvalidDirectiveInReportOnlyMode(const String& directiveName) const
456 {
457     logToConsole("The Content Security Policy directive '" + directiveName + "' is ignored when delivered in a report-only policy.");
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 }