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