1 /* 2 * Copyright (C) 2011 Google, Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "core/frame/ContentSecurityPolicy.h" 28 29 #include "RuntimeEnabledFeatures.h" 30 #include "bindings/v8/ScriptCallStackFactory.h" 31 #include "bindings/v8/ScriptController.h" 32 #include "core/dom/DOMStringList.h" 33 #include "core/dom/Document.h" 34 #include "core/events/SecurityPolicyViolationEvent.h" 35 #include "core/frame/ContentSecurityPolicyResponseHeaders.h" 36 #include "core/frame/DOMWindow.h" 37 #include "core/frame/Frame.h" 38 #include "core/inspector/InspectorInstrumentation.h" 39 #include "core/inspector/ScriptCallStack.h" 40 #include "core/loader/DocumentLoader.h" 41 #include "core/loader/PingLoader.h" 42 #include "core/frame/UseCounter.h" 43 #include "platform/JSONValues.h" 44 #include "platform/NotImplemented.h" 45 #include "platform/ParsingUtilities.h" 46 #include "platform/network/FormData.h" 47 #include "platform/network/ResourceResponse.h" 48 #include "platform/weborigin/KURL.h" 49 #include "platform/weborigin/KnownPorts.h" 50 #include "platform/weborigin/SchemeRegistry.h" 51 #include "platform/weborigin/SecurityOrigin.h" 52 #include "wtf/SHA1.h" 53 #include "wtf/StringHasher.h" 54 #include "wtf/text/Base64.h" 55 #include "wtf/text/StringBuilder.h" 56 57 namespace WTF { 58 59 struct VectorIntHash { 60 static unsigned hash(const Vector<uint8_t>& v) { return StringHasher::computeHash(v.data(), v.size()); } 61 static bool equal(const Vector<uint8_t>& a, const Vector<uint8_t>& b) { return a == b; }; 62 static const bool safeToCompareToEmptyOrDeleted = true; 63 }; 64 template<> struct DefaultHash<Vector<uint8_t> > { 65 typedef VectorIntHash Hash; 66 }; 67 68 } // namespace WTF 69 70 namespace WebCore { 71 72 typedef std::pair<unsigned, Vector<uint8_t> > SourceHashValue; 73 74 // Normally WebKit uses "static" for internal linkage, but using "static" for 75 // these functions causes a compile error because these functions are used as 76 // template parameters. 77 namespace { 78 79 bool isDirectiveNameCharacter(UChar c) 80 { 81 return isASCIIAlphanumeric(c) || c == '-'; 82 } 83 84 bool isDirectiveValueCharacter(UChar c) 85 { 86 return isASCIISpace(c) || (c >= 0x21 && c <= 0x7e); // Whitespace + VCHAR 87 } 88 89 // Only checks for general Base64 encoded chars, not '=' chars since '=' is 90 // positional and may only appear at the end of a Base64 encoded string. 91 bool isBase64EncodedCharacter(UChar c) 92 { 93 return isASCIIAlphanumeric(c) || c == '+' || c == '/'; 94 } 95 96 bool isNonceCharacter(UChar c) 97 { 98 return isBase64EncodedCharacter(c) || c == '='; 99 } 100 101 bool isSourceCharacter(UChar c) 102 { 103 return !isASCIISpace(c); 104 } 105 106 bool isPathComponentCharacter(UChar c) 107 { 108 return c != '?' && c != '#'; 109 } 110 111 bool isHostCharacter(UChar c) 112 { 113 return isASCIIAlphanumeric(c) || c == '-'; 114 } 115 116 bool isSchemeContinuationCharacter(UChar c) 117 { 118 return isASCIIAlphanumeric(c) || c == '+' || c == '-' || c == '.'; 119 } 120 121 bool isNotASCIISpace(UChar c) 122 { 123 return !isASCIISpace(c); 124 } 125 126 bool isNotColonOrSlash(UChar c) 127 { 128 return c != ':' && c != '/'; 129 } 130 131 bool isMediaTypeCharacter(UChar c) 132 { 133 return !isASCIISpace(c) && c != '/'; 134 } 135 136 // CSP 1.0 Directives 137 static const char connectSrc[] = "connect-src"; 138 static const char defaultSrc[] = "default-src"; 139 static const char fontSrc[] = "font-src"; 140 static const char frameSrc[] = "frame-src"; 141 static const char imgSrc[] = "img-src"; 142 static const char mediaSrc[] = "media-src"; 143 static const char objectSrc[] = "object-src"; 144 static const char reportURI[] = "report-uri"; 145 static const char sandbox[] = "sandbox"; 146 static const char scriptSrc[] = "script-src"; 147 static const char styleSrc[] = "style-src"; 148 149 // CSP 1.1 Directives 150 static const char baseURI[] = "base-uri"; 151 static const char formAction[] = "form-action"; 152 static const char pluginTypes[] = "plugin-types"; 153 static const char reflectedXSS[] = "reflected-xss"; 154 155 bool isDirectiveName(const String& name) 156 { 157 return (equalIgnoringCase(name, connectSrc) 158 || equalIgnoringCase(name, defaultSrc) 159 || equalIgnoringCase(name, fontSrc) 160 || equalIgnoringCase(name, frameSrc) 161 || equalIgnoringCase(name, imgSrc) 162 || equalIgnoringCase(name, mediaSrc) 163 || equalIgnoringCase(name, objectSrc) 164 || equalIgnoringCase(name, reportURI) 165 || equalIgnoringCase(name, sandbox) 166 || equalIgnoringCase(name, scriptSrc) 167 || equalIgnoringCase(name, styleSrc) 168 || equalIgnoringCase(name, baseURI) 169 || equalIgnoringCase(name, formAction) 170 || equalIgnoringCase(name, pluginTypes) 171 || equalIgnoringCase(name, reflectedXSS) 172 ); 173 } 174 175 UseCounter::Feature getUseCounterType(ContentSecurityPolicy::HeaderType type) 176 { 177 switch (type) { 178 case ContentSecurityPolicy::Enforce: 179 return UseCounter::ContentSecurityPolicy; 180 case ContentSecurityPolicy::Report: 181 return UseCounter::ContentSecurityPolicyReportOnly; 182 } 183 ASSERT_NOT_REACHED(); 184 return UseCounter::NumberOfFeatures; 185 } 186 187 } // namespace 188 189 static bool isSourceListNone(const UChar* begin, const UChar* end) 190 { 191 skipWhile<UChar, isASCIISpace>(begin, end); 192 193 const UChar* position = begin; 194 skipWhile<UChar, isSourceCharacter>(position, end); 195 if (!equalIgnoringCase("'none'", begin, position - begin)) 196 return false; 197 198 skipWhile<UChar, isASCIISpace>(position, end); 199 if (position != end) 200 return false; 201 202 return true; 203 } 204 205 class CSPSource { 206 public: 207 CSPSource(ContentSecurityPolicy* policy, const String& scheme, const String& host, int port, const String& path, bool hostHasWildcard, bool portHasWildcard) 208 : m_policy(policy) 209 , m_scheme(scheme) 210 , m_host(host) 211 , m_port(port) 212 , m_path(path) 213 , m_hostHasWildcard(hostHasWildcard) 214 , m_portHasWildcard(portHasWildcard) 215 { 216 } 217 218 bool matches(const KURL& url) const 219 { 220 if (!schemeMatches(url)) 221 return false; 222 if (isSchemeOnly()) 223 return true; 224 return hostMatches(url) && portMatches(url) && pathMatches(url); 225 } 226 227 private: 228 bool schemeMatches(const KURL& url) const 229 { 230 if (m_scheme.isEmpty()) { 231 String protectedResourceScheme(m_policy->securityOrigin()->protocol()); 232 if (equalIgnoringCase("http", protectedResourceScheme)) 233 return url.protocolIs("http") || url.protocolIs("https"); 234 return equalIgnoringCase(url.protocol(), protectedResourceScheme); 235 } 236 return equalIgnoringCase(url.protocol(), m_scheme); 237 } 238 239 bool hostMatches(const KURL& url) const 240 { 241 const String& host = url.host(); 242 if (equalIgnoringCase(host, m_host)) 243 return true; 244 return m_hostHasWildcard && host.endsWith("." + m_host, false); 245 246 } 247 248 bool pathMatches(const KURL& url) const 249 { 250 if (m_path.isEmpty()) 251 return true; 252 253 String path = decodeURLEscapeSequences(url.path()); 254 255 if (m_path.endsWith("/")) 256 return path.startsWith(m_path, false); 257 258 return path == m_path; 259 } 260 261 bool portMatches(const KURL& url) const 262 { 263 if (m_portHasWildcard) 264 return true; 265 266 int port = url.port(); 267 268 if (port == m_port) 269 return true; 270 271 if (!port) 272 return isDefaultPortForProtocol(m_port, url.protocol()); 273 274 if (!m_port) 275 return isDefaultPortForProtocol(port, url.protocol()); 276 277 return false; 278 } 279 280 bool isSchemeOnly() const { return m_host.isEmpty(); } 281 282 ContentSecurityPolicy* m_policy; 283 String m_scheme; 284 String m_host; 285 int m_port; 286 String m_path; 287 288 bool m_hostHasWildcard; 289 bool m_portHasWildcard; 290 }; 291 292 class CSPSourceList { 293 public: 294 CSPSourceList(ContentSecurityPolicy*, const String& directiveName); 295 296 void parse(const UChar* begin, const UChar* end); 297 298 bool matches(const KURL&); 299 bool allowInline() const { return m_allowInline; } 300 bool allowEval() const { return m_allowEval; } 301 bool allowNonce(const String& nonce) const { return !nonce.isNull() && m_nonces.contains(nonce); } 302 bool allowHash(const SourceHashValue& hashValue) const { return m_hashes.contains(hashValue); } 303 uint8_t hashAlgorithmsUsed() const { return m_hashAlgorithmsUsed; } 304 305 private: 306 bool parseSource(const UChar* begin, const UChar* end, String& scheme, String& host, int& port, String& path, bool& hostHasWildcard, bool& portHasWildcard); 307 bool parseScheme(const UChar* begin, const UChar* end, String& scheme); 308 bool parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard); 309 bool parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard); 310 bool parsePath(const UChar* begin, const UChar* end, String& path); 311 bool parseNonce(const UChar* begin, const UChar* end, String& nonce); 312 bool parseHash(const UChar* begin, const UChar* end, Vector<uint8_t>& hash, ContentSecurityPolicy::HashAlgorithms&); 313 314 void addSourceSelf(); 315 void addSourceStar(); 316 void addSourceUnsafeInline(); 317 void addSourceUnsafeEval(); 318 void addSourceNonce(const String& nonce); 319 void addSourceHash(const ContentSecurityPolicy::HashAlgorithms&, const Vector<uint8_t>& hash); 320 321 ContentSecurityPolicy* m_policy; 322 Vector<CSPSource> m_list; 323 String m_directiveName; 324 bool m_allowStar; 325 bool m_allowInline; 326 bool m_allowEval; 327 HashSet<String> m_nonces; 328 HashSet<SourceHashValue> m_hashes; 329 uint8_t m_hashAlgorithmsUsed; 330 }; 331 332 CSPSourceList::CSPSourceList(ContentSecurityPolicy* policy, const String& directiveName) 333 : m_policy(policy) 334 , m_directiveName(directiveName) 335 , m_allowStar(false) 336 , m_allowInline(false) 337 , m_allowEval(false) 338 , m_hashAlgorithmsUsed(0) 339 { 340 } 341 342 bool CSPSourceList::matches(const KURL& url) 343 { 344 if (m_allowStar) 345 return true; 346 347 KURL effectiveURL = SecurityOrigin::shouldUseInnerURL(url) ? SecurityOrigin::extractInnerURL(url) : url; 348 349 for (size_t i = 0; i < m_list.size(); ++i) { 350 if (m_list[i].matches(effectiveURL)) 351 return true; 352 } 353 354 return false; 355 } 356 357 // source-list = *WSP [ source *( 1*WSP source ) *WSP ] 358 // / *WSP "'none'" *WSP 359 // 360 void CSPSourceList::parse(const UChar* begin, const UChar* end) 361 { 362 // We represent 'none' as an empty m_list. 363 if (isSourceListNone(begin, end)) 364 return; 365 366 const UChar* position = begin; 367 while (position < end) { 368 skipWhile<UChar, isASCIISpace>(position, end); 369 if (position == end) 370 return; 371 372 const UChar* beginSource = position; 373 skipWhile<UChar, isSourceCharacter>(position, end); 374 375 String scheme, host, path; 376 int port = 0; 377 bool hostHasWildcard = false; 378 bool portHasWildcard = false; 379 380 if (parseSource(beginSource, position, scheme, host, port, path, hostHasWildcard, portHasWildcard)) { 381 // Wildcard hosts and keyword sources ('self', 'unsafe-inline', 382 // etc.) aren't stored in m_list, but as attributes on the source 383 // list itself. 384 if (scheme.isEmpty() && host.isEmpty()) 385 continue; 386 if (isDirectiveName(host)) 387 m_policy->reportDirectiveAsSourceExpression(m_directiveName, host); 388 m_list.append(CSPSource(m_policy, scheme, host, port, path, hostHasWildcard, portHasWildcard)); 389 } else { 390 m_policy->reportInvalidSourceExpression(m_directiveName, String(beginSource, position - beginSource)); 391 } 392 393 ASSERT(position == end || isASCIISpace(*position)); 394 } 395 } 396 397 // source = scheme ":" 398 // / ( [ scheme "://" ] host [ port ] [ path ] ) 399 // / "'self'" 400 bool CSPSourceList::parseSource(const UChar* begin, const UChar* end, String& scheme, String& host, int& port, String& path, bool& hostHasWildcard, bool& portHasWildcard) 401 { 402 if (begin == end) 403 return false; 404 405 if (equalIgnoringCase("'none'", begin, end - begin)) 406 return false; 407 408 if (end - begin == 1 && *begin == '*') { 409 addSourceStar(); 410 return true; 411 } 412 413 if (equalIgnoringCase("'self'", begin, end - begin)) { 414 addSourceSelf(); 415 return true; 416 } 417 418 if (equalIgnoringCase("'unsafe-inline'", begin, end - begin)) { 419 addSourceUnsafeInline(); 420 return true; 421 } 422 423 if (equalIgnoringCase("'unsafe-eval'", begin, end - begin)) { 424 addSourceUnsafeEval(); 425 return true; 426 } 427 428 if (m_policy->experimentalFeaturesEnabled()) { 429 String nonce; 430 if (!parseNonce(begin, end, nonce)) 431 return false; 432 433 if (!nonce.isNull()) { 434 addSourceNonce(nonce); 435 return true; 436 } 437 438 Vector<uint8_t> hash; 439 ContentSecurityPolicy::HashAlgorithms algorithm = ContentSecurityPolicy::HashAlgorithmsNone; 440 if (!parseHash(begin, end, hash, algorithm)) 441 return false; 442 443 if (hash.size() > 0) { 444 addSourceHash(algorithm, hash); 445 return true; 446 } 447 } 448 449 const UChar* position = begin; 450 const UChar* beginHost = begin; 451 const UChar* beginPath = end; 452 const UChar* beginPort = 0; 453 454 skipWhile<UChar, isNotColonOrSlash>(position, end); 455 456 if (position == end) { 457 // host 458 // ^ 459 return parseHost(beginHost, position, host, hostHasWildcard); 460 } 461 462 if (position < end && *position == '/') { 463 // host/path || host/ || / 464 // ^ ^ ^ 465 return parseHost(beginHost, position, host, hostHasWildcard) && parsePath(position, end, path); 466 } 467 468 if (position < end && *position == ':') { 469 if (end - position == 1) { 470 // scheme: 471 // ^ 472 return parseScheme(begin, position, scheme); 473 } 474 475 if (position[1] == '/') { 476 // scheme://host || scheme:// 477 // ^ ^ 478 if (!parseScheme(begin, position, scheme) 479 || !skipExactly<UChar>(position, end, ':') 480 || !skipExactly<UChar>(position, end, '/') 481 || !skipExactly<UChar>(position, end, '/')) 482 return false; 483 if (position == end) 484 return true; 485 beginHost = position; 486 skipWhile<UChar, isNotColonOrSlash>(position, end); 487 } 488 489 if (position < end && *position == ':') { 490 // host:port || scheme://host:port 491 // ^ ^ 492 beginPort = position; 493 skipUntil<UChar>(position, end, '/'); 494 } 495 } 496 497 if (position < end && *position == '/') { 498 // scheme://host/path || scheme://host:port/path 499 // ^ ^ 500 if (position == beginHost) 501 return false; 502 beginPath = position; 503 } 504 505 if (!parseHost(beginHost, beginPort ? beginPort : beginPath, host, hostHasWildcard)) 506 return false; 507 508 if (beginPort) { 509 if (!parsePort(beginPort, beginPath, port, portHasWildcard)) 510 return false; 511 } else { 512 port = 0; 513 } 514 515 if (beginPath != end) { 516 if (!parsePath(beginPath, end, path)) 517 return false; 518 } 519 520 return true; 521 } 522 523 // nonce-source = "'nonce-" nonce-value "'" 524 // nonce-value = 1*( ALPHA / DIGIT / "+" / "/" / "=" ) 525 // 526 bool CSPSourceList::parseNonce(const UChar* begin, const UChar* end, String& nonce) 527 { 528 DEFINE_STATIC_LOCAL(const String, noncePrefix, ("'nonce-")); 529 530 if (!equalIgnoringCase(noncePrefix.characters8(), begin, noncePrefix.length())) 531 return true; 532 533 const UChar* position = begin + noncePrefix.length(); 534 const UChar* nonceBegin = position; 535 536 skipWhile<UChar, isNonceCharacter>(position, end); 537 ASSERT(nonceBegin <= position); 538 539 if ((position + 1) != end || *position != '\'' || !(position - nonceBegin)) 540 return false; 541 542 nonce = String(nonceBegin, position - nonceBegin); 543 return true; 544 } 545 546 // hash-source = "'" hash-algorithm "-" hash-value "'" 547 // hash-algorithm = "sha1" / "sha256" 548 // hash-value = 1*( ALPHA / DIGIT / "+" / "/" / "=" ) 549 // 550 bool CSPSourceList::parseHash(const UChar* begin, const UChar* end, Vector<uint8_t>& hash, ContentSecurityPolicy::HashAlgorithms& hashAlgorithm) 551 { 552 DEFINE_STATIC_LOCAL(const String, sha1Prefix, ("'sha1-")); 553 DEFINE_STATIC_LOCAL(const String, sha256Prefix, ("'sha256-")); 554 555 String prefix; 556 if (equalIgnoringCase(sha1Prefix.characters8(), begin, sha1Prefix.length())) { 557 prefix = sha1Prefix; 558 hashAlgorithm = ContentSecurityPolicy::HashAlgorithmsSha1; 559 } else if (equalIgnoringCase(sha256Prefix.characters8(), begin, sha256Prefix.length())) { 560 notImplemented(); 561 } else { 562 return true; 563 } 564 565 const UChar* position = begin + prefix.length(); 566 const UChar* hashBegin = position; 567 568 skipWhile<UChar, isBase64EncodedCharacter>(position, end); 569 ASSERT(hashBegin <= position); 570 571 // Base64 encodings may end with exactly one or two '=' characters 572 skipExactly<UChar>(position, position + 1, '='); 573 skipExactly<UChar>(position, position + 1, '='); 574 575 if ((position + 1) != end || *position != '\'' || !(position - hashBegin)) 576 return false; 577 578 Vector<char> hashVector; 579 base64Decode(hashBegin, position - hashBegin, hashVector); 580 hash.append(reinterpret_cast<uint8_t*>(hashVector.data()), hashVector.size()); 581 return true; 582 } 583 584 // ; <scheme> production from RFC 3986 585 // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) 586 // 587 bool CSPSourceList::parseScheme(const UChar* begin, const UChar* end, String& scheme) 588 { 589 ASSERT(begin <= end); 590 ASSERT(scheme.isEmpty()); 591 592 if (begin == end) 593 return false; 594 595 const UChar* position = begin; 596 597 if (!skipExactly<UChar, isASCIIAlpha>(position, end)) 598 return false; 599 600 skipWhile<UChar, isSchemeContinuationCharacter>(position, end); 601 602 if (position != end) 603 return false; 604 605 scheme = String(begin, end - begin); 606 return true; 607 } 608 609 // host = [ "*." ] 1*host-char *( "." 1*host-char ) 610 // / "*" 611 // host-char = ALPHA / DIGIT / "-" 612 // 613 bool CSPSourceList::parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard) 614 { 615 ASSERT(begin <= end); 616 ASSERT(host.isEmpty()); 617 ASSERT(!hostHasWildcard); 618 619 if (begin == end) 620 return false; 621 622 const UChar* position = begin; 623 624 if (skipExactly<UChar>(position, end, '*')) { 625 hostHasWildcard = true; 626 627 if (position == end) 628 return true; 629 630 if (!skipExactly<UChar>(position, end, '.')) 631 return false; 632 } 633 634 const UChar* hostBegin = position; 635 636 while (position < end) { 637 if (!skipExactly<UChar, isHostCharacter>(position, end)) 638 return false; 639 640 skipWhile<UChar, isHostCharacter>(position, end); 641 642 if (position < end && !skipExactly<UChar>(position, end, '.')) 643 return false; 644 } 645 646 ASSERT(position == end); 647 host = String(hostBegin, end - hostBegin); 648 return true; 649 } 650 651 bool CSPSourceList::parsePath(const UChar* begin, const UChar* end, String& path) 652 { 653 ASSERT(begin <= end); 654 ASSERT(path.isEmpty()); 655 656 const UChar* position = begin; 657 skipWhile<UChar, isPathComponentCharacter>(position, end); 658 // path/to/file.js?query=string || path/to/file.js#anchor 659 // ^ ^ 660 if (position < end) 661 m_policy->reportInvalidPathCharacter(m_directiveName, String(begin, end - begin), *position); 662 663 path = decodeURLEscapeSequences(String(begin, position - begin)); 664 665 ASSERT(position <= end); 666 ASSERT(position == end || (*position == '#' || *position == '?')); 667 return true; 668 } 669 670 // port = ":" ( 1*DIGIT / "*" ) 671 // 672 bool CSPSourceList::parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard) 673 { 674 ASSERT(begin <= end); 675 ASSERT(!port); 676 ASSERT(!portHasWildcard); 677 678 if (!skipExactly<UChar>(begin, end, ':')) 679 ASSERT_NOT_REACHED(); 680 681 if (begin == end) 682 return false; 683 684 if (end - begin == 1 && *begin == '*') { 685 port = 0; 686 portHasWildcard = true; 687 return true; 688 } 689 690 const UChar* position = begin; 691 skipWhile<UChar, isASCIIDigit>(position, end); 692 693 if (position != end) 694 return false; 695 696 bool ok; 697 port = charactersToIntStrict(begin, end - begin, &ok); 698 return ok; 699 } 700 701 void CSPSourceList::addSourceSelf() 702 { 703 m_list.append(CSPSource(m_policy, m_policy->securityOrigin()->protocol(), m_policy->securityOrigin()->host(), m_policy->securityOrigin()->port(), String(), false, false)); 704 } 705 706 void CSPSourceList::addSourceStar() 707 { 708 m_allowStar = true; 709 } 710 711 void CSPSourceList::addSourceUnsafeInline() 712 { 713 m_allowInline = true; 714 } 715 716 void CSPSourceList::addSourceUnsafeEval() 717 { 718 m_allowEval = true; 719 } 720 721 void CSPSourceList::addSourceNonce(const String& nonce) 722 { 723 m_nonces.add(nonce); 724 } 725 726 void CSPSourceList::addSourceHash(const ContentSecurityPolicy::HashAlgorithms& algorithm, const Vector<uint8_t>& hash) 727 { 728 m_hashes.add(SourceHashValue(algorithm, hash)); 729 m_hashAlgorithmsUsed |= algorithm; 730 } 731 732 class CSPDirective { 733 public: 734 CSPDirective(const String& name, const String& value, ContentSecurityPolicy* policy) 735 : m_name(name) 736 , m_text(name + ' ' + value) 737 , m_policy(policy) 738 { 739 } 740 741 const String& text() const { return m_text; } 742 743 protected: 744 const ContentSecurityPolicy* policy() const { return m_policy; } 745 746 private: 747 String m_name; 748 String m_text; 749 ContentSecurityPolicy* m_policy; 750 }; 751 752 class MediaListDirective : public CSPDirective { 753 public: 754 MediaListDirective(const String& name, const String& value, ContentSecurityPolicy* policy) 755 : CSPDirective(name, value, policy) 756 { 757 Vector<UChar> characters; 758 value.appendTo(characters); 759 parse(characters.data(), characters.data() + characters.size()); 760 } 761 762 bool allows(const String& type) 763 { 764 return m_pluginTypes.contains(type); 765 } 766 767 private: 768 void parse(const UChar* begin, const UChar* end) 769 { 770 const UChar* position = begin; 771 772 // 'plugin-types ____;' OR 'plugin-types;' 773 if (position == end) { 774 policy()->reportInvalidPluginTypes(String()); 775 return; 776 } 777 778 while (position < end) { 779 // _____ OR _____mime1/mime1 780 // ^ ^ 781 skipWhile<UChar, isASCIISpace>(position, end); 782 if (position == end) 783 return; 784 785 // mime1/mime1 mime2/mime2 786 // ^ 787 begin = position; 788 if (!skipExactly<UChar, isMediaTypeCharacter>(position, end)) { 789 skipWhile<UChar, isNotASCIISpace>(position, end); 790 policy()->reportInvalidPluginTypes(String(begin, position - begin)); 791 continue; 792 } 793 skipWhile<UChar, isMediaTypeCharacter>(position, end); 794 795 // mime1/mime1 mime2/mime2 796 // ^ 797 if (!skipExactly<UChar>(position, end, '/')) { 798 skipWhile<UChar, isNotASCIISpace>(position, end); 799 policy()->reportInvalidPluginTypes(String(begin, position - begin)); 800 continue; 801 } 802 803 // mime1/mime1 mime2/mime2 804 // ^ 805 if (!skipExactly<UChar, isMediaTypeCharacter>(position, end)) { 806 skipWhile<UChar, isNotASCIISpace>(position, end); 807 policy()->reportInvalidPluginTypes(String(begin, position - begin)); 808 continue; 809 } 810 skipWhile<UChar, isMediaTypeCharacter>(position, end); 811 812 // mime1/mime1 mime2/mime2 OR mime1/mime1 OR mime1/mime1/error 813 // ^ ^ ^ 814 if (position < end && isNotASCIISpace(*position)) { 815 skipWhile<UChar, isNotASCIISpace>(position, end); 816 policy()->reportInvalidPluginTypes(String(begin, position - begin)); 817 continue; 818 } 819 m_pluginTypes.add(String(begin, position - begin)); 820 821 ASSERT(position == end || isASCIISpace(*position)); 822 } 823 } 824 825 HashSet<String> m_pluginTypes; 826 }; 827 828 class SourceListDirective : public CSPDirective { 829 public: 830 SourceListDirective(const String& name, const String& value, ContentSecurityPolicy* policy) 831 : CSPDirective(name, value, policy) 832 , m_sourceList(policy, name) 833 { 834 Vector<UChar> characters; 835 value.appendTo(characters); 836 837 m_sourceList.parse(characters.data(), characters.data() + characters.size()); 838 } 839 840 bool allows(const KURL& url) 841 { 842 return m_sourceList.matches(url.isEmpty() ? policy()->url() : url); 843 } 844 845 bool allowInline() const { return m_sourceList.allowInline(); } 846 bool allowEval() const { return m_sourceList.allowEval(); } 847 bool allowNonce(const String& nonce) const { return m_sourceList.allowNonce(nonce.stripWhiteSpace()); } 848 bool allowHash(const SourceHashValue& hashValue) const { return m_sourceList.allowHash(hashValue); } 849 850 uint8_t hashAlgorithmsUsed() const { return m_sourceList.hashAlgorithmsUsed(); } 851 852 private: 853 CSPSourceList m_sourceList; 854 }; 855 856 class CSPDirectiveList { 857 WTF_MAKE_FAST_ALLOCATED; 858 public: 859 static PassOwnPtr<CSPDirectiveList> create(ContentSecurityPolicy*, const UChar* begin, const UChar* end, ContentSecurityPolicy::HeaderType); 860 861 void parse(const UChar* begin, const UChar* end); 862 863 const String& header() const { return m_header; } 864 ContentSecurityPolicy::HeaderType headerType() const { return m_headerType; } 865 866 bool allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const; 867 bool allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const; 868 bool allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const; 869 bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const; 870 bool allowEval(ScriptState*, ContentSecurityPolicy::ReportingStatus) const; 871 bool allowPluginType(const String& type, const String& typeAttribute, const KURL&, ContentSecurityPolicy::ReportingStatus) const; 872 873 bool allowScriptFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const; 874 bool allowObjectFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const; 875 bool allowChildFrameFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const; 876 bool allowImageFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const; 877 bool allowStyleFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const; 878 bool allowFontFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const; 879 bool allowMediaFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const; 880 bool allowConnectToSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const; 881 bool allowFormAction(const KURL&, ContentSecurityPolicy::ReportingStatus) const; 882 bool allowBaseURI(const KURL&, ContentSecurityPolicy::ReportingStatus) const; 883 bool allowScriptNonce(const String&) const; 884 bool allowStyleNonce(const String&) const; 885 bool allowScriptHash(const SourceHashValue&) const; 886 887 void gatherReportURIs(DOMStringList&) const; 888 const String& evalDisabledErrorMessage() const { return m_evalDisabledErrorMessage; } 889 ReflectedXSSDisposition reflectedXSSDisposition() const { return m_reflectedXSSDisposition; } 890 bool isReportOnly() const { return m_reportOnly; } 891 const Vector<KURL>& reportURIs() const { return m_reportURIs; } 892 893 private: 894 CSPDirectiveList(ContentSecurityPolicy*, ContentSecurityPolicy::HeaderType); 895 896 bool parseDirective(const UChar* begin, const UChar* end, String& name, String& value); 897 void parseReportURI(const String& name, const String& value); 898 void parsePluginTypes(const String& name, const String& value); 899 void parseReflectedXSS(const String& name, const String& value); 900 void addDirective(const String& name, const String& value); 901 void applySandboxPolicy(const String& name, const String& sandboxPolicy); 902 903 template <class CSPDirectiveType> 904 void setCSPDirective(const String& name, const String& value, OwnPtr<CSPDirectiveType>&); 905 906 SourceListDirective* operativeDirective(SourceListDirective*) const; 907 void reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL) const; 908 void reportViolationWithLocation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL, const String& contextURL, const WTF::OrdinalNumber& contextLine) const; 909 void reportViolationWithState(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL, ScriptState*) const; 910 911 bool checkEval(SourceListDirective*) const; 912 bool checkInline(SourceListDirective*) const; 913 bool checkNonce(SourceListDirective*, const String&) const; 914 bool checkHash(SourceListDirective*, const SourceHashValue&) const; 915 bool checkSource(SourceListDirective*, const KURL&) const; 916 bool checkMediaType(MediaListDirective*, const String& type, const String& typeAttribute) const; 917 918 void setEvalDisabledErrorMessage(const String& errorMessage) { m_evalDisabledErrorMessage = errorMessage; } 919 920 bool checkEvalAndReportViolation(SourceListDirective*, const String& consoleMessage, ScriptState*) const; 921 bool checkInlineAndReportViolation(SourceListDirective*, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, bool isScript) const; 922 923 bool checkSourceAndReportViolation(SourceListDirective*, const KURL&, const String& effectiveDirective) const; 924 bool checkMediaTypeAndReportViolation(MediaListDirective*, const String& type, const String& typeAttribute, const String& consoleMessage) const; 925 926 bool denyIfEnforcingPolicy() const { return m_reportOnly; } 927 928 ContentSecurityPolicy* m_policy; 929 930 String m_header; 931 ContentSecurityPolicy::HeaderType m_headerType; 932 933 bool m_reportOnly; 934 bool m_haveSandboxPolicy; 935 ReflectedXSSDisposition m_reflectedXSSDisposition; 936 937 OwnPtr<MediaListDirective> m_pluginTypes; 938 OwnPtr<SourceListDirective> m_baseURI; 939 OwnPtr<SourceListDirective> m_connectSrc; 940 OwnPtr<SourceListDirective> m_defaultSrc; 941 OwnPtr<SourceListDirective> m_fontSrc; 942 OwnPtr<SourceListDirective> m_formAction; 943 OwnPtr<SourceListDirective> m_frameSrc; 944 OwnPtr<SourceListDirective> m_imgSrc; 945 OwnPtr<SourceListDirective> m_mediaSrc; 946 OwnPtr<SourceListDirective> m_objectSrc; 947 OwnPtr<SourceListDirective> m_scriptSrc; 948 OwnPtr<SourceListDirective> m_styleSrc; 949 950 Vector<KURL> m_reportURIs; 951 952 String m_evalDisabledErrorMessage; 953 }; 954 955 CSPDirectiveList::CSPDirectiveList(ContentSecurityPolicy* policy, ContentSecurityPolicy::HeaderType type) 956 : m_policy(policy) 957 , m_headerType(type) 958 , m_reportOnly(false) 959 , m_haveSandboxPolicy(false) 960 , m_reflectedXSSDisposition(ReflectedXSSUnset) 961 { 962 m_reportOnly = type == ContentSecurityPolicy::Report; 963 } 964 965 PassOwnPtr<CSPDirectiveList> CSPDirectiveList::create(ContentSecurityPolicy* policy, const UChar* begin, const UChar* end, ContentSecurityPolicy::HeaderType type) 966 { 967 OwnPtr<CSPDirectiveList> directives = adoptPtr(new CSPDirectiveList(policy, type)); 968 directives->parse(begin, end); 969 970 if (!directives->checkEval(directives->operativeDirective(directives->m_scriptSrc.get()))) { 971 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"; 972 directives->setEvalDisabledErrorMessage(message); 973 } 974 975 if (directives->isReportOnly() && directives->reportURIs().isEmpty()) 976 policy->reportMissingReportURI(String(begin, end - begin)); 977 978 return directives.release(); 979 } 980 981 void CSPDirectiveList::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL) const 982 { 983 String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage; 984 m_policy->client()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message); 985 m_policy->reportViolation(directiveText, effectiveDirective, message, blockedURL, m_reportURIs, m_header); 986 } 987 988 void CSPDirectiveList::reportViolationWithLocation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL, const String& contextURL, const WTF::OrdinalNumber& contextLine) const 989 { 990 String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage; 991 m_policy->client()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message, contextURL, contextLine.oneBasedInt()); 992 m_policy->reportViolation(directiveText, effectiveDirective, message, blockedURL, m_reportURIs, m_header); 993 } 994 995 void CSPDirectiveList::reportViolationWithState(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL, ScriptState* state) const 996 { 997 String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage; 998 m_policy->client()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message, state); 999 m_policy->reportViolation(directiveText, effectiveDirective, message, blockedURL, m_reportURIs, m_header); 1000 } 1001 1002 bool CSPDirectiveList::checkEval(SourceListDirective* directive) const 1003 { 1004 return !directive || directive->allowEval(); 1005 } 1006 1007 bool CSPDirectiveList::checkInline(SourceListDirective* directive) const 1008 { 1009 return !directive || directive->allowInline(); 1010 } 1011 1012 bool CSPDirectiveList::checkNonce(SourceListDirective* directive, const String& nonce) const 1013 { 1014 return !directive || directive->allowNonce(nonce); 1015 } 1016 1017 bool CSPDirectiveList::checkHash(SourceListDirective* directive, const SourceHashValue& hashValue) const 1018 { 1019 return !directive || directive->allowHash(hashValue); 1020 } 1021 1022 bool CSPDirectiveList::checkSource(SourceListDirective* directive, const KURL& url) const 1023 { 1024 return !directive || directive->allows(url); 1025 } 1026 1027 bool CSPDirectiveList::checkMediaType(MediaListDirective* directive, const String& type, const String& typeAttribute) const 1028 { 1029 if (!directive) 1030 return true; 1031 if (typeAttribute.isEmpty() || typeAttribute.stripWhiteSpace() != type) 1032 return false; 1033 return directive->allows(type); 1034 } 1035 1036 SourceListDirective* CSPDirectiveList::operativeDirective(SourceListDirective* directive) const 1037 { 1038 return directive ? directive : m_defaultSrc.get(); 1039 } 1040 1041 bool CSPDirectiveList::checkEvalAndReportViolation(SourceListDirective* directive, const String& consoleMessage, ScriptState* state) const 1042 { 1043 if (checkEval(directive)) 1044 return true; 1045 1046 String suffix = String(); 1047 if (directive == m_defaultSrc) 1048 suffix = " Note that 'script-src' was not explicitly set, so 'default-src' is used as a fallback."; 1049 1050 reportViolationWithState(directive->text(), scriptSrc, consoleMessage + "\"" + directive->text() + "\"." + suffix + "\n", KURL(), state); 1051 if (!m_reportOnly) { 1052 m_policy->reportBlockedScriptExecutionToInspector(directive->text()); 1053 return false; 1054 } 1055 return true; 1056 } 1057 1058 bool CSPDirectiveList::checkMediaTypeAndReportViolation(MediaListDirective* directive, const String& type, const String& typeAttribute, const String& consoleMessage) const 1059 { 1060 if (checkMediaType(directive, type, typeAttribute)) 1061 return true; 1062 1063 String message = consoleMessage + "\'" + directive->text() + "\'."; 1064 if (typeAttribute.isEmpty()) 1065 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]\" ...>')."; 1066 1067 reportViolation(directive->text(), pluginTypes, message + "\n", KURL()); 1068 return denyIfEnforcingPolicy(); 1069 } 1070 1071 bool CSPDirectiveList::checkInlineAndReportViolation(SourceListDirective* directive, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, bool isScript) const 1072 { 1073 if (checkInline(directive)) 1074 return true; 1075 1076 String suffix = String(); 1077 if (directive == m_defaultSrc) 1078 suffix = " Note that '" + String(isScript ? "script" : "style") + "-src' was not explicitly set, so 'default-src' is used as a fallback."; 1079 1080 reportViolationWithLocation(directive->text(), isScript ? scriptSrc : styleSrc, consoleMessage + "\"" + directive->text() + "\"." + suffix + "\n", KURL(), contextURL, contextLine); 1081 1082 if (!m_reportOnly) { 1083 if (isScript) 1084 m_policy->reportBlockedScriptExecutionToInspector(directive->text()); 1085 return false; 1086 } 1087 return true; 1088 } 1089 1090 bool CSPDirectiveList::checkSourceAndReportViolation(SourceListDirective* directive, const KURL& url, const String& effectiveDirective) const 1091 { 1092 if (checkSource(directive, url)) 1093 return true; 1094 1095 String prefix; 1096 if (baseURI == effectiveDirective) 1097 prefix = "Refused to set the document's base URI to '"; 1098 else if (connectSrc == effectiveDirective) 1099 prefix = "Refused to connect to '"; 1100 else if (fontSrc == effectiveDirective) 1101 prefix = "Refused to load the font '"; 1102 else if (formAction == effectiveDirective) 1103 prefix = "Refused to send form data to '"; 1104 else if (frameSrc == effectiveDirective) 1105 prefix = "Refused to frame '"; 1106 else if (imgSrc == effectiveDirective) 1107 prefix = "Refused to load the image '"; 1108 else if (mediaSrc == effectiveDirective) 1109 prefix = "Refused to load media from '"; 1110 else if (objectSrc == effectiveDirective) 1111 prefix = "Refused to load plugin data from '"; 1112 else if (scriptSrc == effectiveDirective) 1113 prefix = "Refused to load the script '"; 1114 else if (styleSrc == effectiveDirective) 1115 prefix = "Refused to load the stylesheet '"; 1116 1117 String suffix = String(); 1118 if (directive == m_defaultSrc) 1119 suffix = " Note that '" + effectiveDirective + "' was not explicitly set, so 'default-src' is used as a fallback."; 1120 1121 reportViolation(directive->text(), effectiveDirective, prefix + url.elidedString() + "' because it violates the following Content Security Policy directive: \"" + directive->text() + "\"." + suffix + "\n", url); 1122 return denyIfEnforcingPolicy(); 1123 } 1124 1125 bool CSPDirectiveList::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1126 { 1127 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute JavaScript URL because it violates the following Content Security Policy directive: ")); 1128 if (reportingStatus == ContentSecurityPolicy::SendReport) 1129 return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true); 1130 1131 return checkInline(operativeDirective(m_scriptSrc.get())); 1132 } 1133 1134 bool CSPDirectiveList::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1135 { 1136 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute inline event handler because it violates the following Content Security Policy directive: ")); 1137 if (reportingStatus == ContentSecurityPolicy::SendReport) 1138 return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true); 1139 return checkInline(operativeDirective(m_scriptSrc.get())); 1140 } 1141 1142 bool CSPDirectiveList::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1143 { 1144 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute inline script because it violates the following Content Security Policy directive: ")); 1145 return reportingStatus == ContentSecurityPolicy::SendReport ? 1146 checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true) : 1147 checkInline(operativeDirective(m_scriptSrc.get())); 1148 } 1149 1150 bool CSPDirectiveList::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1151 { 1152 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to apply inline style because it violates the following Content Security Policy directive: ")); 1153 return reportingStatus == ContentSecurityPolicy::SendReport ? 1154 checkInlineAndReportViolation(operativeDirective(m_styleSrc.get()), consoleMessage, contextURL, contextLine, false) : 1155 checkInline(operativeDirective(m_styleSrc.get())); 1156 } 1157 1158 bool CSPDirectiveList::allowEval(ScriptState* state, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1159 { 1160 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: ")); 1161 1162 return reportingStatus == ContentSecurityPolicy::SendReport ? 1163 checkEvalAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, state) : 1164 checkEval(operativeDirective(m_scriptSrc.get())); 1165 } 1166 1167 bool CSPDirectiveList::allowPluginType(const String& type, const String& typeAttribute, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1168 { 1169 return reportingStatus == ContentSecurityPolicy::SendReport ? 1170 checkMediaTypeAndReportViolation(m_pluginTypes.get(), type, typeAttribute, "Refused to load '" + url.elidedString() + "' (MIME type '" + typeAttribute + "') because it violates the following Content Security Policy Directive: ") : 1171 checkMediaType(m_pluginTypes.get(), type, typeAttribute); 1172 } 1173 1174 bool CSPDirectiveList::allowScriptFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1175 { 1176 return reportingStatus == ContentSecurityPolicy::SendReport ? 1177 checkSourceAndReportViolation(operativeDirective(m_scriptSrc.get()), url, scriptSrc) : 1178 checkSource(operativeDirective(m_scriptSrc.get()), url); 1179 } 1180 1181 bool CSPDirectiveList::allowObjectFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1182 { 1183 if (url.isBlankURL()) 1184 return true; 1185 return reportingStatus == ContentSecurityPolicy::SendReport ? 1186 checkSourceAndReportViolation(operativeDirective(m_objectSrc.get()), url, objectSrc) : 1187 checkSource(operativeDirective(m_objectSrc.get()), url); 1188 } 1189 1190 bool CSPDirectiveList::allowChildFrameFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1191 { 1192 if (url.isBlankURL()) 1193 return true; 1194 return reportingStatus == ContentSecurityPolicy::SendReport ? 1195 checkSourceAndReportViolation(operativeDirective(m_frameSrc.get()), url, frameSrc) : 1196 checkSource(operativeDirective(m_frameSrc.get()), url); 1197 } 1198 1199 bool CSPDirectiveList::allowImageFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1200 { 1201 return reportingStatus == ContentSecurityPolicy::SendReport ? 1202 checkSourceAndReportViolation(operativeDirective(m_imgSrc.get()), url, imgSrc) : 1203 checkSource(operativeDirective(m_imgSrc.get()), url); 1204 } 1205 1206 bool CSPDirectiveList::allowStyleFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1207 { 1208 return reportingStatus == ContentSecurityPolicy::SendReport ? 1209 checkSourceAndReportViolation(operativeDirective(m_styleSrc.get()), url, styleSrc) : 1210 checkSource(operativeDirective(m_styleSrc.get()), url); 1211 } 1212 1213 bool CSPDirectiveList::allowFontFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1214 { 1215 return reportingStatus == ContentSecurityPolicy::SendReport ? 1216 checkSourceAndReportViolation(operativeDirective(m_fontSrc.get()), url, fontSrc) : 1217 checkSource(operativeDirective(m_fontSrc.get()), url); 1218 } 1219 1220 bool CSPDirectiveList::allowMediaFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1221 { 1222 return reportingStatus == ContentSecurityPolicy::SendReport ? 1223 checkSourceAndReportViolation(operativeDirective(m_mediaSrc.get()), url, mediaSrc) : 1224 checkSource(operativeDirective(m_mediaSrc.get()), url); 1225 } 1226 1227 bool CSPDirectiveList::allowConnectToSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1228 { 1229 return reportingStatus == ContentSecurityPolicy::SendReport ? 1230 checkSourceAndReportViolation(operativeDirective(m_connectSrc.get()), url, connectSrc) : 1231 checkSource(operativeDirective(m_connectSrc.get()), url); 1232 } 1233 1234 void CSPDirectiveList::gatherReportURIs(DOMStringList& list) const 1235 { 1236 for (size_t i = 0; i < m_reportURIs.size(); ++i) 1237 list.append(m_reportURIs[i].string()); 1238 } 1239 1240 bool CSPDirectiveList::allowFormAction(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1241 { 1242 return reportingStatus == ContentSecurityPolicy::SendReport ? 1243 checkSourceAndReportViolation(m_formAction.get(), url, formAction) : 1244 checkSource(m_formAction.get(), url); 1245 } 1246 1247 bool CSPDirectiveList::allowBaseURI(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1248 { 1249 return reportingStatus == ContentSecurityPolicy::SendReport ? 1250 checkSourceAndReportViolation(m_baseURI.get(), url, baseURI) : 1251 checkSource(m_baseURI.get(), url); 1252 } 1253 1254 bool CSPDirectiveList::allowScriptNonce(const String& nonce) const 1255 { 1256 return checkNonce(operativeDirective(m_scriptSrc.get()), nonce); 1257 } 1258 1259 bool CSPDirectiveList::allowStyleNonce(const String& nonce) const 1260 { 1261 return checkNonce(operativeDirective(m_styleSrc.get()), nonce); 1262 } 1263 1264 bool CSPDirectiveList::allowScriptHash(const SourceHashValue& hashValue) const 1265 { 1266 return checkHash(operativeDirective(m_scriptSrc.get()), hashValue); 1267 } 1268 1269 // policy = directive-list 1270 // directive-list = [ directive *( ";" [ directive ] ) ] 1271 // 1272 void CSPDirectiveList::parse(const UChar* begin, const UChar* end) 1273 { 1274 m_header = String(begin, end - begin); 1275 1276 if (begin == end) 1277 return; 1278 1279 const UChar* position = begin; 1280 while (position < end) { 1281 const UChar* directiveBegin = position; 1282 skipUntil<UChar>(position, end, ';'); 1283 1284 String name, value; 1285 if (parseDirective(directiveBegin, position, name, value)) { 1286 ASSERT(!name.isEmpty()); 1287 addDirective(name, value); 1288 } 1289 1290 ASSERT(position == end || *position == ';'); 1291 skipExactly<UChar>(position, end, ';'); 1292 } 1293 } 1294 1295 // directive = *WSP [ directive-name [ WSP directive-value ] ] 1296 // directive-name = 1*( ALPHA / DIGIT / "-" ) 1297 // directive-value = *( WSP / <VCHAR except ";"> ) 1298 // 1299 bool CSPDirectiveList::parseDirective(const UChar* begin, const UChar* end, String& name, String& value) 1300 { 1301 ASSERT(name.isEmpty()); 1302 ASSERT(value.isEmpty()); 1303 1304 const UChar* position = begin; 1305 skipWhile<UChar, isASCIISpace>(position, end); 1306 1307 // Empty directive (e.g. ";;;"). Exit early. 1308 if (position == end) 1309 return false; 1310 1311 const UChar* nameBegin = position; 1312 skipWhile<UChar, isDirectiveNameCharacter>(position, end); 1313 1314 // The directive-name must be non-empty. 1315 if (nameBegin == position) { 1316 skipWhile<UChar, isNotASCIISpace>(position, end); 1317 m_policy->reportUnsupportedDirective(String(nameBegin, position - nameBegin)); 1318 return false; 1319 } 1320 1321 name = String(nameBegin, position - nameBegin); 1322 1323 if (position == end) 1324 return true; 1325 1326 if (!skipExactly<UChar, isASCIISpace>(position, end)) { 1327 skipWhile<UChar, isNotASCIISpace>(position, end); 1328 m_policy->reportUnsupportedDirective(String(nameBegin, position - nameBegin)); 1329 return false; 1330 } 1331 1332 skipWhile<UChar, isASCIISpace>(position, end); 1333 1334 const UChar* valueBegin = position; 1335 skipWhile<UChar, isDirectiveValueCharacter>(position, end); 1336 1337 if (position != end) { 1338 m_policy->reportInvalidDirectiveValueCharacter(name, String(valueBegin, end - valueBegin)); 1339 return false; 1340 } 1341 1342 // The directive-value may be empty. 1343 if (valueBegin == position) 1344 return true; 1345 1346 value = String(valueBegin, position - valueBegin); 1347 return true; 1348 } 1349 1350 void CSPDirectiveList::parseReportURI(const String& name, const String& value) 1351 { 1352 if (!m_reportURIs.isEmpty()) { 1353 m_policy->reportDuplicateDirective(name); 1354 return; 1355 } 1356 1357 Vector<UChar> characters; 1358 value.appendTo(characters); 1359 1360 const UChar* position = characters.data(); 1361 const UChar* end = position + characters.size(); 1362 1363 while (position < end) { 1364 skipWhile<UChar, isASCIISpace>(position, end); 1365 1366 const UChar* urlBegin = position; 1367 skipWhile<UChar, isNotASCIISpace>(position, end); 1368 1369 if (urlBegin < position) { 1370 String url = String(urlBegin, position - urlBegin); 1371 m_reportURIs.append(m_policy->completeURL(url)); 1372 } 1373 } 1374 } 1375 1376 1377 template<class CSPDirectiveType> 1378 void CSPDirectiveList::setCSPDirective(const String& name, const String& value, OwnPtr<CSPDirectiveType>& directive) 1379 { 1380 if (directive) { 1381 m_policy->reportDuplicateDirective(name); 1382 return; 1383 } 1384 directive = adoptPtr(new CSPDirectiveType(name, value, m_policy)); 1385 } 1386 1387 void CSPDirectiveList::applySandboxPolicy(const String& name, const String& sandboxPolicy) 1388 { 1389 if (m_haveSandboxPolicy) { 1390 m_policy->reportDuplicateDirective(name); 1391 return; 1392 } 1393 m_haveSandboxPolicy = true; 1394 String invalidTokens; 1395 m_policy->enforceSandboxFlags(parseSandboxPolicy(sandboxPolicy, invalidTokens)); 1396 if (!invalidTokens.isNull()) 1397 m_policy->reportInvalidSandboxFlags(invalidTokens); 1398 } 1399 1400 void CSPDirectiveList::parseReflectedXSS(const String& name, const String& value) 1401 { 1402 if (m_reflectedXSSDisposition != ReflectedXSSUnset) { 1403 m_policy->reportDuplicateDirective(name); 1404 m_reflectedXSSDisposition = ReflectedXSSInvalid; 1405 return; 1406 } 1407 1408 if (value.isEmpty()) { 1409 m_reflectedXSSDisposition = ReflectedXSSInvalid; 1410 m_policy->reportInvalidReflectedXSS(value); 1411 return; 1412 } 1413 1414 Vector<UChar> characters; 1415 value.appendTo(characters); 1416 1417 const UChar* position = characters.data(); 1418 const UChar* end = position + characters.size(); 1419 1420 skipWhile<UChar, isASCIISpace>(position, end); 1421 const UChar* begin = position; 1422 skipWhile<UChar, isNotASCIISpace>(position, end); 1423 1424 // value1 1425 // ^ 1426 if (equalIgnoringCase("allow", begin, position - begin)) { 1427 m_reflectedXSSDisposition = AllowReflectedXSS; 1428 } else if (equalIgnoringCase("filter", begin, position - begin)) { 1429 m_reflectedXSSDisposition = FilterReflectedXSS; 1430 } else if (equalIgnoringCase("block", begin, position - begin)) { 1431 m_reflectedXSSDisposition = BlockReflectedXSS; 1432 } else { 1433 m_reflectedXSSDisposition = ReflectedXSSInvalid; 1434 m_policy->reportInvalidReflectedXSS(value); 1435 return; 1436 } 1437 1438 skipWhile<UChar, isASCIISpace>(position, end); 1439 if (position == end && m_reflectedXSSDisposition != ReflectedXSSUnset) 1440 return; 1441 1442 // value1 value2 1443 // ^ 1444 m_reflectedXSSDisposition = ReflectedXSSInvalid; 1445 m_policy->reportInvalidReflectedXSS(value); 1446 } 1447 1448 void CSPDirectiveList::addDirective(const String& name, const String& value) 1449 { 1450 ASSERT(!name.isEmpty()); 1451 1452 if (equalIgnoringCase(name, defaultSrc)) { 1453 setCSPDirective<SourceListDirective>(name, value, m_defaultSrc); 1454 } else if (equalIgnoringCase(name, scriptSrc)) { 1455 setCSPDirective<SourceListDirective>(name, value, m_scriptSrc); 1456 m_policy->usesScriptHashAlgorithms(m_scriptSrc->hashAlgorithmsUsed()); 1457 } else if (equalIgnoringCase(name, objectSrc)) { 1458 setCSPDirective<SourceListDirective>(name, value, m_objectSrc); 1459 } else if (equalIgnoringCase(name, frameSrc)) { 1460 setCSPDirective<SourceListDirective>(name, value, m_frameSrc); 1461 } else if (equalIgnoringCase(name, imgSrc)) { 1462 setCSPDirective<SourceListDirective>(name, value, m_imgSrc); 1463 } else if (equalIgnoringCase(name, styleSrc)) { 1464 setCSPDirective<SourceListDirective>(name, value, m_styleSrc); 1465 } else if (equalIgnoringCase(name, fontSrc)) { 1466 setCSPDirective<SourceListDirective>(name, value, m_fontSrc); 1467 } else if (equalIgnoringCase(name, mediaSrc)) { 1468 setCSPDirective<SourceListDirective>(name, value, m_mediaSrc); 1469 } else if (equalIgnoringCase(name, connectSrc)) { 1470 setCSPDirective<SourceListDirective>(name, value, m_connectSrc); 1471 } else if (equalIgnoringCase(name, sandbox)) { 1472 applySandboxPolicy(name, value); 1473 } else if (equalIgnoringCase(name, reportURI)) { 1474 parseReportURI(name, value); 1475 } else if (m_policy->experimentalFeaturesEnabled()) { 1476 if (equalIgnoringCase(name, baseURI)) 1477 setCSPDirective<SourceListDirective>(name, value, m_baseURI); 1478 else if (equalIgnoringCase(name, formAction)) 1479 setCSPDirective<SourceListDirective>(name, value, m_formAction); 1480 else if (equalIgnoringCase(name, pluginTypes)) 1481 setCSPDirective<MediaListDirective>(name, value, m_pluginTypes); 1482 else if (equalIgnoringCase(name, reflectedXSS)) 1483 parseReflectedXSS(name, value); 1484 else 1485 m_policy->reportUnsupportedDirective(name); 1486 } else { 1487 m_policy->reportUnsupportedDirective(name); 1488 } 1489 } 1490 1491 ContentSecurityPolicy::ContentSecurityPolicy(ExecutionContextClient* client) 1492 : m_client(client) 1493 , m_overrideInlineStyleAllowed(false) 1494 , m_sourceHashAlgorithmsUsed(HashAlgorithmsNone) 1495 { 1496 } 1497 1498 ContentSecurityPolicy::~ContentSecurityPolicy() 1499 { 1500 } 1501 1502 void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other) 1503 { 1504 ASSERT(m_policies.isEmpty()); 1505 for (CSPDirectiveListVector::const_iterator iter = other->m_policies.begin(); iter != other->m_policies.end(); ++iter) 1506 addPolicyFromHeaderValue((*iter)->header(), (*iter)->headerType()); 1507 } 1508 1509 void ContentSecurityPolicy::didReceiveHeaders(const ContentSecurityPolicyResponseHeaders& headers) 1510 { 1511 if (!headers.contentSecurityPolicy().isEmpty()) 1512 didReceiveHeader(headers.contentSecurityPolicy(), ContentSecurityPolicy::Enforce); 1513 if (!headers.contentSecurityPolicyReportOnly().isEmpty()) 1514 didReceiveHeader(headers.contentSecurityPolicyReportOnly(), ContentSecurityPolicy::Report); 1515 1516 // FIXME: Remove this reporting (and the 'xWebKitCSP*' methods) after the next release branch. 1517 if (m_client->isDocument()) { 1518 Document* document = static_cast<Document*>(m_client); 1519 if (!headers.xWebKitCSP().isEmpty()) 1520 UseCounter::countDeprecation(*document, UseCounter::PrefixedContentSecurityPolicy); 1521 if (!headers.xWebKitCSPReportOnly().isEmpty()) 1522 UseCounter::countDeprecation(*document, UseCounter::PrefixedContentSecurityPolicyReportOnly); 1523 } 1524 } 1525 1526 void ContentSecurityPolicy::didReceiveHeader(const String& header, HeaderType type) 1527 { 1528 addPolicyFromHeaderValue(header, type); 1529 } 1530 1531 void ContentSecurityPolicy::addPolicyFromHeaderValue(const String& header, HeaderType type) 1532 { 1533 if (m_client->isDocument()) { 1534 Document* document = static_cast<Document*>(m_client); 1535 UseCounter::count(*document, getUseCounterType(type)); 1536 } 1537 1538 Vector<UChar> characters; 1539 header.appendTo(characters); 1540 1541 const UChar* begin = characters.data(); 1542 const UChar* end = begin + characters.size(); 1543 1544 // RFC2616, section 4.2 specifies that headers appearing multiple times can 1545 // be combined with a comma. Walk the header string, and parse each comma 1546 // separated chunk as a separate header. 1547 const UChar* position = begin; 1548 while (position < end) { 1549 skipUntil<UChar>(position, end, ','); 1550 1551 // header1,header2 OR header1 1552 // ^ ^ 1553 OwnPtr<CSPDirectiveList> policy = CSPDirectiveList::create(this, begin, position, type); 1554 1555 // We disable 'eval()' even in the case of report-only policies, and rely on the check in the V8Initializer::codeGenerationCheckCallbackInMainThread callback to determine whether the call should execute or not. 1556 if (!policy->allowEval(0, SuppressReport)) 1557 m_client->disableEval(policy->evalDisabledErrorMessage()); 1558 1559 m_policies.append(policy.release()); 1560 1561 // Skip the comma, and begin the next header from the current position. 1562 ASSERT(position == end || *position == ','); 1563 skipExactly<UChar>(position, end, ','); 1564 begin = position; 1565 } 1566 } 1567 1568 void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value) 1569 { 1570 m_overrideInlineStyleAllowed = value; 1571 } 1572 1573 const String& ContentSecurityPolicy::deprecatedHeader() const 1574 { 1575 return m_policies.isEmpty() ? emptyString() : m_policies[0]->header(); 1576 } 1577 1578 ContentSecurityPolicy::HeaderType ContentSecurityPolicy::deprecatedHeaderType() const 1579 { 1580 return m_policies.isEmpty() ? Enforce : m_policies[0]->headerType(); 1581 } 1582 1583 template<bool (CSPDirectiveList::*allowed)(ContentSecurityPolicy::ReportingStatus) const> 1584 bool isAllowedByAll(const CSPDirectiveListVector& policies, ContentSecurityPolicy::ReportingStatus reportingStatus) 1585 { 1586 for (size_t i = 0; i < policies.size(); ++i) { 1587 if (!(policies[i].get()->*allowed)(reportingStatus)) 1588 return false; 1589 } 1590 return true; 1591 } 1592 1593 template<bool (CSPDirectiveList::*allowed)(ScriptState* state, ContentSecurityPolicy::ReportingStatus) const> 1594 bool isAllowedByAllWithState(const CSPDirectiveListVector& policies, ScriptState* state, ContentSecurityPolicy::ReportingStatus reportingStatus) 1595 { 1596 for (size_t i = 0; i < policies.size(); ++i) { 1597 if (!(policies[i].get()->*allowed)(state, reportingStatus)) 1598 return false; 1599 } 1600 return true; 1601 } 1602 1603 template<bool (CSPDirectiveList::*allowed)(const String&, const WTF::OrdinalNumber&, ContentSecurityPolicy::ReportingStatus) const> 1604 bool isAllowedByAllWithContext(const CSPDirectiveListVector& policies, const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) 1605 { 1606 for (size_t i = 0; i < policies.size(); ++i) { 1607 if (!(policies[i].get()->*allowed)(contextURL, contextLine, reportingStatus)) 1608 return false; 1609 } 1610 return true; 1611 } 1612 1613 template<bool (CSPDirectiveList::*allowed)(const String&) const> 1614 bool isAllowedByAllWithNonce(const CSPDirectiveListVector& policies, const String& nonce) 1615 { 1616 for (size_t i = 0; i < policies.size(); ++i) { 1617 if (!(policies[i].get()->*allowed)(nonce)) 1618 return false; 1619 } 1620 return true; 1621 } 1622 1623 template<bool (CSPDirectiveList::*allowed)(const SourceHashValue&) const> 1624 bool isAllowedByAllWithHash(const CSPDirectiveListVector& policies, const SourceHashValue& hashValue) 1625 { 1626 for (size_t i = 0; i < policies.size(); ++i) { 1627 if (!(policies[i].get()->*allowed)(hashValue)) 1628 return false; 1629 } 1630 return true; 1631 } 1632 1633 template<bool (CSPDirectiveList::*allowFromURL)(const KURL&, ContentSecurityPolicy::ReportingStatus) const> 1634 bool isAllowedByAllWithURL(const CSPDirectiveListVector& policies, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) 1635 { 1636 if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol())) 1637 return true; 1638 1639 for (size_t i = 0; i < policies.size(); ++i) { 1640 if (!(policies[i].get()->*allowFromURL)(url, reportingStatus)) 1641 return false; 1642 } 1643 return true; 1644 } 1645 1646 bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1647 { 1648 return isAllowedByAllWithContext<&CSPDirectiveList::allowJavaScriptURLs>(m_policies, contextURL, contextLine, reportingStatus); 1649 } 1650 1651 bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1652 { 1653 return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineEventHandlers>(m_policies, contextURL, contextLine, reportingStatus); 1654 } 1655 1656 bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1657 { 1658 return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineScript>(m_policies, contextURL, contextLine, reportingStatus); 1659 } 1660 1661 bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1662 { 1663 if (m_overrideInlineStyleAllowed) 1664 return true; 1665 return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineStyle>(m_policies, contextURL, contextLine, reportingStatus); 1666 } 1667 1668 bool ContentSecurityPolicy::allowEval(ScriptState* state, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1669 { 1670 return isAllowedByAllWithState<&CSPDirectiveList::allowEval>(m_policies, state, reportingStatus); 1671 } 1672 1673 String ContentSecurityPolicy::evalDisabledErrorMessage() const 1674 { 1675 for (size_t i = 0; i < m_policies.size(); ++i) { 1676 if (!m_policies[i]->allowEval(0, SuppressReport)) 1677 return m_policies[i]->evalDisabledErrorMessage(); 1678 } 1679 return String(); 1680 } 1681 1682 bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1683 { 1684 for (size_t i = 0; i < m_policies.size(); ++i) { 1685 if (!m_policies[i]->allowPluginType(type, typeAttribute, url, reportingStatus)) 1686 return false; 1687 } 1688 return true; 1689 } 1690 1691 bool ContentSecurityPolicy::allowScriptFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1692 { 1693 return isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus); 1694 } 1695 1696 bool ContentSecurityPolicy::allowScriptNonce(const String& nonce) const 1697 { 1698 return isAllowedByAllWithNonce<&CSPDirectiveList::allowScriptNonce>(m_policies, nonce); 1699 } 1700 1701 bool ContentSecurityPolicy::allowStyleNonce(const String& nonce) const 1702 { 1703 return isAllowedByAllWithNonce<&CSPDirectiveList::allowStyleNonce>(m_policies, nonce); 1704 } 1705 1706 bool ContentSecurityPolicy::allowScriptHash(const String& source) const 1707 { 1708 // TODO(jww) We don't currently have a WTF SHA256 implementation. Once we 1709 // have that, we should implement a proper check for sha256 hash values here. 1710 if (HashAlgorithmsSha1 & m_sourceHashAlgorithmsUsed) { 1711 Vector<uint8_t, 20> digest; 1712 SHA1 sourceSha1; 1713 sourceSha1.addBytes(UTF8Encoding().normalizeAndEncode(source, WTF::EntitiesForUnencodables)); 1714 sourceSha1.computeHash(digest); 1715 1716 if (isAllowedByAllWithHash<&CSPDirectiveList::allowScriptHash>(m_policies, SourceHashValue(HashAlgorithmsSha1, Vector<uint8_t>(digest)))) 1717 return true; 1718 } 1719 1720 return false; 1721 } 1722 1723 void ContentSecurityPolicy::usesScriptHashAlgorithms(uint8_t algorithms) 1724 { 1725 m_sourceHashAlgorithmsUsed |= algorithms; 1726 } 1727 1728 bool ContentSecurityPolicy::allowObjectFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1729 { 1730 return isAllowedByAllWithURL<&CSPDirectiveList::allowObjectFromSource>(m_policies, url, reportingStatus); 1731 } 1732 1733 bool ContentSecurityPolicy::allowChildFrameFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1734 { 1735 return isAllowedByAllWithURL<&CSPDirectiveList::allowChildFrameFromSource>(m_policies, url, reportingStatus); 1736 } 1737 1738 bool ContentSecurityPolicy::allowImageFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1739 { 1740 return isAllowedByAllWithURL<&CSPDirectiveList::allowImageFromSource>(m_policies, url, reportingStatus); 1741 } 1742 1743 bool ContentSecurityPolicy::allowStyleFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1744 { 1745 return isAllowedByAllWithURL<&CSPDirectiveList::allowStyleFromSource>(m_policies, url, reportingStatus); 1746 } 1747 1748 bool ContentSecurityPolicy::allowFontFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1749 { 1750 return isAllowedByAllWithURL<&CSPDirectiveList::allowFontFromSource>(m_policies, url, reportingStatus); 1751 } 1752 1753 bool ContentSecurityPolicy::allowMediaFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1754 { 1755 return isAllowedByAllWithURL<&CSPDirectiveList::allowMediaFromSource>(m_policies, url, reportingStatus); 1756 } 1757 1758 bool ContentSecurityPolicy::allowConnectToSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1759 { 1760 return isAllowedByAllWithURL<&CSPDirectiveList::allowConnectToSource>(m_policies, url, reportingStatus); 1761 } 1762 1763 bool ContentSecurityPolicy::allowFormAction(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1764 { 1765 return isAllowedByAllWithURL<&CSPDirectiveList::allowFormAction>(m_policies, url, reportingStatus); 1766 } 1767 1768 bool ContentSecurityPolicy::allowBaseURI(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 1769 { 1770 return isAllowedByAllWithURL<&CSPDirectiveList::allowBaseURI>(m_policies, url, reportingStatus); 1771 } 1772 1773 bool ContentSecurityPolicy::isActive() const 1774 { 1775 return !m_policies.isEmpty(); 1776 } 1777 1778 ReflectedXSSDisposition ContentSecurityPolicy::reflectedXSSDisposition() const 1779 { 1780 ReflectedXSSDisposition disposition = ReflectedXSSUnset; 1781 for (size_t i = 0; i < m_policies.size(); ++i) { 1782 if (m_policies[i]->reflectedXSSDisposition() > disposition) 1783 disposition = std::max(disposition, m_policies[i]->reflectedXSSDisposition()); 1784 } 1785 return disposition; 1786 } 1787 1788 void ContentSecurityPolicy::gatherReportURIs(DOMStringList& list) const 1789 { 1790 for (size_t i = 0; i < m_policies.size(); ++i) 1791 m_policies[i]->gatherReportURIs(list); 1792 } 1793 1794 SecurityOrigin* ContentSecurityPolicy::securityOrigin() const 1795 { 1796 return m_client->securityContext().securityOrigin(); 1797 } 1798 1799 const KURL ContentSecurityPolicy::url() const 1800 { 1801 return m_client->contextURL(); 1802 } 1803 1804 KURL ContentSecurityPolicy::completeURL(const String& url) const 1805 { 1806 return m_client->contextCompleteURL(url); 1807 } 1808 1809 void ContentSecurityPolicy::enforceSandboxFlags(SandboxFlags mask) const 1810 { 1811 if (m_client->isDocument()) 1812 static_cast<Document*>(m_client)->enforceSandboxFlags(mask); 1813 } 1814 1815 static String stripURLForUseInReport(Document* document, const KURL& url) 1816 { 1817 if (!url.isValid()) 1818 return String(); 1819 if (!url.isHierarchical() || url.protocolIs("file")) 1820 return url.protocol(); 1821 return document->securityOrigin()->canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url)->toString(); 1822 } 1823 1824 static void gatherSecurityPolicyViolationEventData(SecurityPolicyViolationEventInit& init, Document* document, const String& directiveText, const String& effectiveDirective, const KURL& blockedURL, const String& header) 1825 { 1826 init.documentURI = document->url().string(); 1827 init.referrer = document->referrer(); 1828 init.blockedURI = stripURLForUseInReport(document, blockedURL); 1829 init.violatedDirective = directiveText; 1830 init.effectiveDirective = effectiveDirective; 1831 init.originalPolicy = header; 1832 init.sourceFile = String(); 1833 init.lineNumber = 0; 1834 init.columnNumber = 0; 1835 init.statusCode = 0; 1836 1837 if (!SecurityOrigin::isSecure(document->url()) && document->loader()) 1838 init.statusCode = document->loader()->response().httpStatusCode(); 1839 1840 RefPtr<ScriptCallStack> stack = createScriptCallStack(1, false); 1841 if (!stack) 1842 return; 1843 1844 const ScriptCallFrame& callFrame = stack->at(0); 1845 1846 if (callFrame.lineNumber()) { 1847 KURL source = KURL(ParsedURLString, callFrame.sourceURL()); 1848 init.sourceFile = stripURLForUseInReport(document, source); 1849 init.lineNumber = callFrame.lineNumber(); 1850 init.columnNumber = callFrame.columnNumber(); 1851 } 1852 } 1853 1854 void ContentSecurityPolicy::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL, const Vector<KURL>& reportURIs, const String& header) 1855 { 1856 // FIXME: Support sending reports from worker. 1857 if (!m_client->isDocument()) 1858 return; 1859 1860 Document* document = static_cast<Document*>(m_client); 1861 Frame* frame = document->frame(); 1862 if (!frame) 1863 return; 1864 1865 SecurityPolicyViolationEventInit violationData; 1866 gatherSecurityPolicyViolationEventData(violationData, document, directiveText, effectiveDirective, blockedURL, header); 1867 1868 if (experimentalFeaturesEnabled()) 1869 frame->domWindow()->enqueueDocumentEvent(SecurityPolicyViolationEvent::create(EventTypeNames::securitypolicyviolation, violationData)); 1870 1871 if (reportURIs.isEmpty()) 1872 return; 1873 1874 // We need to be careful here when deciding what information to send to the 1875 // report-uri. Currently, we send only the current document's URL and the 1876 // directive that was violated. The document's URL is safe to send because 1877 // it's the document itself that's requesting that it be sent. You could 1878 // make an argument that we shouldn't send HTTPS document URLs to HTTP 1879 // report-uris (for the same reasons that we supress the Referer in that 1880 // case), but the Referer is sent implicitly whereas this request is only 1881 // sent explicitly. As for which directive was violated, that's pretty 1882 // harmless information. 1883 1884 RefPtr<JSONObject> cspReport = JSONObject::create(); 1885 cspReport->setString("document-uri", violationData.documentURI); 1886 cspReport->setString("referrer", violationData.referrer); 1887 cspReport->setString("violated-directive", violationData.violatedDirective); 1888 if (experimentalFeaturesEnabled()) 1889 cspReport->setString("effective-directive", violationData.effectiveDirective); 1890 cspReport->setString("original-policy", violationData.originalPolicy); 1891 cspReport->setString("blocked-uri", violationData.blockedURI); 1892 if (!violationData.sourceFile.isEmpty() && violationData.lineNumber) { 1893 cspReport->setString("source-file", violationData.sourceFile); 1894 cspReport->setNumber("line-number", violationData.lineNumber); 1895 cspReport->setNumber("column-number", violationData.columnNumber); 1896 } 1897 cspReport->setNumber("status-code", violationData.statusCode); 1898 1899 RefPtr<JSONObject> reportObject = JSONObject::create(); 1900 reportObject->setObject("csp-report", cspReport.release()); 1901 String stringifiedReport = reportObject->toJSONString(); 1902 1903 if (!shouldSendViolationReport(stringifiedReport)) 1904 return; 1905 1906 RefPtr<FormData> report = FormData::create(stringifiedReport.utf8()); 1907 1908 for (size_t i = 0; i < reportURIs.size(); ++i) 1909 PingLoader::sendViolationReport(frame, reportURIs[i], report, PingLoader::ContentSecurityPolicyViolationReport); 1910 1911 didSendViolationReport(stringifiedReport); 1912 } 1913 1914 void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) const 1915 { 1916 DEFINE_STATIC_LOCAL(String, allow, ("allow")); 1917 DEFINE_STATIC_LOCAL(String, options, ("options")); 1918 DEFINE_STATIC_LOCAL(String, policyURI, ("policy-uri")); 1919 DEFINE_STATIC_LOCAL(String, allowMessage, ("The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect.")); 1920 DEFINE_STATIC_LOCAL(String, optionsMessage, ("The 'options' directive has been replaced with 'unsafe-inline' and 'unsafe-eval' source expressions for the 'script-src' and 'style-src' directives. Please use those directives instead, as 'options' has no effect.")); 1921 DEFINE_STATIC_LOCAL(String, policyURIMessage, ("The 'policy-uri' directive has been removed from the specification. Please specify a complete policy via the Content-Security-Policy header.")); 1922 1923 String message = "Unrecognized Content-Security-Policy directive '" + name + "'.\n"; 1924 if (equalIgnoringCase(name, allow)) 1925 message = allowMessage; 1926 else if (equalIgnoringCase(name, options)) 1927 message = optionsMessage; 1928 else if (equalIgnoringCase(name, policyURI)) 1929 message = policyURIMessage; 1930 1931 logToConsole(message); 1932 } 1933 1934 void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const 1935 { 1936 String message = "The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?"; 1937 logToConsole(message); 1938 } 1939 1940 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const 1941 { 1942 String message = "Ignoring duplicate Content-Security-Policy directive '" + name + "'.\n"; 1943 logToConsole(message); 1944 } 1945 1946 void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) const 1947 { 1948 String message; 1949 if (pluginType.isNull()) 1950 message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n"; 1951 else 1952 message = "Invalid plugin type in 'plugin-types' Content Security Policy directive: '" + pluginType + "'.\n"; 1953 logToConsole(message); 1954 } 1955 1956 void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags) const 1957 { 1958 logToConsole("Error while parsing the 'sandbox' Content Security Policy directive: " + invalidFlags); 1959 } 1960 1961 void ContentSecurityPolicy::reportInvalidReflectedXSS(const String& invalidValue) const 1962 { 1963 logToConsole("The 'reflected-xss' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Valid values are \"allow\", \"filter\", and \"block\"."); 1964 } 1965 1966 void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) const 1967 { 1968 String message = "The value for Content Security Policy directive '" + directiveName + "' contains an invalid character: '" + value + "'. Non-whitespace characters outside ASCII 0x21-0x7E must be percent-encoded, as described in RFC 3986, section 2.1: http://tools.ietf.org/html/rfc3986#section-2.1."; 1969 logToConsole(message); 1970 } 1971 1972 void ContentSecurityPolicy::reportInvalidPathCharacter(const String& directiveName, const String& value, const char invalidChar) const 1973 { 1974 ASSERT(invalidChar == '#' || invalidChar == '?'); 1975 1976 String ignoring = "The fragment identifier, including the '#', will be ignored."; 1977 if (invalidChar == '?') 1978 ignoring = "The query component, including the '?', will be ignored."; 1979 String message = "The source list for Content Security Policy directive '" + directiveName + "' contains a source with an invalid path: '" + value + "'. " + ignoring; 1980 logToConsole(message); 1981 } 1982 1983 void ContentSecurityPolicy::reportInvalidSourceExpression(const String& directiveName, const String& source) const 1984 { 1985 String message = "The source list for Content Security Policy directive '" + directiveName + "' contains an invalid source: '" + source + "'. It will be ignored."; 1986 if (equalIgnoringCase(source, "'none'")) 1987 message = message + " Note that 'none' has no effect unless it is the only expression in the source list."; 1988 logToConsole(message); 1989 } 1990 1991 void ContentSecurityPolicy::reportMissingReportURI(const String& policy) const 1992 { 1993 logToConsole("The Content Security Policy '" + policy + "' was delivered in report-only mode, but does not specify a 'report-uri'; the policy will have no effect. Please either add a 'report-uri' directive, or deliver the policy via the 'Content-Security-Policy' header."); 1994 } 1995 1996 void ContentSecurityPolicy::logToConsole(const String& message) const 1997 { 1998 m_client->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message); 1999 } 2000 2001 void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String& directiveText) const 2002 { 2003 m_client->reportBlockedScriptExecutionToInspector(directiveText); 2004 } 2005 2006 bool ContentSecurityPolicy::experimentalFeaturesEnabled() const 2007 { 2008 return RuntimeEnabledFeatures::experimentalContentSecurityPolicyFeaturesEnabled(); 2009 } 2010 2011 bool ContentSecurityPolicy::shouldBypassMainWorld(ExecutionContext* context) 2012 { 2013 if (context && context->isDocument()) { 2014 Document* document = toDocument(context); 2015 if (document->frame()) 2016 return document->frame()->script().shouldBypassMainWorldContentSecurityPolicy(); 2017 } 2018 return false; 2019 } 2020 2021 bool ContentSecurityPolicy::shouldSendViolationReport(const String& report) const 2022 { 2023 // Collisions have no security impact, so we can save space by storing only the string's hash rather than the whole report. 2024 return !m_violationReportsSent.contains(report.impl()->hash()); 2025 } 2026 2027 void ContentSecurityPolicy::didSendViolationReport(const String& report) 2028 { 2029 m_violationReportsSent.add(report.impl()->hash()); 2030 } 2031 2032 } // namespace WebCore 2033