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