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