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