Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(806)

Side by Side Diff: third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp

Issue 2449873004: Removing CSPSourceList level up to SourceListDirective. (Closed)
Patch Set: Exporting CSPDirective Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/frame/csp/SourceListDirective.h" 5 #include "core/frame/csp/SourceListDirective.h"
6 6
7 #include "core/frame/csp/CSPSourceList.h" 7 #include "core/frame/csp/CSPSource.h"
8 #include "core/frame/csp/ContentSecurityPolicy.h" 8 #include "core/frame/csp/ContentSecurityPolicy.h"
9 #include "platform/network/ContentSecurityPolicyParsers.h" 9 #include "platform/network/ContentSecurityPolicyParsers.h"
10 #include "platform/weborigin/KURL.h" 10 #include "platform/weborigin/KURL.h"
11 #include "platform/weborigin/SecurityOrigin.h"
12 #include "wtf/HashSet.h"
13 #include "wtf/text/Base64.h"
14 #include "wtf/text/ParsingUtilities.h"
15 #include "wtf/text/StringToNumber.h"
11 #include "wtf/text/WTFString.h" 16 #include "wtf/text/WTFString.h"
12 17
13 namespace blink { 18 namespace blink {
14 19
15 SourceListDirective::SourceListDirective(const String& name, 20 SourceListDirective::SourceListDirective(const String& name,
16 const String& value, 21 const String& value,
17 ContentSecurityPolicy* policy) 22 ContentSecurityPolicy* policy)
18 : CSPDirective(name, value, policy), m_sourceList(policy, name) { 23 : CSPDirective(name, value, policy),
24 m_policy(policy),
25 m_directiveName(name),
26 m_allowSelf(false),
27 m_allowStar(false),
28 m_allowInline(false),
29 m_allowEval(false),
30 m_allowDynamic(false),
31 m_allowHashedAttributes(false),
32 m_hashAlgorithmsUsed(0) {
19 Vector<UChar> characters; 33 Vector<UChar> characters;
20 value.appendTo(characters); 34 value.appendTo(characters);
21 35 parse(characters.data(), characters.data() + characters.size());
22 m_sourceList.parse(characters.data(), characters.data() + characters.size()); 36 }
37
38 static bool isSourceListNone(const UChar* begin, const UChar* end) {
39 skipWhile<UChar, isASCIISpace>(begin, end);
40
41 const UChar* position = begin;
42 skipWhile<UChar, isSourceCharacter>(position, end);
43 if (!equalIgnoringCase("'none'", StringView(begin, position - begin)))
44 return false;
45
46 skipWhile<UChar, isASCIISpace>(position, end);
47 if (position != end)
48 return false;
49
50 return true;
23 } 51 }
24 52
25 bool SourceListDirective::allows( 53 bool SourceListDirective::allows(
26 const KURL& url, 54 const KURL& url,
27 ResourceRequest::RedirectStatus redirectStatus) const { 55 ResourceRequest::RedirectStatus redirectStatus) const {
28 return m_sourceList.matches(url, redirectStatus); 56 // Wildcards match network schemes ('http', 'https', 'ftp', 'ws', 'wss'), and
57 // the scheme of the protected resource:
58 // https://w3c.github.io/webappsec-csp/#match-url-to-source-expression. Other
59 // schemes, including custom schemes, must be explicitly listed in a source
60 // list.
61 if (m_allowStar) {
62 if (url.protocolIsInHTTPFamily() || url.protocolIs("ftp") ||
63 url.protocolIs("ws") || url.protocolIs("wss") ||
64 m_policy->protocolMatchesSelf(url))
65 return true;
66
67 return hasSourceMatchInList(url, redirectStatus);
68 }
69
70 KURL effectiveURL =
71 m_policy->selfMatchesInnerURL() && SecurityOrigin::shouldUseInnerURL(url)
72 ? SecurityOrigin::extractInnerURL(url)
73 : url;
74
75 if (m_allowSelf && m_policy->urlMatchesSelf(effectiveURL))
76 return true;
77
78 return hasSourceMatchInList(effectiveURL, redirectStatus);
29 } 79 }
30 80
31 bool SourceListDirective::allowInline() const { 81 bool SourceListDirective::allowInline() const {
32 return m_sourceList.allowInline(); 82 return m_allowInline;
33 } 83 }
34 84
35 bool SourceListDirective::allowEval() const { 85 bool SourceListDirective::allowEval() const {
36 return m_sourceList.allowEval(); 86 return m_allowEval;
37 } 87 }
38 88
39 bool SourceListDirective::allowDynamic() const { 89 bool SourceListDirective::allowDynamic() const {
40 return m_sourceList.allowDynamic(); 90 return m_allowDynamic;
41 } 91 }
42 92
43 bool SourceListDirective::allowNonce(const String& nonce) const { 93 bool SourceListDirective::allowNonce(const String& nonce) const {
44 return m_sourceList.allowNonce(nonce.stripWhiteSpace()); 94 String nonceStripped = nonce.stripWhiteSpace();
amalika 2016/10/27 12:34:19 Changed this to strip white space inside!
95 return !nonceStripped.isNull() && m_nonces.contains(nonceStripped);
45 } 96 }
46 97
47 bool SourceListDirective::allowHash(const CSPHashValue& hashValue) const { 98 bool SourceListDirective::allowHash(const CSPHashValue& hashValue) const {
48 return m_sourceList.allowHash(hashValue); 99 return m_hashes.contains(hashValue);
49 } 100 }
50 101
51 bool SourceListDirective::allowHashedAttributes() const { 102 bool SourceListDirective::allowHashedAttributes() const {
52 return m_sourceList.allowHashedAttributes(); 103 return m_allowHashedAttributes;
104 }
105
106 uint8_t SourceListDirective::hashAlgorithmsUsed() const {
107 return m_hashAlgorithmsUsed;
53 } 108 }
54 109
55 bool SourceListDirective::isHashOrNoncePresent() const { 110 bool SourceListDirective::isHashOrNoncePresent() const {
56 return m_sourceList.isHashOrNoncePresent(); 111 return !m_nonces.isEmpty() ||
57 } 112 m_hashAlgorithmsUsed != ContentSecurityPolicyHashAlgorithmNone;
58 113 }
59 uint8_t SourceListDirective::hashAlgorithmsUsed() const { 114
60 return m_sourceList.hashAlgorithmsUsed(); 115 // source-list = *WSP [ source *( 1*WSP source ) *WSP ]
116 // / *WSP "'none'" *WSP
117 //
118 void SourceListDirective::parse(const UChar* begin, const UChar* end) {
119 // We represent 'none' as an empty m_list.
120 if (isSourceListNone(begin, end))
121 return;
122
123 const UChar* position = begin;
124 while (position < end) {
125 skipWhile<UChar, isASCIISpace>(position, end);
126 if (position == end)
127 return;
128
129 const UChar* beginSource = position;
130 skipWhile<UChar, isSourceCharacter>(position, end);
131
132 String scheme, host, path;
133 int port = 0;
134 CSPSource::WildcardDisposition hostWildcard = CSPSource::NoWildcard;
135 CSPSource::WildcardDisposition portWildcard = CSPSource::NoWildcard;
136
137 if (parseSource(beginSource, position, scheme, host, port, path,
138 hostWildcard, portWildcard)) {
139 // Wildcard hosts and keyword sources ('self', 'unsafe-inline',
140 // etc.) aren't stored in m_list, but as attributes on the source
141 // list itself.
142 if (scheme.isEmpty() && host.isEmpty())
143 continue;
144 if (m_policy->isDirectiveName(host))
145 m_policy->reportDirectiveAsSourceExpression(m_directiveName, host);
146 m_list.append(new CSPSource(m_policy, scheme, host, port, path,
147 hostWildcard, portWildcard));
148 } else {
149 m_policy->reportInvalidSourceExpression(
150 m_directiveName, String(beginSource, position - beginSource));
151 }
152
153 DCHECK(position == end || isASCIISpace(*position));
154 }
155 }
156
157 // source = scheme ":"
158 // / ( [ scheme "://" ] host [ port ] [ path ] )
159 // / "'self'"
160 bool SourceListDirective::parseSource(
161 const UChar* begin,
162 const UChar* end,
163 String& scheme,
164 String& host,
165 int& port,
166 String& path,
167 CSPSource::WildcardDisposition& hostWildcard,
168 CSPSource::WildcardDisposition& portWildcard) {
169 if (begin == end)
170 return false;
171
172 StringView token(begin, end - begin);
173
174 if (equalIgnoringCase("'none'", token))
175 return false;
176
177 if (end - begin == 1 && *begin == '*') {
178 addSourceStar();
179 return true;
180 }
181
182 if (equalIgnoringCase("'self'", token)) {
183 addSourceSelf();
184 return true;
185 }
186
187 if (equalIgnoringCase("'unsafe-inline'", token)) {
188 addSourceUnsafeInline();
189 return true;
190 }
191
192 if (equalIgnoringCase("'unsafe-eval'", token)) {
193 addSourceUnsafeEval();
194 return true;
195 }
196
197 if (equalIgnoringCase("'strict-dynamic'", token)) {
198 addSourceStrictDynamic();
199 return true;
200 }
201
202 if (equalIgnoringCase("'unsafe-hashed-attributes'", token)) {
203 addSourceUnsafeHashedAttributes();
204 return true;
205 }
206
207 String nonce;
208 if (!parseNonce(begin, end, nonce))
209 return false;
210
211 if (!nonce.isNull()) {
212 addSourceNonce(nonce);
213 return true;
214 }
215
216 DigestValue hash;
217 ContentSecurityPolicyHashAlgorithm algorithm =
218 ContentSecurityPolicyHashAlgorithmNone;
219 if (!parseHash(begin, end, hash, algorithm))
220 return false;
221
222 if (hash.size() > 0) {
223 addSourceHash(algorithm, hash);
224 return true;
225 }
226
227 const UChar* position = begin;
228 const UChar* beginHost = begin;
229 const UChar* beginPath = end;
230 const UChar* beginPort = 0;
231
232 skipWhile<UChar, isNotColonOrSlash>(position, end);
233
234 if (position == end) {
235 // host
236 // ^
237 return parseHost(beginHost, position, host, hostWildcard);
238 }
239
240 if (position < end && *position == '/') {
241 // host/path || host/ || /
242 // ^ ^ ^
243 return parseHost(beginHost, position, host, hostWildcard) &&
244 parsePath(position, end, path);
245 }
246
247 if (position < end && *position == ':') {
248 if (end - position == 1) {
249 // scheme:
250 // ^
251 return parseScheme(begin, position, scheme);
252 }
253
254 if (position[1] == '/') {
255 // scheme://host || scheme://
256 // ^ ^
257 if (!parseScheme(begin, position, scheme) ||
258 !skipExactly<UChar>(position, end, ':') ||
259 !skipExactly<UChar>(position, end, '/') ||
260 !skipExactly<UChar>(position, end, '/'))
261 return false;
262 if (position == end)
263 return false;
264 beginHost = position;
265 skipWhile<UChar, isNotColonOrSlash>(position, end);
266 }
267
268 if (position < end && *position == ':') {
269 // host:port || scheme://host:port
270 // ^ ^
271 beginPort = position;
272 skipUntil<UChar>(position, end, '/');
273 }
274 }
275
276 if (position < end && *position == '/') {
277 // scheme://host/path || scheme://host:port/path
278 // ^ ^
279 if (position == beginHost)
280 return false;
281 beginPath = position;
282 }
283
284 if (!parseHost(beginHost, beginPort ? beginPort : beginPath, host,
285 hostWildcard))
286 return false;
287
288 if (beginPort) {
289 if (!parsePort(beginPort, beginPath, port, portWildcard))
290 return false;
291 } else {
292 port = 0;
293 }
294
295 if (beginPath != end) {
296 if (!parsePath(beginPath, end, path))
297 return false;
298 }
299
300 return true;
301 }
302
303 // nonce-source = "'nonce-" nonce-value "'"
304 // nonce-value = 1*( ALPHA / DIGIT / "+" / "/" / "=" )
305 //
306 bool SourceListDirective::parseNonce(const UChar* begin,
307 const UChar* end,
308 String& nonce) {
309 size_t nonceLength = end - begin;
310 StringView prefix("'nonce-");
311
312 // TODO(esprehn): Should be StringView(begin, nonceLength).startsWith(prefix).
313 if (nonceLength <= prefix.length() ||
314 !equalIgnoringCase(prefix, StringView(begin, prefix.length())))
315 return true;
316
317 const UChar* position = begin + prefix.length();
318 const UChar* nonceBegin = position;
319
320 DCHECK(position < end);
321 skipWhile<UChar, isNonceCharacter>(position, end);
322 DCHECK(nonceBegin <= position);
323
324 if (position + 1 != end || *position != '\'' || position == nonceBegin)
325 return false;
326
327 nonce = String(nonceBegin, position - nonceBegin);
328 return true;
329 }
330
331 // hash-source = "'" hash-algorithm "-" hash-value "'"
332 // hash-algorithm = "sha1" / "sha256" / "sha384" / "sha512"
333 // hash-value = 1*( ALPHA / DIGIT / "+" / "/" / "=" )
334 //
335 bool SourceListDirective::parseHash(
336 const UChar* begin,
337 const UChar* end,
338 DigestValue& hash,
339 ContentSecurityPolicyHashAlgorithm& hashAlgorithm) {
340 // Any additions or subtractions from this struct should also modify the
341 // respective entries in the kAlgorithmMap array in checkDigest().
342 static const struct {
343 const char* prefix;
344 ContentSecurityPolicyHashAlgorithm type;
345 } kSupportedPrefixes[] = {
346 // FIXME: Drop support for SHA-1. It's not in the spec.
347 {"'sha1-", ContentSecurityPolicyHashAlgorithmSha1},
348 {"'sha256-", ContentSecurityPolicyHashAlgorithmSha256},
349 {"'sha384-", ContentSecurityPolicyHashAlgorithmSha384},
350 {"'sha512-", ContentSecurityPolicyHashAlgorithmSha512},
351 {"'sha-256-", ContentSecurityPolicyHashAlgorithmSha256},
352 {"'sha-384-", ContentSecurityPolicyHashAlgorithmSha384},
353 {"'sha-512-", ContentSecurityPolicyHashAlgorithmSha512}};
354
355 StringView prefix;
356 hashAlgorithm = ContentSecurityPolicyHashAlgorithmNone;
357 size_t hashLength = end - begin;
358
359 for (const auto& algorithm : kSupportedPrefixes) {
360 prefix = algorithm.prefix;
361 // TODO(esprehn): Should be StringView(begin, end -
362 // begin).startsWith(prefix).
363 if (hashLength > prefix.length() &&
364 equalIgnoringCase(prefix, StringView(begin, prefix.length()))) {
365 hashAlgorithm = algorithm.type;
366 break;
367 }
368 }
369
370 if (hashAlgorithm == ContentSecurityPolicyHashAlgorithmNone)
371 return true;
372
373 const UChar* position = begin + prefix.length();
374 const UChar* hashBegin = position;
375
376 DCHECK(position < end);
377 skipWhile<UChar, isBase64EncodedCharacter>(position, end);
378 DCHECK(hashBegin <= position);
379
380 // Base64 encodings may end with exactly one or two '=' characters
381 if (position < end)
382 skipExactly<UChar>(position, position + 1, '=');
383 if (position < end)
384 skipExactly<UChar>(position, position + 1, '=');
385
386 if (position + 1 != end || *position != '\'' || position == hashBegin)
387 return false;
388
389 Vector<char> hashVector;
390 // We accept base64url-encoded data here by normalizing it to base64.
391 base64Decode(normalizeToBase64(String(hashBegin, position - hashBegin)),
392 hashVector);
393 if (hashVector.size() > kMaxDigestSize)
394 return false;
395 hash.append(reinterpret_cast<uint8_t*>(hashVector.data()), hashVector.size());
396 return true;
397 }
398
399 // ; <scheme> production from RFC 3986
400 // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
401 //
402 bool SourceListDirective::parseScheme(const UChar* begin,
403 const UChar* end,
404 String& scheme) {
405 DCHECK(begin <= end);
406 DCHECK(scheme.isEmpty());
407
408 if (begin == end)
409 return false;
410
411 const UChar* position = begin;
412
413 if (!skipExactly<UChar, isASCIIAlpha>(position, end))
414 return false;
415
416 skipWhile<UChar, isSchemeContinuationCharacter>(position, end);
417
418 if (position != end)
419 return false;
420
421 scheme = String(begin, end - begin);
422 return true;
423 }
424
425 // host = [ "*." ] 1*host-char *( "." 1*host-char )
426 // / "*"
427 // host-char = ALPHA / DIGIT / "-"
428 //
429 bool SourceListDirective::parseHost(
430 const UChar* begin,
431 const UChar* end,
432 String& host,
433 CSPSource::WildcardDisposition& hostWildcard) {
434 DCHECK(begin <= end);
435 DCHECK(host.isEmpty());
436 DCHECK(hostWildcard == CSPSource::NoWildcard);
437
438 if (begin == end)
439 return false;
440
441 const UChar* position = begin;
442
443 if (skipExactly<UChar>(position, end, '*')) {
444 hostWildcard = CSPSource::HasWildcard;
445
446 if (position == end)
447 return true;
448
449 if (!skipExactly<UChar>(position, end, '.'))
450 return false;
451 }
452
453 const UChar* hostBegin = position;
454
455 while (position < end) {
456 if (!skipExactly<UChar, isHostCharacter>(position, end))
457 return false;
458
459 skipWhile<UChar, isHostCharacter>(position, end);
460
461 if (position < end && !skipExactly<UChar>(position, end, '.'))
462 return false;
463 }
464
465 DCHECK(position == end);
466 host = String(hostBegin, end - hostBegin);
467 return true;
468 }
469
470 bool SourceListDirective::parsePath(const UChar* begin,
471 const UChar* end,
472 String& path) {
473 DCHECK(begin <= end);
474 DCHECK(path.isEmpty());
475
476 const UChar* position = begin;
477 skipWhile<UChar, isPathComponentCharacter>(position, end);
478 // path/to/file.js?query=string || path/to/file.js#anchor
479 // ^ ^
480 if (position < end) {
481 m_policy->reportInvalidPathCharacter(m_directiveName,
482 String(begin, end - begin), *position);
483 }
484
485 path = decodeURLEscapeSequences(String(begin, position - begin));
486
487 DCHECK(position <= end);
488 DCHECK(position == end || (*position == '#' || *position == '?'));
489 return true;
490 }
491
492 // port = ":" ( 1*DIGIT / "*" )
493 //
494 bool SourceListDirective::parsePort(
495 const UChar* begin,
496 const UChar* end,
497 int& port,
498 CSPSource::WildcardDisposition& portWildcard) {
499 DCHECK(begin <= end);
500 DCHECK(!port);
501 DCHECK(portWildcard == CSPSource::NoWildcard);
502
503 if (!skipExactly<UChar>(begin, end, ':'))
504 NOTREACHED();
505
506 if (begin == end)
507 return false;
508
509 if (end - begin == 1 && *begin == '*') {
510 port = 0;
511 portWildcard = CSPSource::HasWildcard;
512 return true;
513 }
514
515 const UChar* position = begin;
516 skipWhile<UChar, isASCIIDigit>(position, end);
517
518 if (position != end)
519 return false;
520
521 bool ok;
522 port = charactersToIntStrict(begin, end - begin, &ok);
523 return ok;
524 }
525
526 void SourceListDirective::addSourceSelf() {
527 m_allowSelf = true;
528 }
529
530 void SourceListDirective::addSourceStar() {
531 m_allowStar = true;
532 }
533
534 void SourceListDirective::addSourceUnsafeInline() {
535 m_allowInline = true;
536 }
537
538 void SourceListDirective::addSourceUnsafeEval() {
539 m_allowEval = true;
540 }
541
542 void SourceListDirective::addSourceStrictDynamic() {
543 m_allowDynamic = true;
544 }
545
546 void SourceListDirective::addSourceUnsafeHashedAttributes() {
547 m_allowHashedAttributes = true;
548 }
549
550 void SourceListDirective::addSourceNonce(const String& nonce) {
551 m_nonces.add(nonce);
552 }
553
554 void SourceListDirective::addSourceHash(
555 const ContentSecurityPolicyHashAlgorithm& algorithm,
556 const DigestValue& hash) {
557 m_hashes.add(CSPHashValue(algorithm, hash));
558 m_hashAlgorithmsUsed |= algorithm;
559 }
560
561 bool SourceListDirective::hasSourceMatchInList(
562 const KURL& url,
563 ResourceRequest::RedirectStatus redirectStatus) const {
564 for (size_t i = 0; i < m_list.size(); ++i) {
565 if (m_list[i]->matches(url, redirectStatus))
566 return true;
567 }
568
569 return false;
61 } 570 }
62 571
63 DEFINE_TRACE(SourceListDirective) { 572 DEFINE_TRACE(SourceListDirective) {
64 visitor->trace(m_sourceList); 573 visitor->trace(m_policy);
574 visitor->trace(m_list);
65 CSPDirective::trace(visitor); 575 CSPDirective::trace(visitor);
66 } 576 }
67 577
68 } // namespace blink 578 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698