Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / frame / csp / CSPSourceList.cpp
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/CSPSourceList.h"
7
8 #include "core/frame/csp/CSPSource.h"
9 #include "core/frame/csp/ContentSecurityPolicy.h"
10 #include "platform/ParsingUtilities.h"
11 #include "platform/weborigin/KURL.h"
12 #include "platform/weborigin/SecurityOrigin.h"
13 #include "wtf/HashSet.h"
14 #include "wtf/text/Base64.h"
15 #include "wtf/text/WTFString.h"
16
17 namespace blink {
18
19 static bool isSourceListNone(const UChar* begin, const UChar* end)
20 {
21     skipWhile<UChar, isASCIISpace>(begin, end);
22
23     const UChar* position = begin;
24     skipWhile<UChar, isSourceCharacter>(position, end);
25     if (!equalIgnoringCase("'none'", begin, position - begin))
26         return false;
27
28     skipWhile<UChar, isASCIISpace>(position, end);
29     if (position != end)
30         return false;
31
32     return true;
33 }
34
35 CSPSourceList::CSPSourceList(ContentSecurityPolicy* policy, const String& directiveName)
36     : m_policy(policy)
37     , m_directiveName(directiveName)
38     , m_allowSelf(false)
39     , m_allowStar(false)
40     , m_allowInline(false)
41     , m_allowEval(false)
42     , m_hashAlgorithmsUsed(0)
43 {
44 }
45
46 bool CSPSourceList::matches(const KURL& url) const
47 {
48     if (m_allowStar)
49         return true;
50
51     KURL effectiveURL = SecurityOrigin::shouldUseInnerURL(url) ? SecurityOrigin::extractInnerURL(url) : url;
52
53     if (m_allowSelf && m_policy->urlMatchesSelf(effectiveURL))
54         return true;
55
56     for (size_t i = 0; i < m_list.size(); ++i) {
57         if (m_list[i].matches(effectiveURL))
58             return true;
59     }
60
61     return false;
62 }
63
64 bool CSPSourceList::allowInline() const
65 {
66     return m_allowInline;
67 }
68
69 bool CSPSourceList::allowEval() const
70 {
71     return m_allowEval;
72 }
73
74 bool CSPSourceList::allowNonce(const String& nonce) const
75 {
76     return !nonce.isNull() && m_nonces.contains(nonce);
77 }
78
79 bool CSPSourceList::allowHash(const CSPHashValue& hashValue) const
80 {
81     return m_hashes.contains(hashValue);
82 }
83
84 uint8_t CSPSourceList::hashAlgorithmsUsed() const
85 {
86     return m_hashAlgorithmsUsed;
87 }
88
89 bool CSPSourceList::isHashOrNoncePresent() const
90 {
91     return !m_nonces.isEmpty() || m_hashAlgorithmsUsed != ContentSecurityPolicyHashAlgorithmNone;
92 }
93
94 // source-list       = *WSP [ source *( 1*WSP source ) *WSP ]
95 //                   / *WSP "'none'" *WSP
96 //
97 void CSPSourceList::parse(const UChar* begin, const UChar* end)
98 {
99     // We represent 'none' as an empty m_list.
100     if (isSourceListNone(begin, end))
101         return;
102
103     const UChar* position = begin;
104     while (position < end) {
105         skipWhile<UChar, isASCIISpace>(position, end);
106         if (position == end)
107             return;
108
109         const UChar* beginSource = position;
110         skipWhile<UChar, isSourceCharacter>(position, end);
111
112         String scheme, host, path;
113         int port = 0;
114         CSPSource::WildcardDisposition hostWildcard = CSPSource::NoWildcard;
115         CSPSource::WildcardDisposition portWildcard = CSPSource::NoWildcard;
116
117         if (parseSource(beginSource, position, scheme, host, port, path, hostWildcard, portWildcard)) {
118             // Wildcard hosts and keyword sources ('self', 'unsafe-inline',
119             // etc.) aren't stored in m_list, but as attributes on the source
120             // list itself.
121             if (scheme.isEmpty() && host.isEmpty())
122                 continue;
123             if (m_policy->isDirectiveName(host))
124                 m_policy->reportDirectiveAsSourceExpression(m_directiveName, host);
125             m_list.append(CSPSource(m_policy, scheme, host, port, path, hostWildcard, portWildcard));
126         } else {
127             m_policy->reportInvalidSourceExpression(m_directiveName, String(beginSource, position - beginSource));
128         }
129
130         ASSERT(position == end || isASCIISpace(*position));
131     }
132 }
133
134 // source            = scheme ":"
135 //                   / ( [ scheme "://" ] host [ port ] [ path ] )
136 //                   / "'self'"
137 bool CSPSourceList::parseSource(const UChar* begin, const UChar* end, String& scheme, String& host, int& port, String& path, CSPSource::WildcardDisposition& hostWildcard, CSPSource::WildcardDisposition& portWildcard)
138 {
139     if (begin == end)
140         return false;
141
142     if (equalIgnoringCase("'none'", begin, end - begin))
143         return false;
144
145     if (end - begin == 1 && *begin == '*') {
146         addSourceStar();
147         return true;
148     }
149
150     if (equalIgnoringCase("'self'", begin, end - begin)) {
151         addSourceSelf();
152         return true;
153     }
154
155     if (equalIgnoringCase("'unsafe-inline'", begin, end - begin)) {
156         addSourceUnsafeInline();
157         return true;
158     }
159
160     if (equalIgnoringCase("'unsafe-eval'", begin, end - begin)) {
161         addSourceUnsafeEval();
162         return true;
163     }
164
165     String nonce;
166     if (!parseNonce(begin, end, nonce))
167         return false;
168
169     if (!nonce.isNull()) {
170         addSourceNonce(nonce);
171         return true;
172     }
173
174     DigestValue hash;
175     ContentSecurityPolicyHashAlgorithm algorithm = ContentSecurityPolicyHashAlgorithmNone;
176     if (!parseHash(begin, end, hash, algorithm))
177         return false;
178
179     if (hash.size() > 0) {
180         addSourceHash(algorithm, hash);
181         return true;
182     }
183
184     const UChar* position = begin;
185     const UChar* beginHost = begin;
186     const UChar* beginPath = end;
187     const UChar* beginPort = 0;
188
189     skipWhile<UChar, isNotColonOrSlash>(position, end);
190
191     if (position == end) {
192         // host
193         //     ^
194         return parseHost(beginHost, position, host, hostWildcard);
195     }
196
197     if (position < end && *position == '/') {
198         // host/path || host/ || /
199         //     ^            ^    ^
200         return parseHost(beginHost, position, host, hostWildcard) && parsePath(position, end, path);
201     }
202
203     if (position < end && *position == ':') {
204         if (end - position == 1) {
205             // scheme:
206             //       ^
207             return parseScheme(begin, position, scheme);
208         }
209
210         if (position[1] == '/') {
211             // scheme://host || scheme://
212             //       ^                ^
213             if (!parseScheme(begin, position, scheme)
214                 || !skipExactly<UChar>(position, end, ':')
215                 || !skipExactly<UChar>(position, end, '/')
216                 || !skipExactly<UChar>(position, end, '/'))
217                 return false;
218             if (position == end)
219                 return false;
220             beginHost = position;
221             skipWhile<UChar, isNotColonOrSlash>(position, end);
222         }
223
224         if (position < end && *position == ':') {
225             // host:port || scheme://host:port
226             //     ^                     ^
227             beginPort = position;
228             skipUntil<UChar>(position, end, '/');
229         }
230     }
231
232     if (position < end && *position == '/') {
233         // scheme://host/path || scheme://host:port/path
234         //              ^                          ^
235         if (position == beginHost)
236             return false;
237         beginPath = position;
238     }
239
240     if (!parseHost(beginHost, beginPort ? beginPort : beginPath, host, hostWildcard))
241         return false;
242
243     if (beginPort) {
244         if (!parsePort(beginPort, beginPath, port, portWildcard))
245             return false;
246     } else {
247         port = 0;
248     }
249
250     if (beginPath != end) {
251         if (!parsePath(beginPath, end, path))
252             return false;
253     }
254
255     return true;
256 }
257
258 // nonce-source      = "'nonce-" nonce-value "'"
259 // nonce-value        = 1*( ALPHA / DIGIT / "+" / "/" / "=" )
260 //
261 bool CSPSourceList::parseNonce(const UChar* begin, const UChar* end, String& nonce)
262 {
263     size_t nonceLength = end - begin;
264     const char* prefix = "'nonce-";
265
266     if (nonceLength <= strlen(prefix) || !equalIgnoringCase(prefix, begin, strlen(prefix)))
267         return true;
268
269     const UChar* position = begin + strlen(prefix);
270     const UChar* nonceBegin = position;
271
272     ASSERT(position < end);
273     skipWhile<UChar, isNonceCharacter>(position, end);
274     ASSERT(nonceBegin <= position);
275
276     if (position + 1 != end || *position != '\'' || position == nonceBegin)
277         return false;
278
279     nonce = String(nonceBegin, position - nonceBegin);
280     return true;
281 }
282
283 // hash-source       = "'" hash-algorithm "-" hash-value "'"
284 // hash-algorithm    = "sha1" / "sha256" / "sha384" / "sha512"
285 // hash-value        = 1*( ALPHA / DIGIT / "+" / "/" / "=" )
286 //
287 bool CSPSourceList::parseHash(const UChar* begin, const UChar* end, DigestValue& hash, ContentSecurityPolicyHashAlgorithm& hashAlgorithm)
288 {
289     // Any additions or subtractions from this struct should also modify the
290     // respective entries in the kAlgorithmMap array in checkDigest().
291     static const struct {
292         const char* prefix;
293         ContentSecurityPolicyHashAlgorithm type;
294     } kSupportedPrefixes[] = {
295         { "'sha1-", ContentSecurityPolicyHashAlgorithmSha1 },
296         { "'sha256-", ContentSecurityPolicyHashAlgorithmSha256 },
297         { "'sha384-", ContentSecurityPolicyHashAlgorithmSha384 },
298         { "'sha512-", ContentSecurityPolicyHashAlgorithmSha512 }
299     };
300
301     String prefix;
302     hashAlgorithm = ContentSecurityPolicyHashAlgorithmNone;
303     size_t hashLength = end - begin;
304
305     for (const auto& algorithm : kSupportedPrefixes) {
306         if (hashLength > strlen(algorithm.prefix) && equalIgnoringCase(algorithm.prefix, begin, strlen(algorithm.prefix))) {
307             prefix = algorithm.prefix;
308             hashAlgorithm = algorithm.type;
309             break;
310         }
311     }
312
313     if (hashAlgorithm == ContentSecurityPolicyHashAlgorithmNone)
314         return true;
315
316     const UChar* position = begin + prefix.length();
317     const UChar* hashBegin = position;
318
319     ASSERT(position < end);
320     skipWhile<UChar, isBase64EncodedCharacter>(position, end);
321     ASSERT(hashBegin <= position);
322
323     // Base64 encodings may end with exactly one or two '=' characters
324     if (position < end)
325         skipExactly<UChar>(position, position + 1, '=');
326     if (position < end)
327         skipExactly<UChar>(position, position + 1, '=');
328
329     if (position + 1 != end || *position != '\'' || position == hashBegin)
330         return false;
331
332     Vector<char> hashVector;
333     base64Decode(hashBegin, position - hashBegin, hashVector);
334     if (hashVector.size() > kMaxDigestSize)
335         return false;
336     hash.append(reinterpret_cast<uint8_t*>(hashVector.data()), hashVector.size());
337     return true;
338 }
339
340 //                     ; <scheme> production from RFC 3986
341 // scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
342 //
343 bool CSPSourceList::parseScheme(const UChar* begin, const UChar* end, String& scheme)
344 {
345     ASSERT(begin <= end);
346     ASSERT(scheme.isEmpty());
347
348     if (begin == end)
349         return false;
350
351     const UChar* position = begin;
352
353     if (!skipExactly<UChar, isASCIIAlpha>(position, end))
354         return false;
355
356     skipWhile<UChar, isSchemeContinuationCharacter>(position, end);
357
358     if (position != end)
359         return false;
360
361     scheme = String(begin, end - begin);
362     return true;
363 }
364
365 // host              = [ "*." ] 1*host-char *( "." 1*host-char )
366 //                   / "*"
367 // host-char         = ALPHA / DIGIT / "-"
368 //
369 bool CSPSourceList::parseHost(const UChar* begin, const UChar* end, String& host, CSPSource::WildcardDisposition& hostWildcard)
370 {
371     ASSERT(begin <= end);
372     ASSERT(host.isEmpty());
373     ASSERT(hostWildcard == CSPSource::NoWildcard);
374
375     if (begin == end)
376         return false;
377
378     const UChar* position = begin;
379
380     if (skipExactly<UChar>(position, end, '*')) {
381         hostWildcard = CSPSource::HasWildcard;
382
383         if (position == end)
384             return true;
385
386         if (!skipExactly<UChar>(position, end, '.'))
387             return false;
388     }
389
390     const UChar* hostBegin = position;
391
392     while (position < end) {
393         if (!skipExactly<UChar, isHostCharacter>(position, end))
394             return false;
395
396         skipWhile<UChar, isHostCharacter>(position, end);
397
398         if (position < end && !skipExactly<UChar>(position, end, '.'))
399             return false;
400     }
401
402     ASSERT(position == end);
403     host = String(hostBegin, end - hostBegin);
404     return true;
405 }
406
407 bool CSPSourceList::parsePath(const UChar* begin, const UChar* end, String& path)
408 {
409     ASSERT(begin <= end);
410     ASSERT(path.isEmpty());
411
412     const UChar* position = begin;
413     skipWhile<UChar, isPathComponentCharacter>(position, end);
414     // path/to/file.js?query=string || path/to/file.js#anchor
415     //                ^                               ^
416     if (position < end)
417         m_policy->reportInvalidPathCharacter(m_directiveName, String(begin, end - begin), *position);
418
419     path = decodeURLEscapeSequences(String(begin, position - begin));
420
421     ASSERT(position <= end);
422     ASSERT(position == end || (*position == '#' || *position == '?'));
423     return true;
424 }
425
426 // port              = ":" ( 1*DIGIT / "*" )
427 //
428 bool CSPSourceList::parsePort(const UChar* begin, const UChar* end, int& port, CSPSource::WildcardDisposition& portWildcard)
429 {
430     ASSERT(begin <= end);
431     ASSERT(!port);
432     ASSERT(portWildcard == CSPSource::NoWildcard);
433
434     if (!skipExactly<UChar>(begin, end, ':'))
435         ASSERT_NOT_REACHED();
436
437     if (begin == end)
438         return false;
439
440     if (end - begin == 1 && *begin == '*') {
441         port = 0;
442         portWildcard = CSPSource::HasWildcard;
443         return true;
444     }
445
446     const UChar* position = begin;
447     skipWhile<UChar, isASCIIDigit>(position, end);
448
449     if (position != end)
450         return false;
451
452     bool ok;
453     port = charactersToIntStrict(begin, end - begin, &ok);
454     return ok;
455 }
456
457 void CSPSourceList::addSourceSelf()
458 {
459     m_allowSelf = true;
460 }
461
462 void CSPSourceList::addSourceStar()
463 {
464     m_allowStar = true;
465 }
466
467 void CSPSourceList::addSourceUnsafeInline()
468 {
469     m_allowInline = true;
470 }
471
472 void CSPSourceList::addSourceUnsafeEval()
473 {
474     m_allowEval = true;
475 }
476
477 void CSPSourceList::addSourceNonce(const String& nonce)
478 {
479     m_nonces.add(nonce);
480 }
481
482 void CSPSourceList::addSourceHash(const ContentSecurityPolicyHashAlgorithm& algorithm, const DigestValue& hash)
483 {
484     m_hashes.add(CSPHashValue(algorithm, hash));
485     m_hashAlgorithmsUsed |= algorithm;
486 }
487
488
489 } // namespace blink