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