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