Home | History | Annotate | Download | only in csp
      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/csp/ContentSecurityPolicy.h"
     28 
     29 #include "bindings/v8/ScriptCallStackFactory.h"
     30 #include "bindings/v8/ScriptController.h"
     31 #include "core/dom/DOMStringList.h"
     32 #include "core/dom/Document.h"
     33 #include "core/events/SecurityPolicyViolationEvent.h"
     34 #include "core/frame/LocalDOMWindow.h"
     35 #include "core/frame/LocalFrame.h"
     36 #include "core/frame/UseCounter.h"
     37 #include "core/frame/csp/CSPDirectiveList.h"
     38 #include "core/frame/csp/CSPSource.h"
     39 #include "core/frame/csp/CSPSourceList.h"
     40 #include "core/frame/csp/MediaListDirective.h"
     41 #include "core/frame/csp/SourceListDirective.h"
     42 #include "core/inspector/InspectorInstrumentation.h"
     43 #include "core/inspector/ScriptCallStack.h"
     44 #include "core/loader/DocumentLoader.h"
     45 #include "core/loader/PingLoader.h"
     46 #include "platform/Crypto.h"
     47 #include "platform/JSONValues.h"
     48 #include "platform/NotImplemented.h"
     49 #include "platform/ParsingUtilities.h"
     50 #include "platform/RuntimeEnabledFeatures.h"
     51 #include "platform/network/ContentSecurityPolicyParsers.h"
     52 #include "platform/network/ContentSecurityPolicyResponseHeaders.h"
     53 #include "platform/network/FormData.h"
     54 #include "platform/network/ResourceResponse.h"
     55 #include "platform/weborigin/KURL.h"
     56 #include "platform/weborigin/KnownPorts.h"
     57 #include "platform/weborigin/SchemeRegistry.h"
     58 #include "platform/weborigin/SecurityOrigin.h"
     59 #include "public/platform/Platform.h"
     60 #include "public/platform/WebArrayBuffer.h"
     61 #include "public/platform/WebCrypto.h"
     62 #include "public/platform/WebCryptoAlgorithm.h"
     63 #include "wtf/StringHasher.h"
     64 #include "wtf/text/StringBuilder.h"
     65 #include "wtf/text/StringUTF8Adaptor.h"
     66 
     67 namespace WebCore {
     68 
     69 // CSP 1.0 Directives
     70 const char ContentSecurityPolicy::ConnectSrc[] = "connect-src";
     71 const char ContentSecurityPolicy::DefaultSrc[] = "default-src";
     72 const char ContentSecurityPolicy::FontSrc[] = "font-src";
     73 const char ContentSecurityPolicy::FrameSrc[] = "frame-src";
     74 const char ContentSecurityPolicy::ImgSrc[] = "img-src";
     75 const char ContentSecurityPolicy::MediaSrc[] = "media-src";
     76 const char ContentSecurityPolicy::ObjectSrc[] = "object-src";
     77 const char ContentSecurityPolicy::ReportURI[] = "report-uri";
     78 const char ContentSecurityPolicy::Sandbox[] = "sandbox";
     79 const char ContentSecurityPolicy::ScriptSrc[] = "script-src";
     80 const char ContentSecurityPolicy::StyleSrc[] = "style-src";
     81 
     82 // CSP 1.1 Directives
     83 const char ContentSecurityPolicy::BaseURI[] = "base-uri";
     84 const char ContentSecurityPolicy::ChildSrc[] = "child-src";
     85 const char ContentSecurityPolicy::FormAction[] = "form-action";
     86 const char ContentSecurityPolicy::FrameAncestors[] = "frame-ancestors";
     87 const char ContentSecurityPolicy::PluginTypes[] = "plugin-types";
     88 const char ContentSecurityPolicy::ReflectedXSS[] = "reflected-xss";
     89 const char ContentSecurityPolicy::Referrer[] = "referrer";
     90 
     91 bool ContentSecurityPolicy::isDirectiveName(const String& name)
     92 {
     93     return (equalIgnoringCase(name, ConnectSrc)
     94         || equalIgnoringCase(name, DefaultSrc)
     95         || equalIgnoringCase(name, FontSrc)
     96         || equalIgnoringCase(name, FrameSrc)
     97         || equalIgnoringCase(name, ImgSrc)
     98         || equalIgnoringCase(name, MediaSrc)
     99         || equalIgnoringCase(name, ObjectSrc)
    100         || equalIgnoringCase(name, ReportURI)
    101         || equalIgnoringCase(name, Sandbox)
    102         || equalIgnoringCase(name, ScriptSrc)
    103         || equalIgnoringCase(name, StyleSrc)
    104         || equalIgnoringCase(name, BaseURI)
    105         || equalIgnoringCase(name, ChildSrc)
    106         || equalIgnoringCase(name, FormAction)
    107         || equalIgnoringCase(name, FrameAncestors)
    108         || equalIgnoringCase(name, PluginTypes)
    109         || equalIgnoringCase(name, ReflectedXSS)
    110         || equalIgnoringCase(name, Referrer)
    111     );
    112 }
    113 
    114 static UseCounter::Feature getUseCounterType(ContentSecurityPolicyHeaderType type)
    115 {
    116     switch (type) {
    117     case ContentSecurityPolicyHeaderTypeEnforce:
    118         return UseCounter::ContentSecurityPolicy;
    119     case ContentSecurityPolicyHeaderTypeReport:
    120         return UseCounter::ContentSecurityPolicyReportOnly;
    121     }
    122     ASSERT_NOT_REACHED();
    123     return UseCounter::NumberOfFeatures;
    124 }
    125 
    126 static ReferrerPolicy mergeReferrerPolicies(ReferrerPolicy a, ReferrerPolicy b)
    127 {
    128     if (a != b)
    129         return ReferrerPolicyNever;
    130     return a;
    131 }
    132 
    133 ContentSecurityPolicy::ContentSecurityPolicy(ExecutionContext* executionContext)
    134     : m_executionContext(executionContext)
    135     , m_overrideInlineStyleAllowed(false)
    136     , m_scriptHashAlgorithmsUsed(ContentSecurityPolicyHashAlgorithmNone)
    137     , m_styleHashAlgorithmsUsed(ContentSecurityPolicyHashAlgorithmNone)
    138 {
    139 }
    140 
    141 ContentSecurityPolicy::~ContentSecurityPolicy()
    142 {
    143 }
    144 
    145 void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other)
    146 {
    147     ASSERT(m_policies.isEmpty());
    148     for (CSPDirectiveListVector::const_iterator iter = other->m_policies.begin(); iter != other->m_policies.end(); ++iter)
    149         addPolicyFromHeaderValue((*iter)->header(), (*iter)->headerType(), (*iter)->headerSource());
    150 }
    151 
    152 void ContentSecurityPolicy::didReceiveHeaders(const ContentSecurityPolicyResponseHeaders& headers)
    153 {
    154     if (!headers.contentSecurityPolicy().isEmpty())
    155         didReceiveHeader(headers.contentSecurityPolicy(), ContentSecurityPolicyHeaderTypeEnforce, ContentSecurityPolicyHeaderSourceHTTP);
    156     if (!headers.contentSecurityPolicyReportOnly().isEmpty())
    157         didReceiveHeader(headers.contentSecurityPolicyReportOnly(), ContentSecurityPolicyHeaderTypeReport, ContentSecurityPolicyHeaderSourceHTTP);
    158 }
    159 
    160 void ContentSecurityPolicy::didReceiveHeader(const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicyHeaderSource source)
    161 {
    162     addPolicyFromHeaderValue(header, type, source);
    163 }
    164 
    165 void ContentSecurityPolicy::addPolicyFromHeaderValue(const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicyHeaderSource source)
    166 {
    167     Document* document = this->document();
    168     if (document) {
    169         UseCounter::count(*document, getUseCounterType(type));
    170 
    171         // CSP 1.1 defines report-only in a <meta> element as invalid. Measure for now, disable in experimental mode.
    172         if (source == ContentSecurityPolicyHeaderSourceMeta && type == ContentSecurityPolicyHeaderTypeReport) {
    173             UseCounter::count(*document, UseCounter::ContentSecurityPolicyReportOnlyInMeta);
    174             if (experimentalFeaturesEnabled()) {
    175                 reportReportOnlyInMeta(header);
    176                 return;
    177             }
    178         }
    179     }
    180 
    181 
    182     Vector<UChar> characters;
    183     header.appendTo(characters);
    184 
    185     const UChar* begin = characters.data();
    186     const UChar* end = begin + characters.size();
    187 
    188     // RFC2616, section 4.2 specifies that headers appearing multiple times can
    189     // be combined with a comma. Walk the header string, and parse each comma
    190     // separated chunk as a separate header.
    191     const UChar* position = begin;
    192     while (position < end) {
    193         skipUntil<UChar>(position, end, ',');
    194 
    195         // header1,header2 OR header1
    196         //        ^                  ^
    197         OwnPtr<CSPDirectiveList> policy = CSPDirectiveList::create(this, begin, position, type, source);
    198 
    199         // 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.
    200         if (!policy->allowEval(0, SuppressReport))
    201             m_executionContext->disableEval(policy->evalDisabledErrorMessage());
    202 
    203         m_policies.append(policy.release());
    204 
    205         // Skip the comma, and begin the next header from the current position.
    206         ASSERT(position == end || *position == ',');
    207         skipExactly<UChar>(position, end, ',');
    208         begin = position;
    209     }
    210 
    211     if (document && type != ContentSecurityPolicyHeaderTypeReport && didSetReferrerPolicy())
    212         document->setReferrerPolicy(referrerPolicy());
    213 }
    214 
    215 void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value)
    216 {
    217     m_overrideInlineStyleAllowed = value;
    218 }
    219 
    220 const String& ContentSecurityPolicy::deprecatedHeader() const
    221 {
    222     return m_policies.isEmpty() ? emptyString() : m_policies[0]->header();
    223 }
    224 
    225 ContentSecurityPolicyHeaderType ContentSecurityPolicy::deprecatedHeaderType() const
    226 {
    227     return m_policies.isEmpty() ? ContentSecurityPolicyHeaderTypeEnforce : m_policies[0]->headerType();
    228 }
    229 
    230 template<bool (CSPDirectiveList::*allowed)(ContentSecurityPolicy::ReportingStatus) const>
    231 bool isAllowedByAll(const CSPDirectiveListVector& policies, ContentSecurityPolicy::ReportingStatus reportingStatus)
    232 {
    233     for (size_t i = 0; i < policies.size(); ++i) {
    234         if (!(policies[i].get()->*allowed)(reportingStatus))
    235             return false;
    236     }
    237     return true;
    238 }
    239 
    240 template<bool (CSPDirectiveList::*allowed)(ScriptState* scriptState, ContentSecurityPolicy::ReportingStatus) const>
    241 bool isAllowedByAllWithState(const CSPDirectiveListVector& policies, ScriptState* scriptState, ContentSecurityPolicy::ReportingStatus reportingStatus)
    242 {
    243     for (size_t i = 0; i < policies.size(); ++i) {
    244         if (!(policies[i].get()->*allowed)(scriptState, reportingStatus))
    245             return false;
    246     }
    247     return true;
    248 }
    249 
    250 template<bool (CSPDirectiveList::*allowed)(const String&, const WTF::OrdinalNumber&, ContentSecurityPolicy::ReportingStatus) const>
    251 bool isAllowedByAllWithContext(const CSPDirectiveListVector& policies, const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus)
    252 {
    253     for (size_t i = 0; i < policies.size(); ++i) {
    254         if (!(policies[i].get()->*allowed)(contextURL, contextLine, reportingStatus))
    255             return false;
    256     }
    257     return true;
    258 }
    259 
    260 template<bool (CSPDirectiveList::*allowed)(const String&) const>
    261 bool isAllowedByAllWithNonce(const CSPDirectiveListVector& policies, const String& nonce)
    262 {
    263     for (size_t i = 0; i < policies.size(); ++i) {
    264         if (!(policies[i].get()->*allowed)(nonce))
    265             return false;
    266     }
    267     return true;
    268 }
    269 
    270 template<bool (CSPDirectiveList::*allowed)(const CSPHashValue&) const>
    271 bool isAllowedByAllWithHash(const CSPDirectiveListVector& policies, const CSPHashValue& hashValue)
    272 {
    273     for (size_t i = 0; i < policies.size(); ++i) {
    274         if (!(policies[i].get()->*allowed)(hashValue))
    275             return false;
    276     }
    277     return true;
    278 }
    279 
    280 template<bool (CSPDirectiveList::*allowFromURL)(const KURL&, ContentSecurityPolicy::ReportingStatus) const>
    281 bool isAllowedByAllWithURL(const CSPDirectiveListVector& policies, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus)
    282 {
    283     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
    284         return true;
    285 
    286     for (size_t i = 0; i < policies.size(); ++i) {
    287         if (!(policies[i].get()->*allowFromURL)(url, reportingStatus))
    288             return false;
    289     }
    290     return true;
    291 }
    292 
    293 template<bool (CSPDirectiveList::*allowed)(LocalFrame*, ContentSecurityPolicy::ReportingStatus) const>
    294 bool isAllowedByAllWithFrame(const CSPDirectiveListVector& policies, LocalFrame* frame, ContentSecurityPolicy::ReportingStatus reportingStatus)
    295 {
    296     for (size_t i = 0; i < policies.size(); ++i) {
    297         if (!(policies[i].get()->*allowed)(frame, reportingStatus))
    298             return false;
    299     }
    300     return true;
    301 }
    302 
    303 template<bool (CSPDirectiveList::*allowed)(const CSPHashValue&) const>
    304 bool checkDigest(const String& source, uint8_t hashAlgorithmsUsed, const CSPDirectiveListVector& policies)
    305 {
    306     // Any additions or subtractions from this struct should also modify the
    307     // respective entries in the kSupportedPrefixes array in
    308     // CSPSourceList::parseHash().
    309     static const struct {
    310         ContentSecurityPolicyHashAlgorithm cspHashAlgorithm;
    311         HashAlgorithm algorithm;
    312     } kAlgorithmMap[] = {
    313         { ContentSecurityPolicyHashAlgorithmSha1, HashAlgorithmSha1 },
    314         { ContentSecurityPolicyHashAlgorithmSha256, HashAlgorithmSha256 },
    315         { ContentSecurityPolicyHashAlgorithmSha384, HashAlgorithmSha384 },
    316         { ContentSecurityPolicyHashAlgorithmSha512, HashAlgorithmSha512 }
    317     };
    318 
    319     // Only bother normalizing the source/computing digests if there are any checks to be done.
    320     if (hashAlgorithmsUsed == ContentSecurityPolicyHashAlgorithmNone)
    321         return false;
    322 
    323     StringUTF8Adaptor normalizedSource(source, StringUTF8Adaptor::Normalize, WTF::EntitiesForUnencodables);
    324 
    325     // See comment in CSPSourceList::parseHash about why we are using this sizeof
    326     // calculation instead of WTF_ARRAY_LENGTH.
    327     for (size_t i = 0; i < (sizeof(kAlgorithmMap) / sizeof(kAlgorithmMap[0])); i++) {
    328         DigestValue digest;
    329         if (kAlgorithmMap[i].cspHashAlgorithm & hashAlgorithmsUsed) {
    330             bool digestSuccess = computeDigest(kAlgorithmMap[i].algorithm, normalizedSource.data(), normalizedSource.length(), digest);
    331             if (digestSuccess && isAllowedByAllWithHash<allowed>(policies, CSPHashValue(kAlgorithmMap[i].cspHashAlgorithm, digest)))
    332                 return true;
    333         }
    334     }
    335 
    336     return false;
    337 }
    338 
    339 bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    340 {
    341     return isAllowedByAllWithContext<&CSPDirectiveList::allowJavaScriptURLs>(m_policies, contextURL, contextLine, reportingStatus);
    342 }
    343 
    344 bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    345 {
    346     return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineEventHandlers>(m_policies, contextURL, contextLine, reportingStatus);
    347 }
    348 
    349 bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    350 {
    351     return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineScript>(m_policies, contextURL, contextLine, reportingStatus);
    352 }
    353 
    354 bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    355 {
    356     if (m_overrideInlineStyleAllowed)
    357         return true;
    358     return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineStyle>(m_policies, contextURL, contextLine, reportingStatus);
    359 }
    360 
    361 bool ContentSecurityPolicy::allowEval(ScriptState* scriptState, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    362 {
    363     return isAllowedByAllWithState<&CSPDirectiveList::allowEval>(m_policies, scriptState, reportingStatus);
    364 }
    365 
    366 String ContentSecurityPolicy::evalDisabledErrorMessage() const
    367 {
    368     for (size_t i = 0; i < m_policies.size(); ++i) {
    369         if (!m_policies[i]->allowEval(0, SuppressReport))
    370             return m_policies[i]->evalDisabledErrorMessage();
    371     }
    372     return String();
    373 }
    374 
    375 bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    376 {
    377     for (size_t i = 0; i < m_policies.size(); ++i) {
    378         if (!m_policies[i]->allowPluginType(type, typeAttribute, url, reportingStatus))
    379             return false;
    380     }
    381     return true;
    382 }
    383 
    384 bool ContentSecurityPolicy::allowScriptFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    385 {
    386     return isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus);
    387 }
    388 
    389 bool ContentSecurityPolicy::allowScriptNonce(const String& nonce) const
    390 {
    391     return isAllowedByAllWithNonce<&CSPDirectiveList::allowScriptNonce>(m_policies, nonce);
    392 }
    393 
    394 bool ContentSecurityPolicy::allowStyleNonce(const String& nonce) const
    395 {
    396     return isAllowedByAllWithNonce<&CSPDirectiveList::allowStyleNonce>(m_policies, nonce);
    397 }
    398 
    399 bool ContentSecurityPolicy::allowScriptHash(const String& source) const
    400 {
    401     return checkDigest<&CSPDirectiveList::allowScriptHash>(source, m_scriptHashAlgorithmsUsed, m_policies);
    402 }
    403 
    404 bool ContentSecurityPolicy::allowStyleHash(const String& source) const
    405 {
    406     return checkDigest<&CSPDirectiveList::allowStyleHash>(source, m_styleHashAlgorithmsUsed, m_policies);
    407 }
    408 
    409 void ContentSecurityPolicy::usesScriptHashAlgorithms(uint8_t algorithms)
    410 {
    411     m_scriptHashAlgorithmsUsed |= algorithms;
    412 }
    413 
    414 void ContentSecurityPolicy::usesStyleHashAlgorithms(uint8_t algorithms)
    415 {
    416     m_styleHashAlgorithmsUsed |= algorithms;
    417 }
    418 
    419 bool ContentSecurityPolicy::allowObjectFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    420 {
    421     return isAllowedByAllWithURL<&CSPDirectiveList::allowObjectFromSource>(m_policies, url, reportingStatus);
    422 }
    423 
    424 bool ContentSecurityPolicy::allowChildFrameFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    425 {
    426     return isAllowedByAllWithURL<&CSPDirectiveList::allowChildFrameFromSource>(m_policies, url, reportingStatus);
    427 }
    428 
    429 bool ContentSecurityPolicy::allowImageFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    430 {
    431     return isAllowedByAllWithURL<&CSPDirectiveList::allowImageFromSource>(m_policies, url, reportingStatus);
    432 }
    433 
    434 bool ContentSecurityPolicy::allowStyleFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    435 {
    436     return isAllowedByAllWithURL<&CSPDirectiveList::allowStyleFromSource>(m_policies, url, reportingStatus);
    437 }
    438 
    439 bool ContentSecurityPolicy::allowFontFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    440 {
    441     return isAllowedByAllWithURL<&CSPDirectiveList::allowFontFromSource>(m_policies, url, reportingStatus);
    442 }
    443 
    444 bool ContentSecurityPolicy::allowMediaFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    445 {
    446     return isAllowedByAllWithURL<&CSPDirectiveList::allowMediaFromSource>(m_policies, url, reportingStatus);
    447 }
    448 
    449 bool ContentSecurityPolicy::allowConnectToSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    450 {
    451     return isAllowedByAllWithURL<&CSPDirectiveList::allowConnectToSource>(m_policies, url, reportingStatus);
    452 }
    453 
    454 bool ContentSecurityPolicy::allowFormAction(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    455 {
    456     return isAllowedByAllWithURL<&CSPDirectiveList::allowFormAction>(m_policies, url, reportingStatus);
    457 }
    458 
    459 bool ContentSecurityPolicy::allowBaseURI(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    460 {
    461     return isAllowedByAllWithURL<&CSPDirectiveList::allowBaseURI>(m_policies, url, reportingStatus);
    462 }
    463 
    464 bool ContentSecurityPolicy::allowAncestors(LocalFrame* frame, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    465 {
    466     return isAllowedByAllWithFrame<&CSPDirectiveList::allowAncestors>(m_policies, frame, reportingStatus);
    467 }
    468 
    469 bool ContentSecurityPolicy::allowChildContextFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    470 {
    471     return isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>(m_policies, url, reportingStatus);
    472 }
    473 
    474 bool ContentSecurityPolicy::allowWorkerContextFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    475 {
    476     // CSP 1.1 moves workers from 'script-src' to the new 'child-src'. Measure the impact of this backwards-incompatible change.
    477     if (m_executionContext->isDocument()) {
    478         Document* document = static_cast<Document*>(m_executionContext);
    479         UseCounter::count(*document, UseCounter::WorkerSubjectToCSP);
    480         if (isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>(m_policies, url, SuppressReport) && !isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, SuppressReport))
    481             UseCounter::count(*document, UseCounter::WorkerAllowedByChildBlockedByScript);
    482     }
    483 
    484     return experimentalFeaturesEnabled() ?
    485         isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>(m_policies, url, reportingStatus) :
    486         isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus);
    487 }
    488 
    489 bool ContentSecurityPolicy::isActive() const
    490 {
    491     return !m_policies.isEmpty();
    492 }
    493 
    494 ReflectedXSSDisposition ContentSecurityPolicy::reflectedXSSDisposition() const
    495 {
    496     ReflectedXSSDisposition disposition = ReflectedXSSUnset;
    497     for (size_t i = 0; i < m_policies.size(); ++i) {
    498         if (m_policies[i]->reflectedXSSDisposition() > disposition)
    499             disposition = std::max(disposition, m_policies[i]->reflectedXSSDisposition());
    500     }
    501     return disposition;
    502 }
    503 
    504 ReferrerPolicy ContentSecurityPolicy::referrerPolicy() const
    505 {
    506     ReferrerPolicy policy = ReferrerPolicyDefault;
    507     bool first = true;
    508     for (size_t i = 0; i < m_policies.size(); ++i) {
    509         if (m_policies[i]->didSetReferrerPolicy()) {
    510             if (first)
    511                 policy = m_policies[i]->referrerPolicy();
    512             else
    513                 policy = mergeReferrerPolicies(policy, m_policies[i]->referrerPolicy());
    514         }
    515     }
    516     return policy;
    517 }
    518 
    519 bool ContentSecurityPolicy::didSetReferrerPolicy() const
    520 {
    521     for (size_t i = 0; i < m_policies.size(); ++i) {
    522         if (m_policies[i]->didSetReferrerPolicy())
    523             return true;
    524     }
    525     return false;
    526 }
    527 
    528 SecurityOrigin* ContentSecurityPolicy::securityOrigin() const
    529 {
    530     return m_executionContext->securityContext().securityOrigin();
    531 }
    532 
    533 const KURL ContentSecurityPolicy::url() const
    534 {
    535     return m_executionContext->contextURL();
    536 }
    537 
    538 KURL ContentSecurityPolicy::completeURL(const String& url) const
    539 {
    540     return m_executionContext->contextCompleteURL(url);
    541 }
    542 
    543 void ContentSecurityPolicy::enforceSandboxFlags(SandboxFlags mask) const
    544 {
    545     if (Document* document = this->document())
    546         document->enforceSandboxFlags(mask);
    547 }
    548 
    549 static String stripURLForUseInReport(Document* document, const KURL& url)
    550 {
    551     if (!url.isValid())
    552         return String();
    553     if (!url.isHierarchical() || url.protocolIs("file"))
    554         return url.protocol();
    555     return document->securityOrigin()->canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url)->toString();
    556 }
    557 
    558 static void gatherSecurityPolicyViolationEventData(SecurityPolicyViolationEventInit& init, Document* document, const String& directiveText, const String& effectiveDirective, const KURL& blockedURL, const String& header)
    559 {
    560     init.documentURI = document->url().string();
    561     init.referrer = document->referrer();
    562     init.blockedURI = stripURLForUseInReport(document, blockedURL);
    563     init.violatedDirective = directiveText;
    564     init.effectiveDirective = effectiveDirective;
    565     init.originalPolicy = header;
    566     init.sourceFile = String();
    567     init.lineNumber = 0;
    568     init.columnNumber = 0;
    569     init.statusCode = 0;
    570 
    571     if (!SecurityOrigin::isSecure(document->url()) && document->loader())
    572         init.statusCode = document->loader()->response().httpStatusCode();
    573 
    574     RefPtrWillBeRawPtr<ScriptCallStack> stack = createScriptCallStack(1, false);
    575     if (!stack)
    576         return;
    577 
    578     const ScriptCallFrame& callFrame = stack->at(0);
    579 
    580     if (callFrame.lineNumber()) {
    581         KURL source = KURL(ParsedURLString, callFrame.sourceURL());
    582         init.sourceFile = stripURLForUseInReport(document, source);
    583         init.lineNumber = callFrame.lineNumber();
    584         init.columnNumber = callFrame.columnNumber();
    585     }
    586 }
    587 
    588 void ContentSecurityPolicy::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL, const Vector<KURL>& reportURIs, const String& header)
    589 {
    590     // FIXME: Support sending reports from worker.
    591     if (!m_executionContext->isDocument())
    592         return;
    593 
    594     Document* document = this->document();
    595     LocalFrame* frame = document->frame();
    596     if (!frame)
    597         return;
    598 
    599     SecurityPolicyViolationEventInit violationData;
    600     gatherSecurityPolicyViolationEventData(violationData, document, directiveText, effectiveDirective, blockedURL, header);
    601 
    602     if (experimentalFeaturesEnabled())
    603         frame->domWindow()->enqueueDocumentEvent(SecurityPolicyViolationEvent::create(EventTypeNames::securitypolicyviolation, violationData));
    604 
    605     if (reportURIs.isEmpty())
    606         return;
    607 
    608     // We need to be careful here when deciding what information to send to the
    609     // report-uri. Currently, we send only the current document's URL and the
    610     // directive that was violated. The document's URL is safe to send because
    611     // it's the document itself that's requesting that it be sent. You could
    612     // make an argument that we shouldn't send HTTPS document URLs to HTTP
    613     // report-uris (for the same reasons that we supress the Referer in that
    614     // case), but the Referer is sent implicitly whereas this request is only
    615     // sent explicitly. As for which directive was violated, that's pretty
    616     // harmless information.
    617 
    618     RefPtr<JSONObject> cspReport = JSONObject::create();
    619     cspReport->setString("document-uri", violationData.documentURI);
    620     cspReport->setString("referrer", violationData.referrer);
    621     cspReport->setString("violated-directive", violationData.violatedDirective);
    622     if (experimentalFeaturesEnabled())
    623         cspReport->setString("effective-directive", violationData.effectiveDirective);
    624     cspReport->setString("original-policy", violationData.originalPolicy);
    625     cspReport->setString("blocked-uri", violationData.blockedURI);
    626     if (!violationData.sourceFile.isEmpty() && violationData.lineNumber) {
    627         cspReport->setString("source-file", violationData.sourceFile);
    628         cspReport->setNumber("line-number", violationData.lineNumber);
    629         cspReport->setNumber("column-number", violationData.columnNumber);
    630     }
    631     cspReport->setNumber("status-code", violationData.statusCode);
    632 
    633     RefPtr<JSONObject> reportObject = JSONObject::create();
    634     reportObject->setObject("csp-report", cspReport.release());
    635     String stringifiedReport = reportObject->toJSONString();
    636 
    637     if (!shouldSendViolationReport(stringifiedReport))
    638         return;
    639 
    640     RefPtr<FormData> report = FormData::create(stringifiedReport.utf8());
    641 
    642     for (size_t i = 0; i < reportURIs.size(); ++i)
    643         PingLoader::sendViolationReport(frame, reportURIs[i], report, PingLoader::ContentSecurityPolicyViolationReport);
    644 
    645     didSendViolationReport(stringifiedReport);
    646 }
    647 
    648 void ContentSecurityPolicy::reportInvalidReferrer(const String& invalidValue) const
    649 {
    650     logToConsole("The 'referrer' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Valid values are \"always\", \"default\", \"never\", and \"origin\".");
    651 }
    652 
    653 void ContentSecurityPolicy::reportReportOnlyInMeta(const String& header) const
    654 {
    655     logToConsole("The report-only Content Security Policy '" + header + "' was delivered via a <meta> element, which is disallowed. The policy has been ignored.");
    656 }
    657 
    658 void ContentSecurityPolicy::reportMetaOutsideHead(const String& header) const
    659 {
    660     logToConsole("The Content Security Policy '" + header + "' was delivered via a <meta> element outside the document's <head>, which is disallowed. The policy has been ignored.");
    661 }
    662 
    663 void ContentSecurityPolicy::reportInvalidInReportOnly(const String& name) const
    664 {
    665     logToConsole("The Content Security Policy directive '" + name + "' is ignored when delivered in a report-only policy.");
    666 }
    667 
    668 void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) const
    669 {
    670     DEFINE_STATIC_LOCAL(String, allow, ("allow"));
    671     DEFINE_STATIC_LOCAL(String, options, ("options"));
    672     DEFINE_STATIC_LOCAL(String, policyURI, ("policy-uri"));
    673     DEFINE_STATIC_LOCAL(String, allowMessage, ("The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect."));
    674     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."));
    675     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."));
    676 
    677     String message = "Unrecognized Content-Security-Policy directive '" + name + "'.\n";
    678     if (equalIgnoringCase(name, allow))
    679         message = allowMessage;
    680     else if (equalIgnoringCase(name, options))
    681         message = optionsMessage;
    682     else if (equalIgnoringCase(name, policyURI))
    683         message = policyURIMessage;
    684 
    685     logToConsole(message);
    686 }
    687 
    688 void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const
    689 {
    690     String message = "The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?";
    691     logToConsole(message);
    692 }
    693 
    694 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const
    695 {
    696     String message = "Ignoring duplicate Content-Security-Policy directive '" + name + "'.\n";
    697     logToConsole(message);
    698 }
    699 
    700 void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) const
    701 {
    702     String message;
    703     if (pluginType.isNull())
    704         message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n";
    705     else
    706         message = "Invalid plugin type in 'plugin-types' Content Security Policy directive: '" + pluginType + "'.\n";
    707     logToConsole(message);
    708 }
    709 
    710 void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags) const
    711 {
    712     logToConsole("Error while parsing the 'sandbox' Content Security Policy directive: " + invalidFlags);
    713 }
    714 
    715 void ContentSecurityPolicy::reportInvalidReflectedXSS(const String& invalidValue) const
    716 {
    717     logToConsole("The 'reflected-xss' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Valid values are \"allow\", \"filter\", and \"block\".");
    718 }
    719 
    720 void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) const
    721 {
    722     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.";
    723     logToConsole(message);
    724 }
    725 
    726 void ContentSecurityPolicy::reportInvalidPathCharacter(const String& directiveName, const String& value, const char invalidChar) const
    727 {
    728     ASSERT(invalidChar == '#' || invalidChar == '?');
    729 
    730     String ignoring = "The fragment identifier, including the '#', will be ignored.";
    731     if (invalidChar == '?')
    732         ignoring = "The query component, including the '?', will be ignored.";
    733     String message = "The source list for Content Security Policy directive '" + directiveName + "' contains a source with an invalid path: '" + value + "'. " + ignoring;
    734     logToConsole(message);
    735 }
    736 
    737 void ContentSecurityPolicy::reportInvalidSourceExpression(const String& directiveName, const String& source) const
    738 {
    739     String message = "The source list for Content Security Policy directive '" + directiveName + "' contains an invalid source: '" + source + "'. It will be ignored.";
    740     if (equalIgnoringCase(source, "'none'"))
    741         message = message + " Note that 'none' has no effect unless it is the only expression in the source list.";
    742     logToConsole(message);
    743 }
    744 
    745 void ContentSecurityPolicy::reportMissingReportURI(const String& policy) const
    746 {
    747     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.");
    748 }
    749 
    750 void ContentSecurityPolicy::logToConsole(const String& message) const
    751 {
    752     m_executionContext->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message);
    753 }
    754 
    755 void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String& directiveText) const
    756 {
    757     m_executionContext->reportBlockedScriptExecutionToInspector(directiveText);
    758 }
    759 
    760 bool ContentSecurityPolicy::experimentalFeaturesEnabled() const
    761 {
    762     return RuntimeEnabledFeatures::experimentalContentSecurityPolicyFeaturesEnabled();
    763 }
    764 
    765 bool ContentSecurityPolicy::shouldBypassMainWorld(ExecutionContext* context)
    766 {
    767     if (context && context->isDocument()) {
    768         Document* document = toDocument(context);
    769         if (document->frame())
    770             return document->frame()->script().shouldBypassMainWorldContentSecurityPolicy();
    771     }
    772     return false;
    773 }
    774 
    775 bool ContentSecurityPolicy::shouldSendViolationReport(const String& report) const
    776 {
    777     // Collisions have no security impact, so we can save space by storing only the string's hash rather than the whole report.
    778     return !m_violationReportsSent.contains(report.impl()->hash());
    779 }
    780 
    781 void ContentSecurityPolicy::didSendViolationReport(const String& report)
    782 {
    783     m_violationReportsSent.add(report.impl()->hash());
    784 }
    785 
    786 } // namespace WebCore
    787