• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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