Use NeverDestroyed instead of DEPRECATED_DEFINE_STATIC_LOCAL
[WebKit-https.git] / Source / WebCore / page / ContentSecurityPolicy.cpp
1 /*
2  * Copyright (C) 2011 Google, Inc. All rights reserved.
3  * Copyright (C) 2013 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 "DOMStringList.h"
31 #include "Document.h"
32 #include "FormData.h"
33 #include "FormDataList.h"
34 #include "Frame.h"
35 #include "InspectorInstrumentation.h"
36 #include "JSMainThreadExecState.h"
37 #include "PingLoader.h"
38 #include "RuntimeEnabledFeatures.h"
39 #include "SchemeRegistry.h"
40 #include "ScriptController.h"
41 #include "ScriptState.h"
42 #include "SecurityOrigin.h"
43 #include "SecurityPolicyViolationEvent.h"
44 #include "TextEncoding.h"
45 #include "URL.h"
46 #include <inspector/InspectorValues.h>
47 #include <inspector/ScriptCallStack.h>
48 #include <inspector/ScriptCallStackFactory.h>
49 #include <wtf/HashSet.h>
50 #include <wtf/NeverDestroyed.h>
51 #include <wtf/text/TextPosition.h>
52
53 using namespace Inspector;
54
55 namespace WebCore {
56
57 // Normally WebKit uses "static" for internal linkage, but using "static" for
58 // these functions causes a compile error because these functions are used as
59 // template parameters.
60 namespace {
61
62 bool isDirectiveNameCharacter(UChar c)
63 {
64     return isASCIIAlphanumeric(c) || c == '-';
65 }
66
67 bool isDirectiveValueCharacter(UChar c)
68 {
69     return isASCIISpace(c) || (c >= 0x21 && c <= 0x7e); // Whitespace + VCHAR
70 }
71
72 bool isSourceCharacter(UChar c)
73 {
74     return !isASCIISpace(c);
75 }
76
77 bool isPathComponentCharacter(UChar c)
78 {
79     return c != '?' && c != '#';
80 }
81
82 bool isHostCharacter(UChar c)
83 {
84     return isASCIIAlphanumeric(c) || c == '-';
85 }
86
87 bool isSchemeContinuationCharacter(UChar c)
88 {
89     return isASCIIAlphanumeric(c) || c == '+' || c == '-' || c == '.';
90 }
91
92 bool isNotASCIISpace(UChar c)
93 {
94     return !isASCIISpace(c);
95 }
96
97 bool isNotColonOrSlash(UChar c)
98 {
99     return c != ':' && c != '/';
100 }
101
102 bool isMediaTypeCharacter(UChar c)
103 {
104     return !isASCIISpace(c) && c != '/';
105 }
106
107 // CSP 1.0 Directives
108 static const char connectSrc[] = "connect-src";
109 static const char defaultSrc[] = "default-src";
110 static const char fontSrc[] = "font-src";
111 static const char frameSrc[] = "frame-src";
112 static const char imgSrc[] = "img-src";
113 static const char mediaSrc[] = "media-src";
114 static const char objectSrc[] = "object-src";
115 static const char reportURI[] = "report-uri";
116 static const char sandbox[] = "sandbox";
117 static const char scriptSrc[] = "script-src";
118 static const char styleSrc[] = "style-src";
119
120 // CSP 1.1 Directives
121 static const char baseURI[] = "base-uri";
122 static const char formAction[] = "form-action";
123 static const char pluginTypes[] = "plugin-types";
124 #if ENABLE(CSP_NEXT)
125 static const char reflectedXSS[] = "reflected-xss";
126 #endif
127
128 bool isDirectiveName(const String& name)
129 {
130     return (equalIgnoringCase(name, connectSrc)
131         || equalIgnoringCase(name, defaultSrc)
132         || equalIgnoringCase(name, fontSrc)
133         || equalIgnoringCase(name, frameSrc)
134         || equalIgnoringCase(name, imgSrc)
135         || equalIgnoringCase(name, mediaSrc)
136         || equalIgnoringCase(name, objectSrc)
137         || equalIgnoringCase(name, reportURI)
138         || equalIgnoringCase(name, sandbox)
139         || equalIgnoringCase(name, scriptSrc)
140         || equalIgnoringCase(name, styleSrc)
141 #if ENABLE(CSP_NEXT)
142         || equalIgnoringCase(name, baseURI)
143         || equalIgnoringCase(name, formAction)
144         || equalIgnoringCase(name, pluginTypes)
145         || equalIgnoringCase(name, reflectedXSS)
146 #endif
147     );
148 }
149
150 } // namespace
151
152 static bool skipExactly(const UChar*& position, const UChar* end, UChar delimiter)
153 {
154     if (position < end && *position == delimiter) {
155         ++position;
156         return true;
157     }
158     return false;
159 }
160
161 template<bool characterPredicate(UChar)>
162 static bool skipExactly(const UChar*& position, const UChar* end)
163 {
164     if (position < end && characterPredicate(*position)) {
165         ++position;
166         return true;
167     }
168     return false;
169 }
170
171 static void skipUntil(const UChar*& position, const UChar* end, UChar delimiter)
172 {
173     while (position < end && *position != delimiter)
174         ++position;
175 }
176
177 template<bool characterPredicate(UChar)>
178 static void skipWhile(const UChar*& position, const UChar* end)
179 {
180     while (position < end && characterPredicate(*position))
181         ++position;
182 }
183
184 static bool isSourceListNone(const String& value)
185 {
186     auto characters = StringView(value).upconvertedCharacters();
187     const UChar* begin = characters;
188     const UChar* end = characters + value.length();
189     skipWhile<isASCIISpace>(begin, end);
190
191     const UChar* position = begin;
192     skipWhile<isSourceCharacter>(position, end);
193     if (!equalIgnoringCase("'none'", begin, position - begin))
194         return false;
195
196     skipWhile<isASCIISpace>(position, end);
197     if (position != end)
198         return false;
199
200     return true;
201 }
202
203 class CSPSource {
204 public:
205     CSPSource(ContentSecurityPolicy* policy, const String& scheme, const String& host, int port, const String& path, bool hostHasWildcard, bool portHasWildcard)
206         : m_policy(policy)
207         , m_scheme(scheme)
208         , m_host(host)
209         , m_port(port)
210         , m_path(path)
211         , m_hostHasWildcard(hostHasWildcard)
212         , m_portHasWildcard(portHasWildcard)
213     {
214     }
215
216     bool matches(const URL& url) const
217     {
218         if (!schemeMatches(url))
219             return false;
220         if (isSchemeOnly())
221             return true;
222         return hostMatches(url) && portMatches(url) && pathMatches(url);
223     }
224
225 private:
226     bool schemeMatches(const URL& url) const
227     {
228         if (m_scheme.isEmpty()) {
229             String protectedResourceScheme(m_policy->securityOrigin()->protocol());
230 #if ENABLE(CSP_NEXT)
231             if (equalIgnoringCase("http", protectedResourceScheme))
232                 return url.protocolIsInHTTPFamily();
233 #endif
234             return equalIgnoringCase(url.protocol(), protectedResourceScheme);
235         }
236         return equalIgnoringCase(url.protocol(), m_scheme);
237     }
238
239     bool hostMatches(const URL& url) const
240     {
241         const String& host = url.host();
242         if (equalIgnoringCase(host, m_host))
243             return true;
244         return m_hostHasWildcard && host.endsWith("." + m_host, false);
245
246     }
247
248     bool pathMatches(const URL& url) const
249     {
250         if (m_path.isEmpty())
251             return true;
252
253         String path = decodeURLEscapeSequences(url.path());
254
255         if (m_path.endsWith("/"))
256             return path.startsWith(m_path, false);
257
258         return path == m_path;
259     }
260
261     bool portMatches(const URL& url) const
262     {
263         if (m_portHasWildcard)
264             return true;
265
266         int port = url.port();
267
268         if (port == m_port)
269             return true;
270
271         if (!port)
272             return isDefaultPortForProtocol(m_port, url.protocol());
273
274         if (!m_port)
275             return isDefaultPortForProtocol(port, url.protocol());
276
277         return false;
278     }
279
280     bool isSchemeOnly() const { return m_host.isEmpty(); }
281
282     ContentSecurityPolicy* m_policy;
283     String m_scheme;
284     String m_host;
285     int m_port;
286     String m_path;
287
288     bool m_hostHasWildcard;
289     bool m_portHasWildcard;
290 };
291
292 class CSPSourceList {
293 public:
294     CSPSourceList(ContentSecurityPolicy*, const String& directiveName);
295
296     void parse(const String&);
297     bool matches(const URL&);
298     bool allowInline() const { return m_allowInline; }
299     bool allowEval() const { return m_allowEval; }
300
301 private:
302     void parse(const UChar* begin, const UChar* end);
303
304     bool parseSource(const UChar* begin, const UChar* end, String& scheme, String& host, int& port, String& path, bool& hostHasWildcard, bool& portHasWildcard);
305     bool parseScheme(const UChar* begin, const UChar* end, String& scheme);
306     bool parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard);
307     bool parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard);
308     bool parsePath(const UChar* begin, const UChar* end, String& path);
309
310     void addSourceSelf();
311     void addSourceStar();
312     void addSourceUnsafeInline();
313     void addSourceUnsafeEval();
314
315     ContentSecurityPolicy* m_policy;
316     Vector<CSPSource> m_list;
317     String m_directiveName;
318     bool m_allowStar;
319     bool m_allowInline;
320     bool m_allowEval;
321 };
322
323 CSPSourceList::CSPSourceList(ContentSecurityPolicy* policy, const String& directiveName)
324     : m_policy(policy)
325     , m_directiveName(directiveName)
326     , m_allowStar(false)
327     , m_allowInline(false)
328     , m_allowEval(false)
329 {
330 }
331
332 void CSPSourceList::parse(const String& value)
333 {
334     // We represent 'none' as an empty m_list.
335     if (isSourceListNone(value))
336         return;
337     auto characters = StringView(value).upconvertedCharacters();
338     parse(characters, characters + value.length());
339 }
340
341 bool CSPSourceList::matches(const URL& url)
342 {
343     if (m_allowStar)
344         return true;
345
346     URL effectiveURL = SecurityOrigin::shouldUseInnerURL(url) ? SecurityOrigin::extractInnerURL(url) : url;
347
348     for (auto& entry : m_list) {
349         if (entry.matches(effectiveURL))
350             return true;
351     }
352
353     return false;
354 }
355
356 // source-list       = *WSP [ source *( 1*WSP source ) *WSP ]
357 //                   / *WSP "'none'" *WSP
358 //
359 void CSPSourceList::parse(const UChar* begin, const UChar* end)
360 {
361     const UChar* position = begin;
362
363     while (position < end) {
364         skipWhile<isASCIISpace>(position, end);
365         if (position == end)
366             return;
367
368         const UChar* beginSource = position;
369         skipWhile<isSourceCharacter>(position, end);
370
371         String scheme, host, path;
372         int port = 0;
373         bool hostHasWildcard = false;
374         bool portHasWildcard = false;
375
376         if (parseSource(beginSource, position, scheme, host, port, path, hostHasWildcard, portHasWildcard)) {
377             // Wildcard hosts and keyword sources ('self', 'unsafe-inline',
378             // etc.) aren't stored in m_list, but as attributes on the source
379             // list itself.
380             if (scheme.isEmpty() && host.isEmpty())
381                 continue;
382             if (isDirectiveName(host))
383                 m_policy->reportDirectiveAsSourceExpression(m_directiveName, host);
384             m_list.append(CSPSource(m_policy, scheme, host, port, path, hostHasWildcard, portHasWildcard));
385         } else
386             m_policy->reportInvalidSourceExpression(m_directiveName, String(beginSource, position - beginSource));
387
388         ASSERT(position == end || isASCIISpace(*position));
389      }
390 }
391
392 // source            = scheme ":"
393 //                   / ( [ scheme "://" ] host [ port ] [ path ] )
394 //                   / "'self'"
395 //
396 bool CSPSourceList::parseSource(const UChar* begin, const UChar* end,
397                                 String& scheme, String& host, int& port, String& path,
398                                 bool& hostHasWildcard, bool& portHasWildcard)
399 {
400     if (begin == end)
401         return false;
402
403     if (equalIgnoringCase("'none'", begin, end - begin))
404         return false;
405
406     if (end - begin == 1 && *begin == '*') {
407         addSourceStar();
408         return true;
409     }
410
411     if (equalIgnoringCase("'self'", begin, end - begin)) {
412         addSourceSelf();
413         return true;
414     }
415
416     if (equalIgnoringCase("'unsafe-inline'", begin, end - begin)) {
417         addSourceUnsafeInline();
418         return true;
419     }
420
421     if (equalIgnoringCase("'unsafe-eval'", begin, end - begin)) {
422         addSourceUnsafeEval();
423         return true;
424     }
425
426     const UChar* position = begin;
427     const UChar* beginHost = begin;
428     const UChar* beginPath = end;
429     const UChar* beginPort = nullptr;
430
431     skipWhile<isNotColonOrSlash>(position, end);
432
433     if (position == end) {
434         // host
435         //     ^
436         return parseHost(beginHost, position, host, hostHasWildcard);
437     }
438
439     if (position < end && *position == '/') {
440         // host/path || host/ || /
441         //     ^            ^    ^
442         if (!parseHost(beginHost, position, host, hostHasWildcard)
443             || !parsePath(position, end, path)
444             || position != end)
445             return false;
446         return true;
447     }
448
449     if (position < end && *position == ':') {
450         if (end - position == 1) {
451             // scheme:
452             //       ^
453             return parseScheme(begin, position, scheme);
454         }
455
456         if (position[1] == '/') {
457             // scheme://host || scheme://
458             //       ^                ^
459             if (!parseScheme(begin, position, scheme)
460                 || !skipExactly(position, end, ':')
461                 || !skipExactly(position, end, '/')
462                 || !skipExactly(position, end, '/'))
463                 return false;
464             if (position == end)
465                 return true;
466             beginHost = position;
467             skipWhile<isNotColonOrSlash>(position, end);
468         }
469
470         if (position < end && *position == ':') {
471             // host:port || scheme://host:port
472             //     ^                     ^
473             beginPort = position;
474             skipUntil(position, end, '/');
475         }
476     }
477
478     if (position < end && *position == '/') {
479         // scheme://host/path || scheme://host:port/path
480         //              ^                          ^
481         if (position == beginHost)
482             return false;
483
484         beginPath = position;
485     }
486
487     if (!parseHost(beginHost, beginPort ? beginPort : beginPath, host, hostHasWildcard))
488         return false;
489
490     if (beginPort) {
491         if (!parsePort(beginPort, beginPath, port, portHasWildcard))
492             return false;
493     } else {
494         port = 0;
495     }
496
497     if (beginPath != end) {
498         if (!parsePath(beginPath, end, path))
499             return false;
500     }
501
502     return true;
503 }
504
505 //                     ; <scheme> production from RFC 3986
506 // scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
507 //
508 bool CSPSourceList::parseScheme(const UChar* begin, const UChar* end, String& scheme)
509 {
510     ASSERT(begin <= end);
511     ASSERT(scheme.isEmpty());
512
513     if (begin == end)
514         return false;
515
516     const UChar* position = begin;
517
518     if (!skipExactly<isASCIIAlpha>(position, end))
519         return false;
520
521     skipWhile<isSchemeContinuationCharacter>(position, end);
522
523     if (position != end)
524         return false;
525
526     scheme = String(begin, end - begin);
527     return true;
528 }
529
530 // host              = [ "*." ] 1*host-char *( "." 1*host-char )
531 //                   / "*"
532 // host-char         = ALPHA / DIGIT / "-"
533 //
534 bool CSPSourceList::parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard)
535 {
536     ASSERT(begin <= end);
537     ASSERT(host.isEmpty());
538     ASSERT(!hostHasWildcard);
539
540     if (begin == end)
541         return false;
542
543     const UChar* position = begin;
544
545     if (skipExactly(position, end, '*')) {
546         hostHasWildcard = true;
547
548         if (position == end)
549             return true;
550
551         if (!skipExactly(position, end, '.'))
552             return false;
553     }
554
555     const UChar* hostBegin = position;
556
557     while (position < end) {
558         if (!skipExactly<isHostCharacter>(position, end))
559             return false;
560
561         skipWhile<isHostCharacter>(position, end);
562
563         if (position < end && !skipExactly(position, end, '.'))
564             return false;
565     }
566
567     ASSERT(position == end);
568     host = String(hostBegin, end - hostBegin);
569     return true;
570 }
571
572 bool CSPSourceList::parsePath(const UChar* begin, const UChar* end, String& path)
573 {
574     ASSERT(begin <= end);
575     ASSERT(path.isEmpty());
576
577     const UChar* position = begin;
578     skipWhile<isPathComponentCharacter>(position, end);
579     // path/to/file.js?query=string || path/to/file.js#anchor
580     //                ^                               ^
581     if (position < end)
582         m_policy->reportInvalidPathCharacter(m_directiveName, String(begin, end - begin), *position);
583
584     path = decodeURLEscapeSequences(String(begin, position - begin));
585
586     ASSERT(position <= end);
587     ASSERT(position == end || (*position == '#' || *position == '?'));
588     return true;
589 }
590
591 // port              = ":" ( 1*DIGIT / "*" )
592 //
593 bool CSPSourceList::parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard)
594 {
595     ASSERT(begin <= end);
596     ASSERT(!port);
597     ASSERT(!portHasWildcard);
598
599     if (!skipExactly(begin, end, ':'))
600         ASSERT_NOT_REACHED();
601
602     if (begin == end)
603         return false;
604
605     if (end - begin == 1 && *begin == '*') {
606         port = 0;
607         portHasWildcard = true;
608         return true;
609     }
610
611     const UChar* position = begin;
612     skipWhile<isASCIIDigit>(position, end);
613
614     if (position != end)
615         return false;
616
617     bool ok;
618     port = charactersToIntStrict(begin, end - begin, &ok);
619     return ok;
620 }
621
622 void CSPSourceList::addSourceSelf()
623 {
624     m_list.append(CSPSource(m_policy, m_policy->securityOrigin()->protocol(), m_policy->securityOrigin()->host(), m_policy->securityOrigin()->port(), String(), false, false));
625 }
626
627 void CSPSourceList::addSourceStar()
628 {
629     m_allowStar = true;
630 }
631
632 void CSPSourceList::addSourceUnsafeInline()
633 {
634     m_allowInline = true;
635 }
636
637 void CSPSourceList::addSourceUnsafeEval()
638 {
639     m_allowEval = true;
640 }
641
642 class CSPDirective {
643 public:
644     CSPDirective(const String& name, const String& value, ContentSecurityPolicy* policy)
645         : m_name(name)
646         , m_text(name + ' ' + value)
647         , m_policy(policy)
648     {
649     }
650
651     const String& text() const { return m_text; }
652
653 protected:
654     const ContentSecurityPolicy* policy() const { return m_policy; }
655
656 private:
657     String m_name;
658     String m_text;
659     ContentSecurityPolicy* m_policy;
660 };
661
662 class MediaListDirective : public CSPDirective {
663 public:
664     MediaListDirective(const String& name, const String& value, ContentSecurityPolicy* policy)
665         : CSPDirective(name, value, policy)
666     {
667         parse(value);
668     }
669
670     bool allows(const String& type)
671     {
672         return m_pluginTypes.contains(type);
673     }
674
675 private:
676     void parse(const String& value)
677     {
678         auto characters = StringView(value).upconvertedCharacters();
679         const UChar* begin = characters;
680         const UChar* position = begin;
681         const UChar* end = begin + value.length();
682
683         // 'plugin-types ____;' OR 'plugin-types;'
684         if (value.isEmpty()) {
685             policy()->reportInvalidPluginTypes(value);
686             return;
687         }
688
689         while (position < end) {
690             // _____ OR _____mime1/mime1
691             // ^        ^
692             skipWhile<isASCIISpace>(position, end);
693             if (position == end)
694                 return;
695
696             // mime1/mime1 mime2/mime2
697             // ^
698             begin = position;
699             if (!skipExactly<isMediaTypeCharacter>(position, end)) {
700                 skipWhile<isNotASCIISpace>(position, end);
701                 policy()->reportInvalidPluginTypes(String(begin, position - begin));
702                 continue;
703             }
704             skipWhile<isMediaTypeCharacter>(position, end);
705
706             // mime1/mime1 mime2/mime2
707             //      ^
708             if (!skipExactly(position, end, '/')) {
709                 skipWhile<isNotASCIISpace>(position, end);
710                 policy()->reportInvalidPluginTypes(String(begin, position - begin));
711                 continue;
712             }
713
714             // mime1/mime1 mime2/mime2
715             //       ^
716             if (!skipExactly<isMediaTypeCharacter>(position, end)) {
717                 skipWhile<isNotASCIISpace>(position, end);
718                 policy()->reportInvalidPluginTypes(String(begin, position - begin));
719                 continue;
720             }
721             skipWhile<isMediaTypeCharacter>(position, end);
722
723             // mime1/mime1 mime2/mime2 OR mime1/mime1  OR mime1/mime1/error
724             //            ^                          ^               ^
725             if (position < end && isNotASCIISpace(*position)) {
726                 skipWhile<isNotASCIISpace>(position, end);
727                 policy()->reportInvalidPluginTypes(String(begin, position - begin));
728                 continue;
729             }
730             m_pluginTypes.add(String(begin, position - begin));
731
732             ASSERT(position == end || isASCIISpace(*position));
733         }
734     }
735
736     HashSet<String> m_pluginTypes;
737 };
738
739 class SourceListDirective : public CSPDirective {
740 public:
741     SourceListDirective(const String& name, const String& value, ContentSecurityPolicy* policy)
742         : CSPDirective(name, value, policy)
743         , m_sourceList(policy, name)
744     {
745         m_sourceList.parse(value);
746     }
747
748     bool allows(const URL& url)
749     {
750         return m_sourceList.matches(url.isEmpty() ? policy()->url() : url);
751     }
752
753     bool allowInline() const { return m_sourceList.allowInline(); }
754     bool allowEval() const { return m_sourceList.allowEval(); }
755
756 private:
757     CSPSourceList m_sourceList;
758 };
759
760 class CSPDirectiveList {
761     WTF_MAKE_FAST_ALLOCATED;
762 public:
763     static std::unique_ptr<CSPDirectiveList> create(ContentSecurityPolicy*, const String&, ContentSecurityPolicy::HeaderType);
764     CSPDirectiveList(ContentSecurityPolicy*, ContentSecurityPolicy::HeaderType);
765
766     const String& header() const { return m_header; }
767     ContentSecurityPolicy::HeaderType headerType() const { return m_headerType; }
768
769     bool allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
770     bool allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
771     bool allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
772     bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
773     bool allowEval(JSC::ExecState*, ContentSecurityPolicy::ReportingStatus) const;
774     bool allowPluginType(const String& type, const String& typeAttribute, const URL&, ContentSecurityPolicy::ReportingStatus) const;
775
776     bool allowScriptFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
777     bool allowObjectFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
778     bool allowChildFrameFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
779     bool allowImageFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
780     bool allowStyleFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
781     bool allowFontFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
782     bool allowMediaFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
783     bool allowConnectToSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
784     bool allowFormAction(const URL&, ContentSecurityPolicy::ReportingStatus) const;
785     bool allowBaseURI(const URL&, ContentSecurityPolicy::ReportingStatus) const;
786
787     void gatherReportURIs(DOMStringList&) const;
788     const String& evalDisabledErrorMessage() const { return m_evalDisabledErrorMessage; }
789     ContentSecurityPolicy::ReflectedXSSDisposition reflectedXSSDisposition() const { return m_reflectedXSSDisposition; }
790     bool isReportOnly() const { return m_reportOnly; }
791     const Vector<URL>& reportURIs() const { return m_reportURIs; }
792
793 private:
794     void parse(const String&);
795
796     bool parseDirective(const UChar* begin, const UChar* end, String& name, String& value);
797     void parseReportURI(const String& name, const String& value);
798     void parsePluginTypes(const String& name, const String& value);
799     void parseReflectedXSS(const String& name, const String& value);
800     void addDirective(const String& name, const String& value);
801     void applySandboxPolicy(const String& name, const String& sandboxPolicy);
802
803     template <class CSPDirectiveType>
804     void setCSPDirective(const String& name, const String& value, std::unique_ptr<CSPDirectiveType>&);
805
806     SourceListDirective* operativeDirective(SourceListDirective*) const;
807     void reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const URL& blockedURL = URL(), const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), JSC::ExecState* = nullptr) const;
808
809     bool checkEval(SourceListDirective*) const;
810     bool checkInline(SourceListDirective*) const;
811     bool checkSource(SourceListDirective*, const URL&) const;
812     bool checkMediaType(MediaListDirective*, const String& type, const String& typeAttribute) const;
813
814     void setEvalDisabledErrorMessage(const String& errorMessage) { m_evalDisabledErrorMessage = errorMessage; }
815
816     bool checkEvalAndReportViolation(SourceListDirective*, const String& consoleMessage, const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), JSC::ExecState* = nullptr) const;
817     bool checkInlineAndReportViolation(SourceListDirective*, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, bool isScript) const;
818
819     bool checkSourceAndReportViolation(SourceListDirective*, const URL&, const String& effectiveDirective) const;
820     bool checkMediaTypeAndReportViolation(MediaListDirective*, const String& type, const String& typeAttribute, const String& consoleMessage) const;
821
822     bool denyIfEnforcingPolicy() const { return m_reportOnly; }
823
824     ContentSecurityPolicy* m_policy;
825
826     String m_header;
827     ContentSecurityPolicy::HeaderType m_headerType;
828
829     bool m_reportOnly;
830     bool m_haveSandboxPolicy;
831     ContentSecurityPolicy::ReflectedXSSDisposition m_reflectedXSSDisposition;
832
833     std::unique_ptr<MediaListDirective> m_pluginTypes;
834     std::unique_ptr<SourceListDirective> m_baseURI;
835     std::unique_ptr<SourceListDirective> m_connectSrc;
836     std::unique_ptr<SourceListDirective> m_defaultSrc;
837     std::unique_ptr<SourceListDirective> m_fontSrc;
838     std::unique_ptr<SourceListDirective> m_formAction;
839     std::unique_ptr<SourceListDirective> m_frameSrc;
840     std::unique_ptr<SourceListDirective> m_imgSrc;
841     std::unique_ptr<SourceListDirective> m_mediaSrc;
842     std::unique_ptr<SourceListDirective> m_objectSrc;
843     std::unique_ptr<SourceListDirective> m_scriptSrc;
844     std::unique_ptr<SourceListDirective> m_styleSrc;
845
846     Vector<URL> m_reportURIs;
847
848     String m_evalDisabledErrorMessage;
849 };
850
851 CSPDirectiveList::CSPDirectiveList(ContentSecurityPolicy* policy, ContentSecurityPolicy::HeaderType type)
852     : m_policy(policy)
853     , m_headerType(type)
854     , m_reportOnly(false)
855     , m_haveSandboxPolicy(false)
856     , m_reflectedXSSDisposition(ContentSecurityPolicy::ReflectedXSSUnset)
857 {
858     m_reportOnly = (type == ContentSecurityPolicy::Report || type == ContentSecurityPolicy::PrefixedReport);
859 }
860
861 std::unique_ptr<CSPDirectiveList> CSPDirectiveList::create(ContentSecurityPolicy* policy, const String& header, ContentSecurityPolicy::HeaderType type)
862 {
863     auto directives = std::make_unique<CSPDirectiveList>(policy, type);
864     directives->parse(header);
865
866     if (!directives->checkEval(directives->operativeDirective(directives->m_scriptSrc.get()))) {
867         String message = makeString("Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: \"", directives->operativeDirective(directives->m_scriptSrc.get())->text(), "\".\n");
868         directives->setEvalDisabledErrorMessage(message);
869     }
870
871     if (directives->isReportOnly() && directives->reportURIs().isEmpty())
872         policy->reportMissingReportURI(header);
873
874     return directives;
875 }
876
877 void CSPDirectiveList::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const URL& blockedURL, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
878 {
879     String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage;
880     m_policy->reportViolation(directiveText, effectiveDirective, message, blockedURL, m_reportURIs, m_header, contextURL, contextLine, state);
881 }
882
883 bool CSPDirectiveList::checkEval(SourceListDirective* directive) const
884 {
885     return !directive || directive->allowEval();
886 }
887
888 bool CSPDirectiveList::checkInline(SourceListDirective* directive) const
889 {
890     return !directive || directive->allowInline();
891 }
892
893 bool CSPDirectiveList::checkSource(SourceListDirective* directive, const URL& url) const
894 {
895     return !directive || directive->allows(url);
896 }
897
898 bool CSPDirectiveList::checkMediaType(MediaListDirective* directive, const String& type, const String& typeAttribute) const
899 {
900     if (!directive)
901         return true;
902     if (typeAttribute.isEmpty() || typeAttribute.stripWhiteSpace() != type)
903         return false;
904     return directive->allows(type);
905 }
906
907 SourceListDirective* CSPDirectiveList::operativeDirective(SourceListDirective* directive) const
908 {
909     return directive ? directive : m_defaultSrc.get();
910 }
911
912 bool CSPDirectiveList::checkEvalAndReportViolation(SourceListDirective* directive, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
913 {
914     if (checkEval(directive))
915         return true;
916
917     String suffix = String();
918     if (directive == m_defaultSrc.get())
919         suffix = " Note that 'script-src' was not explicitly set, so 'default-src' is used as a fallback.";
920
921     reportViolation(directive->text(), scriptSrc, consoleMessage + "\"" + directive->text() + "\"." + suffix + "\n", URL(), contextURL, contextLine, state);
922     if (!m_reportOnly) {
923         m_policy->reportBlockedScriptExecutionToInspector(directive->text());
924         return false;
925     }
926     return true;
927 }
928
929 bool CSPDirectiveList::checkMediaTypeAndReportViolation(MediaListDirective* directive, const String& type, const String& typeAttribute, const String& consoleMessage) const
930 {
931     if (checkMediaType(directive, type, typeAttribute))
932         return true;
933
934     String message = makeString(consoleMessage, '\'', directive->text(), "\'.");
935     if (typeAttribute.isEmpty())
936         message = message + " When enforcing the 'plugin-types' directive, the plugin's media type must be explicitly declared with a 'type' attribute on the containing element (e.g. '<object type=\"[TYPE GOES HERE]\" ...>').";
937
938     reportViolation(directive->text(), pluginTypes, message + "\n", URL());
939     return denyIfEnforcingPolicy();
940 }
941
942 bool CSPDirectiveList::checkInlineAndReportViolation(SourceListDirective* directive, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, bool isScript) const
943 {
944     if (checkInline(directive))
945         return true;
946
947     String suffix = String();
948     if (directive == m_defaultSrc.get())
949         suffix = makeString(" Note that '", (isScript ? "script" : "style"), "-src' was not explicitly set, so 'default-src' is used as a fallback.");
950
951     reportViolation(directive->text(), isScript ? scriptSrc : styleSrc, consoleMessage + "\"" + directive->text() + "\"." + suffix + "\n", URL(), contextURL, contextLine);
952
953     if (!m_reportOnly) {
954         if (isScript)
955             m_policy->reportBlockedScriptExecutionToInspector(directive->text());
956         return false;
957     }
958     return true;
959 }
960
961 bool CSPDirectiveList::checkSourceAndReportViolation(SourceListDirective* directive, const URL& url, const String& effectiveDirective) const
962 {
963     if (checkSource(directive, url))
964         return true;
965
966     String prefix;
967     if (baseURI == effectiveDirective)
968         prefix = "Refused to set the document's base URI to '";
969     else if (connectSrc == effectiveDirective)
970         prefix = "Refused to connect to '";
971     else if (fontSrc == effectiveDirective)
972         prefix = "Refused to load the font '";
973     else if (formAction == effectiveDirective)
974         prefix = "Refused to send form data to '";
975     else if (frameSrc == effectiveDirective)
976         prefix = "Refused to load frame '";
977     else if (imgSrc == effectiveDirective)
978         prefix = "Refused to load the image '";
979     else if (mediaSrc == effectiveDirective)
980         prefix = "Refused to load media from '";
981     else if (objectSrc == effectiveDirective)
982         prefix = "Refused to load plugin data from '";
983     else if (scriptSrc == effectiveDirective)
984         prefix = "Refused to load the script '";
985     else if (styleSrc == effectiveDirective)
986         prefix = "Refused to load the stylesheet '";
987
988     String suffix = String();
989     if (directive == m_defaultSrc.get())
990         suffix = " Note that '" + effectiveDirective + "' was not explicitly set, so 'default-src' is used as a fallback.";
991
992     reportViolation(directive->text(), effectiveDirective, prefix + url.stringCenterEllipsizedToLength() + "' because it violates the following Content Security Policy directive: \"" + directive->text() + "\"." + suffix + "\n", url);
993     return denyIfEnforcingPolicy();
994 }
995
996 bool CSPDirectiveList::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
997 {
998     static NeverDestroyed<String> consoleMessage(ASCIILiteral("Refused to execute JavaScript URL because it violates the following Content Security Policy directive: "));
999     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1000         checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true)
1001         : (m_reportOnly || checkInline(operativeDirective(m_scriptSrc.get())));
1002 }
1003
1004 bool CSPDirectiveList::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1005 {
1006     static NeverDestroyed<String> consoleMessage(ASCIILiteral("Refused to execute inline event handler because it violates the following Content Security Policy directive: "));
1007     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1008         checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true)
1009         : (m_reportOnly || checkInline(operativeDirective(m_scriptSrc.get())));
1010 }
1011
1012 bool CSPDirectiveList::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1013 {
1014     static NeverDestroyed<String> consoleMessage(ASCIILiteral("Refused to execute inline script because it violates the following Content Security Policy directive: "));
1015     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1016         checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true) :
1017         (m_reportOnly || checkInline(operativeDirective(m_scriptSrc.get())));
1018 }
1019
1020 bool CSPDirectiveList::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1021 {
1022     static NeverDestroyed<String> consoleMessage(ASCIILiteral("Refused to apply inline style because it violates the following Content Security Policy directive: "));
1023     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1024         checkInlineAndReportViolation(operativeDirective(m_styleSrc.get()), consoleMessage, contextURL, contextLine, false) :
1025         (m_reportOnly || checkInline(operativeDirective(m_styleSrc.get())));
1026 }
1027
1028 bool CSPDirectiveList::allowEval(JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1029 {
1030     static NeverDestroyed<String> consoleMessage(ASCIILiteral("Refused to evaluate script because it violates the following Content Security Policy directive: "));
1031     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1032         checkEvalAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, String(), WTF::OrdinalNumber::beforeFirst(), state) :
1033         (m_reportOnly || checkEval(operativeDirective(m_scriptSrc.get())));
1034 }
1035
1036 bool CSPDirectiveList::allowPluginType(const String& type, const String& typeAttribute, const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1037 {
1038     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1039         checkMediaTypeAndReportViolation(m_pluginTypes.get(), type, typeAttribute, "Refused to load '" + url.stringCenterEllipsizedToLength() + "' (MIME type '" + typeAttribute + "') because it violates the following Content Security Policy Directive: ") :
1040         (m_reportOnly || checkMediaType(m_pluginTypes.get(), type, typeAttribute));
1041 }
1042
1043 bool CSPDirectiveList::allowScriptFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1044 {
1045     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1046         checkSourceAndReportViolation(operativeDirective(m_scriptSrc.get()), url, scriptSrc) :
1047         (m_reportOnly || checkSource(operativeDirective(m_scriptSrc.get()), url));
1048 }
1049
1050 bool CSPDirectiveList::allowObjectFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1051 {
1052     if (url.isBlankURL())
1053         return true;
1054     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1055         checkSourceAndReportViolation(operativeDirective(m_objectSrc.get()), url, objectSrc) :
1056         (m_reportOnly || checkSource(operativeDirective(m_objectSrc.get()), url));
1057 }
1058
1059 bool CSPDirectiveList::allowChildFrameFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1060 {
1061     if (url.isBlankURL())
1062         return true;
1063     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1064         checkSourceAndReportViolation(operativeDirective(m_frameSrc.get()), url, frameSrc) :
1065         (m_reportOnly || checkSource(operativeDirective(m_frameSrc.get()), url));
1066 }
1067
1068 bool CSPDirectiveList::allowImageFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1069 {
1070     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1071         checkSourceAndReportViolation(operativeDirective(m_imgSrc.get()), url, imgSrc) :
1072         (m_reportOnly || checkSource(operativeDirective(m_imgSrc.get()), url));
1073 }
1074
1075 bool CSPDirectiveList::allowStyleFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1076 {
1077     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1078         checkSourceAndReportViolation(operativeDirective(m_styleSrc.get()), url, styleSrc) :
1079         (m_reportOnly || checkSource(operativeDirective(m_styleSrc.get()), url));
1080 }
1081
1082 bool CSPDirectiveList::allowFontFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1083 {
1084     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1085         checkSourceAndReportViolation(operativeDirective(m_fontSrc.get()), url, fontSrc) :
1086         (m_reportOnly || checkSource(operativeDirective(m_fontSrc.get()), url));
1087 }
1088
1089 bool CSPDirectiveList::allowMediaFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1090 {
1091     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1092         checkSourceAndReportViolation(operativeDirective(m_mediaSrc.get()), url, mediaSrc) :
1093         (m_reportOnly || checkSource(operativeDirective(m_mediaSrc.get()), url));
1094 }
1095
1096 bool CSPDirectiveList::allowConnectToSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1097 {
1098     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1099         checkSourceAndReportViolation(operativeDirective(m_connectSrc.get()), url, connectSrc) :
1100         (m_reportOnly || checkSource(operativeDirective(m_connectSrc.get()), url));
1101 }
1102
1103 void CSPDirectiveList::gatherReportURIs(DOMStringList& list) const
1104 {
1105     for (auto& uri : m_reportURIs)
1106         list.append(uri.string());
1107 }
1108
1109 bool CSPDirectiveList::allowFormAction(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1110 {
1111     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1112         checkSourceAndReportViolation(m_formAction.get(), url, formAction) :
1113         (m_reportOnly || checkSource(m_formAction.get(), url));
1114 }
1115
1116 bool CSPDirectiveList::allowBaseURI(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1117 {
1118     return reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport ?
1119         checkSourceAndReportViolation(m_baseURI.get(), url, baseURI) :
1120         (m_reportOnly || checkSource(m_baseURI.get(), url));
1121 }
1122
1123 // policy            = directive-list
1124 // directive-list    = [ directive *( ";" [ directive ] ) ]
1125 //
1126 void CSPDirectiveList::parse(const String& policy)
1127 {
1128     m_header = policy;
1129     if (policy.isEmpty())
1130         return;
1131
1132     auto characters = StringView(policy).upconvertedCharacters();
1133     const UChar* position = characters;
1134     const UChar* end = position + policy.length();
1135
1136     while (position < end) {
1137         const UChar* directiveBegin = position;
1138         skipUntil(position, end, ';');
1139
1140         String name, value;
1141         if (parseDirective(directiveBegin, position, name, value)) {
1142             ASSERT(!name.isEmpty());
1143             addDirective(name, value);
1144         }
1145
1146         ASSERT(position == end || *position == ';');
1147         skipExactly(position, end, ';');
1148     }
1149 }
1150
1151 // directive         = *WSP [ directive-name [ WSP directive-value ] ]
1152 // directive-name    = 1*( ALPHA / DIGIT / "-" )
1153 // directive-value   = *( WSP / <VCHAR except ";"> )
1154 //
1155 bool CSPDirectiveList::parseDirective(const UChar* begin, const UChar* end, String& name, String& value)
1156 {
1157     ASSERT(name.isEmpty());
1158     ASSERT(value.isEmpty());
1159
1160     const UChar* position = begin;
1161     skipWhile<isASCIISpace>(position, end);
1162
1163     // Empty directive (e.g. ";;;"). Exit early.
1164     if (position == end)
1165         return false;
1166
1167     const UChar* nameBegin = position;
1168     skipWhile<isDirectiveNameCharacter>(position, end);
1169
1170     // The directive-name must be non-empty.
1171     if (nameBegin == position) {
1172         skipWhile<isNotASCIISpace>(position, end);
1173         m_policy->reportUnsupportedDirective(String(nameBegin, position - nameBegin));
1174         return false;
1175     }
1176
1177     name = String(nameBegin, position - nameBegin);
1178
1179     if (position == end)
1180         return true;
1181
1182     if (!skipExactly<isASCIISpace>(position, end)) {
1183         skipWhile<isNotASCIISpace>(position, end);
1184         m_policy->reportUnsupportedDirective(String(nameBegin, position - nameBegin));
1185         return false;
1186     }
1187
1188     skipWhile<isASCIISpace>(position, end);
1189
1190     const UChar* valueBegin = position;
1191     skipWhile<isDirectiveValueCharacter>(position, end);
1192
1193     if (position != end) {
1194         m_policy->reportInvalidDirectiveValueCharacter(name, String(valueBegin, end - valueBegin));
1195         return false;
1196     }
1197
1198     // The directive-value may be empty.
1199     if (valueBegin == position)
1200         return true;
1201
1202     value = String(valueBegin, position - valueBegin);
1203     return true;
1204 }
1205
1206 void CSPDirectiveList::parseReportURI(const String& name, const String& value)
1207 {
1208     if (!m_reportURIs.isEmpty()) {
1209         m_policy->reportDuplicateDirective(name);
1210         return;
1211     }
1212
1213     auto characters = StringView(value).upconvertedCharacters();
1214     const UChar* position = characters;
1215     const UChar* end = position + value.length();
1216
1217     while (position < end) {
1218         skipWhile<isASCIISpace>(position, end);
1219
1220         const UChar* urlBegin = position;
1221         skipWhile<isNotASCIISpace>(position, end);
1222
1223         if (urlBegin < position) {
1224             String url = String(urlBegin, position - urlBegin);
1225             m_reportURIs.append(m_policy->completeURL(url));
1226         }
1227     }
1228 }
1229
1230
1231 template<class CSPDirectiveType>
1232 void CSPDirectiveList::setCSPDirective(const String& name, const String& value, std::unique_ptr<CSPDirectiveType>& directive)
1233 {
1234     if (directive) {
1235         m_policy->reportDuplicateDirective(name);
1236         return;
1237     }
1238     directive = std::make_unique<CSPDirectiveType>(name, value, m_policy);
1239 }
1240
1241 void CSPDirectiveList::applySandboxPolicy(const String& name, const String& sandboxPolicy)
1242 {
1243     if (m_haveSandboxPolicy) {
1244         m_policy->reportDuplicateDirective(name);
1245         return;
1246     }
1247     m_haveSandboxPolicy = true;
1248     String invalidTokens;
1249     m_policy->enforceSandboxFlags(SecurityContext::parseSandboxPolicy(sandboxPolicy, invalidTokens));
1250     if (!invalidTokens.isNull())
1251         m_policy->reportInvalidSandboxFlags(invalidTokens);
1252 }
1253
1254 void CSPDirectiveList::parseReflectedXSS(const String& name, const String& value)
1255 {
1256     if (m_reflectedXSSDisposition != ContentSecurityPolicy::ReflectedXSSUnset) {
1257         m_policy->reportDuplicateDirective(name);
1258         m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid;
1259         return;
1260     }
1261
1262     if (value.isEmpty()) {
1263         m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid;
1264         m_policy->reportInvalidReflectedXSS(value);
1265         return;
1266     }
1267
1268     auto characters = StringView(value).upconvertedCharacters();
1269     const UChar* position = characters;
1270     const UChar* end = position + value.length();
1271
1272     skipWhile<isASCIISpace>(position, end);
1273     const UChar* begin = position;
1274     skipWhile<isNotASCIISpace>(position, end);
1275
1276     // value1
1277     //       ^
1278     if (equalIgnoringCase("allow", begin, position - begin))
1279         m_reflectedXSSDisposition = ContentSecurityPolicy::AllowReflectedXSS;
1280     else if (equalIgnoringCase("filter", begin, position - begin))
1281         m_reflectedXSSDisposition = ContentSecurityPolicy::FilterReflectedXSS;
1282     else if (equalIgnoringCase("block", begin, position - begin))
1283         m_reflectedXSSDisposition = ContentSecurityPolicy::BlockReflectedXSS;
1284     else {
1285         m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid;
1286         m_policy->reportInvalidReflectedXSS(value);
1287         return;
1288     }
1289
1290     skipWhile<isASCIISpace>(position, end);
1291     if (position == end && m_reflectedXSSDisposition != ContentSecurityPolicy::ReflectedXSSUnset)
1292         return;
1293
1294     // value1 value2
1295     //        ^
1296     m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid;
1297     m_policy->reportInvalidReflectedXSS(value);
1298 }
1299
1300 void CSPDirectiveList::addDirective(const String& name, const String& value)
1301 {
1302     ASSERT(!name.isEmpty());
1303
1304     if (equalIgnoringCase(name, defaultSrc))
1305         setCSPDirective<SourceListDirective>(name, value, m_defaultSrc);
1306     else if (equalIgnoringCase(name, scriptSrc))
1307         setCSPDirective<SourceListDirective>(name, value, m_scriptSrc);
1308     else if (equalIgnoringCase(name, objectSrc))
1309         setCSPDirective<SourceListDirective>(name, value, m_objectSrc);
1310     else if (equalIgnoringCase(name, frameSrc))
1311         setCSPDirective<SourceListDirective>(name, value, m_frameSrc);
1312     else if (equalIgnoringCase(name, imgSrc))
1313         setCSPDirective<SourceListDirective>(name, value, m_imgSrc);
1314     else if (equalIgnoringCase(name, styleSrc))
1315         setCSPDirective<SourceListDirective>(name, value, m_styleSrc);
1316     else if (equalIgnoringCase(name, fontSrc))
1317         setCSPDirective<SourceListDirective>(name, value, m_fontSrc);
1318     else if (equalIgnoringCase(name, mediaSrc))
1319         setCSPDirective<SourceListDirective>(name, value, m_mediaSrc);
1320     else if (equalIgnoringCase(name, connectSrc))
1321         setCSPDirective<SourceListDirective>(name, value, m_connectSrc);
1322     else if (equalIgnoringCase(name, sandbox))
1323         applySandboxPolicy(name, value);
1324     else if (equalIgnoringCase(name, reportURI))
1325         parseReportURI(name, value);
1326 #if ENABLE(CSP_NEXT)
1327     else if (m_policy->experimentalFeaturesEnabled()) {
1328         if (equalIgnoringCase(name, baseURI))
1329             setCSPDirective<SourceListDirective>(name, value, m_baseURI);
1330         else if (equalIgnoringCase(name, formAction))
1331             setCSPDirective<SourceListDirective>(name, value, m_formAction);
1332         else if (equalIgnoringCase(name, pluginTypes))
1333             setCSPDirective<MediaListDirective>(name, value, m_pluginTypes);
1334         else if (equalIgnoringCase(name, reflectedXSS))
1335             parseReflectedXSS(name, value);
1336         else
1337             m_policy->reportUnsupportedDirective(name);
1338     }
1339 #endif
1340     else
1341         m_policy->reportUnsupportedDirective(name);
1342 }
1343
1344 ContentSecurityPolicy::ContentSecurityPolicy(ScriptExecutionContext* scriptExecutionContext)
1345     : m_scriptExecutionContext(scriptExecutionContext)
1346     , m_overrideInlineStyleAllowed(false)
1347 {
1348 }
1349
1350 ContentSecurityPolicy::~ContentSecurityPolicy()
1351 {
1352 }
1353
1354 void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other) 
1355 {
1356     ASSERT(m_policies.isEmpty());
1357     for (auto& policy : other->m_policies)
1358         didReceiveHeader(policy->header(), policy->headerType());
1359 }
1360
1361 void ContentSecurityPolicy::didReceiveHeader(const String& header, HeaderType type)
1362 {
1363     // RFC2616, section 4.2 specifies that headers appearing multiple times can
1364     // be combined with a comma. Walk the header string, and parse each comma
1365     // separated chunk as a separate header.
1366     auto characters = StringView(header).upconvertedCharacters();
1367     const UChar* begin = characters;
1368     const UChar* position = begin;
1369     const UChar* end = begin + header.length();
1370     while (position < end) {
1371         skipUntil(position, end, ',');
1372
1373         // header1,header2 OR header1
1374         //        ^                  ^
1375         std::unique_ptr<CSPDirectiveList> policy = CSPDirectiveList::create(this, String(begin, position - begin), type);
1376         if (!policy->allowEval(0, ContentSecurityPolicy::ReportingStatus::SuppressReport))
1377             m_scriptExecutionContext->disableEval(policy->evalDisabledErrorMessage());
1378
1379         m_policies.append(policy.release());
1380
1381         // Skip the comma, and begin the next header from the current position.
1382         ASSERT(position == end || *position == ',');
1383         skipExactly(position, end, ',');
1384         begin = position;
1385     }
1386 }
1387
1388 void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value)
1389 {
1390     m_overrideInlineStyleAllowed = value;
1391 }
1392
1393 const String& ContentSecurityPolicy::deprecatedHeader() const
1394 {
1395     return m_policies.isEmpty() ? emptyString() : m_policies[0]->header();
1396 }
1397
1398 ContentSecurityPolicy::HeaderType ContentSecurityPolicy::deprecatedHeaderType() const
1399 {
1400     return m_policies.isEmpty() ? Enforce : m_policies[0]->headerType();
1401 }
1402
1403 template<bool (CSPDirectiveList::*allowed)(ContentSecurityPolicy::ReportingStatus) const>
1404 bool isAllowedByAll(const CSPDirectiveListVector& policies, ContentSecurityPolicy::ReportingStatus reportingStatus)
1405 {
1406     for (auto& policy : policies) {
1407         if (!(policy.get()->*allowed)(reportingStatus))
1408             return false;
1409     }
1410     return true;
1411 }
1412
1413 template<bool (CSPDirectiveList::*allowed)(JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus) const>
1414 bool isAllowedByAllWithState(const CSPDirectiveListVector& policies, JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus reportingStatus)
1415 {
1416     for (auto& policy : policies) {
1417         if (!(policy.get()->*allowed)(state, reportingStatus))
1418             return false;
1419     }
1420     return true;
1421 }
1422
1423 template<bool (CSPDirectiveList::*allowed)(const String&, const WTF::OrdinalNumber&, ContentSecurityPolicy::ReportingStatus) const>
1424 bool isAllowedByAllWithContext(const CSPDirectiveListVector& policies, const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus)
1425 {
1426     for (auto& policy : policies) {
1427         if (!(policy.get()->*allowed)(contextURL, contextLine, reportingStatus))
1428             return false;
1429     }
1430     return true;
1431 }
1432
1433 template<bool (CSPDirectiveList::*allowFromURL)(const URL&, ContentSecurityPolicy::ReportingStatus) const>
1434 bool isAllowedByAllWithURL(const CSPDirectiveListVector& policies, const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus)
1435 {
1436     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
1437         return true;
1438
1439     for (auto& policy : policies) {
1440         if (!(policy.get()->*allowFromURL)(url, reportingStatus))
1441             return false;
1442     }
1443     return true;
1444 }
1445
1446 bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1447 {
1448     return overrideContentSecurityPolicy || isAllowedByAllWithContext<&CSPDirectiveList::allowJavaScriptURLs>(m_policies, contextURL, contextLine, reportingStatus);
1449 }
1450
1451 bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1452 {
1453     return overrideContentSecurityPolicy || isAllowedByAllWithContext<&CSPDirectiveList::allowInlineEventHandlers>(m_policies, contextURL, contextLine, reportingStatus);
1454 }
1455
1456 bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1457 {
1458     return overrideContentSecurityPolicy || isAllowedByAllWithContext<&CSPDirectiveList::allowInlineScript>(m_policies, contextURL, contextLine, reportingStatus);
1459 }
1460
1461 bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1462 {
1463     return overrideContentSecurityPolicy || m_overrideInlineStyleAllowed || isAllowedByAllWithContext<&CSPDirectiveList::allowInlineStyle>(m_policies, contextURL, contextLine, reportingStatus);
1464 }
1465
1466 bool ContentSecurityPolicy::allowEval(JSC::ExecState* state, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1467 {
1468     return overrideContentSecurityPolicy || isAllowedByAllWithState<&CSPDirectiveList::allowEval>(m_policies, state, reportingStatus);
1469 }
1470
1471 String ContentSecurityPolicy::evalDisabledErrorMessage() const
1472 {
1473     for (auto& policy : m_policies) {
1474         if (!policy->allowEval(0, ContentSecurityPolicy::ReportingStatus::SuppressReport))
1475             return policy->evalDisabledErrorMessage();
1476     }
1477     return String();
1478 }
1479
1480 bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1481 {
1482     if (overrideContentSecurityPolicy)
1483         return true;
1484     for (auto& policy : m_policies) {
1485         if (!policy->allowPluginType(type, typeAttribute, url, reportingStatus))
1486             return false;
1487     }
1488     return true;
1489 }
1490
1491 bool ContentSecurityPolicy::allowScriptFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1492 {
1493     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus);
1494 }
1495
1496 bool ContentSecurityPolicy::allowObjectFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1497 {
1498     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&CSPDirectiveList::allowObjectFromSource>(m_policies, url, reportingStatus);
1499 }
1500
1501 bool ContentSecurityPolicy::allowChildFrameFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1502 {
1503     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&CSPDirectiveList::allowChildFrameFromSource>(m_policies, url, reportingStatus);
1504 }
1505
1506 bool ContentSecurityPolicy::allowImageFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1507 {
1508     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&CSPDirectiveList::allowImageFromSource>(m_policies, url, reportingStatus);
1509 }
1510
1511 bool ContentSecurityPolicy::allowStyleFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1512 {
1513     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&CSPDirectiveList::allowStyleFromSource>(m_policies, url, reportingStatus);
1514 }
1515
1516 bool ContentSecurityPolicy::allowFontFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1517 {
1518     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&CSPDirectiveList::allowFontFromSource>(m_policies, url, reportingStatus);
1519 }
1520
1521 bool ContentSecurityPolicy::allowMediaFromSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1522 {
1523     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&CSPDirectiveList::allowMediaFromSource>(m_policies, url, reportingStatus);
1524 }
1525
1526 bool ContentSecurityPolicy::allowConnectToSource(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1527 {
1528     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&CSPDirectiveList::allowConnectToSource>(m_policies, url, reportingStatus);
1529 }
1530
1531 bool ContentSecurityPolicy::allowFormAction(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1532 {
1533     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&CSPDirectiveList::allowFormAction>(m_policies, url, reportingStatus);
1534 }
1535
1536 bool ContentSecurityPolicy::allowBaseURI(const URL& url, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1537 {
1538     return overrideContentSecurityPolicy || isAllowedByAllWithURL<&CSPDirectiveList::allowBaseURI>(m_policies, url, reportingStatus);
1539 }
1540
1541 bool ContentSecurityPolicy::isActive() const
1542 {
1543     return !m_policies.isEmpty();
1544 }
1545
1546 ContentSecurityPolicy::ReflectedXSSDisposition ContentSecurityPolicy::reflectedXSSDisposition() const
1547 {
1548     ReflectedXSSDisposition disposition = ReflectedXSSUnset;
1549     for (auto& policy : m_policies) {
1550         if (policy->reflectedXSSDisposition() > disposition)
1551             disposition = std::max(disposition, policy->reflectedXSSDisposition());
1552     }
1553     return disposition;
1554 }
1555
1556 void ContentSecurityPolicy::gatherReportURIs(DOMStringList& list) const
1557 {
1558     for (auto& policy : m_policies)
1559         policy->gatherReportURIs(list);
1560 }
1561
1562 SecurityOrigin* ContentSecurityPolicy::securityOrigin() const
1563 {
1564     return m_scriptExecutionContext->securityOrigin();
1565 }
1566
1567 const URL& ContentSecurityPolicy::url() const
1568 {
1569     return m_scriptExecutionContext->url();
1570 }
1571
1572 URL ContentSecurityPolicy::completeURL(const String& url) const
1573 {
1574     return m_scriptExecutionContext->completeURL(url);
1575 }
1576
1577 void ContentSecurityPolicy::enforceSandboxFlags(SandboxFlags mask) const
1578 {
1579     m_scriptExecutionContext->enforceSandboxFlags(mask);
1580 }
1581
1582 static String stripURLForUseInReport(Document& document, const URL& url)
1583 {
1584     if (!url.isValid())
1585         return String();
1586     if (!url.isHierarchical() || url.protocolIs("file"))
1587         return url.protocol();
1588     return document.securityOrigin()->canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url).get().toString();
1589 }
1590
1591 #if ENABLE(CSP_NEXT)
1592 static void gatherSecurityPolicyViolationEventData(SecurityPolicyViolationEventInit& init, Document& document, const String& directiveText, const String& effectiveDirective, const URL& blockedURL, const String& header)
1593 {
1594     init.documentURI = document.url().string();
1595     init.referrer = document.referrer();
1596     init.blockedURI = stripURLForUseInReport(document, blockedURL);
1597     init.violatedDirective = directiveText;
1598     init.effectiveDirective = effectiveDirective;
1599     init.originalPolicy = header;
1600     init.sourceFile = String();
1601     init.lineNumber = 0;
1602
1603     RefPtr<ScriptCallStack> stack = createScriptCallStack(JSMainThreadExecState::currentState(), 2);
1604     const ScriptCallFrame* callFrame = stack->firstNonNativeCallFrame();
1605     if (callFrame && callFrame->lineNumber()) {
1606         URL source = URL(URL(), callFrame->sourceURL());
1607         init.sourceFile = stripURLForUseInReport(document, source);
1608         init.lineNumber = callFrame->lineNumber();
1609     }
1610 }
1611 #endif
1612
1613 void ContentSecurityPolicy::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const URL& blockedURL, const Vector<URL>& reportURIs, const String& header, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
1614 {
1615     logToConsole(consoleMessage, contextURL, contextLine, state);
1616
1617     // FIXME: Support sending reports from worker.
1618     if (!is<Document>(*m_scriptExecutionContext))
1619         return;
1620
1621     Document& document = downcast<Document>(*m_scriptExecutionContext);
1622     Frame* frame = document.frame();
1623     if (!frame)
1624         return;
1625
1626 #if ENABLE(CSP_NEXT)
1627     if (experimentalFeaturesEnabled()) {
1628         // 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.
1629         SecurityPolicyViolationEventInit init;
1630         gatherSecurityPolicyViolationEventData(init, document, directiveText, effectiveDirective, blockedURL, header);
1631         document.enqueueDocumentEvent(SecurityPolicyViolationEvent::create(eventNames().securitypolicyviolationEvent, init));
1632     }
1633 #endif
1634
1635     if (reportURIs.isEmpty())
1636         return;
1637
1638     // We need to be careful here when deciding what information to send to the
1639     // report-uri. Currently, we send only the current document's URL and the
1640     // directive that was violated. The document's URL is safe to send because
1641     // it's the document itself that's requesting that it be sent. You could
1642     // make an argument that we shouldn't send HTTPS document URLs to HTTP
1643     // report-uris (for the same reasons that we suppress the Referer in that
1644     // case), but the Referer is sent implicitly whereas this request is only
1645     // sent explicitly. As for which directive was violated, that's pretty
1646     // harmless information.
1647
1648     RefPtr<InspectorObject> cspReport = InspectorObject::create();
1649     cspReport->setString(ASCIILiteral("document-uri"), document.url().strippedForUseAsReferrer());
1650     cspReport->setString(ASCIILiteral("referrer"), document.referrer());
1651     cspReport->setString(ASCIILiteral("violated-directive"), directiveText);
1652 #if ENABLE(CSP_NEXT)
1653     if (experimentalFeaturesEnabled())
1654         cspReport->setString(ASCIILiteral("effective-directive"), effectiveDirective);
1655 #else
1656     UNUSED_PARAM(effectiveDirective);
1657 #endif
1658     cspReport->setString(ASCIILiteral("original-policy"), header);
1659     cspReport->setString(ASCIILiteral("blocked-uri"), stripURLForUseInReport(document, blockedURL));
1660
1661     RefPtr<ScriptCallStack> stack = createScriptCallStack(JSMainThreadExecState::currentState(), 2);
1662     const ScriptCallFrame* callFrame = stack->firstNonNativeCallFrame();
1663     if (callFrame && callFrame->lineNumber()) {
1664         URL source = URL(URL(), callFrame->sourceURL());
1665         cspReport->setString(ASCIILiteral("source-file"), stripURLForUseInReport(document, source));
1666         cspReport->setInteger(ASCIILiteral("line-number"), callFrame->lineNumber());
1667     }
1668
1669     RefPtr<InspectorObject> reportObject = InspectorObject::create();
1670     reportObject->setObject(ASCIILiteral("csp-report"), cspReport.release());
1671
1672     RefPtr<FormData> report = FormData::create(reportObject->toJSONString().utf8());
1673
1674     for (const auto& url : reportURIs)
1675         PingLoader::sendViolationReport(*frame, url, report);
1676 }
1677
1678 void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) const
1679 {
1680     static NeverDestroyed<String> allow(ASCIILiteral("allow"));
1681     static NeverDestroyed<String> options(ASCIILiteral("options"));
1682     static NeverDestroyed<String> policyURI(ASCIILiteral("policy-uri"));
1683     static NeverDestroyed<String> allowMessage(ASCIILiteral("The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect."));
1684     static NeverDestroyed<String> optionsMessage(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."));
1685     static NeverDestroyed<String> policyURIMessage(ASCIILiteral("The 'policy-uri' directive has been removed from the specification. Please specify a complete policy via the Content-Security-Policy header."));
1686
1687     String message;
1688     if (equalIgnoringCase(name, allow))
1689         message = allowMessage;
1690     else if (equalIgnoringCase(name, options))
1691         message = optionsMessage;
1692     else if (equalIgnoringCase(name, policyURI))
1693         message = policyURIMessage;
1694     else
1695         message = makeString("Unrecognized Content-Security-Policy directive '", name, "'.\n");
1696
1697     logToConsole(message);
1698 }
1699
1700 void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const
1701 {
1702     String message = "The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?";
1703     logToConsole(message);
1704 }
1705
1706 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const
1707 {
1708     String message = makeString("Ignoring duplicate Content-Security-Policy directive '", name, "'.\n");
1709     logToConsole(message);
1710 }
1711
1712 void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) const
1713 {
1714     String message;
1715     if (pluginType.isNull())
1716         message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n";
1717     else
1718         message = makeString("Invalid plugin type in 'plugin-types' Content Security Policy directive: '", pluginType, "'.\n");
1719     logToConsole(message);
1720 }
1721
1722 void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags) const
1723 {
1724     logToConsole("Error while parsing the 'sandbox' Content Security Policy directive: " + invalidFlags);
1725 }
1726
1727 void ContentSecurityPolicy::reportInvalidReflectedXSS(const String& invalidValue) const
1728 {
1729     logToConsole("The 'reflected-xss' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Value values are \"allow\", \"filter\", and \"block\".");
1730 }
1731
1732 void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) const
1733 {
1734     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.");
1735     logToConsole(message);
1736 }
1737
1738 void ContentSecurityPolicy::reportInvalidPathCharacter(const String& directiveName, const String& value, const char invalidChar) const
1739 {
1740     ASSERT(invalidChar == '#' || invalidChar == '?');
1741
1742     String ignoring;
1743     if (invalidChar == '?')
1744         ignoring = "The query component, including the '?', will be ignored.";
1745     else
1746         ignoring = "The fragment identifier, including the '#', will be ignored.";
1747
1748     String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains a source with an invalid path: '", value, "'. ", ignoring);
1749     logToConsole(message);
1750 }
1751
1752 void ContentSecurityPolicy::reportInvalidSourceExpression(const String& directiveName, const String& source) const
1753 {
1754     String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains an invalid source: '", source, "'. It will be ignored.");
1755     if (equalIgnoringCase(source, "'none'"))
1756         message = makeString(message, " Note that 'none' has no effect unless it is the only expression in the source list.");
1757     logToConsole(message);
1758 }
1759
1760 void ContentSecurityPolicy::reportMissingReportURI(const String& policy) const
1761 {
1762     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.");
1763 }
1764
1765 void ContentSecurityPolicy::logToConsole(const String& message, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
1766 {
1767     // FIXME: <http://webkit.org/b/114317> ContentSecurityPolicy::logToConsole should include a column number
1768     m_scriptExecutionContext->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, contextURL, contextLine.oneBasedInt(), 0, state);
1769 }
1770
1771 void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String& directiveText) const
1772 {
1773     InspectorInstrumentation::scriptExecutionBlockedByCSP(m_scriptExecutionContext, directiveText);
1774 }
1775
1776 bool ContentSecurityPolicy::experimentalFeaturesEnabled() const
1777 {
1778 #if ENABLE(CSP_NEXT)
1779     return RuntimeEnabledFeatures::sharedFeatures().experimentalContentSecurityPolicyFeaturesEnabled();
1780 #else
1781     return false;
1782 #endif
1783 }
1784
1785 bool ContentSecurityPolicy::shouldBypassMainWorldContentSecurityPolicy(ScriptExecutionContext& context)
1786 {
1787     if (is<Document>(context)) {
1788         auto& document = downcast<Document>(context);
1789         return document.frame() && document.frame()->script().shouldBypassMainWorldContentSecurityPolicy();
1790     }
1791     
1792     return false;
1793 }
1794     
1795 }