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