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