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