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
15namespace blink {
16
17CSPDirectiveList::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
30PassOwnPtr<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
46void 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
53void 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
60void 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
67void 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
76bool CSPDirectiveList::checkEval(SourceListDirective* directive) const
77{
78    return !directive || directive->allowEval();
79}
80
81bool CSPDirectiveList::checkInline(SourceListDirective* directive) const
82{
83    return !directive || (directive->allowInline() && !directive->isHashOrNoncePresent());
84}
85
86bool CSPDirectiveList::checkNonce(SourceListDirective* directive, const String& nonce) const
87{
88    return !directive || directive->allowNonce(nonce);
89}
90
91bool CSPDirectiveList::checkHash(SourceListDirective* directive, const CSPHashValue& hashValue) const
92{
93    return !directive || directive->allowHash(hashValue);
94}
95
96bool CSPDirectiveList::checkSource(SourceListDirective* directive, const KURL& url) const
97{
98    return !directive || directive->allows(url);
99}
100
101bool 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
114bool 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
123SourceListDirective* CSPDirectiveList::operativeDirective(SourceListDirective* directive) const
124{
125    return directive ? directive : m_defaultSrc.get();
126}
127
128SourceListDirective* CSPDirectiveList::operativeDirective(SourceListDirective* directive, SourceListDirective* override) const
129{
130    return directive ? directive : override;
131}
132
133bool 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
150bool 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
163bool 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
188bool 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
225bool 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
234bool 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
243bool 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
251bool 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
259bool 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
267bool 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
276bool 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
283bool 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
290bool 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
299bool 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
320bool 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
327bool 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
334bool 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
341bool 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
348bool 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
355bool 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
362bool 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
369bool 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
376bool 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
383bool CSPDirectiveList::allowScriptNonce(const String& nonce) const
384{
385    return checkNonce(operativeDirective(m_scriptSrc.get()), nonce);
386}
387
388bool CSPDirectiveList::allowStyleNonce(const String& nonce) const
389{
390    return checkNonce(operativeDirective(m_styleSrc.get()), nonce);
391}
392
393bool CSPDirectiveList::allowScriptHash(const CSPHashValue& hashValue) const
394{
395    return checkHash(operativeDirective(m_scriptSrc.get()), hashValue);
396}
397
398bool 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//
406void 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//
433bool 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
484void 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
511template<class CSPDirectiveType>
512void 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
521void 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
538void 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
586void 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
639void 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