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