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