Home | History | Annotate | Download | only in csp
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "config.h"
      6 #include "core/frame/csp/CSPDirectiveList.h"
      7 
      8 #include "core/frame/LocalFrame.h"
      9 #include "platform/ParsingUtilities.h"
     10 #include "platform/weborigin/KURL.h"
     11 #include "wtf/text/WTFString.h"
     12 
     13 namespace WebCore {
     14 
     15 CSPDirectiveList::CSPDirectiveList(ContentSecurityPolicy* policy, ContentSecurityPolicyHeaderType type, ContentSecurityPolicyHeaderSource source)
     16     : m_policy(policy)
     17     , m_headerType(type)
     18     , m_headerSource(source)
     19     , m_reportOnly(false)
     20     , m_haveSandboxPolicy(false)
     21     , m_reflectedXSSDisposition(ReflectedXSSUnset)
     22     , m_didSetReferrerPolicy(false)
     23     , m_referrerPolicy(ReferrerPolicyDefault)
     24 {
     25     m_reportOnly = type == ContentSecurityPolicyHeaderTypeReport;
     26 }
     27 
     28 PassOwnPtr<CSPDirectiveList> CSPDirectiveList::create(ContentSecurityPolicy* policy, const UChar* begin, const UChar* end, ContentSecurityPolicyHeaderType type, ContentSecurityPolicyHeaderSource source)
     29 {
     30     OwnPtr<CSPDirectiveList> directives = adoptPtr(new CSPDirectiveList(policy, type, source));
     31     directives->parse(begin, end);
     32 
     33     if (!directives->checkEval(directives->operativeDirective(directives->m_scriptSrc.get()))) {
     34         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";
     35         directives->setEvalDisabledErrorMessage(message);
     36     }
     37 
     38     if (directives->isReportOnly() && directives->reportURIs().isEmpty())
     39         policy->reportMissingReportURI(String(begin, end - begin));
     40 
     41     return directives.release();
     42 }
     43 
     44 void CSPDirectiveList::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL) const
     45 {
     46     String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage;
     47     m_policy->executionContext()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message);
     48     m_policy->reportViolation(directiveText, effectiveDirective, message, blockedURL, m_reportURIs, m_header);
     49 }
     50 
     51 void CSPDirectiveList::reportViolationWithLocation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL, const String& contextURL, const WTF::OrdinalNumber& contextLine) const
     52 {
     53     String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage;
     54     m_policy->executionContext()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message, contextURL, contextLine.oneBasedInt());
     55     m_policy->reportViolation(directiveText, effectiveDirective, message, blockedURL, m_reportURIs, m_header);
     56 }
     57 
     58 void CSPDirectiveList::reportViolationWithState(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL, ScriptState* scriptState) const
     59 {
     60     String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage;
     61     m_policy->executionContext()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message, scriptState);
     62     m_policy->reportViolation(directiveText, effectiveDirective, message, blockedURL, m_reportURIs, m_header);
     63 }
     64 
     65 bool CSPDirectiveList::checkEval(SourceListDirective* directive) const
     66 {
     67     return !directive || directive->allowEval();
     68 }
     69 
     70 bool CSPDirectiveList::checkInline(SourceListDirective* directive) const
     71 {
     72     return !directive || (directive->allowInline() && !directive->isHashOrNoncePresent());
     73 }
     74 
     75 bool CSPDirectiveList::checkNonce(SourceListDirective* directive, const String& nonce) const
     76 {
     77     return !directive || directive->allowNonce(nonce);
     78 }
     79 
     80 bool CSPDirectiveList::checkHash(SourceListDirective* directive, const CSPHashValue& hashValue) const
     81 {
     82     return !directive || directive->allowHash(hashValue);
     83 }
     84 
     85 bool CSPDirectiveList::checkSource(SourceListDirective* directive, const KURL& url) const
     86 {
     87     return !directive || directive->allows(url);
     88 }
     89 
     90 bool CSPDirectiveList::checkAncestors(SourceListDirective* directive, LocalFrame* frame) const
     91 {
     92     if (!frame || !directive)
     93         return true;
     94 
     95     for (Frame* current = frame->tree().parent(); current; current = current->tree().parent()) {
     96         // FIXME: To make this work for out-of-process iframes, we need to propagate URL information of ancestor frames across processes.
     97         if (!current->isLocalFrame() || !directive->allows(toLocalFrame(current)->document()->url()))
     98             return false;
     99     }
    100     return true;
    101 }
    102 
    103 bool CSPDirectiveList::checkMediaType(MediaListDirective* directive, const String& type, const String& typeAttribute) const
    104 {
    105     if (!directive)
    106         return true;
    107     if (typeAttribute.isEmpty() || typeAttribute.stripWhiteSpace() != type)
    108         return false;
    109     return directive->allows(type);
    110 }
    111 
    112 SourceListDirective* CSPDirectiveList::operativeDirective(SourceListDirective* directive) const
    113 {
    114     return directive ? directive : m_defaultSrc.get();
    115 }
    116 
    117 SourceListDirective* CSPDirectiveList::operativeDirective(SourceListDirective* directive, SourceListDirective* override) const
    118 {
    119     return directive ? directive : override;
    120 }
    121 
    122 bool CSPDirectiveList::checkEvalAndReportViolation(SourceListDirective* directive, const String& consoleMessage, ScriptState* scriptState) const
    123 {
    124     if (checkEval(directive))
    125         return true;
    126 
    127     String suffix = String();
    128     if (directive == m_defaultSrc)
    129         suffix = " Note that 'script-src' was not explicitly set, so 'default-src' is used as a fallback.";
    130 
    131     reportViolationWithState(directive->text(), ContentSecurityPolicy::ScriptSrc, consoleMessage + "\"" + directive->text() + "\"." + suffix + "\n", KURL(), scriptState);
    132     if (!m_reportOnly) {
    133         m_policy->reportBlockedScriptExecutionToInspector(directive->text());
    134         return false;
    135     }
    136     return true;
    137 }
    138 
    139 bool CSPDirectiveList::checkMediaTypeAndReportViolation(MediaListDirective* directive, const String& type, const String& typeAttribute, const String& consoleMessage) const
    140 {
    141     if (checkMediaType(directive, type, typeAttribute))
    142         return true;
    143 
    144     String message = consoleMessage + "\'" + directive->text() + "\'.";
    145     if (typeAttribute.isEmpty())
    146         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]\" ...>').";
    147 
    148     reportViolation(directive->text(), ContentSecurityPolicy::PluginTypes, message + "\n", KURL());
    149     return denyIfEnforcingPolicy();
    150 }
    151 
    152 bool CSPDirectiveList::checkInlineAndReportViolation(SourceListDirective* directive, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, bool isScript) const
    153 {
    154     if (checkInline(directive))
    155         return true;
    156 
    157     String suffix = String();
    158     if (directive->allowInline() && directive->isHashOrNoncePresent()) {
    159         // If inline is allowed, but a hash or nonce is present, we ignore 'unsafe-inline'. Throw a reasonable error.
    160         suffix = " Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list.";
    161     } else {
    162         suffix = " Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.";
    163         if (directive == m_defaultSrc)
    164             suffix = suffix + " Note also that '" + String(isScript ? "script" : "style") + "-src' was not explicitly set, so 'default-src' is used as a fallback.";
    165     }
    166 
    167     reportViolationWithLocation(directive->text(), isScript ? ContentSecurityPolicy::ScriptSrc : ContentSecurityPolicy::StyleSrc, consoleMessage + "\"" + directive->text() + "\"." + suffix + "\n", KURL(), contextURL, contextLine);
    168 
    169     if (!m_reportOnly) {
    170         if (isScript)
    171             m_policy->reportBlockedScriptExecutionToInspector(directive->text());
    172         return false;
    173     }
    174     return true;
    175 }
    176 
    177 bool CSPDirectiveList::checkSourceAndReportViolation(SourceListDirective* directive, const KURL& url, const String& effectiveDirective) const
    178 {
    179     if (checkSource(directive, url))
    180         return true;
    181 
    182     String prefix;
    183     if (ContentSecurityPolicy::BaseURI == effectiveDirective)
    184         prefix = "Refused to set the document's base URI to '";
    185     else if (ContentSecurityPolicy::ChildSrc == effectiveDirective)
    186         prefix = "Refused to create a child context containing '";
    187     else if (ContentSecurityPolicy::ConnectSrc == effectiveDirective)
    188         prefix = "Refused to connect to '";
    189     else if (ContentSecurityPolicy::FontSrc == effectiveDirective)
    190         prefix = "Refused to load the font '";
    191     else if (ContentSecurityPolicy::FormAction == effectiveDirective)
    192         prefix = "Refused to send form data to '";
    193     else if (ContentSecurityPolicy::FrameSrc == effectiveDirective)
    194         prefix = "Refused to frame '";
    195     else if (ContentSecurityPolicy::ImgSrc == effectiveDirective)
    196         prefix = "Refused to load the image '";
    197     else if (ContentSecurityPolicy::MediaSrc == effectiveDirective)
    198         prefix = "Refused to load media from '";
    199     else if (ContentSecurityPolicy::ObjectSrc == effectiveDirective)
    200         prefix = "Refused to load plugin data from '";
    201     else if (ContentSecurityPolicy::ScriptSrc == effectiveDirective)
    202         prefix = "Refused to load the script '";
    203     else if (ContentSecurityPolicy::StyleSrc == effectiveDirective)
    204         prefix = "Refused to load the stylesheet '";
    205 
    206     String suffix = String();
    207     if (directive == m_defaultSrc)
    208         suffix = " Note that '" + effectiveDirective + "' was not explicitly set, so 'default-src' is used as a fallback.";
    209 
    210     reportViolation(directive->text(), effectiveDirective, prefix + url.elidedString() + "' because it violates the following Content Security Policy directive: \"" + directive->text() + "\"." + suffix + "\n", url);
    211     return denyIfEnforcingPolicy();
    212 }
    213 
    214 bool CSPDirectiveList::checkAncestorsAndReportViolation(SourceListDirective* directive, LocalFrame* frame) const
    215 {
    216     if (checkAncestors(directive, frame))
    217         return true;
    218 
    219     reportViolation(directive->text(), "frame-ancestors", "Refused to display '" + frame->document()->url().elidedString() + " in a frame because an ancestor violates the following Content Security Policy directive: \"" + directive->text() + "\".", frame->document()->url());
    220     return denyIfEnforcingPolicy();
    221 }
    222 
    223 bool CSPDirectiveList::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    224 {
    225     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute JavaScript URL because it violates the following Content Security Policy directive: "));
    226     if (reportingStatus == ContentSecurityPolicy::SendReport)
    227         return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true);
    228 
    229     return checkInline(operativeDirective(m_scriptSrc.get()));
    230 }
    231 
    232 bool CSPDirectiveList::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    233 {
    234     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute inline event handler because it violates the following Content Security Policy directive: "));
    235     if (reportingStatus == ContentSecurityPolicy::SendReport)
    236         return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true);
    237     return checkInline(operativeDirective(m_scriptSrc.get()));
    238 }
    239 
    240 bool CSPDirectiveList::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    241 {
    242     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute inline script because it violates the following Content Security Policy directive: "));
    243     return reportingStatus == ContentSecurityPolicy::SendReport ?
    244         checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true) :
    245         checkInline(operativeDirective(m_scriptSrc.get()));
    246 }
    247 
    248 bool CSPDirectiveList::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    249 {
    250     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to apply inline style because it violates the following Content Security Policy directive: "));
    251     return reportingStatus == ContentSecurityPolicy::SendReport ?
    252         checkInlineAndReportViolation(operativeDirective(m_styleSrc.get()), consoleMessage, contextURL, contextLine, false) :
    253         checkInline(operativeDirective(m_styleSrc.get()));
    254 }
    255 
    256 bool CSPDirectiveList::allowEval(ScriptState* scriptState, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    257 {
    258     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: "));
    259 
    260     return reportingStatus == ContentSecurityPolicy::SendReport ?
    261         checkEvalAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, scriptState) :
    262         checkEval(operativeDirective(m_scriptSrc.get()));
    263 }
    264 
    265 bool CSPDirectiveList::allowPluginType(const String& type, const String& typeAttribute, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    266 {
    267     return reportingStatus == ContentSecurityPolicy::SendReport ?
    268         checkMediaTypeAndReportViolation(m_pluginTypes.get(), type, typeAttribute, "Refused to load '" + url.elidedString() + "' (MIME type '" + typeAttribute + "') because it violates the following Content Security Policy Directive: ") :
    269         checkMediaType(m_pluginTypes.get(), type, typeAttribute);
    270 }
    271 
    272 bool CSPDirectiveList::allowScriptFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    273 {
    274     return reportingStatus == ContentSecurityPolicy::SendReport ?
    275         checkSourceAndReportViolation(operativeDirective(m_scriptSrc.get()), url, ContentSecurityPolicy::ScriptSrc) :
    276         checkSource(operativeDirective(m_scriptSrc.get()), url);
    277 }
    278 
    279 bool CSPDirectiveList::allowObjectFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    280 {
    281     if (url.protocolIsAbout())
    282         return true;
    283     return reportingStatus == ContentSecurityPolicy::SendReport ?
    284         checkSourceAndReportViolation(operativeDirective(m_objectSrc.get()), url, ContentSecurityPolicy::ObjectSrc) :
    285         checkSource(operativeDirective(m_objectSrc.get()), url);
    286 }
    287 
    288 bool CSPDirectiveList::allowChildFrameFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    289 {
    290     if (url.protocolIsAbout())
    291         return true;
    292 
    293     // 'frame-src' is the only directive which overrides something other than the default sources.
    294     // It overrides 'child-src', which overrides the default sources. So, we do this nested set
    295     // of calls to 'operativeDirective()' to grab 'frame-src' if it exists, 'child-src' if it
    296     // doesn't, and 'defaut-src' if neither are available.
    297     //
    298     // All of this only applies, of course, if we're in CSP 1.1. In CSP 1.0, 'frame-src'
    299     // overrides 'default-src' directly.
    300     SourceListDirective* whichDirective = m_policy->experimentalFeaturesEnabled() ?
    301         operativeDirective(m_frameSrc.get(), operativeDirective(m_childSrc.get())) :
    302         operativeDirective(m_frameSrc.get());
    303 
    304     return reportingStatus == ContentSecurityPolicy::SendReport ?
    305         checkSourceAndReportViolation(whichDirective, url, ContentSecurityPolicy::FrameSrc) :
    306         checkSource(whichDirective, url);
    307 }
    308 
    309 bool CSPDirectiveList::allowImageFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    310 {
    311     return reportingStatus == ContentSecurityPolicy::SendReport ?
    312         checkSourceAndReportViolation(operativeDirective(m_imgSrc.get()), url, ContentSecurityPolicy::ImgSrc) :
    313         checkSource(operativeDirective(m_imgSrc.get()), url);
    314 }
    315 
    316 bool CSPDirectiveList::allowStyleFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    317 {
    318     return reportingStatus == ContentSecurityPolicy::SendReport ?
    319         checkSourceAndReportViolation(operativeDirective(m_styleSrc.get()), url, ContentSecurityPolicy::StyleSrc) :
    320         checkSource(operativeDirective(m_styleSrc.get()), url);
    321 }
    322 
    323 bool CSPDirectiveList::allowFontFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    324 {
    325     return reportingStatus == ContentSecurityPolicy::SendReport ?
    326         checkSourceAndReportViolation(operativeDirective(m_fontSrc.get()), url, ContentSecurityPolicy::FontSrc) :
    327         checkSource(operativeDirective(m_fontSrc.get()), url);
    328 }
    329 
    330 bool CSPDirectiveList::allowMediaFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    331 {
    332     return reportingStatus == ContentSecurityPolicy::SendReport ?
    333         checkSourceAndReportViolation(operativeDirective(m_mediaSrc.get()), url, ContentSecurityPolicy::MediaSrc) :
    334         checkSource(operativeDirective(m_mediaSrc.get()), url);
    335 }
    336 
    337 bool CSPDirectiveList::allowConnectToSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    338 {
    339     return reportingStatus == ContentSecurityPolicy::SendReport ?
    340         checkSourceAndReportViolation(operativeDirective(m_connectSrc.get()), url, ContentSecurityPolicy::ConnectSrc) :
    341         checkSource(operativeDirective(m_connectSrc.get()), url);
    342 }
    343 
    344 bool CSPDirectiveList::allowFormAction(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    345 {
    346     return reportingStatus == ContentSecurityPolicy::SendReport ?
    347         checkSourceAndReportViolation(m_formAction.get(), url, ContentSecurityPolicy::FormAction) :
    348         checkSource(m_formAction.get(), url);
    349 }
    350 
    351 bool CSPDirectiveList::allowBaseURI(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    352 {
    353     return reportingStatus == ContentSecurityPolicy::SendReport ?
    354         checkSourceAndReportViolation(m_baseURI.get(), url, ContentSecurityPolicy::BaseURI) :
    355         checkSource(m_baseURI.get(), url);
    356 }
    357 
    358 bool CSPDirectiveList::allowAncestors(LocalFrame* frame, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    359 {
    360     return reportingStatus == ContentSecurityPolicy::SendReport ?
    361         checkAncestorsAndReportViolation(m_frameAncestors.get(), frame) :
    362         checkAncestors(m_frameAncestors.get(), frame);
    363 }
    364 
    365 bool CSPDirectiveList::allowChildContextFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
    366 {
    367     return reportingStatus == ContentSecurityPolicy::SendReport ?
    368         checkSourceAndReportViolation(operativeDirective(m_childSrc.get()), url, ContentSecurityPolicy::ChildSrc) :
    369         checkSource(operativeDirective(m_childSrc.get()), url);
    370 }
    371 
    372 bool CSPDirectiveList::allowScriptNonce(const String& nonce) const
    373 {
    374     return checkNonce(operativeDirective(m_scriptSrc.get()), nonce);
    375 }
    376 
    377 bool CSPDirectiveList::allowStyleNonce(const String& nonce) const
    378 {
    379     return checkNonce(operativeDirective(m_styleSrc.get()), nonce);
    380 }
    381 
    382 bool CSPDirectiveList::allowScriptHash(const CSPHashValue& hashValue) const
    383 {
    384     return checkHash(operativeDirective(m_scriptSrc.get()), hashValue);
    385 }
    386 
    387 bool CSPDirectiveList::allowStyleHash(const CSPHashValue& hashValue) const
    388 {
    389     return checkHash(operativeDirective(m_styleSrc.get()), hashValue);
    390 }
    391 
    392 // policy            = directive-list
    393 // directive-list    = [ directive *( ";" [ directive ] ) ]
    394 //
    395 void CSPDirectiveList::parse(const UChar* begin, const UChar* end)
    396 {
    397     m_header = String(begin, end - begin);
    398 
    399     if (begin == end)
    400         return;
    401 
    402     const UChar* position = begin;
    403     while (position < end) {
    404         const UChar* directiveBegin = position;
    405         skipUntil<UChar>(position, end, ';');
    406 
    407         String name, value;
    408         if (parseDirective(directiveBegin, position, name, value)) {
    409             ASSERT(!name.isEmpty());
    410             addDirective(name, value);
    411         }
    412 
    413         ASSERT(position == end || *position == ';');
    414         skipExactly<UChar>(position, end, ';');
    415     }
    416 }
    417 
    418 // directive         = *WSP [ directive-name [ WSP directive-value ] ]
    419 // directive-name    = 1*( ALPHA / DIGIT / "-" )
    420 // directive-value   = *( WSP / <VCHAR except ";"> )
    421 //
    422 bool CSPDirectiveList::parseDirective(const UChar* begin, const UChar* end, String& name, String& value)
    423 {
    424     ASSERT(name.isEmpty());
    425     ASSERT(value.isEmpty());
    426 
    427     const UChar* position = begin;
    428     skipWhile<UChar, isASCIISpace>(position, end);
    429 
    430     // Empty directive (e.g. ";;;"). Exit early.
    431     if (position == end)
    432         return false;
    433 
    434     const UChar* nameBegin = position;
    435     skipWhile<UChar, isCSPDirectiveNameCharacter>(position, end);
    436 
    437     // The directive-name must be non-empty.
    438     if (nameBegin == position) {
    439         skipWhile<UChar, isNotASCIISpace>(position, end);
    440         m_policy->reportUnsupportedDirective(String(nameBegin, position - nameBegin));
    441         return false;
    442     }
    443 
    444     name = String(nameBegin, position - nameBegin);
    445 
    446     if (position == end)
    447         return true;
    448 
    449     if (!skipExactly<UChar, isASCIISpace>(position, end)) {
    450         skipWhile<UChar, isNotASCIISpace>(position, end);
    451         m_policy->reportUnsupportedDirective(String(nameBegin, position - nameBegin));
    452         return false;
    453     }
    454 
    455     skipWhile<UChar, isASCIISpace>(position, end);
    456 
    457     const UChar* valueBegin = position;
    458     skipWhile<UChar, isCSPDirectiveValueCharacter>(position, end);
    459 
    460     if (position != end) {
    461         m_policy->reportInvalidDirectiveValueCharacter(name, String(valueBegin, end - valueBegin));
    462         return false;
    463     }
    464 
    465     // The directive-value may be empty.
    466     if (valueBegin == position)
    467         return true;
    468 
    469     value = String(valueBegin, position - valueBegin);
    470     return true;
    471 }
    472 
    473 void CSPDirectiveList::parseReportURI(const String& name, const String& value)
    474 {
    475     if (!m_reportURIs.isEmpty()) {
    476         m_policy->reportDuplicateDirective(name);
    477         return;
    478     }
    479 
    480     Vector<UChar> characters;
    481     value.appendTo(characters);
    482 
    483     const UChar* position = characters.data();
    484     const UChar* end = position + characters.size();
    485 
    486     while (position < end) {
    487         skipWhile<UChar, isASCIISpace>(position, end);
    488 
    489         const UChar* urlBegin = position;
    490         skipWhile<UChar, isNotASCIISpace>(position, end);
    491 
    492         if (urlBegin < position) {
    493             String url = String(urlBegin, position - urlBegin);
    494             m_reportURIs.append(m_policy->completeURL(url));
    495         }
    496     }
    497 }
    498 
    499 
    500 template<class CSPDirectiveType>
    501 void CSPDirectiveList::setCSPDirective(const String& name, const String& value, OwnPtr<CSPDirectiveType>& directive)
    502 {
    503     if (directive) {
    504         m_policy->reportDuplicateDirective(name);
    505         return;
    506     }
    507     directive = adoptPtr(new CSPDirectiveType(name, value, m_policy));
    508 }
    509 
    510 void CSPDirectiveList::applySandboxPolicy(const String& name, const String& sandboxPolicy)
    511 {
    512     if (m_reportOnly) {
    513         m_policy->reportInvalidInReportOnly(name);
    514         return;
    515     }
    516     if (m_haveSandboxPolicy) {
    517         m_policy->reportDuplicateDirective(name);
    518         return;
    519     }
    520     m_haveSandboxPolicy = true;
    521     String invalidTokens;
    522     m_policy->enforceSandboxFlags(parseSandboxPolicy(sandboxPolicy, invalidTokens));
    523     if (!invalidTokens.isNull())
    524         m_policy->reportInvalidSandboxFlags(invalidTokens);
    525 }
    526 
    527 void CSPDirectiveList::parseReflectedXSS(const String& name, const String& value)
    528 {
    529     if (m_reflectedXSSDisposition != ReflectedXSSUnset) {
    530         m_policy->reportDuplicateDirective(name);
    531         m_reflectedXSSDisposition = ReflectedXSSInvalid;
    532         return;
    533     }
    534 
    535     if (value.isEmpty()) {
    536         m_reflectedXSSDisposition = ReflectedXSSInvalid;
    537         m_policy->reportInvalidReflectedXSS(value);
    538         return;
    539     }
    540 
    541     Vector<UChar> characters;
    542     value.appendTo(characters);
    543 
    544     const UChar* position = characters.data();
    545     const UChar* end = position + characters.size();
    546 
    547     skipWhile<UChar, isASCIISpace>(position, end);
    548     const UChar* begin = position;
    549     skipWhile<UChar, isNotASCIISpace>(position, end);
    550 
    551     // value1
    552     //       ^
    553     if (equalIgnoringCase("allow", begin, position - begin)) {
    554         m_reflectedXSSDisposition = AllowReflectedXSS;
    555     } else if (equalIgnoringCase("filter", begin, position - begin)) {
    556         m_reflectedXSSDisposition = FilterReflectedXSS;
    557     } else if (equalIgnoringCase("block", begin, position - begin)) {
    558         m_reflectedXSSDisposition = BlockReflectedXSS;
    559     } else {
    560         m_reflectedXSSDisposition = ReflectedXSSInvalid;
    561         m_policy->reportInvalidReflectedXSS(value);
    562         return;
    563     }
    564 
    565     skipWhile<UChar, isASCIISpace>(position, end);
    566     if (position == end && m_reflectedXSSDisposition != ReflectedXSSUnset)
    567         return;
    568 
    569     // value1 value2
    570     //        ^
    571     m_reflectedXSSDisposition = ReflectedXSSInvalid;
    572     m_policy->reportInvalidReflectedXSS(value);
    573 }
    574 
    575 void CSPDirectiveList::parseReferrer(const String& name, const String& value)
    576 {
    577     if (m_didSetReferrerPolicy) {
    578         m_policy->reportDuplicateDirective(name);
    579         m_referrerPolicy = ReferrerPolicyNever;
    580         return;
    581     }
    582 
    583     m_didSetReferrerPolicy = true;
    584 
    585     if (value.isEmpty()) {
    586         m_policy->reportInvalidReferrer(value);
    587         m_referrerPolicy = ReferrerPolicyNever;
    588         return;
    589     }
    590 
    591     Vector<UChar> characters;
    592     value.appendTo(characters);
    593 
    594     const UChar* position = characters.data();
    595     const UChar* end = position + characters.size();
    596 
    597     skipWhile<UChar, isASCIISpace>(position, end);
    598     const UChar* begin = position;
    599     skipWhile<UChar, isNotASCIISpace>(position, end);
    600 
    601     // value1
    602     //       ^
    603     if (equalIgnoringCase("always", begin, position - begin)) {
    604         m_referrerPolicy = ReferrerPolicyAlways;
    605     } else if (equalIgnoringCase("default", begin, position - begin)) {
    606         m_referrerPolicy = ReferrerPolicyDefault;
    607     } else if (equalIgnoringCase("never", begin, position - begin)) {
    608         m_referrerPolicy = ReferrerPolicyNever;
    609     } else if (equalIgnoringCase("origin", begin, position - begin)) {
    610         m_referrerPolicy = ReferrerPolicyOrigin;
    611     } else {
    612         m_referrerPolicy = ReferrerPolicyNever;
    613         m_policy->reportInvalidReferrer(value);
    614         return;
    615     }
    616 
    617     skipWhile<UChar, isASCIISpace>(position, end);
    618     if (position == end)
    619         return;
    620 
    621     // value1 value2
    622     //        ^
    623     m_referrerPolicy = ReferrerPolicyNever;
    624     m_policy->reportInvalidReferrer(value);
    625 
    626 }
    627 
    628 void CSPDirectiveList::addDirective(const String& name, const String& value)
    629 {
    630     ASSERT(!name.isEmpty());
    631 
    632     if (equalIgnoringCase(name, ContentSecurityPolicy::DefaultSrc)) {
    633         setCSPDirective<SourceListDirective>(name, value, m_defaultSrc);
    634     } else if (equalIgnoringCase(name, ContentSecurityPolicy::ScriptSrc)) {
    635         setCSPDirective<SourceListDirective>(name, value, m_scriptSrc);
    636         m_policy->usesScriptHashAlgorithms(m_scriptSrc->hashAlgorithmsUsed());
    637     } else if (equalIgnoringCase(name, ContentSecurityPolicy::ObjectSrc)) {
    638         setCSPDirective<SourceListDirective>(name, value, m_objectSrc);
    639     } else if (equalIgnoringCase(name, ContentSecurityPolicy::FrameAncestors)) {
    640         setCSPDirective<SourceListDirective>(name, value, m_frameAncestors);
    641     } else if (equalIgnoringCase(name, ContentSecurityPolicy::FrameSrc)) {
    642         setCSPDirective<SourceListDirective>(name, value, m_frameSrc);
    643     } else if (equalIgnoringCase(name, ContentSecurityPolicy::ImgSrc)) {
    644         setCSPDirective<SourceListDirective>(name, value, m_imgSrc);
    645     } else if (equalIgnoringCase(name, ContentSecurityPolicy::StyleSrc)) {
    646         setCSPDirective<SourceListDirective>(name, value, m_styleSrc);
    647         m_policy->usesStyleHashAlgorithms(m_styleSrc->hashAlgorithmsUsed());
    648     } else if (equalIgnoringCase(name, ContentSecurityPolicy::FontSrc)) {
    649         setCSPDirective<SourceListDirective>(name, value, m_fontSrc);
    650     } else if (equalIgnoringCase(name, ContentSecurityPolicy::MediaSrc)) {
    651         setCSPDirective<SourceListDirective>(name, value, m_mediaSrc);
    652     } else if (equalIgnoringCase(name, ContentSecurityPolicy::ConnectSrc)) {
    653         setCSPDirective<SourceListDirective>(name, value, m_connectSrc);
    654     } else if (equalIgnoringCase(name, ContentSecurityPolicy::Sandbox)) {
    655         applySandboxPolicy(name, value);
    656     } else if (equalIgnoringCase(name, ContentSecurityPolicy::ReportURI)) {
    657         parseReportURI(name, value);
    658     } else if (m_policy->experimentalFeaturesEnabled()) {
    659         if (equalIgnoringCase(name, ContentSecurityPolicy::BaseURI))
    660             setCSPDirective<SourceListDirective>(name, value, m_baseURI);
    661         else if (equalIgnoringCase(name, ContentSecurityPolicy::ChildSrc))
    662             setCSPDirective<SourceListDirective>(name, value, m_childSrc);
    663         else if (equalIgnoringCase(name, ContentSecurityPolicy::FormAction))
    664             setCSPDirective<SourceListDirective>(name, value, m_formAction);
    665         else if (equalIgnoringCase(name, ContentSecurityPolicy::PluginTypes))
    666             setCSPDirective<MediaListDirective>(name, value, m_pluginTypes);
    667         else if (equalIgnoringCase(name, ContentSecurityPolicy::ReflectedXSS))
    668             parseReflectedXSS(name, value);
    669         else if (equalIgnoringCase(name, ContentSecurityPolicy::Referrer))
    670             parseReferrer(name, value);
    671         else
    672             m_policy->reportUnsupportedDirective(name);
    673     } else {
    674         m_policy->reportUnsupportedDirective(name);
    675     }
    676 }
    677 
    678 
    679 } // namespace WebCore
    680