CSP: Extract helper classes into their own files
[WebKit-https.git] / Source / WebCore / page / csp / ContentSecurityPolicyDirectiveList.cpp
1 /*
2  * Copyright (C) 2011 Google, Inc. All rights reserved.
3  * Copyright (C) 2016 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "ContentSecurityPolicyDirectiveList.h"
29
30 #include "ParsingUtilities.h"
31 #include "SecurityContext.h"
32 #include <wtf/NeverDestroyed.h>
33
34 namespace WebCore {
35
36 // CSP 1.0 Directives
37 static const char connectSrc[] = "connect-src";
38 static const char defaultSrc[] = "default-src";
39 static const char fontSrc[] = "font-src";
40 static const char frameSrc[] = "frame-src";
41 static const char imgSrc[] = "img-src";
42 static const char mediaSrc[] = "media-src";
43 static const char objectSrc[] = "object-src";
44 static const char reportURI[] = "report-uri";
45 static const char sandbox[] = "sandbox";
46 static const char scriptSrc[] = "script-src";
47 static const char styleSrc[] = "style-src";
48
49 // CSP 1.1 Directives
50 static const char baseURI[] = "base-uri";
51 static const char formAction[] = "form-action";
52 static const char pluginTypes[] = "plugin-types";
53 #if ENABLE(CSP_NEXT)
54 static const char reflectedXSS[] = "reflected-xss";
55 #endif
56
57 #if ENABLE(CSP_NEXT)
58
59 static inline bool isExperimentalDirectiveName(const String& name)
60 {
61     return equalLettersIgnoringASCIICase(name, baseURI)
62         || equalLettersIgnoringASCIICase(name, formAction)
63         || equalLettersIgnoringASCIICase(name, pluginTypes)
64         || equalLettersIgnoringASCIICase(name, reflectedXSS);
65 }
66
67 #else
68
69 static inline bool isExperimentalDirectiveName(const String&)
70 {
71     return false;
72 }
73
74 #endif
75
76 bool isCSPDirectiveName(const String& name)
77 {
78     return equalLettersIgnoringASCIICase(name, connectSrc)
79         || equalLettersIgnoringASCIICase(name, defaultSrc)
80         || equalLettersIgnoringASCIICase(name, fontSrc)
81         || equalLettersIgnoringASCIICase(name, frameSrc)
82         || equalLettersIgnoringASCIICase(name, imgSrc)
83         || equalLettersIgnoringASCIICase(name, mediaSrc)
84         || equalLettersIgnoringASCIICase(name, objectSrc)
85         || equalLettersIgnoringASCIICase(name, reportURI)
86         || equalLettersIgnoringASCIICase(name, sandbox)
87         || equalLettersIgnoringASCIICase(name, scriptSrc)
88         || equalLettersIgnoringASCIICase(name, styleSrc)
89         || isExperimentalDirectiveName(name);
90 }
91
92 static bool isDirectiveNameCharacter(UChar c)
93 {
94     return isASCIIAlphanumeric(c) || c == '-';
95 }
96
97 static bool isDirectiveValueCharacter(UChar c)
98 {
99     return isASCIISpace(c) || (c >= 0x21 && c <= 0x7e); // Whitespace + VCHAR
100 }
101
102 static bool isNotASCIISpace(UChar c)
103 {
104     return !isASCIISpace(c);
105 }
106
107 ContentSecurityPolicyDirectiveList::ContentSecurityPolicyDirectiveList(ContentSecurityPolicy& policy, ContentSecurityPolicyHeaderType type)
108     : m_policy(policy)
109     , m_headerType(type)
110     , m_reportOnly(false)
111     , m_haveSandboxPolicy(false)
112     , m_reflectedXSSDisposition(ContentSecurityPolicy::ReflectedXSSUnset)
113 {
114     m_reportOnly = (type == ContentSecurityPolicyHeaderType::Report || type == ContentSecurityPolicyHeaderType::PrefixedReport);
115 }
116
117 std::unique_ptr<ContentSecurityPolicyDirectiveList> ContentSecurityPolicyDirectiveList::create(ContentSecurityPolicy& policy, const String& header, ContentSecurityPolicyHeaderType type)
118 {
119     auto directives = std::make_unique<ContentSecurityPolicyDirectiveList>(policy, type);
120     directives->parse(header);
121
122     if (!directives->checkEval(directives->operativeDirective(directives->m_scriptSrc.get()))) {
123         String message = makeString("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");
124         directives->setEvalDisabledErrorMessage(message);
125     }
126
127     if (directives->isReportOnly() && directives->reportURIs().isEmpty())
128         policy.reportMissingReportURI(header);
129
130     return directives;
131 }
132
133 void ContentSecurityPolicyDirectiveList::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const URL& blockedURL, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
134 {
135     String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage;
136     m_policy.reportViolation(directiveText, effectiveDirective, message, blockedURL, m_reportURIs, m_header, contextURL, contextLine, state);
137 }
138
139 bool ContentSecurityPolicyDirectiveList::checkEval(ContentSecurityPolicySourceListDirective* directive) const
140 {
141     return !directive || directive->allowEval();
142 }
143
144 bool ContentSecurityPolicyDirectiveList::checkInline(ContentSecurityPolicySourceListDirective* directive) const
145 {
146     return !directive || directive->allowInline();
147 }
148
149 bool ContentSecurityPolicyDirectiveList::checkSource(ContentSecurityPolicySourceListDirective* directive, const URL& url) const
150 {
151     return !directive || directive->allows(url);
152 }
153
154 bool ContentSecurityPolicyDirectiveList::checkMediaType(ContentSecurityPolicyMediaListDirective* directive, const String& type, const String& typeAttribute) const
155 {
156     if (!directive)
157         return true;
158     if (typeAttribute.isEmpty() || typeAttribute.stripWhiteSpace() != type)
159         return false;
160     return directive->allows(type);
161 }
162
163 ContentSecurityPolicySourceListDirective* ContentSecurityPolicyDirectiveList::operativeDirective(ContentSecurityPolicySourceListDirective* directive) const
164 {
165     return directive ? directive : m_defaultSrc.get();
166 }
167
168 bool ContentSecurityPolicyDirectiveList::checkEvalAndReportViolation(ContentSecurityPolicySourceListDirective* directive, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
169 {
170     if (checkEval(directive))
171         return true;
172
173     String suffix = String();
174     if (directive == m_defaultSrc.get())
175         suffix = " Note that 'script-src' was not explicitly set, so 'default-src' is used as a fallback.";
176
177     reportViolation(directive->text(), scriptSrc, consoleMessage + "\"" + directive->text() + "\"." + suffix + "\n", URL(), contextURL, contextLine, state);
178     if (!m_reportOnly) {
179         m_policy.reportBlockedScriptExecutionToInspector(directive->text());
180         return false;
181     }
182     return true;
183 }
184
185 bool ContentSecurityPolicyDirectiveList::checkMediaTypeAndReportViolation(ContentSecurityPolicyMediaListDirective* directive, const String& type, const String& typeAttribute, const String& consoleMessage) const
186 {
187     if (checkMediaType(directive, type, typeAttribute))
188         return true;
189
190     String message = makeString(consoleMessage, '\'', directive->text(), "\'.");
191     if (typeAttribute.isEmpty())
192         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]\" ...>').";
193
194     reportViolation(directive->text(), pluginTypes, message + "\n", URL());
195     return denyIfEnforcingPolicy();
196 }
197
198 bool ContentSecurityPolicyDirectiveList::checkInlineAndReportViolation(ContentSecurityPolicySourceListDirective* directive, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, bool isScript) const
199 {
200     if (checkInline(directive))
201         return true;
202
203     String suffix = String();
204     if (directive == m_defaultSrc.get())
205         suffix = makeString(" Note that '", (isScript ? "script" : "style"), "-src' was not explicitly set, so 'default-src' is used as a fallback.");
206
207     reportViolation(directive->text(), isScript ? scriptSrc : styleSrc, consoleMessage + "\"" + directive->text() + "\"." + suffix + "\n", URL(), contextURL, contextLine);
208
209     if (!m_reportOnly) {
210         if (isScript)
211             m_policy.reportBlockedScriptExecutionToInspector(directive->text());
212         return false;
213     }
214     return true;
215 }
216
217 bool ContentSecurityPolicyDirectiveList::checkSourceAndReportViolation(ContentSecurityPolicySourceListDirective* directive, const URL& url, const String& effectiveDirective) const
218 {
219     if (checkSource(directive, url))
220         return true;
221
222     const char* prefix;
223     if (baseURI == effectiveDirective)
224         prefix = "Refused to set the document's base URI to '";
225     else if (connectSrc == effectiveDirective)
226         prefix = "Refused to connect to '";
227     else if (fontSrc == effectiveDirective)
228         prefix = "Refused to load the font '";
229     else if (formAction == effectiveDirective)
230         prefix = "Refused to send form data to '";
231     else if (frameSrc == effectiveDirective)
232         prefix = "Refused to load frame '";
233     else if (imgSrc == effectiveDirective)
234         prefix = "Refused to load the image '";
235     else if (mediaSrc == effectiveDirective)
236         prefix = "Refused to load media from '";
237     else if (objectSrc == effectiveDirective)
238         prefix = "Refused to load plugin data from '";
239     else if (scriptSrc == effectiveDirective)
240         prefix = "Refused to load the script '";
241     else if (styleSrc == effectiveDirective)
242         prefix = "Refused to load the stylesheet '";
243     else
244         prefix = "";
245
246     String suffix;
247     if (directive == m_defaultSrc.get())
248         suffix = " Note that '" + effectiveDirective + "' was not explicitly set, so 'default-src' is used as a fallback.";
249
250     reportViolation(directive->text(), effectiveDirective, makeString(prefix, url.stringCenterEllipsizedToLength(), "' because it violates the following Content Security Policy directive: \"", directive->text(), "\".", suffix, '\n'), url);
251     return denyIfEnforcingPolicy();
252 }
253
254 bool ContentSecurityPolicyDirectiveList::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
255 {
256     static NeverDestroyed<String> consoleMessage(ASCIILiteral("Refused to execute JavaScript URL because it violates the following Content Security Policy directive: "));
257     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
258         return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true);
259     return m_reportOnly || checkInline(operativeDirective(m_scriptSrc.get()));
260 }
261
262 bool ContentSecurityPolicyDirectiveList::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
263 {
264     static NeverDestroyed<String> consoleMessage(ASCIILiteral("Refused to execute inline event handler because it violates the following Content Security Policy directive: "));
265     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
266         return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true);
267     return m_reportOnly || checkInline(operativeDirective(m_scriptSrc.get()));
268 }
269
270 bool ContentSecurityPolicyDirectiveList::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
271 {
272     static NeverDestroyed<String> consoleMessage(ASCIILiteral("Refused to execute inline script because it violates the following Content Security Policy directive: "));
273     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
274         return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true);
275     return m_reportOnly || checkInline(operativeDirective(m_scriptSrc.get()));
276 }
277
278 bool ContentSecurityPolicyDirectiveList::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
279 {
280     static NeverDestroyed<String> consoleMessage(ASCIILiteral("Refused to apply inline style because it violates the following Content Security Policy directive: "));
281     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
282         return checkInlineAndReportViolation(operativeDirective(m_styleSrc.get()), consoleMessage, contextURL, contextLine, false);
283     return m_reportOnly || checkInline(operativeDirective(m_styleSrc.get()));
284 }
285
286 bool ContentSecurityPolicyDirectiveList::allowEval(JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus reportingStatus) const
287 {
288     static NeverDestroyed<String> consoleMessage(ASCIILiteral("Refused to evaluate script because it violates the following Content Security Policy directive: "));
289     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
290         return checkEvalAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, String(), WTF::OrdinalNumber::beforeFirst(), state);
291     return m_reportOnly || checkEval(operativeDirective(m_scriptSrc.get()));
292 }
293
294 bool ContentSecurityPolicyDirectiveList::allowPluginType(const String& type, const String& typeAttribute, const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
295 {
296     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
297         return checkMediaTypeAndReportViolation(m_pluginTypes.get(), type, typeAttribute, "Refused to load '" + url.stringCenterEllipsizedToLength() + "' (MIME type '" + typeAttribute + "') because it violates the following Content Security Policy Directive: ");
298     return m_reportOnly || checkMediaType(m_pluginTypes.get(), type, typeAttribute);
299 }
300
301 bool ContentSecurityPolicyDirectiveList::allowScriptFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
302 {
303     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
304         return checkSourceAndReportViolation(operativeDirective(m_scriptSrc.get()), url, scriptSrc);
305     return m_reportOnly || checkSource(operativeDirective(m_scriptSrc.get()), url);
306 }
307
308 bool ContentSecurityPolicyDirectiveList::allowObjectFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
309 {
310     if (url.isBlankURL())
311         return true;
312     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
313         return checkSourceAndReportViolation(operativeDirective(m_objectSrc.get()), url, objectSrc);
314     return m_reportOnly || checkSource(operativeDirective(m_objectSrc.get()), url);
315 }
316
317 bool ContentSecurityPolicyDirectiveList::allowChildFrameFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
318 {
319     if (url.isBlankURL())
320         return true;
321     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
322         return checkSourceAndReportViolation(operativeDirective(m_frameSrc.get()), url, frameSrc);
323     return m_reportOnly || checkSource(operativeDirective(m_frameSrc.get()), url);
324 }
325
326 bool ContentSecurityPolicyDirectiveList::allowImageFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
327 {
328     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
329         return checkSourceAndReportViolation(operativeDirective(m_imgSrc.get()), url, imgSrc);
330     return m_reportOnly || checkSource(operativeDirective(m_imgSrc.get()), url);
331 }
332
333 bool ContentSecurityPolicyDirectiveList::allowStyleFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
334 {
335     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
336         return checkSourceAndReportViolation(operativeDirective(m_styleSrc.get()), url, styleSrc);
337     return m_reportOnly || checkSource(operativeDirective(m_styleSrc.get()), url);
338 }
339
340 bool ContentSecurityPolicyDirectiveList::allowFontFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
341 {
342     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
343         return checkSourceAndReportViolation(operativeDirective(m_fontSrc.get()), url, fontSrc);
344     return m_reportOnly || checkSource(operativeDirective(m_fontSrc.get()), url);
345 }
346
347 bool ContentSecurityPolicyDirectiveList::allowMediaFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
348 {
349     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
350         return checkSourceAndReportViolation(operativeDirective(m_mediaSrc.get()), url, mediaSrc);
351     return m_reportOnly || checkSource(operativeDirective(m_mediaSrc.get()), url);
352 }
353
354 bool ContentSecurityPolicyDirectiveList::allowConnectToSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
355 {
356     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
357         return checkSourceAndReportViolation(operativeDirective(m_connectSrc.get()), url, connectSrc);
358     return m_reportOnly || checkSource(operativeDirective(m_connectSrc.get()), url);
359 }
360
361 bool ContentSecurityPolicyDirectiveList::allowFormAction(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
362 {
363     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
364         return checkSourceAndReportViolation(m_formAction.get(), url, formAction);
365     return m_reportOnly || checkSource(m_formAction.get(), url);
366 }
367
368 bool ContentSecurityPolicyDirectiveList::allowBaseURI(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
369 {
370     if (reportingStatus == ContentSecurityPolicy::ReportingStatus::SendReport)
371         return checkSourceAndReportViolation(m_baseURI.get(), url, baseURI);
372     return m_reportOnly || checkSource(m_baseURI.get(), url);
373 }
374
375 // policy            = directive-list
376 // directive-list    = [ directive *( ";" [ directive ] ) ]
377 //
378 void ContentSecurityPolicyDirectiveList::parse(const String& policy)
379 {
380     m_header = policy;
381     if (policy.isEmpty())
382         return;
383
384     auto characters = StringView(policy).upconvertedCharacters();
385     const UChar* position = characters;
386     const UChar* end = position + policy.length();
387
388     while (position < end) {
389         const UChar* directiveBegin = position;
390         skipUntil<UChar>(position, end, ';');
391
392         String name, value;
393         if (parseDirective(directiveBegin, position, name, value)) {
394             ASSERT(!name.isEmpty());
395             addDirective(name, value);
396         }
397
398         ASSERT(position == end || *position == ';');
399         skipExactly<UChar>(position, end, ';');
400     }
401 }
402
403 // directive         = *WSP [ directive-name [ WSP directive-value ] ]
404 // directive-name    = 1*( ALPHA / DIGIT / "-" )
405 // directive-value   = *( WSP / <VCHAR except ";"> )
406 //
407 bool ContentSecurityPolicyDirectiveList::parseDirective(const UChar* begin, const UChar* end, String& name, String& value)
408 {
409     ASSERT(name.isEmpty());
410     ASSERT(value.isEmpty());
411
412     const UChar* position = begin;
413     skipWhile<UChar, isASCIISpace>(position, end);
414
415     // Empty directive (e.g. ";;;"). Exit early.
416     if (position == end)
417         return false;
418
419     const UChar* nameBegin = position;
420     skipWhile<UChar, isDirectiveNameCharacter>(position, end);
421
422     // The directive-name must be non-empty.
423     if (nameBegin == position) {
424         skipWhile<UChar, isNotASCIISpace>(position, end);
425         m_policy.reportUnsupportedDirective(String(nameBegin, position - nameBegin));
426         return false;
427     }
428
429     name = String(nameBegin, position - nameBegin);
430
431     if (position == end)
432         return true;
433
434     if (!skipExactly<UChar, isASCIISpace>(position, end)) {
435         skipWhile<UChar, isNotASCIISpace>(position, end);
436         m_policy.reportUnsupportedDirective(String(nameBegin, position - nameBegin));
437         return false;
438     }
439
440     skipWhile<UChar, isASCIISpace>(position, end);
441
442     const UChar* valueBegin = position;
443     skipWhile<UChar, isDirectiveValueCharacter>(position, end);
444
445     if (position != end) {
446         m_policy.reportInvalidDirectiveValueCharacter(name, String(valueBegin, end - valueBegin));
447         return false;
448     }
449
450     // The directive-value may be empty.
451     if (valueBegin == position)
452         return true;
453
454     value = String(valueBegin, position - valueBegin);
455     return true;
456 }
457
458 void ContentSecurityPolicyDirectiveList::parseReportURI(const String& name, const String& value)
459 {
460     if (!m_reportURIs.isEmpty()) {
461         m_policy.reportDuplicateDirective(name);
462         return;
463     }
464
465     auto characters = StringView(value).upconvertedCharacters();
466     const UChar* position = characters;
467     const UChar* end = position + value.length();
468
469     while (position < end) {
470         skipWhile<UChar, isASCIISpace>(position, end);
471
472         const UChar* urlBegin = position;
473         skipWhile<UChar, isNotASCIISpace>(position, end);
474
475         if (urlBegin < position)
476             m_reportURIs.append(value.substring(urlBegin - characters, position - urlBegin));
477     }
478 }
479
480
481 template<class CSPDirectiveType>
482 void ContentSecurityPolicyDirectiveList::setCSPDirective(const String& name, const String& value, std::unique_ptr<CSPDirectiveType>& directive)
483 {
484     if (directive) {
485         m_policy.reportDuplicateDirective(name);
486         return;
487     }
488     directive = std::make_unique<CSPDirectiveType>(name, value, m_policy);
489 }
490
491 void ContentSecurityPolicyDirectiveList::applySandboxPolicy(const String& name, const String& sandboxPolicy)
492 {
493     if (m_haveSandboxPolicy) {
494         m_policy.reportDuplicateDirective(name);
495         return;
496     }
497     m_haveSandboxPolicy = true;
498     String invalidTokens;
499     m_policy.enforceSandboxFlags(SecurityContext::parseSandboxPolicy(sandboxPolicy, invalidTokens));
500     if (!invalidTokens.isNull())
501         m_policy.reportInvalidSandboxFlags(invalidTokens);
502 }
503
504 void ContentSecurityPolicyDirectiveList::parseReflectedXSS(const String& name, const String& value)
505 {
506     if (m_reflectedXSSDisposition != ContentSecurityPolicy::ReflectedXSSUnset) {
507         m_policy.reportDuplicateDirective(name);
508         m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid;
509         return;
510     }
511
512     if (value.isEmpty()) {
513         m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid;
514         m_policy.reportInvalidReflectedXSS(value);
515         return;
516     }
517
518     auto characters = StringView(value).upconvertedCharacters();
519     const UChar* position = characters;
520     const UChar* end = position + value.length();
521
522     skipWhile<UChar, isASCIISpace>(position, end);
523     const UChar* begin = position;
524     skipWhile<UChar, isNotASCIISpace>(position, end);
525
526     // value1
527     //       ^
528     if (equalLettersIgnoringASCIICase(begin, position - begin, "allow"))
529         m_reflectedXSSDisposition = ContentSecurityPolicy::AllowReflectedXSS;
530     else if (equalLettersIgnoringASCIICase(begin, position - begin, "filter"))
531         m_reflectedXSSDisposition = ContentSecurityPolicy::FilterReflectedXSS;
532     else if (equalLettersIgnoringASCIICase(begin, position - begin, "block"))
533         m_reflectedXSSDisposition = ContentSecurityPolicy::BlockReflectedXSS;
534     else {
535         m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid;
536         m_policy.reportInvalidReflectedXSS(value);
537         return;
538     }
539
540     skipWhile<UChar, isASCIISpace>(position, end);
541     if (position == end && m_reflectedXSSDisposition != ContentSecurityPolicy::ReflectedXSSUnset)
542         return;
543
544     // value1 value2
545     //        ^
546     m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid;
547     m_policy.reportInvalidReflectedXSS(value);
548 }
549
550 void ContentSecurityPolicyDirectiveList::addDirective(const String& name, const String& value)
551 {
552     ASSERT(!name.isEmpty());
553
554     if (equalLettersIgnoringASCIICase(name, defaultSrc))
555         setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_defaultSrc);
556     else if (equalLettersIgnoringASCIICase(name, scriptSrc))
557         setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_scriptSrc);
558     else if (equalLettersIgnoringASCIICase(name, objectSrc))
559         setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_objectSrc);
560     else if (equalLettersIgnoringASCIICase(name, frameSrc))
561         setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_frameSrc);
562     else if (equalLettersIgnoringASCIICase(name, imgSrc))
563         setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_imgSrc);
564     else if (equalLettersIgnoringASCIICase(name, styleSrc))
565         setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_styleSrc);
566     else if (equalLettersIgnoringASCIICase(name, fontSrc))
567         setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_fontSrc);
568     else if (equalLettersIgnoringASCIICase(name, mediaSrc))
569         setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_mediaSrc);
570     else if (equalLettersIgnoringASCIICase(name, connectSrc))
571         setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_connectSrc);
572     else if (equalLettersIgnoringASCIICase(name, sandbox))
573         applySandboxPolicy(name, value);
574     else if (equalLettersIgnoringASCIICase(name, reportURI))
575         parseReportURI(name, value);
576 #if ENABLE(CSP_NEXT)
577     else if (m_policy.experimentalFeaturesEnabled()) {
578         if (equalLettersIgnoringASCIICase(name, baseURI))
579             setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_baseURI);
580         else if (equalLettersIgnoringASCIICase(name, formAction))
581             setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_formAction);
582         else if (equalLettersIgnoringASCIICase(name, pluginTypes))
583             setCSPDirective<ContentSecurityPolicyMediaListDirective>(name, value, m_pluginTypes);
584         else if (equalLettersIgnoringASCIICase(name, reflectedXSS))
585             parseReflectedXSS(name, value);
586         else
587             m_policy.reportUnsupportedDirective(name);
588     }
589 #endif
590     else
591         m_policy.reportUnsupportedDirective(name);
592 }
593
594 } // namespace WebCore