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