Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2011 Google, Inc. All rights reserved. | 2 * Copyright (C) 2011 Google, Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
| 8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
| 10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
| (...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 304 ContentSecurityPolicyHeaderType type, | 304 ContentSecurityPolicyHeaderType type, |
| 305 ContentSecurityPolicyHeaderSource source) { | 305 ContentSecurityPolicyHeaderSource source) { |
| 306 addAndReportPolicyFromHeaderValue(header, type, source); | 306 addAndReportPolicyFromHeaderValue(header, type, source); |
| 307 | 307 |
| 308 // This might be called after we've been bound to an execution context. For | 308 // This might be called after we've been bound to an execution context. For |
| 309 // example, a <meta> element might be injected after page load. | 309 // example, a <meta> element might be injected after page load. |
| 310 if (m_executionContext) | 310 if (m_executionContext) |
| 311 applyPolicySideEffectsToExecutionContext(); | 311 applyPolicySideEffectsToExecutionContext(); |
| 312 } | 312 } |
| 313 | 313 |
| 314 bool ContentSecurityPolicy::checkAllowBlanketEnforcement( | |
|
amalika
2016/10/11 19:06:46
This is the most important function.
After I ran
| |
| 315 const ResourceResponse& response, | |
| 316 const KURL& parentUrl) { | |
| 317 if (response.url().isEmpty() || response.url().protocolIsAbout() || | |
| 318 response.url().protocolIsAbout() || response.url().protocolIs("blob") || | |
| 319 response.url().protocolIs("filesystem")) { | |
| 320 return true; | |
| 321 } | |
| 322 | |
| 323 if (parentUrl.protocol() == response.url().protocol() && | |
| 324 parentUrl.host() == response.url().host() && | |
| 325 parentUrl.port() == response.url().port()) { | |
| 326 return true; | |
| 327 } | |
| 328 | |
| 329 HTTPHeaderMap::const_iterator it = | |
| 330 response.httpHeaderFields().find(HTTPNames::Allow_CSP_From); | |
| 331 | |
| 332 String header = | |
| 333 it != response.httpHeaderFields().end() ? it->value : nullAtom; | |
| 334 | |
| 335 if (header.isEmpty() || !header.containsOnlyASCII()) | |
| 336 return false; | |
| 337 | |
| 338 Vector<String> headers; | |
| 339 header.split(',', headers); | |
| 340 for (size_t i = 0; i < headers.size(); i++) { | |
| 341 String currentHeader = headers[i].stripWhiteSpace(); | |
| 342 if (equalIgnoringCase(currentHeader, "*")) { | |
| 343 return true; | |
| 344 } | |
| 345 const KURL allowed(ParsedURLString, currentHeader); | |
| 346 if (allowed.isValid() && parentUrl.protocol() == allowed.protocol() && | |
| 347 parentUrl.host() == allowed.host() && | |
| 348 parentUrl.port() == allowed.port()) { | |
| 349 return true; | |
| 350 } | |
| 351 } | |
| 352 | |
| 353 return false; | |
| 354 } | |
| 355 | |
| 314 void ContentSecurityPolicy::addPolicyFromHeaderValue( | 356 void ContentSecurityPolicy::addPolicyFromHeaderValue( |
| 315 const String& header, | 357 const String& header, |
| 316 ContentSecurityPolicyHeaderType type, | 358 ContentSecurityPolicyHeaderType type, |
| 317 ContentSecurityPolicyHeaderSource source) { | 359 ContentSecurityPolicyHeaderSource source) { |
| 318 // If this is a report-only header inside a <meta> element, bail out. | 360 // If this is a report-only header inside a <meta> element, bail out. |
| 319 if (source == ContentSecurityPolicyHeaderSourceMeta && | 361 if (source == ContentSecurityPolicyHeaderSourceMeta && |
| 320 type == ContentSecurityPolicyHeaderTypeReport) { | 362 type == ContentSecurityPolicyHeaderTypeReport) { |
| 321 reportReportOnlyInMeta(header); | 363 reportReportOnlyInMeta(header); |
| 322 return; | 364 return; |
| 323 } | 365 } |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 350 m_disableEvalErrorMessage.isNull()) | 392 m_disableEvalErrorMessage.isNull()) |
| 351 m_disableEvalErrorMessage = policy->evalDisabledErrorMessage(); | 393 m_disableEvalErrorMessage = policy->evalDisabledErrorMessage(); |
| 352 | 394 |
| 353 m_policies.append(policy.release()); | 395 m_policies.append(policy.release()); |
| 354 | 396 |
| 355 // Skip the comma, and begin the next header from the current position. | 397 // Skip the comma, and begin the next header from the current position. |
| 356 ASSERT(position == end || *position == ','); | 398 ASSERT(position == end || *position == ','); |
| 357 skipExactly<UChar>(position, end, ','); | 399 skipExactly<UChar>(position, end, ','); |
| 358 begin = position; | 400 begin = position; |
| 359 } | 401 } |
| 360 } | 402 } |
| 361 | 403 |
| 362 void ContentSecurityPolicy::reportAccumulatedHeaders( | 404 void ContentSecurityPolicy::reportAccumulatedHeaders( |
| 363 FrameLoaderClient* client) const { | 405 FrameLoaderClient* client) const { |
| 364 // Notify the embedder about headers that have accumulated before the | 406 // Notify the embedder about headers that have accumulated before the |
| 365 // navigation got committed. See comments in | 407 // navigation got committed. See comments in |
| 366 // addAndReportPolicyFromHeaderValue for more details and context. | 408 // addAndReportPolicyFromHeaderValue for more details and context. |
| 367 DCHECK(client); | 409 DCHECK(client); |
| 368 for (const auto& policy : m_policies) { | 410 for (const auto& policy : m_policies) { |
| 369 client->didAddContentSecurityPolicy(policy->header(), policy->headerType(), | 411 client->didAddContentSecurityPolicy( |
| 370 policy->headerSource()); | 412 policy->header(), policy->headerType(), policy->headerSource()); |
| 371 } | 413 } |
| 372 } | 414 } |
| 373 | 415 |
| 374 void ContentSecurityPolicy::addAndReportPolicyFromHeaderValue( | 416 void ContentSecurityPolicy::addAndReportPolicyFromHeaderValue( |
| 375 const String& header, | 417 const String& header, |
| 376 ContentSecurityPolicyHeaderType type, | 418 ContentSecurityPolicyHeaderType type, |
| 377 ContentSecurityPolicyHeaderSource source) { | 419 ContentSecurityPolicyHeaderSource source) { |
| 378 // Notify about the new header, so that it can be reported back to the | 420 // Notify about the new header, so that it can be reported back to the |
| 379 // browser process. This is needed in order to: | 421 // browser process. This is needed in order to: |
| 380 // 1) replicate CSP directives (i.e. frame-src) to OOPIFs (only for now / | 422 // 1) replicate CSP directives (i.e. frame-src) to OOPIFs (only for now / |
| 381 // short-term). | 423 // short-term). |
| 382 // 2) enforce CSP in the browser process (not yet / long-term - see | 424 // 2) enforce CSP in the browser process (not yet / long-term - see |
| 383 // https://crbug.com/376522). | 425 // https://crbug.com/376522). |
| 384 if (document() && document()->frame()) | 426 if (document() && document()->frame()) { |
| 385 document()->frame()->client()->didAddContentSecurityPolicy(header, type, | 427 document()->frame()->client()->didAddContentSecurityPolicy(header, type, |
| 386 source); | 428 source); |
| 387 | 429 } |
| 388 addPolicyFromHeaderValue(header, type, source); | 430 |
| 389 } | 431 addPolicyFromHeaderValue(header, type, source); |
| 390 | 432 } |
| 391 void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value) { | 433 |
| 392 m_overrideInlineStyleAllowed = value; | 434 void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value) { |
| 393 } | 435 m_overrideInlineStyleAllowed = value; |
| 394 | 436 } |
| 395 void ContentSecurityPolicy::setOverrideURLForSelf(const KURL& url) { | 437 |
| 396 // Create a temporary CSPSource so that 'self' expressions can be resolved | 438 void ContentSecurityPolicy::setOverrideURLForSelf(const KURL& url) { |
| 397 // before we bind to an execution context (for 'frame-ancestor' resolution, | 439 // Create a temporary CSPSource so that 'self' expressions can be resolved |
| 398 // for example). This CSPSource will be overwritten when we bind this object | 440 // before we bind to an execution context (for 'frame-ancestor' resolution, |
| 399 // to an execution context. | 441 // for example). This CSPSource will be overwritten when we bind this object |
| 400 RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url); | 442 // to an execution context. |
| 401 m_selfProtocol = origin->protocol(); | 443 RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url); |
| 402 m_selfSource = | 444 m_selfProtocol = origin->protocol(); |
| 403 new CSPSource(this, m_selfProtocol, origin->host(), origin->port(), | 445 m_selfSource = |
| 404 String(), CSPSource::NoWildcard, CSPSource::NoWildcard); | 446 new CSPSource(this, m_selfProtocol, origin->host(), origin->port(), |
| 405 } | 447 String(), CSPSource::NoWildcard, CSPSource::NoWildcard); |
| 406 | 448 } |
| 407 std::unique_ptr<Vector<CSPHeaderAndType>> ContentSecurityPolicy::headers() | 449 |
| 408 const { | 450 std::unique_ptr<Vector<CSPHeaderAndType>> ContentSecurityPolicy::headers() |
| 409 std::unique_ptr<Vector<CSPHeaderAndType>> headers = | 451 const { |
| 410 wrapUnique(new Vector<CSPHeaderAndType>); | 452 std::unique_ptr<Vector<CSPHeaderAndType>> headers = |
| 411 for (const auto& policy : m_policies) { | 453 wrapUnique(new Vector<CSPHeaderAndType>); |
| 412 CSPHeaderAndType headerAndType(policy->header(), policy->headerType()); | 454 for (const auto& policy : m_policies) { |
| 413 headers->append(headerAndType); | 455 CSPHeaderAndType headerAndType(policy->header(), policy->headerType()); |
| 414 } | 456 headers->append(headerAndType); |
| 415 return headers; | 457 } |
| 416 } | 458 return headers; |
| 417 | 459 } |
| 418 template <bool (CSPDirectiveList::*allowed)( | 460 |
| 419 ContentSecurityPolicy::ReportingStatus) const> | 461 template <bool (CSPDirectiveList::*allowed)( |
| 420 bool isAllowedByAll(const CSPDirectiveListVector& policies, | 462 ContentSecurityPolicy::ReportingStatus) const> |
| 421 ContentSecurityPolicy::ReportingStatus reportingStatus) { | 463 bool isAllowedByAll(const CSPDirectiveListVector& policies, |
| 422 bool isAllowed = true; | 464 ContentSecurityPolicy::ReportingStatus reportingStatus) { |
| 423 for (const auto& policy : policies) | 465 bool isAllowed = true; |
| 424 isAllowed &= (policy.get()->*allowed)(reportingStatus); | 466 for (const auto& policy : policies) |
| 425 return isAllowed; | 467 isAllowed &= (policy.get()->*allowed)(reportingStatus); |
| 426 } | 468 return isAllowed; |
| 427 | 469 } |
| 428 template <bool (CSPDirectiveList::*allowed)( | 470 |
| 429 ScriptState* scriptState, | 471 template <bool (CSPDirectiveList::*allowed)( |
| 430 ContentSecurityPolicy::ReportingStatus, | 472 ScriptState* scriptState, |
| 431 ContentSecurityPolicy::ExceptionStatus) const> | 473 ContentSecurityPolicy::ReportingStatus, |
| 432 bool isAllowedByAllWithStateAndExceptionStatus( | 474 ContentSecurityPolicy::ExceptionStatus) const> |
| 433 const CSPDirectiveListVector& policies, | 475 bool isAllowedByAllWithStateAndExceptionStatus( |
| 434 ScriptState* scriptState, | 476 const CSPDirectiveListVector& policies, |
| 435 ContentSecurityPolicy::ReportingStatus reportingStatus, | 477 ScriptState* scriptState, |
| 436 ContentSecurityPolicy::ExceptionStatus exceptionStatus) { | 478 ContentSecurityPolicy::ReportingStatus reportingStatus, |
| 437 bool isAllowed = true; | 479 ContentSecurityPolicy::ExceptionStatus exceptionStatus) { |
| 438 for (const auto& policy : policies) | 480 bool isAllowed = true; |
| 439 isAllowed &= | 481 for (const auto& policy : policies) { |
| 440 (policy.get()->*allowed)(scriptState, reportingStatus, exceptionStatus); | 482 isAllowed &= (policy.get()->*allowed)(scriptState, reportingStatus, |
| 441 return isAllowed; | 483 exceptionStatus); |
| 442 } | 484 } |
| 443 | 485 return isAllowed; |
| 444 template <bool (CSPDirectiveList::*allowed)( | 486 } |
| 445 const String&, | 487 |
| 446 const WTF::OrdinalNumber&, | 488 template <bool (CSPDirectiveList::*allowed)( |
| 447 ContentSecurityPolicy::ReportingStatus) const> | 489 const String&, |
| 448 bool isAllowedByAllWithContext( | 490 const WTF::OrdinalNumber&, |
| 449 const CSPDirectiveListVector& policies, | 491 ContentSecurityPolicy::ReportingStatus) const> |
| 450 const String& contextURL, | 492 bool isAllowedByAllWithContext( |
| 451 const WTF::OrdinalNumber& contextLine, | 493 const CSPDirectiveListVector& policies, |
| 452 ContentSecurityPolicy::ReportingStatus reportingStatus) { | 494 const String& contextURL, |
| 453 bool isAllowed = true; | 495 const WTF::OrdinalNumber& contextLine, |
| 454 for (const auto& policy : policies) | 496 ContentSecurityPolicy::ReportingStatus reportingStatus) { |
| 455 isAllowed &= | 497 bool isAllowed = true; |
| 456 (policy.get()->*allowed)(contextURL, contextLine, reportingStatus); | 498 for (const auto& policy : policies) { |
| 457 return isAllowed; | 499 isAllowed &= |
| 458 } | 500 (policy.get()->*allowed)(contextURL, contextLine, reportingStatus); |
| 459 | 501 } |
| 460 template < | 502 return isAllowed; |
| 461 bool (CSPDirectiveList::*allowed)(const String&, | 503 } |
| 462 const String&, | 504 |
| 463 const WTF::OrdinalNumber&, | 505 template < |
| 464 ContentSecurityPolicy::ReportingStatus, | 506 bool (CSPDirectiveList::*allowed)(const String&, |
| 465 const String& content) const> | 507 const String&, |
| 466 bool isAllowedByAllWithContextAndContent( | 508 const WTF::OrdinalNumber&, |
| 467 const CSPDirectiveListVector& policies, | 509 ContentSecurityPolicy::ReportingStatus, |
| 468 const String& contextURL, | 510 const String& content) const> |
| 469 const String& nonce, | 511 bool isAllowedByAllWithContextAndContent( |
| 470 const WTF::OrdinalNumber& contextLine, | 512 const CSPDirectiveListVector& policies, |
| 471 ContentSecurityPolicy::ReportingStatus reportingStatus, | 513 const String& contextURL, |
| 472 const String& content) { | 514 const String& nonce, |
| 473 bool isAllowed = true; | 515 const WTF::OrdinalNumber& contextLine, |
| 474 for (const auto& policy : policies) | 516 ContentSecurityPolicy::ReportingStatus reportingStatus, |
| 475 isAllowed &= (policy.get()->*allowed)(contextURL, nonce, contextLine, | 517 const String& content) { |
| 476 reportingStatus, content); | 518 bool isAllowed = true; |
| 477 return isAllowed; | 519 for (const auto& policy : policies) { |
| 478 } | 520 isAllowed &= (policy.get()->*allowed)(contextURL, nonce, contextLine, |
| 479 | 521 reportingStatus, content); |
| 480 template < | 522 } |
| 481 bool (CSPDirectiveList::*allowed)(const String&, | 523 return isAllowed; |
| 482 const String&, | 524 } |
| 483 ParserDisposition, | 525 |
| 484 const WTF::OrdinalNumber&, | 526 template < |
| 485 ContentSecurityPolicy::ReportingStatus, | 527 bool (CSPDirectiveList::*allowed)(const String&, |
| 486 const String& content) const> | 528 const String&, |
| 487 bool isAllowedByAllWithContextAndContentAndParser( | 529 ParserDisposition, |
| 488 const CSPDirectiveListVector& policies, | 530 const WTF::OrdinalNumber&, |
| 489 const String& contextURL, | 531 ContentSecurityPolicy::ReportingStatus, |
| 490 const String& nonce, | 532 const String& content) const> |
| 491 ParserDisposition parserDisposition, | 533 bool isAllowedByAllWithContextAndContentAndParser( |
| 492 const WTF::OrdinalNumber& contextLine, | 534 const CSPDirectiveListVector& policies, |
| 493 ContentSecurityPolicy::ReportingStatus reportingStatus, | 535 const String& contextURL, |
| 494 const String& content) { | 536 const String& nonce, |
| 495 bool isAllowed = true; | 537 ParserDisposition parserDisposition, |
| 496 for (const auto& policy : policies) { | 538 const WTF::OrdinalNumber& contextLine, |
| 497 isAllowed &= | 539 ContentSecurityPolicy::ReportingStatus reportingStatus, |
| 498 (policy.get()->*allowed)(contextURL, nonce, parserDisposition, | 540 const String& content) { |
| 499 contextLine, reportingStatus, content); | 541 bool isAllowed = true; |
| 500 } | 542 for (const auto& policy : policies) { |
| 501 return isAllowed; | 543 isAllowed &= |
| 502 } | 544 (policy.get()->*allowed)(contextURL, nonce, parserDisposition, |
| 503 | 545 contextLine, reportingStatus, content); |
| 504 template <bool (CSPDirectiveList::*allowed)(const CSPHashValue&, | 546 } |
| 505 ContentSecurityPolicy::InlineType) | 547 return isAllowed; |
| 506 const> | 548 } |
| 507 bool isAllowedByAllWithHash(const CSPDirectiveListVector& policies, | 549 |
| 508 const CSPHashValue& hashValue, | 550 template <bool (CSPDirectiveList::*allowed)(const CSPHashValue&, |
| 509 ContentSecurityPolicy::InlineType type) { | 551 ContentSecurityPolicy::InlineType) |
| 510 bool isAllowed = true; | 552 const> |
| 511 for (const auto& policy : policies) | 553 bool isAllowedByAllWithHash(const CSPDirectiveListVector& policies, |
| 512 isAllowed &= (policy.get()->*allowed)(hashValue, type); | 554 const CSPHashValue& hashValue, |
| 513 return isAllowed; | 555 ContentSecurityPolicy::InlineType type) { |
| 514 } | 556 bool isAllowed = true; |
| 515 | 557 for (const auto& policy : policies) |
| 516 template <bool (CSPDirectiveList::*allowFromURL)( | 558 isAllowed &= (policy.get()->*allowed)(hashValue, type); |
| 517 const KURL&, | 559 return isAllowed; |
| 518 RedirectStatus, | 560 } |
| 519 ContentSecurityPolicy::ReportingStatus) const> | 561 |
| 520 bool isAllowedByAllWithURL( | 562 template <bool (CSPDirectiveList::*allowFromURL)( |
| 521 const CSPDirectiveListVector& policies, | 563 const KURL&, |
| 522 const KURL& url, | 564 RedirectStatus, |
| 523 RedirectStatus redirectStatus, | 565 ContentSecurityPolicy::ReportingStatus) const> |
| 524 ContentSecurityPolicy::ReportingStatus reportingStatus) { | 566 bool isAllowedByAllWithURL( |
| 525 if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol())) | 567 const CSPDirectiveListVector& policies, |
| 568 const KURL& url, | |
| 569 RedirectStatus redirectStatus, | |
| 570 ContentSecurityPolicy::ReportingStatus reportingStatus) { | |
| 571 if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol())) | |
| 572 return true; | |
| 573 | |
| 574 bool isAllowed = true; | |
| 575 for (const auto& policy : policies) { | |
| 576 isAllowed &= | |
| 577 (policy.get()->*allowFromURL)(url, redirectStatus, reportingStatus); | |
| 578 } | |
| 579 return isAllowed; | |
| 580 } | |
| 581 | |
| 582 template <bool (CSPDirectiveList::*allowFromURLWithNonce)( | |
| 583 const KURL&, | |
| 584 const String& nonce, | |
| 585 RedirectStatus, | |
| 586 ContentSecurityPolicy::ReportingStatus) const> | |
| 587 bool isAllowedByAllWithURLWithNonce( | |
| 588 const CSPDirectiveListVector& policies, | |
| 589 const KURL& url, | |
| 590 const String& nonce, | |
| 591 RedirectStatus redirectStatus, | |
| 592 ContentSecurityPolicy::ReportingStatus reportingStatus) { | |
| 593 if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol())) | |
| 594 return true; | |
| 595 | |
| 596 bool isAllowed = true; | |
| 597 for (const auto& policy : policies) { | |
| 598 isAllowed &= (policy.get()->*allowFromURLWithNonce)( | |
| 599 url, nonce, redirectStatus, reportingStatus); | |
| 600 } | |
| 601 return isAllowed; | |
| 602 } | |
| 603 | |
| 604 template <bool (CSPDirectiveList::*allowFromURLWithNonceAndParser)( | |
| 605 const KURL&, | |
| 606 const String& nonce, | |
| 607 ParserDisposition parserDisposition, | |
| 608 RedirectStatus, | |
| 609 ContentSecurityPolicy::ReportingStatus) const> | |
| 610 bool isAllowedByAllWithURLNonceAndParser( | |
| 611 const CSPDirectiveListVector& policies, | |
| 612 const KURL& url, | |
| 613 const String& nonce, | |
| 614 ParserDisposition parserDisposition, | |
| 615 RedirectStatus redirectStatus, | |
| 616 ContentSecurityPolicy::ReportingStatus reportingStatus) { | |
| 617 if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol())) | |
| 618 return true; | |
| 619 | |
| 620 bool isAllowed = true; | |
| 621 for (const auto& policy : policies) { | |
| 622 isAllowed &= (policy.get()->*allowFromURLWithNonceAndParser)( | |
| 623 url, nonce, parserDisposition, redirectStatus, reportingStatus); | |
| 624 } | |
| 625 return isAllowed; | |
| 626 } | |
| 627 | |
| 628 template <bool (CSPDirectiveList::*allowed)( | |
| 629 LocalFrame*, | |
| 630 const KURL&, | |
| 631 ContentSecurityPolicy::ReportingStatus) const> | |
| 632 bool isAllowedByAllWithFrame( | |
| 633 const CSPDirectiveListVector& policies, | |
| 634 LocalFrame* frame, | |
| 635 const KURL& url, | |
| 636 ContentSecurityPolicy::ReportingStatus reportingStatus) { | |
| 637 bool isAllowed = true; | |
| 638 for (const auto& policy : policies) | |
| 639 isAllowed &= (policy.get()->*allowed)(frame, url, reportingStatus); | |
| 640 return isAllowed; | |
| 641 } | |
| 642 | |
| 643 template <bool (CSPDirectiveList::*allowed)(const CSPHashValue&, | |
| 644 ContentSecurityPolicy::InlineType) | |
| 645 const> | |
| 646 bool checkDigest(const String& source, | |
| 647 ContentSecurityPolicy::InlineType type, | |
| 648 uint8_t hashAlgorithmsUsed, | |
| 649 const CSPDirectiveListVector& policies) { | |
| 650 // Any additions or subtractions from this struct should also modify the | |
| 651 // respective entries in the kSupportedPrefixes array in | |
| 652 // CSPSourceList::parseHash(). | |
| 653 static const struct { | |
| 654 ContentSecurityPolicyHashAlgorithm cspHashAlgorithm; | |
| 655 HashAlgorithm algorithm; | |
| 656 } kAlgorithmMap[] = { | |
| 657 {ContentSecurityPolicyHashAlgorithmSha1, HashAlgorithmSha1}, | |
| 658 {ContentSecurityPolicyHashAlgorithmSha256, HashAlgorithmSha256}, | |
| 659 {ContentSecurityPolicyHashAlgorithmSha384, HashAlgorithmSha384}, | |
| 660 {ContentSecurityPolicyHashAlgorithmSha512, HashAlgorithmSha512}}; | |
| 661 | |
| 662 // Only bother normalizing the source/computing digests if there are any | |
| 663 // checks to be done. | |
| 664 if (hashAlgorithmsUsed == ContentSecurityPolicyHashAlgorithmNone) | |
| 665 return false; | |
| 666 | |
| 667 StringUTF8Adaptor utf8Source(source); | |
| 668 | |
| 669 for (const auto& algorithmMap : kAlgorithmMap) { | |
| 670 DigestValue digest; | |
| 671 if (algorithmMap.cspHashAlgorithm & hashAlgorithmsUsed) { | |
| 672 bool digestSuccess = | |
| 673 computeDigest(algorithmMap.algorithm, utf8Source.data(), | |
| 674 utf8Source.length(), digest); | |
| 675 if (digestSuccess && | |
| 676 isAllowedByAllWithHash<allowed>( | |
| 677 policies, CSPHashValue(algorithmMap.cspHashAlgorithm, digest), | |
| 678 type)) | |
| 679 return true; | |
| 680 } | |
| 681 } | |
| 682 | |
| 683 return false; | |
| 684 } | |
| 685 | |
| 686 bool ContentSecurityPolicy::allowJavaScriptURLs( | |
| 687 const String& contextURL, | |
| 688 const WTF::OrdinalNumber& contextLine, | |
| 689 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 690 return isAllowedByAllWithContext<&CSPDirectiveList::allowJavaScriptURLs>( | |
| 691 m_policies, contextURL, contextLine, reportingStatus); | |
| 692 } | |
| 693 | |
| 694 bool ContentSecurityPolicy::allowInlineEventHandler( | |
| 695 const String& source, | |
| 696 const String& contextURL, | |
| 697 const WTF::OrdinalNumber& contextLine, | |
| 698 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 699 // Inline event handlers may be whitelisted by hash, if | |
| 700 // 'unsafe-hash-attributes' is present in a policy. Check against the digest | |
| 701 // of the |source| first before proceeding on to checking whether inline | |
| 702 // script is allowed. | |
| 703 if (checkDigest<&CSPDirectiveList::allowScriptHash>( | |
| 704 source, InlineType::Attribute, m_scriptHashAlgorithmsUsed, | |
| 705 m_policies)) | |
| 706 return true; | |
| 707 return isAllowedByAllWithContext< | |
| 708 &CSPDirectiveList::allowInlineEventHandlers>( | |
| 709 m_policies, contextURL, contextLine, reportingStatus); | |
| 710 } | |
| 711 | |
| 712 bool ContentSecurityPolicy::allowInlineScript( | |
| 713 const String& contextURL, | |
| 714 const String& nonce, | |
| 715 ParserDisposition parserDisposition, | |
| 716 const WTF::OrdinalNumber& contextLine, | |
| 717 const String& scriptContent, | |
| 718 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 719 return isAllowedByAllWithContextAndContentAndParser< | |
| 720 &CSPDirectiveList::allowInlineScript>(m_policies, contextURL, nonce, | |
| 721 parserDisposition, contextLine, | |
| 722 reportingStatus, scriptContent); | |
| 723 } | |
| 724 | |
| 725 bool ContentSecurityPolicy::allowInlineStyle( | |
| 726 const String& contextURL, | |
| 727 const String& nonce, | |
| 728 const WTF::OrdinalNumber& contextLine, | |
| 729 const String& styleContent, | |
| 730 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 731 if (m_overrideInlineStyleAllowed) | |
| 732 return true; | |
| 733 return isAllowedByAllWithContextAndContent< | |
| 734 &CSPDirectiveList::allowInlineStyle>(m_policies, contextURL, nonce, | |
| 735 contextLine, reportingStatus, | |
| 736 styleContent); | |
| 737 } | |
| 738 | |
| 739 bool ContentSecurityPolicy::allowEval( | |
| 740 ScriptState* scriptState, | |
| 741 ContentSecurityPolicy::ReportingStatus reportingStatus, | |
| 742 ContentSecurityPolicy::ExceptionStatus exceptionStatus) const { | |
| 743 return isAllowedByAllWithStateAndExceptionStatus< | |
| 744 &CSPDirectiveList::allowEval>(m_policies, scriptState, reportingStatus, | |
| 745 exceptionStatus); | |
| 746 } | |
| 747 | |
| 748 String ContentSecurityPolicy::evalDisabledErrorMessage() const { | |
| 749 for (const auto& policy : m_policies) { | |
| 750 if (!policy->allowEval(0, SuppressReport)) | |
| 751 return policy->evalDisabledErrorMessage(); | |
| 752 } | |
| 753 return String(); | |
| 754 } | |
| 755 | |
| 756 bool ContentSecurityPolicy::allowPluginType( | |
| 757 const String& type, | |
| 758 const String& typeAttribute, | |
| 759 const KURL& url, | |
| 760 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 761 for (const auto& policy : m_policies) { | |
| 762 if (!policy->allowPluginType(type, typeAttribute, url, reportingStatus)) | |
| 763 return false; | |
| 764 } | |
| 526 return true; | 765 return true; |
| 527 | 766 } |
| 528 bool isAllowed = true; | 767 |
| 529 for (const auto& policy : policies) | 768 bool ContentSecurityPolicy::allowPluginTypeForDocument( |
| 530 isAllowed &= | 769 const Document& document, |
| 531 (policy.get()->*allowFromURL)(url, redirectStatus, reportingStatus); | 770 const String& type, |
| 532 return isAllowed; | 771 const String& typeAttribute, |
| 533 } | 772 const KURL& url, |
| 534 | 773 ContentSecurityPolicy::ReportingStatus reportingStatus) const { |
| 535 template <bool (CSPDirectiveList::*allowFromURLWithNonce)( | 774 if (document.contentSecurityPolicy() && |
| 536 const KURL&, | 775 !document.contentSecurityPolicy()->allowPluginType(type, typeAttribute, |
| 537 const String& nonce, | 776 url)) |
| 538 RedirectStatus, | 777 return false; |
| 539 ContentSecurityPolicy::ReportingStatus) const> | 778 |
| 540 bool isAllowedByAllWithURLWithNonce( | 779 // CSP says that a plugin document in a nested browsing context should |
| 541 const CSPDirectiveListVector& policies, | 780 // inherit the plugin-types of its parent. |
| 542 const KURL& url, | 781 // |
| 543 const String& nonce, | 782 // FIXME: The plugin-types directive should be pushed down into the |
| 544 RedirectStatus redirectStatus, | 783 // current document instead of reaching up to the parent for it here. |
| 545 ContentSecurityPolicy::ReportingStatus reportingStatus) { | 784 LocalFrame* frame = document.frame(); |
| 546 if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol())) | 785 if (frame && frame->tree().parent() && document.isPluginDocument()) { |
| 786 ContentSecurityPolicy* parentCSP = | |
| 787 frame->tree().parent()->securityContext()->contentSecurityPolicy(); | |
| 788 if (parentCSP && !parentCSP->allowPluginType(type, typeAttribute, url)) | |
| 789 return false; | |
| 790 } | |
| 791 | |
| 547 return true; | 792 return true; |
| 548 | 793 } |
| 549 bool isAllowed = true; | 794 |
| 550 for (const auto& policy : policies) | 795 bool ContentSecurityPolicy::allowScriptFromSource( |
| 551 isAllowed &= (policy.get()->*allowFromURLWithNonce)( | 796 const KURL& url, |
| 552 url, nonce, redirectStatus, reportingStatus); | 797 const String& nonce, |
| 553 return isAllowed; | 798 ParserDisposition parserDisposition, |
| 554 } | 799 RedirectStatus redirectStatus, |
| 555 | 800 ContentSecurityPolicy::ReportingStatus reportingStatus) const { |
| 556 template <bool (CSPDirectiveList::*allowFromURLWithNonceAndParser)( | 801 return isAllowedByAllWithURLNonceAndParser< |
| 557 const KURL&, | 802 &CSPDirectiveList::allowScriptFromSource>( |
| 558 const String& nonce, | 803 m_policies, url, nonce, parserDisposition, redirectStatus, |
| 559 ParserDisposition parserDisposition, | 804 reportingStatus); |
| 560 RedirectStatus, | 805 } |
| 561 ContentSecurityPolicy::ReportingStatus) const> | 806 |
| 562 bool isAllowedByAllWithURLNonceAndParser( | 807 bool ContentSecurityPolicy::allowScriptWithHash(const String& source, |
| 563 const CSPDirectiveListVector& policies, | 808 InlineType type) const { |
| 564 const KURL& url, | 809 return checkDigest<&CSPDirectiveList::allowScriptHash>( |
| 565 const String& nonce, | 810 source, type, m_scriptHashAlgorithmsUsed, m_policies); |
| 566 ParserDisposition parserDisposition, | 811 } |
| 567 RedirectStatus redirectStatus, | 812 |
| 568 ContentSecurityPolicy::ReportingStatus reportingStatus) { | 813 bool ContentSecurityPolicy::allowStyleWithHash(const String& source, |
| 569 if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol())) | 814 InlineType type) const { |
| 815 return checkDigest<&CSPDirectiveList::allowStyleHash>( | |
| 816 source, type, m_styleHashAlgorithmsUsed, m_policies); | |
| 817 } | |
| 818 | |
| 819 bool ContentSecurityPolicy::allowRequestWithoutIntegrity( | |
| 820 WebURLRequest::RequestContext context, | |
| 821 const KURL& url, | |
| 822 RedirectStatus redirectStatus, | |
| 823 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 824 for (const auto& policy : m_policies) { | |
| 825 if (!policy->allowRequestWithoutIntegrity(context, url, redirectStatus, | |
| 826 reportingStatus)) | |
| 827 return false; | |
| 828 } | |
| 570 return true; | 829 return true; |
| 571 | 830 } |
| 572 bool isAllowed = true; | 831 |
| 573 for (const auto& policy : policies) { | 832 bool ContentSecurityPolicy::allowRequest( |
| 574 isAllowed &= (policy.get()->*allowFromURLWithNonceAndParser)( | 833 WebURLRequest::RequestContext context, |
| 575 url, nonce, parserDisposition, redirectStatus, reportingStatus); | 834 const KURL& url, |
| 576 } | 835 const String& nonce, |
| 577 return isAllowed; | 836 const IntegrityMetadataSet& integrityMetadata, |
| 578 } | 837 ParserDisposition parserDisposition, |
| 579 | 838 RedirectStatus redirectStatus, |
| 580 template <bool (CSPDirectiveList::*allowed)( | 839 ReportingStatus reportingStatus) const { |
| 581 LocalFrame*, | 840 if (integrityMetadata.isEmpty() && |
| 582 const KURL&, | 841 !allowRequestWithoutIntegrity(context, url, redirectStatus, |
| 583 ContentSecurityPolicy::ReportingStatus) const> | 842 reportingStatus)) |
| 584 bool isAllowedByAllWithFrame( | 843 return false; |
| 585 const CSPDirectiveListVector& policies, | 844 |
| 586 LocalFrame* frame, | 845 switch (context) { |
| 587 const KURL& url, | 846 case WebURLRequest::RequestContextAudio: |
| 588 ContentSecurityPolicy::ReportingStatus reportingStatus) { | 847 case WebURLRequest::RequestContextTrack: |
| 589 bool isAllowed = true; | 848 case WebURLRequest::RequestContextVideo: |
| 590 for (const auto& policy : policies) | 849 return allowMediaFromSource(url, redirectStatus, reportingStatus); |
| 591 isAllowed &= (policy.get()->*allowed)(frame, url, reportingStatus); | 850 case WebURLRequest::RequestContextBeacon: |
| 592 return isAllowed; | 851 case WebURLRequest::RequestContextEventSource: |
| 593 } | 852 case WebURLRequest::RequestContextFetch: |
| 594 | 853 case WebURLRequest::RequestContextXMLHttpRequest: |
| 595 template <bool (CSPDirectiveList::*allowed)(const CSPHashValue&, | 854 return allowConnectToSource(url, redirectStatus, reportingStatus); |
| 596 ContentSecurityPolicy::InlineType) | 855 case WebURLRequest::RequestContextEmbed: |
| 597 const> | 856 case WebURLRequest::RequestContextObject: |
| 598 bool checkDigest(const String& source, | 857 return allowObjectFromSource(url, redirectStatus, reportingStatus); |
| 599 ContentSecurityPolicy::InlineType type, | 858 case WebURLRequest::RequestContextFavicon: |
| 600 uint8_t hashAlgorithmsUsed, | 859 case WebURLRequest::RequestContextImage: |
| 601 const CSPDirectiveListVector& policies) { | 860 case WebURLRequest::RequestContextImageSet: |
| 602 // Any additions or subtractions from this struct should also modify the | 861 return allowImageFromSource(url, redirectStatus, reportingStatus); |
| 603 // respective entries in the kSupportedPrefixes array in | 862 case WebURLRequest::RequestContextFont: |
| 604 // CSPSourceList::parseHash(). | 863 return allowFontFromSource(url, redirectStatus, reportingStatus); |
| 605 static const struct { | 864 case WebURLRequest::RequestContextForm: |
| 606 ContentSecurityPolicyHashAlgorithm cspHashAlgorithm; | 865 return allowFormAction(url, redirectStatus, reportingStatus); |
| 607 HashAlgorithm algorithm; | 866 case WebURLRequest::RequestContextFrame: |
| 608 } kAlgorithmMap[] = { | 867 case WebURLRequest::RequestContextIframe: |
| 609 {ContentSecurityPolicyHashAlgorithmSha1, HashAlgorithmSha1}, | 868 return allowChildFrameFromSource(url, redirectStatus, reportingStatus); |
| 610 {ContentSecurityPolicyHashAlgorithmSha256, HashAlgorithmSha256}, | 869 case WebURLRequest::RequestContextImport: |
| 611 {ContentSecurityPolicyHashAlgorithmSha384, HashAlgorithmSha384}, | 870 case WebURLRequest::RequestContextScript: |
| 612 {ContentSecurityPolicyHashAlgorithmSha512, HashAlgorithmSha512}}; | 871 return allowScriptFromSource(url, nonce, parserDisposition, |
| 613 | 872 redirectStatus, reportingStatus); |
| 614 // Only bother normalizing the source/computing digests if there are any | 873 case WebURLRequest::RequestContextXSLT: |
| 615 // checks to be done. | 874 return allowScriptFromSource(url, nonce, parserDisposition, |
| 616 if (hashAlgorithmsUsed == ContentSecurityPolicyHashAlgorithmNone) | 875 redirectStatus, reportingStatus); |
| 876 case WebURLRequest::RequestContextManifest: | |
| 877 return allowManifestFromSource(url, redirectStatus, reportingStatus); | |
| 878 case WebURLRequest::RequestContextServiceWorker: | |
| 879 case WebURLRequest::RequestContextSharedWorker: | |
| 880 case WebURLRequest::RequestContextWorker: | |
| 881 return allowWorkerContextFromSource(url, redirectStatus, | |
| 882 reportingStatus); | |
| 883 case WebURLRequest::RequestContextStyle: | |
| 884 return allowStyleFromSource(url, nonce, redirectStatus, | |
| 885 reportingStatus); | |
| 886 case WebURLRequest::RequestContextCSPReport: | |
| 887 case WebURLRequest::RequestContextDownload: | |
| 888 case WebURLRequest::RequestContextHyperlink: | |
| 889 case WebURLRequest::RequestContextInternal: | |
| 890 case WebURLRequest::RequestContextLocation: | |
| 891 case WebURLRequest::RequestContextPing: | |
| 892 case WebURLRequest::RequestContextPlugin: | |
| 893 case WebURLRequest::RequestContextPrefetch: | |
| 894 case WebURLRequest::RequestContextSubresource: | |
| 895 case WebURLRequest::RequestContextUnspecified: | |
| 896 return true; | |
| 897 } | |
| 898 NOTREACHED(); | |
| 899 return true; | |
| 900 } | |
| 901 | |
| 902 void ContentSecurityPolicy::usesScriptHashAlgorithms(uint8_t algorithms) { | |
| 903 m_scriptHashAlgorithmsUsed |= algorithms; | |
| 904 } | |
| 905 | |
| 906 void ContentSecurityPolicy::usesStyleHashAlgorithms(uint8_t algorithms) { | |
| 907 m_styleHashAlgorithmsUsed |= algorithms; | |
| 908 } | |
| 909 | |
| 910 bool ContentSecurityPolicy::allowObjectFromSource( | |
| 911 const KURL& url, | |
| 912 RedirectStatus redirectStatus, | |
| 913 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 914 return isAllowedByAllWithURL<&CSPDirectiveList::allowObjectFromSource>( | |
| 915 m_policies, url, redirectStatus, reportingStatus); | |
| 916 } | |
| 917 | |
| 918 bool ContentSecurityPolicy::allowChildFrameFromSource( | |
| 919 const KURL& url, | |
| 920 RedirectStatus redirectStatus, | |
| 921 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 922 return isAllowedByAllWithURL<&CSPDirectiveList::allowChildFrameFromSource>( | |
| 923 m_policies, url, redirectStatus, reportingStatus); | |
| 924 } | |
| 925 | |
| 926 bool ContentSecurityPolicy::allowImageFromSource( | |
| 927 const KURL& url, | |
| 928 RedirectStatus redirectStatus, | |
| 929 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 930 if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy( | |
| 931 url.protocol(), SchemeRegistry::PolicyAreaImage)) | |
| 932 return true; | |
| 933 return isAllowedByAllWithURL<&CSPDirectiveList::allowImageFromSource>( | |
| 934 m_policies, url, redirectStatus, reportingStatus); | |
| 935 } | |
| 936 | |
| 937 bool ContentSecurityPolicy::allowStyleFromSource( | |
| 938 const KURL& url, | |
| 939 const String& nonce, | |
| 940 RedirectStatus redirectStatus, | |
| 941 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 942 if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy( | |
| 943 url.protocol(), SchemeRegistry::PolicyAreaStyle)) | |
| 944 return true; | |
| 945 return isAllowedByAllWithURLWithNonce< | |
| 946 &CSPDirectiveList::allowStyleFromSource>( | |
| 947 m_policies, url, nonce, redirectStatus, reportingStatus); | |
| 948 } | |
| 949 | |
| 950 bool ContentSecurityPolicy::allowFontFromSource( | |
| 951 const KURL& url, | |
| 952 RedirectStatus redirectStatus, | |
| 953 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 954 return isAllowedByAllWithURL<&CSPDirectiveList::allowFontFromSource>( | |
| 955 m_policies, url, redirectStatus, reportingStatus); | |
| 956 } | |
| 957 | |
| 958 bool ContentSecurityPolicy::allowMediaFromSource( | |
| 959 const KURL& url, | |
| 960 RedirectStatus redirectStatus, | |
| 961 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 962 return isAllowedByAllWithURL<&CSPDirectiveList::allowMediaFromSource>( | |
| 963 m_policies, url, redirectStatus, reportingStatus); | |
| 964 } | |
| 965 | |
| 966 bool ContentSecurityPolicy::allowConnectToSource( | |
| 967 const KURL& url, | |
| 968 RedirectStatus redirectStatus, | |
| 969 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 970 return isAllowedByAllWithURL<&CSPDirectiveList::allowConnectToSource>( | |
| 971 m_policies, url, redirectStatus, reportingStatus); | |
| 972 } | |
| 973 | |
| 974 bool ContentSecurityPolicy::allowFormAction( | |
| 975 const KURL& url, | |
| 976 RedirectStatus redirectStatus, | |
| 977 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 978 return isAllowedByAllWithURL<&CSPDirectiveList::allowFormAction>( | |
| 979 m_policies, url, redirectStatus, reportingStatus); | |
| 980 } | |
| 981 | |
| 982 bool ContentSecurityPolicy::allowBaseURI( | |
| 983 const KURL& url, | |
| 984 RedirectStatus redirectStatus, | |
| 985 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 986 return isAllowedByAllWithURL<&CSPDirectiveList::allowBaseURI>( | |
| 987 m_policies, url, redirectStatus, reportingStatus); | |
| 988 } | |
| 989 | |
| 990 bool ContentSecurityPolicy::allowWorkerContextFromSource( | |
| 991 const KURL& url, | |
| 992 RedirectStatus redirectStatus, | |
| 993 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 994 // CSP 1.1 moves workers from 'script-src' to the new 'child-src'. Measure | |
| 995 // the | |
| 996 // impact of this backwards-incompatible change. | |
| 997 if (Document* document = this->document()) { | |
| 998 UseCounter::count(*document, UseCounter::WorkerSubjectToCSP); | |
| 999 if (isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>( | |
| 1000 m_policies, url, redirectStatus, SuppressReport) && | |
| 1001 !isAllowedByAllWithURLNonceAndParser< | |
| 1002 &CSPDirectiveList::allowScriptFromSource>( | |
| 1003 m_policies, url, AtomicString(), NotParserInserted, | |
| 1004 redirectStatus, SuppressReport)) { | |
| 1005 UseCounter::count(*document, | |
| 1006 UseCounter::WorkerAllowedByChildBlockedByScript); | |
| 1007 } | |
| 1008 } | |
| 1009 | |
| 1010 return isAllowedByAllWithURL< | |
| 1011 &CSPDirectiveList::allowChildContextFromSource>( | |
| 1012 m_policies, url, redirectStatus, reportingStatus); | |
| 1013 } | |
| 1014 | |
| 1015 bool ContentSecurityPolicy::allowManifestFromSource( | |
| 1016 const KURL& url, | |
| 1017 RedirectStatus redirectStatus, | |
| 1018 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 1019 return isAllowedByAllWithURL<&CSPDirectiveList::allowManifestFromSource>( | |
| 1020 m_policies, url, redirectStatus, reportingStatus); | |
| 1021 } | |
| 1022 | |
| 1023 bool ContentSecurityPolicy::allowAncestors( | |
| 1024 LocalFrame* frame, | |
| 1025 const KURL& url, | |
| 1026 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 1027 return isAllowedByAllWithFrame<&CSPDirectiveList::allowAncestors>( | |
| 1028 m_policies, frame, url, reportingStatus); | |
| 1029 } | |
| 1030 | |
| 1031 bool ContentSecurityPolicy::isFrameAncestorsEnforced() const { | |
| 1032 for (const auto& policy : m_policies) { | |
| 1033 if (policy->isFrameAncestorsEnforced()) | |
| 1034 return true; | |
| 1035 } | |
| 617 return false; | 1036 return false; |
| 618 | 1037 } |
| 619 StringUTF8Adaptor utf8Source(source); | 1038 |
| 620 | 1039 bool ContentSecurityPolicy::isActive() const { |
| 621 for (const auto& algorithmMap : kAlgorithmMap) { | 1040 return !m_policies.isEmpty(); |
| 622 DigestValue digest; | 1041 } |
| 623 if (algorithmMap.cspHashAlgorithm & hashAlgorithmsUsed) { | 1042 |
| 624 bool digestSuccess = | 1043 ReflectedXSSDisposition ContentSecurityPolicy::getReflectedXSSDisposition() |
| 625 computeDigest(algorithmMap.algorithm, utf8Source.data(), | 1044 const { |
| 626 utf8Source.length(), digest); | 1045 ReflectedXSSDisposition disposition = ReflectedXSSUnset; |
| 627 if (digestSuccess && | 1046 for (const auto& policy : m_policies) { |
| 628 isAllowedByAllWithHash<allowed>( | 1047 if (policy->getReflectedXSSDisposition() > disposition) { |
| 629 policies, CSPHashValue(algorithmMap.cspHashAlgorithm, digest), | 1048 disposition = |
| 630 type)) | 1049 std::max(disposition, policy->getReflectedXSSDisposition()); |
| 1050 } | |
| 1051 } | |
| 1052 return disposition; | |
| 1053 } | |
| 1054 | |
| 1055 bool ContentSecurityPolicy::didSetReferrerPolicy() const { | |
| 1056 for (const auto& policy : m_policies) { | |
| 1057 if (policy->didSetReferrerPolicy()) | |
| 631 return true; | 1058 return true; |
| 632 } | 1059 } |
| 633 } | |
| 634 | |
| 635 return false; | |
| 636 } | |
| 637 | |
| 638 bool ContentSecurityPolicy::allowJavaScriptURLs( | |
| 639 const String& contextURL, | |
| 640 const WTF::OrdinalNumber& contextLine, | |
| 641 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 642 return isAllowedByAllWithContext<&CSPDirectiveList::allowJavaScriptURLs>( | |
| 643 m_policies, contextURL, contextLine, reportingStatus); | |
| 644 } | |
| 645 | |
| 646 bool ContentSecurityPolicy::allowInlineEventHandler( | |
| 647 const String& source, | |
| 648 const String& contextURL, | |
| 649 const WTF::OrdinalNumber& contextLine, | |
| 650 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 651 // Inline event handlers may be whitelisted by hash, if | |
| 652 // 'unsafe-hash-attributes' is present in a policy. Check against the digest | |
| 653 // of the |source| first before proceeding on to checking whether inline | |
| 654 // script is allowed. | |
| 655 if (checkDigest<&CSPDirectiveList::allowScriptHash>( | |
| 656 source, InlineType::Attribute, m_scriptHashAlgorithmsUsed, | |
| 657 m_policies)) | |
| 658 return true; | |
| 659 return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineEventHandlers>( | |
| 660 m_policies, contextURL, contextLine, reportingStatus); | |
| 661 } | |
| 662 | |
| 663 bool ContentSecurityPolicy::allowInlineScript( | |
| 664 const String& contextURL, | |
| 665 const String& nonce, | |
| 666 ParserDisposition parserDisposition, | |
| 667 const WTF::OrdinalNumber& contextLine, | |
| 668 const String& scriptContent, | |
| 669 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 670 return isAllowedByAllWithContextAndContentAndParser< | |
| 671 &CSPDirectiveList::allowInlineScript>(m_policies, contextURL, nonce, | |
| 672 parserDisposition, contextLine, | |
| 673 reportingStatus, scriptContent); | |
| 674 } | |
| 675 | |
| 676 bool ContentSecurityPolicy::allowInlineStyle( | |
| 677 const String& contextURL, | |
| 678 const String& nonce, | |
| 679 const WTF::OrdinalNumber& contextLine, | |
| 680 const String& styleContent, | |
| 681 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 682 if (m_overrideInlineStyleAllowed) | |
| 683 return true; | |
| 684 return isAllowedByAllWithContextAndContent< | |
| 685 &CSPDirectiveList::allowInlineStyle>(m_policies, contextURL, nonce, | |
| 686 contextLine, reportingStatus, | |
| 687 styleContent); | |
| 688 } | |
| 689 | |
| 690 bool ContentSecurityPolicy::allowEval( | |
| 691 ScriptState* scriptState, | |
| 692 ContentSecurityPolicy::ReportingStatus reportingStatus, | |
| 693 ContentSecurityPolicy::ExceptionStatus exceptionStatus) const { | |
| 694 return isAllowedByAllWithStateAndExceptionStatus< | |
| 695 &CSPDirectiveList::allowEval>(m_policies, scriptState, reportingStatus, | |
| 696 exceptionStatus); | |
| 697 } | |
| 698 | |
| 699 String ContentSecurityPolicy::evalDisabledErrorMessage() const { | |
| 700 for (const auto& policy : m_policies) { | |
| 701 if (!policy->allowEval(0, SuppressReport)) | |
| 702 return policy->evalDisabledErrorMessage(); | |
| 703 } | |
| 704 return String(); | |
| 705 } | |
| 706 | |
| 707 bool ContentSecurityPolicy::allowPluginType( | |
| 708 const String& type, | |
| 709 const String& typeAttribute, | |
| 710 const KURL& url, | |
| 711 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 712 for (const auto& policy : m_policies) { | |
| 713 if (!policy->allowPluginType(type, typeAttribute, url, reportingStatus)) | |
| 714 return false; | |
| 715 } | |
| 716 return true; | |
| 717 } | |
| 718 | |
| 719 bool ContentSecurityPolicy::allowPluginTypeForDocument( | |
| 720 const Document& document, | |
| 721 const String& type, | |
| 722 const String& typeAttribute, | |
| 723 const KURL& url, | |
| 724 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 725 if (document.contentSecurityPolicy() && | |
| 726 !document.contentSecurityPolicy()->allowPluginType(type, typeAttribute, | |
| 727 url)) | |
| 728 return false; | 1060 return false; |
| 729 | 1061 } |
| 730 // CSP says that a plugin document in a nested browsing context should | 1062 |
| 731 // inherit the plugin-types of its parent. | 1063 const KURL ContentSecurityPolicy::url() const { |
| 732 // | 1064 return m_executionContext->contextURL(); |
| 733 // FIXME: The plugin-types directive should be pushed down into the | 1065 } |
| 734 // current document instead of reaching up to the parent for it here. | 1066 |
| 735 LocalFrame* frame = document.frame(); | 1067 KURL ContentSecurityPolicy::completeURL(const String& url) const { |
| 736 if (frame && frame->tree().parent() && document.isPluginDocument()) { | 1068 return m_executionContext->contextCompleteURL(url); |
| 737 ContentSecurityPolicy* parentCSP = | 1069 } |
| 738 frame->tree().parent()->securityContext()->contentSecurityPolicy(); | 1070 |
| 739 if (parentCSP && !parentCSP->allowPluginType(type, typeAttribute, url)) | 1071 void ContentSecurityPolicy::enforceSandboxFlags(SandboxFlags mask) { |
| 740 return false; | 1072 m_sandboxMask |= mask; |
| 741 } | 1073 } |
| 742 | 1074 |
| 743 return true; | 1075 void ContentSecurityPolicy::treatAsPublicAddress() { |
| 744 } | 1076 if (!RuntimeEnabledFeatures::corsRFC1918Enabled()) |
| 745 | 1077 return; |
| 746 bool ContentSecurityPolicy::allowScriptFromSource( | 1078 m_treatAsPublicAddress = true; |
| 747 const KURL& url, | 1079 } |
| 748 const String& nonce, | 1080 |
| 749 ParserDisposition parserDisposition, | 1081 void ContentSecurityPolicy::enforceStrictMixedContentChecking() { |
| 750 RedirectStatus redirectStatus, | 1082 m_insecureRequestPolicy |= kBlockAllMixedContent; |
| 751 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | 1083 } |
| 752 return isAllowedByAllWithURLNonceAndParser< | 1084 |
| 753 &CSPDirectiveList::allowScriptFromSource>( | 1085 void ContentSecurityPolicy::upgradeInsecureRequests() { |
| 754 m_policies, url, nonce, parserDisposition, redirectStatus, | 1086 m_insecureRequestPolicy |= kUpgradeInsecureRequests; |
| 755 reportingStatus); | 1087 } |
| 756 } | 1088 |
| 757 | 1089 static String stripURLForUseInReport(Document* document, |
| 758 bool ContentSecurityPolicy::allowScriptWithHash(const String& source, | 1090 const KURL& url, |
| 759 InlineType type) const { | 1091 RedirectStatus redirectStatus, |
| 760 return checkDigest<&CSPDirectiveList::allowScriptHash>( | 1092 const String& effectiveDirective) { |
| 761 source, type, m_scriptHashAlgorithmsUsed, m_policies); | 1093 if (!url.isValid()) |
| 762 } | 1094 return String(); |
| 763 | 1095 if (!url.isHierarchical() || url.protocolIs("file")) |
| 764 bool ContentSecurityPolicy::allowStyleWithHash(const String& source, | 1096 return url.protocol(); |
| 765 InlineType type) const { | 1097 |
| 766 return checkDigest<&CSPDirectiveList::allowStyleHash>( | 1098 // Until we're more careful about the way we deal with navigations in frames |
| 767 source, type, m_styleHashAlgorithmsUsed, m_policies); | 1099 // (and, by extension, in plugin documents), strip cross-origin 'frame-src' |
| 768 } | 1100 // and 'object-src' violations down to an origin. https://crbug.com/633306 |
| 769 | 1101 bool canSafelyExposeURL = |
| 770 bool ContentSecurityPolicy::allowRequestWithoutIntegrity( | 1102 document->getSecurityOrigin()->canRequest(url) || |
| 771 WebURLRequest::RequestContext context, | 1103 (redirectStatus == RedirectStatus::NoRedirect && |
| 772 const KURL& url, | 1104 !equalIgnoringCase(effectiveDirective, |
| 773 RedirectStatus redirectStatus, | 1105 ContentSecurityPolicy::FrameSrc) && |
| 774 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | 1106 !equalIgnoringCase(effectiveDirective, |
| 775 for (const auto& policy : m_policies) { | 1107 ContentSecurityPolicy::ObjectSrc)); |
| 776 if (!policy->allowRequestWithoutIntegrity(context, url, redirectStatus, | 1108 |
| 777 reportingStatus)) | 1109 if (canSafelyExposeURL) { |
| 778 return false; | 1110 // 'KURL::strippedForUseAsReferrer()' dumps 'String()' for non-webby URLs. |
| 779 } | 1111 // It's better for developers if we return the origin of those URLs rather |
| 780 return true; | 1112 // than nothing. |
| 781 } | 1113 if (url.protocolIsInHTTPFamily()) |
| 782 | 1114 return url.strippedForUseAsReferrer(); |
| 783 bool ContentSecurityPolicy::allowRequest( | 1115 } |
| 784 WebURLRequest::RequestContext context, | 1116 return SecurityOrigin::create(url)->toString(); |
| 785 const KURL& url, | 1117 } |
| 786 const String& nonce, | 1118 |
| 787 const IntegrityMetadataSet& integrityMetadata, | 1119 static void gatherSecurityPolicyViolationEventData( |
| 788 ParserDisposition parserDisposition, | 1120 SecurityPolicyViolationEventInit& init, |
| 789 RedirectStatus redirectStatus, | 1121 Document* document, |
| 790 ReportingStatus reportingStatus) const { | 1122 const String& directiveText, |
| 791 if (integrityMetadata.isEmpty() && | 1123 const String& effectiveDirective, |
| 792 !allowRequestWithoutIntegrity(context, url, redirectStatus, | 1124 const KURL& blockedURL, |
| 793 reportingStatus)) | 1125 const String& header, |
| 1126 RedirectStatus redirectStatus, | |
| 1127 ContentSecurityPolicy::ViolationType violationType, | |
| 1128 int contextLine) { | |
| 1129 if (equalIgnoringCase(effectiveDirective, | |
| 1130 ContentSecurityPolicy::FrameAncestors)) { | |
| 1131 // If this load was blocked via 'frame-ancestors', then the URL of | |
| 1132 // |document| has not yet been initialized. In this case, we'll set both | |
| 1133 // 'documentURI' and 'blockedURI' to the blocked document's URL. | |
| 1134 init.setDocumentURI(blockedURL.getString()); | |
| 1135 init.setBlockedURI(blockedURL.getString()); | |
| 1136 } else { | |
| 1137 init.setDocumentURI(document->url().getString()); | |
| 1138 switch (violationType) { | |
| 1139 case ContentSecurityPolicy::InlineViolation: | |
| 1140 init.setBlockedURI("inline"); | |
| 1141 break; | |
| 1142 case ContentSecurityPolicy::EvalViolation: | |
| 1143 init.setBlockedURI("eval"); | |
| 1144 break; | |
| 1145 case ContentSecurityPolicy::URLViolation: | |
| 1146 init.setBlockedURI(stripURLForUseInReport( | |
| 1147 document, blockedURL, redirectStatus, effectiveDirective)); | |
| 1148 break; | |
| 1149 } | |
| 1150 } | |
| 1151 init.setReferrer(document->referrer()); | |
| 1152 init.setViolatedDirective(directiveText); | |
| 1153 init.setEffectiveDirective(effectiveDirective); | |
| 1154 init.setOriginalPolicy(header); | |
| 1155 init.setSourceFile(String()); | |
| 1156 init.setLineNumber(contextLine); | |
| 1157 init.setColumnNumber(0); | |
| 1158 init.setStatusCode(0); | |
| 1159 | |
| 1160 if (!SecurityOrigin::isSecure(document->url()) && document->loader()) | |
| 1161 init.setStatusCode(document->loader()->response().httpStatusCode()); | |
| 1162 | |
| 1163 std::unique_ptr<SourceLocation> location = | |
| 1164 SourceLocation::capture(document); | |
| 1165 if (location->lineNumber()) { | |
| 1166 KURL source = KURL(ParsedURLString, location->url()); | |
| 1167 init.setSourceFile(stripURLForUseInReport( | |
| 1168 document, source, redirectStatus, effectiveDirective)); | |
| 1169 init.setLineNumber(location->lineNumber()); | |
| 1170 init.setColumnNumber(location->columnNumber()); | |
| 1171 } | |
| 1172 } | |
| 1173 | |
| 1174 void ContentSecurityPolicy::reportViolation( | |
| 1175 const String& directiveText, | |
| 1176 const String& effectiveDirective, | |
| 1177 const String& consoleMessage, | |
| 1178 const KURL& blockedURL, | |
| 1179 const Vector<String>& reportEndpoints, | |
| 1180 const String& header, | |
| 1181 ViolationType violationType, | |
| 1182 LocalFrame* contextFrame, | |
| 1183 RedirectStatus redirectStatus, | |
| 1184 int contextLine) { | |
| 1185 DCHECK(violationType == URLViolation || blockedURL.isEmpty()); | |
| 1186 | |
| 1187 // TODO(lukasza): Support sending reports from OOPIFs - | |
| 1188 // https://crbug.com/611232 (or move CSP child-src and frame-src checks to | |
| 1189 // the | |
| 1190 // browser process - see https://crbug.com/376522). | |
| 1191 if (!m_executionContext && !contextFrame) { | |
| 1192 DCHECK(equalIgnoringCase(effectiveDirective, | |
| 1193 ContentSecurityPolicy::ChildSrc) || | |
| 1194 equalIgnoringCase(effectiveDirective, | |
| 1195 ContentSecurityPolicy::FrameSrc) || | |
| 1196 equalIgnoringCase(effectiveDirective, | |
| 1197 ContentSecurityPolicy::PluginTypes)); | |
| 1198 return; | |
| 1199 } | |
| 1200 | |
| 1201 DCHECK((m_executionContext && !contextFrame) || | |
| 1202 (equalIgnoringCase(effectiveDirective, | |
| 1203 ContentSecurityPolicy::FrameAncestors) && | |
| 1204 contextFrame)); | |
| 1205 | |
| 1206 // FIXME: Support sending reports from worker. | |
| 1207 Document* document = | |
| 1208 contextFrame ? contextFrame->document() : this->document(); | |
| 1209 if (!document) | |
| 1210 return; | |
| 1211 | |
| 1212 SecurityPolicyViolationEventInit violationData; | |
| 1213 gatherSecurityPolicyViolationEventData( | |
| 1214 violationData, document, directiveText, effectiveDirective, blockedURL, | |
| 1215 header, redirectStatus, violationType, contextLine); | |
| 1216 | |
| 1217 // TODO(mkwst): Obviously, we shouldn't hit this check, as extension-loaded | |
| 1218 // resources should be allowed regardless. We apparently do, however, so | |
| 1219 // we should at least stop spamming reporting endpoints. See | |
| 1220 // https://crbug.com/524356 for detail. | |
| 1221 if (!violationData.sourceFile().isEmpty() && | |
| 1222 SchemeRegistry::schemeShouldBypassContentSecurityPolicy( | |
| 1223 KURL(ParsedURLString, violationData.sourceFile()).protocol())) | |
| 1224 return; | |
| 1225 | |
| 1226 // We need to be careful here when deciding what information to send to the | |
| 1227 // report-uri. Currently, we send only the current document's URL and the | |
| 1228 // directive that was violated. The document's URL is safe to send because | |
| 1229 // it's the document itself that's requesting that it be sent. You could | |
| 1230 // make an argument that we shouldn't send HTTPS document URLs to HTTP | |
| 1231 // report-uris (for the same reasons that we supress the Referer in that | |
| 1232 // case), but the Referer is sent implicitly whereas this request is only | |
| 1233 // sent explicitly. As for which directive was violated, that's pretty | |
| 1234 // harmless information. | |
| 1235 | |
| 1236 std::unique_ptr<JSONObject> cspReport = JSONObject::create(); | |
| 1237 cspReport->setString("document-uri", violationData.documentURI()); | |
| 1238 cspReport->setString("referrer", violationData.referrer()); | |
| 1239 cspReport->setString("violated-directive", | |
| 1240 violationData.violatedDirective()); | |
| 1241 cspReport->setString("effective-directive", | |
| 1242 violationData.effectiveDirective()); | |
| 1243 cspReport->setString("original-policy", violationData.originalPolicy()); | |
| 1244 cspReport->setString("blocked-uri", violationData.blockedURI()); | |
| 1245 if (violationData.lineNumber()) | |
| 1246 cspReport->setInteger("line-number", violationData.lineNumber()); | |
| 1247 if (violationData.columnNumber()) | |
| 1248 cspReport->setInteger("column-number", violationData.columnNumber()); | |
| 1249 if (!violationData.sourceFile().isEmpty()) | |
| 1250 cspReport->setString("source-file", violationData.sourceFile()); | |
| 1251 cspReport->setInteger("status-code", violationData.statusCode()); | |
| 1252 | |
| 1253 std::unique_ptr<JSONObject> reportObject = JSONObject::create(); | |
| 1254 reportObject->setObject("csp-report", std::move(cspReport)); | |
| 1255 String stringifiedReport = reportObject->toJSONString(); | |
| 1256 | |
| 1257 if (!shouldSendViolationReport(stringifiedReport)) | |
| 1258 return; | |
| 1259 didSendViolationReport(stringifiedReport); | |
| 1260 | |
| 1261 RefPtr<EncodedFormData> report = | |
| 1262 EncodedFormData::create(stringifiedReport.utf8()); | |
| 1263 | |
| 1264 LocalFrame* frame = document->frame(); | |
| 1265 if (!frame) | |
| 1266 return; | |
| 1267 frame->localDOMWindow()->enqueueDocumentEvent( | |
| 1268 SecurityPolicyViolationEvent::create( | |
| 1269 EventTypeNames::securitypolicyviolation, violationData)); | |
| 1270 | |
| 1271 for (const String& endpoint : reportEndpoints) { | |
| 1272 // If we have a context frame we're dealing with 'frame-ancestors' and we | |
| 1273 // don't have our own execution context. Use the frame's document to | |
| 1274 // complete the endpoint URL, overriding its URL with the blocked | |
| 1275 // document's | |
| 1276 // URL. | |
| 1277 DCHECK(!contextFrame || !m_executionContext); | |
| 1278 DCHECK(!contextFrame || | |
| 1279 equalIgnoringCase(effectiveDirective, FrameAncestors)); | |
| 1280 KURL url = | |
| 1281 contextFrame | |
| 1282 ? frame->document()->completeURLWithOverride(endpoint, blockedURL) | |
| 1283 : completeURL(endpoint); | |
| 1284 PingLoader::sendViolationReport( | |
| 1285 frame, url, report, PingLoader::ContentSecurityPolicyViolationReport); | |
| 1286 } | |
| 1287 } | |
| 1288 | |
| 1289 void ContentSecurityPolicy::reportMixedContent( | |
| 1290 const KURL& mixedURL, | |
| 1291 RedirectStatus redirectStatus) { | |
| 1292 for (const auto& policy : m_policies) | |
| 1293 policy->reportMixedContent(mixedURL, redirectStatus); | |
| 1294 } | |
| 1295 | |
| 1296 void ContentSecurityPolicy::reportInvalidReferrer( | |
| 1297 const String& invalidValue) { | |
| 1298 logToConsole( | |
| 1299 "The 'referrer' Content Security Policy directive has the invalid " | |
| 1300 "value " | |
| 1301 "\"" + | |
| 1302 invalidValue + | |
| 1303 "\". Valid values are \"no-referrer\", \"no-referrer-when-downgrade\", " | |
| 1304 "\"origin\", \"origin-when-cross-origin\", and \"unsafe-url\"."); | |
| 1305 } | |
| 1306 | |
| 1307 void ContentSecurityPolicy::reportReportOnlyInMeta(const String& header) { | |
| 1308 logToConsole( | |
| 1309 "The report-only Content Security Policy '" + header + | |
| 1310 "' was delivered via a <meta> element, which is disallowed. The " | |
| 1311 "policy has been ignored."); | |
| 1312 } | |
| 1313 | |
| 1314 void ContentSecurityPolicy::reportMetaOutsideHead(const String& header) { | |
| 1315 logToConsole("The Content Security Policy '" + header + | |
| 1316 "' was delivered via a <meta> element outside the document's " | |
| 1317 "<head>, which is disallowed. The policy has been ignored."); | |
| 1318 } | |
| 1319 | |
| 1320 void ContentSecurityPolicy::reportValueForEmptyDirective( | |
| 1321 const String& name, | |
| 1322 const String& value) { | |
| 1323 logToConsole("The Content Security Policy directive '" + name + | |
| 1324 "' should be empty, but was delivered with a value of '" + | |
| 1325 value + | |
| 1326 "'. The directive has been applied, and the value ignored."); | |
| 1327 } | |
| 1328 | |
| 1329 void ContentSecurityPolicy::reportInvalidInReportOnly(const String& name) { | |
| 1330 logToConsole("The Content Security Policy directive '" + name + | |
| 1331 "' is ignored when delivered in a report-only policy."); | |
| 1332 } | |
| 1333 | |
| 1334 void ContentSecurityPolicy::reportInvalidDirectiveInMeta( | |
| 1335 const String& directive) { | |
| 1336 logToConsole( | |
| 1337 "Content Security Policies delivered via a <meta> element may not " | |
| 1338 "contain the " + | |
| 1339 directive + " directive."); | |
| 1340 } | |
| 1341 | |
| 1342 void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) { | |
| 1343 DEFINE_STATIC_LOCAL(String, allow, ("allow")); | |
| 1344 DEFINE_STATIC_LOCAL(String, options, ("options")); | |
| 1345 DEFINE_STATIC_LOCAL(String, policyURI, ("policy-uri")); | |
| 1346 DEFINE_STATIC_LOCAL(String, allowMessage, | |
| 1347 ("The 'allow' directive has been replaced with " | |
| 1348 "'default-src'. Please use " | |
| 1349 "that directive instead, as 'allow' has no effect.")); | |
| 1350 DEFINE_STATIC_LOCAL( | |
| 1351 String, optionsMessage, | |
| 1352 ("The 'options' directive has been replaced with 'unsafe-inline' and " | |
| 1353 "'unsafe-eval' source expressions for the 'script-src' and " | |
| 1354 "'style-src' " | |
| 1355 "directives. Please use those directives instead, as 'options' has no " | |
| 1356 "effect.")); | |
| 1357 DEFINE_STATIC_LOCAL(String, policyURIMessage, | |
| 1358 ("The 'policy-uri' directive has been removed from the " | |
| 1359 "specification. Please specify a complete policy via " | |
| 1360 "the Content-Security-Policy header.")); | |
| 1361 | |
| 1362 String message = | |
| 1363 "Unrecognized Content-Security-Policy directive '" + name + "'.\n"; | |
| 1364 MessageLevel level = ErrorMessageLevel; | |
| 1365 if (equalIgnoringCase(name, allow)) { | |
| 1366 message = allowMessage; | |
| 1367 } else if (equalIgnoringCase(name, options)) { | |
| 1368 message = optionsMessage; | |
| 1369 } else if (equalIgnoringCase(name, policyURI)) { | |
| 1370 message = policyURIMessage; | |
| 1371 } else if (isDirectiveName(name)) { | |
| 1372 message = "The Content-Security-Policy directive '" + name + | |
| 1373 "' is implemented behind a flag which is currently disabled.\n"; | |
| 1374 level = InfoMessageLevel; | |
| 1375 } | |
| 1376 | |
| 1377 logToConsole(message, level); | |
| 1378 } | |
| 1379 | |
| 1380 void ContentSecurityPolicy::reportDirectiveAsSourceExpression( | |
| 1381 const String& directiveName, | |
| 1382 const String& sourceExpression) { | |
| 1383 String message = "The Content Security Policy directive '" + directiveName + | |
| 1384 "' contains '" + sourceExpression + | |
| 1385 "' as a source expression. Did you mean '" + | |
| 1386 directiveName + " ...; " + sourceExpression + | |
| 1387 "...' (note the semicolon)?"; | |
| 1388 logToConsole(message); | |
| 1389 } | |
| 1390 | |
| 1391 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) { | |
| 1392 String message = "Ignoring duplicate Content-Security-Policy directive '" + | |
| 1393 name + "'.\n"; | |
| 1394 logToConsole(message); | |
| 1395 } | |
| 1396 | |
| 1397 void ContentSecurityPolicy::reportInvalidPluginTypes( | |
| 1398 const String& pluginType) { | |
| 1399 String message; | |
| 1400 if (pluginType.isNull()) { | |
| 1401 message = | |
| 1402 "'plugin-types' Content Security Policy directive is empty; all " | |
| 1403 "plugins will be blocked.\n"; | |
| 1404 } else if (pluginType == "'none'") { | |
| 1405 message = | |
| 1406 "Invalid plugin type in 'plugin-types' Content Security Policy " | |
| 1407 "directive: '" + | |
| 1408 pluginType + | |
| 1409 "'. Did you mean to set the object-src directive to 'none'?\n"; | |
| 1410 } else { | |
| 1411 message = | |
| 1412 "Invalid plugin type in 'plugin-types' Content Security Policy " | |
| 1413 "directive: '" + | |
| 1414 pluginType + "'.\n"; | |
| 1415 } | |
| 1416 logToConsole(message); | |
| 1417 } | |
| 1418 | |
| 1419 void ContentSecurityPolicy::reportInvalidSandboxFlags( | |
| 1420 const String& invalidFlags) { | |
| 1421 logToConsole( | |
| 1422 "Error while parsing the 'sandbox' Content Security Policy " | |
| 1423 "directive: " + | |
| 1424 invalidFlags); | |
| 1425 } | |
| 1426 | |
| 1427 void ContentSecurityPolicy::reportInvalidReflectedXSS( | |
| 1428 const String& invalidValue) { | |
| 1429 logToConsole( | |
| 1430 "The 'reflected-xss' Content Security Policy directive has the invalid " | |
| 1431 "value \"" + | |
| 1432 invalidValue + | |
| 1433 "\". Valid values are \"allow\", \"filter\", and \"block\"."); | |
| 1434 } | |
| 1435 | |
| 1436 void ContentSecurityPolicy::reportInvalidRequireSRIForTokens( | |
| 1437 const String& invalidTokens) { | |
| 1438 logToConsole( | |
| 1439 "Error while parsing the 'require-sri-for' Content Security Policy " | |
| 1440 "directive: " + | |
| 1441 invalidTokens); | |
| 1442 } | |
| 1443 | |
| 1444 void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter( | |
| 1445 const String& directiveName, | |
| 1446 const String& value) { | |
| 1447 String message = | |
| 1448 "The value for Content Security Policy directive '" + directiveName + | |
| 1449 "' contains an invalid character: '" + value + | |
| 1450 "'. Non-whitespace characters outside ASCII 0x21-0x7E must " | |
| 1451 "be percent-encoded, as described in RFC 3986, section 2.1: " | |
| 1452 "http://tools.ietf.org/html/rfc3986#section-2.1."; | |
| 1453 logToConsole(message); | |
| 1454 } | |
| 1455 | |
| 1456 void ContentSecurityPolicy::reportInvalidPathCharacter( | |
| 1457 const String& directiveName, | |
| 1458 const String& value, | |
| 1459 const char invalidChar) { | |
| 1460 DCHECK(invalidChar == '#' || invalidChar == '?'); | |
| 1461 | |
| 1462 String ignoring = | |
| 1463 "The fragment identifier, including the '#', will be ignored."; | |
| 1464 if (invalidChar == '?') | |
| 1465 ignoring = "The query component, including the '?', will be ignored."; | |
| 1466 String message = "The source list for Content Security Policy directive '" + | |
| 1467 directiveName + | |
| 1468 "' contains a source with an invalid path: '" + value + | |
| 1469 "'. " + ignoring; | |
| 1470 logToConsole(message); | |
| 1471 } | |
| 1472 | |
| 1473 void ContentSecurityPolicy::reportInvalidSourceExpression( | |
| 1474 const String& directiveName, | |
| 1475 const String& source) { | |
| 1476 String message = "The source list for Content Security Policy directive '" + | |
| 1477 directiveName + "' contains an invalid source: '" + | |
| 1478 source + "'. It will be ignored."; | |
| 1479 if (equalIgnoringCase(source, "'none'")) { | |
| 1480 message = message + | |
| 1481 " Note that 'none' has no effect unless it is the only " | |
| 1482 "expression in the source list."; | |
| 1483 } | |
| 1484 logToConsole(message); | |
| 1485 } | |
| 1486 | |
| 1487 void ContentSecurityPolicy::reportMissingReportURI(const String& policy) { | |
| 1488 logToConsole("The Content Security Policy '" + policy + | |
| 1489 "' was delivered in report-only mode, but does not specify a " | |
| 1490 "'report-uri'; the policy will have no effect. Please either " | |
| 1491 "add a 'report-uri' directive, or deliver the policy via the " | |
| 1492 "'Content-Security-Policy' header."); | |
| 1493 } | |
| 1494 | |
| 1495 void ContentSecurityPolicy::logToConsole(const String& message, | |
| 1496 MessageLevel level) { | |
| 1497 logToConsole(ConsoleMessage::create(SecurityMessageSource, level, message)); | |
| 1498 } | |
| 1499 | |
| 1500 void ContentSecurityPolicy::logToConsole(ConsoleMessage* consoleMessage, | |
| 1501 LocalFrame* frame) { | |
| 1502 if (frame) | |
| 1503 frame->document()->addConsoleMessage(consoleMessage); | |
| 1504 else if (m_executionContext) | |
| 1505 m_executionContext->addConsoleMessage(consoleMessage); | |
| 1506 else | |
| 1507 m_consoleMessages.append(consoleMessage); | |
| 1508 } | |
| 1509 | |
| 1510 void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector( | |
| 1511 const String& directiveText) const { | |
| 1512 InspectorInstrumentation::scriptExecutionBlockedByCSP(m_executionContext, | |
| 1513 directiveText); | |
| 1514 } | |
| 1515 | |
| 1516 bool ContentSecurityPolicy::experimentalFeaturesEnabled() const { | |
| 1517 return RuntimeEnabledFeatures:: | |
| 1518 experimentalContentSecurityPolicyFeaturesEnabled(); | |
| 1519 } | |
| 1520 | |
| 1521 bool ContentSecurityPolicy::shouldSendCSPHeader(Resource::Type type) const { | |
| 1522 for (const auto& policy : m_policies) { | |
| 1523 if (policy->shouldSendCSPHeader(type)) | |
| 1524 return true; | |
| 1525 } | |
| 794 return false; | 1526 return false; |
| 795 | 1527 } |
| 796 switch (context) { | 1528 |
| 797 case WebURLRequest::RequestContextAudio: | 1529 bool ContentSecurityPolicy::urlMatchesSelf(const KURL& url) const { |
| 798 case WebURLRequest::RequestContextTrack: | 1530 return m_selfSource->matches(url, RedirectStatus::NoRedirect); |
| 799 case WebURLRequest::RequestContextVideo: | 1531 } |
| 800 return allowMediaFromSource(url, redirectStatus, reportingStatus); | 1532 |
| 801 case WebURLRequest::RequestContextBeacon: | 1533 bool ContentSecurityPolicy::protocolMatchesSelf(const KURL& url) const { |
| 802 case WebURLRequest::RequestContextEventSource: | 1534 if (equalIgnoringCase("http", m_selfProtocol)) |
| 803 case WebURLRequest::RequestContextFetch: | 1535 return url.protocolIsInHTTPFamily(); |
| 804 case WebURLRequest::RequestContextXMLHttpRequest: | 1536 return equalIgnoringCase(url.protocol(), m_selfProtocol); |
| 805 return allowConnectToSource(url, redirectStatus, reportingStatus); | 1537 } |
| 806 case WebURLRequest::RequestContextEmbed: | 1538 |
| 807 case WebURLRequest::RequestContextObject: | 1539 bool ContentSecurityPolicy::selfMatchesInnerURL() const { |
| 808 return allowObjectFromSource(url, redirectStatus, reportingStatus); | 1540 // Due to backwards-compatibility concerns, we allow 'self' to match blob |
| 809 case WebURLRequest::RequestContextFavicon: | 1541 // and |
| 810 case WebURLRequest::RequestContextImage: | 1542 // filesystem URLs if we're in a context that bypasses Content Security |
| 811 case WebURLRequest::RequestContextImageSet: | 1543 // Policy |
| 812 return allowImageFromSource(url, redirectStatus, reportingStatus); | 1544 // in the main world. |
| 813 case WebURLRequest::RequestContextFont: | 1545 // |
| 814 return allowFontFromSource(url, redirectStatus, reportingStatus); | 1546 // TODO(mkwst): Revisit this once embedders have an opportunity to update |
| 815 case WebURLRequest::RequestContextForm: | 1547 // their extension models. |
| 816 return allowFormAction(url, redirectStatus, reportingStatus); | 1548 return m_executionContext && |
| 817 case WebURLRequest::RequestContextFrame: | 1549 SchemeRegistry::schemeShouldBypassContentSecurityPolicy( |
| 818 case WebURLRequest::RequestContextIframe: | 1550 m_executionContext->getSecurityOrigin()->protocol()); |
| 819 return allowChildFrameFromSource(url, redirectStatus, reportingStatus); | 1551 } |
| 820 case WebURLRequest::RequestContextImport: | 1552 |
| 821 case WebURLRequest::RequestContextScript: | 1553 bool ContentSecurityPolicy::shouldBypassMainWorld( |
| 822 return allowScriptFromSource(url, nonce, parserDisposition, | 1554 const ExecutionContext* context) { |
| 823 redirectStatus, reportingStatus); | 1555 if (context && context->isDocument()) { |
| 824 case WebURLRequest::RequestContextXSLT: | 1556 const Document* document = toDocument(context); |
| 825 return allowScriptFromSource(url, nonce, parserDisposition, | 1557 if (document->frame()) |
| 826 redirectStatus, reportingStatus); | 1558 return document->frame()->script().shouldBypassMainWorldCSP(); |
| 827 case WebURLRequest::RequestContextManifest: | 1559 } |
| 828 return allowManifestFromSource(url, redirectStatus, reportingStatus); | 1560 return false; |
| 829 case WebURLRequest::RequestContextServiceWorker: | 1561 } |
| 830 case WebURLRequest::RequestContextSharedWorker: | 1562 |
| 831 case WebURLRequest::RequestContextWorker: | 1563 bool ContentSecurityPolicy::shouldSendViolationReport( |
| 832 return allowWorkerContextFromSource(url, redirectStatus, reportingStatus); | 1564 const String& report) const { |
| 833 case WebURLRequest::RequestContextStyle: | 1565 // Collisions have no security impact, so we can save space by storing only |
| 834 return allowStyleFromSource(url, nonce, redirectStatus, reportingStatus); | 1566 // the string's hash rather than the whole report. |
| 835 case WebURLRequest::RequestContextCSPReport: | 1567 return !m_violationReportsSent.contains(report.impl()->hash()); |
| 836 case WebURLRequest::RequestContextDownload: | 1568 } |
| 837 case WebURLRequest::RequestContextHyperlink: | 1569 |
| 838 case WebURLRequest::RequestContextInternal: | 1570 void ContentSecurityPolicy::didSendViolationReport(const String& report) { |
| 839 case WebURLRequest::RequestContextLocation: | 1571 m_violationReportsSent.add(report.impl()->hash()); |
| 840 case WebURLRequest::RequestContextPing: | 1572 } |
| 841 case WebURLRequest::RequestContextPlugin: | |
| 842 case WebURLRequest::RequestContextPrefetch: | |
| 843 case WebURLRequest::RequestContextSubresource: | |
| 844 case WebURLRequest::RequestContextUnspecified: | |
| 845 return true; | |
| 846 } | |
| 847 ASSERT_NOT_REACHED(); | |
| 848 return true; | |
| 849 } | |
| 850 | |
| 851 void ContentSecurityPolicy::usesScriptHashAlgorithms(uint8_t algorithms) { | |
| 852 m_scriptHashAlgorithmsUsed |= algorithms; | |
| 853 } | |
| 854 | |
| 855 void ContentSecurityPolicy::usesStyleHashAlgorithms(uint8_t algorithms) { | |
| 856 m_styleHashAlgorithmsUsed |= algorithms; | |
| 857 } | |
| 858 | |
| 859 bool ContentSecurityPolicy::allowObjectFromSource( | |
| 860 const KURL& url, | |
| 861 RedirectStatus redirectStatus, | |
| 862 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 863 return isAllowedByAllWithURL<&CSPDirectiveList::allowObjectFromSource>( | |
| 864 m_policies, url, redirectStatus, reportingStatus); | |
| 865 } | |
| 866 | |
| 867 bool ContentSecurityPolicy::allowChildFrameFromSource( | |
| 868 const KURL& url, | |
| 869 RedirectStatus redirectStatus, | |
| 870 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 871 return isAllowedByAllWithURL<&CSPDirectiveList::allowChildFrameFromSource>( | |
| 872 m_policies, url, redirectStatus, reportingStatus); | |
| 873 } | |
| 874 | |
| 875 bool ContentSecurityPolicy::allowImageFromSource( | |
| 876 const KURL& url, | |
| 877 RedirectStatus redirectStatus, | |
| 878 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 879 if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy( | |
| 880 url.protocol(), SchemeRegistry::PolicyAreaImage)) | |
| 881 return true; | |
| 882 return isAllowedByAllWithURL<&CSPDirectiveList::allowImageFromSource>( | |
| 883 m_policies, url, redirectStatus, reportingStatus); | |
| 884 } | |
| 885 | |
| 886 bool ContentSecurityPolicy::allowStyleFromSource( | |
| 887 const KURL& url, | |
| 888 const String& nonce, | |
| 889 RedirectStatus redirectStatus, | |
| 890 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 891 if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy( | |
| 892 url.protocol(), SchemeRegistry::PolicyAreaStyle)) | |
| 893 return true; | |
| 894 return isAllowedByAllWithURLWithNonce< | |
| 895 &CSPDirectiveList::allowStyleFromSource>(m_policies, url, nonce, | |
| 896 redirectStatus, reportingStatus); | |
| 897 } | |
| 898 | |
| 899 bool ContentSecurityPolicy::allowFontFromSource( | |
| 900 const KURL& url, | |
| 901 RedirectStatus redirectStatus, | |
| 902 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 903 return isAllowedByAllWithURL<&CSPDirectiveList::allowFontFromSource>( | |
| 904 m_policies, url, redirectStatus, reportingStatus); | |
| 905 } | |
| 906 | |
| 907 bool ContentSecurityPolicy::allowMediaFromSource( | |
| 908 const KURL& url, | |
| 909 RedirectStatus redirectStatus, | |
| 910 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 911 return isAllowedByAllWithURL<&CSPDirectiveList::allowMediaFromSource>( | |
| 912 m_policies, url, redirectStatus, reportingStatus); | |
| 913 } | |
| 914 | |
| 915 bool ContentSecurityPolicy::allowConnectToSource( | |
| 916 const KURL& url, | |
| 917 RedirectStatus redirectStatus, | |
| 918 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 919 return isAllowedByAllWithURL<&CSPDirectiveList::allowConnectToSource>( | |
| 920 m_policies, url, redirectStatus, reportingStatus); | |
| 921 } | |
| 922 | |
| 923 bool ContentSecurityPolicy::allowFormAction( | |
| 924 const KURL& url, | |
| 925 RedirectStatus redirectStatus, | |
| 926 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 927 return isAllowedByAllWithURL<&CSPDirectiveList::allowFormAction>( | |
| 928 m_policies, url, redirectStatus, reportingStatus); | |
| 929 } | |
| 930 | |
| 931 bool ContentSecurityPolicy::allowBaseURI( | |
| 932 const KURL& url, | |
| 933 RedirectStatus redirectStatus, | |
| 934 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 935 return isAllowedByAllWithURL<&CSPDirectiveList::allowBaseURI>( | |
| 936 m_policies, url, redirectStatus, reportingStatus); | |
| 937 } | |
| 938 | |
| 939 bool ContentSecurityPolicy::allowWorkerContextFromSource( | |
| 940 const KURL& url, | |
| 941 RedirectStatus redirectStatus, | |
| 942 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 943 // CSP 1.1 moves workers from 'script-src' to the new 'child-src'. Measure the | |
| 944 // impact of this backwards-incompatible change. | |
| 945 if (Document* document = this->document()) { | |
| 946 UseCounter::count(*document, UseCounter::WorkerSubjectToCSP); | |
| 947 if (isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>( | |
| 948 m_policies, url, redirectStatus, SuppressReport) && | |
| 949 !isAllowedByAllWithURLNonceAndParser< | |
| 950 &CSPDirectiveList::allowScriptFromSource>( | |
| 951 m_policies, url, AtomicString(), NotParserInserted, redirectStatus, | |
| 952 SuppressReport)) { | |
| 953 UseCounter::count(*document, | |
| 954 UseCounter::WorkerAllowedByChildBlockedByScript); | |
| 955 } | |
| 956 } | |
| 957 | |
| 958 return isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>( | |
| 959 m_policies, url, redirectStatus, reportingStatus); | |
| 960 } | |
| 961 | |
| 962 bool ContentSecurityPolicy::allowManifestFromSource( | |
| 963 const KURL& url, | |
| 964 RedirectStatus redirectStatus, | |
| 965 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 966 return isAllowedByAllWithURL<&CSPDirectiveList::allowManifestFromSource>( | |
| 967 m_policies, url, redirectStatus, reportingStatus); | |
| 968 } | |
| 969 | |
| 970 bool ContentSecurityPolicy::allowAncestors( | |
| 971 LocalFrame* frame, | |
| 972 const KURL& url, | |
| 973 ContentSecurityPolicy::ReportingStatus reportingStatus) const { | |
| 974 return isAllowedByAllWithFrame<&CSPDirectiveList::allowAncestors>( | |
| 975 m_policies, frame, url, reportingStatus); | |
| 976 } | |
| 977 | |
| 978 bool ContentSecurityPolicy::isFrameAncestorsEnforced() const { | |
| 979 for (const auto& policy : m_policies) { | |
| 980 if (policy->isFrameAncestorsEnforced()) | |
| 981 return true; | |
| 982 } | |
| 983 return false; | |
| 984 } | |
| 985 | |
| 986 bool ContentSecurityPolicy::isActive() const { | |
| 987 return !m_policies.isEmpty(); | |
| 988 } | |
| 989 | |
| 990 ReflectedXSSDisposition ContentSecurityPolicy::getReflectedXSSDisposition() | |
| 991 const { | |
| 992 ReflectedXSSDisposition disposition = ReflectedXSSUnset; | |
| 993 for (const auto& policy : m_policies) { | |
| 994 if (policy->getReflectedXSSDisposition() > disposition) | |
| 995 disposition = std::max(disposition, policy->getReflectedXSSDisposition()); | |
| 996 } | |
| 997 return disposition; | |
| 998 } | |
| 999 | |
| 1000 bool ContentSecurityPolicy::didSetReferrerPolicy() const { | |
| 1001 for (const auto& policy : m_policies) { | |
| 1002 if (policy->didSetReferrerPolicy()) | |
| 1003 return true; | |
| 1004 } | |
| 1005 return false; | |
| 1006 } | |
| 1007 | |
| 1008 const KURL ContentSecurityPolicy::url() const { | |
| 1009 return m_executionContext->contextURL(); | |
| 1010 } | |
| 1011 | |
| 1012 KURL ContentSecurityPolicy::completeURL(const String& url) const { | |
| 1013 return m_executionContext->contextCompleteURL(url); | |
| 1014 } | |
| 1015 | |
| 1016 void ContentSecurityPolicy::enforceSandboxFlags(SandboxFlags mask) { | |
| 1017 m_sandboxMask |= mask; | |
| 1018 } | |
| 1019 | |
| 1020 void ContentSecurityPolicy::treatAsPublicAddress() { | |
| 1021 if (!RuntimeEnabledFeatures::corsRFC1918Enabled()) | |
| 1022 return; | |
| 1023 m_treatAsPublicAddress = true; | |
| 1024 } | |
| 1025 | |
| 1026 void ContentSecurityPolicy::enforceStrictMixedContentChecking() { | |
| 1027 m_insecureRequestPolicy |= kBlockAllMixedContent; | |
| 1028 } | |
| 1029 | |
| 1030 void ContentSecurityPolicy::upgradeInsecureRequests() { | |
| 1031 m_insecureRequestPolicy |= kUpgradeInsecureRequests; | |
| 1032 } | |
| 1033 | |
| 1034 static String stripURLForUseInReport(Document* document, | |
| 1035 const KURL& url, | |
| 1036 RedirectStatus redirectStatus, | |
| 1037 const String& effectiveDirective) { | |
| 1038 if (!url.isValid()) | |
| 1039 return String(); | |
| 1040 if (!url.isHierarchical() || url.protocolIs("file")) | |
| 1041 return url.protocol(); | |
| 1042 | |
| 1043 // Until we're more careful about the way we deal with navigations in frames | |
| 1044 // (and, by extension, in plugin documents), strip cross-origin 'frame-src' | |
| 1045 // and 'object-src' violations down to an origin. https://crbug.com/633306 | |
| 1046 bool canSafelyExposeURL = | |
| 1047 document->getSecurityOrigin()->canRequest(url) || | |
| 1048 (redirectStatus == RedirectStatus::NoRedirect && | |
| 1049 !equalIgnoringCase(effectiveDirective, | |
| 1050 ContentSecurityPolicy::FrameSrc) && | |
| 1051 !equalIgnoringCase(effectiveDirective, | |
| 1052 ContentSecurityPolicy::ObjectSrc)); | |
| 1053 | |
| 1054 if (canSafelyExposeURL) { | |
| 1055 // 'KURL::strippedForUseAsReferrer()' dumps 'String()' for non-webby URLs. | |
| 1056 // It's better for developers if we return the origin of those URLs rather | |
| 1057 // than nothing. | |
| 1058 if (url.protocolIsInHTTPFamily()) | |
| 1059 return url.strippedForUseAsReferrer(); | |
| 1060 } | |
| 1061 return SecurityOrigin::create(url)->toString(); | |
| 1062 } | |
| 1063 | |
| 1064 static void gatherSecurityPolicyViolationEventData( | |
| 1065 SecurityPolicyViolationEventInit& init, | |
| 1066 Document* document, | |
| 1067 const String& directiveText, | |
| 1068 const String& effectiveDirective, | |
| 1069 const KURL& blockedURL, | |
| 1070 const String& header, | |
| 1071 RedirectStatus redirectStatus, | |
| 1072 ContentSecurityPolicy::ViolationType violationType, | |
| 1073 int contextLine) { | |
| 1074 if (equalIgnoringCase(effectiveDirective, | |
| 1075 ContentSecurityPolicy::FrameAncestors)) { | |
| 1076 // If this load was blocked via 'frame-ancestors', then the URL of | |
| 1077 // |document| has not yet been initialized. In this case, we'll set both | |
| 1078 // 'documentURI' and 'blockedURI' to the blocked document's URL. | |
| 1079 init.setDocumentURI(blockedURL.getString()); | |
| 1080 init.setBlockedURI(blockedURL.getString()); | |
| 1081 } else { | |
| 1082 init.setDocumentURI(document->url().getString()); | |
| 1083 switch (violationType) { | |
| 1084 case ContentSecurityPolicy::InlineViolation: | |
| 1085 init.setBlockedURI("inline"); | |
| 1086 break; | |
| 1087 case ContentSecurityPolicy::EvalViolation: | |
| 1088 init.setBlockedURI("eval"); | |
| 1089 break; | |
| 1090 case ContentSecurityPolicy::URLViolation: | |
| 1091 init.setBlockedURI(stripURLForUseInReport( | |
| 1092 document, blockedURL, redirectStatus, effectiveDirective)); | |
| 1093 break; | |
| 1094 } | |
| 1095 } | |
| 1096 init.setReferrer(document->referrer()); | |
| 1097 init.setViolatedDirective(directiveText); | |
| 1098 init.setEffectiveDirective(effectiveDirective); | |
| 1099 init.setOriginalPolicy(header); | |
| 1100 init.setSourceFile(String()); | |
| 1101 init.setLineNumber(contextLine); | |
| 1102 init.setColumnNumber(0); | |
| 1103 init.setStatusCode(0); | |
| 1104 | |
| 1105 if (!SecurityOrigin::isSecure(document->url()) && document->loader()) | |
| 1106 init.setStatusCode(document->loader()->response().httpStatusCode()); | |
| 1107 | |
| 1108 std::unique_ptr<SourceLocation> location = SourceLocation::capture(document); | |
| 1109 if (location->lineNumber()) { | |
| 1110 KURL source = KURL(ParsedURLString, location->url()); | |
| 1111 init.setSourceFile(stripURLForUseInReport(document, source, redirectStatus, | |
| 1112 effectiveDirective)); | |
| 1113 init.setLineNumber(location->lineNumber()); | |
| 1114 init.setColumnNumber(location->columnNumber()); | |
| 1115 } | |
| 1116 } | |
| 1117 | |
| 1118 void ContentSecurityPolicy::reportViolation( | |
| 1119 const String& directiveText, | |
| 1120 const String& effectiveDirective, | |
| 1121 const String& consoleMessage, | |
| 1122 const KURL& blockedURL, | |
| 1123 const Vector<String>& reportEndpoints, | |
| 1124 const String& header, | |
| 1125 ViolationType violationType, | |
| 1126 LocalFrame* contextFrame, | |
| 1127 RedirectStatus redirectStatus, | |
| 1128 int contextLine) { | |
| 1129 ASSERT(violationType == URLViolation || blockedURL.isEmpty()); | |
| 1130 | |
| 1131 // TODO(lukasza): Support sending reports from OOPIFs - | |
| 1132 // https://crbug.com/611232 (or move CSP child-src and frame-src checks to the | |
| 1133 // browser process - see https://crbug.com/376522). | |
| 1134 if (!m_executionContext && !contextFrame) { | |
| 1135 DCHECK(equalIgnoringCase(effectiveDirective, | |
| 1136 ContentSecurityPolicy::ChildSrc) || | |
| 1137 equalIgnoringCase(effectiveDirective, | |
| 1138 ContentSecurityPolicy::FrameSrc) || | |
| 1139 equalIgnoringCase(effectiveDirective, | |
| 1140 ContentSecurityPolicy::PluginTypes)); | |
| 1141 return; | |
| 1142 } | |
| 1143 | |
| 1144 ASSERT((m_executionContext && !contextFrame) || | |
| 1145 (equalIgnoringCase(effectiveDirective, | |
| 1146 ContentSecurityPolicy::FrameAncestors) && | |
| 1147 contextFrame)); | |
| 1148 | |
| 1149 // FIXME: Support sending reports from worker. | |
| 1150 Document* document = | |
| 1151 contextFrame ? contextFrame->document() : this->document(); | |
| 1152 if (!document) | |
| 1153 return; | |
| 1154 | |
| 1155 SecurityPolicyViolationEventInit violationData; | |
| 1156 gatherSecurityPolicyViolationEventData( | |
| 1157 violationData, document, directiveText, effectiveDirective, blockedURL, | |
| 1158 header, redirectStatus, violationType, contextLine); | |
| 1159 | |
| 1160 // TODO(mkwst): Obviously, we shouldn't hit this check, as extension-loaded | |
| 1161 // resources should be allowed regardless. We apparently do, however, so | |
| 1162 // we should at least stop spamming reporting endpoints. See | |
| 1163 // https://crbug.com/524356 for detail. | |
| 1164 if (!violationData.sourceFile().isEmpty() && | |
| 1165 SchemeRegistry::schemeShouldBypassContentSecurityPolicy( | |
| 1166 KURL(ParsedURLString, violationData.sourceFile()).protocol())) | |
| 1167 return; | |
| 1168 | |
| 1169 // We need to be careful here when deciding what information to send to the | |
| 1170 // report-uri. Currently, we send only the current document's URL and the | |
| 1171 // directive that was violated. The document's URL is safe to send because | |
| 1172 // it's the document itself that's requesting that it be sent. You could | |
| 1173 // make an argument that we shouldn't send HTTPS document URLs to HTTP | |
| 1174 // report-uris (for the same reasons that we supress the Referer in that | |
| 1175 // case), but the Referer is sent implicitly whereas this request is only | |
| 1176 // sent explicitly. As for which directive was violated, that's pretty | |
| 1177 // harmless information. | |
| 1178 | |
| 1179 std::unique_ptr<JSONObject> cspReport = JSONObject::create(); | |
| 1180 cspReport->setString("document-uri", violationData.documentURI()); | |
| 1181 cspReport->setString("referrer", violationData.referrer()); | |
| 1182 cspReport->setString("violated-directive", violationData.violatedDirective()); | |
| 1183 cspReport->setString("effective-directive", | |
| 1184 violationData.effectiveDirective()); | |
| 1185 cspReport->setString("original-policy", violationData.originalPolicy()); | |
| 1186 cspReport->setString("blocked-uri", violationData.blockedURI()); | |
| 1187 if (violationData.lineNumber()) | |
| 1188 cspReport->setInteger("line-number", violationData.lineNumber()); | |
| 1189 if (violationData.columnNumber()) | |
| 1190 cspReport->setInteger("column-number", violationData.columnNumber()); | |
| 1191 if (!violationData.sourceFile().isEmpty()) | |
| 1192 cspReport->setString("source-file", violationData.sourceFile()); | |
| 1193 cspReport->setInteger("status-code", violationData.statusCode()); | |
| 1194 | |
| 1195 std::unique_ptr<JSONObject> reportObject = JSONObject::create(); | |
| 1196 reportObject->setObject("csp-report", std::move(cspReport)); | |
| 1197 String stringifiedReport = reportObject->toJSONString(); | |
| 1198 | |
| 1199 if (!shouldSendViolationReport(stringifiedReport)) | |
| 1200 return; | |
| 1201 didSendViolationReport(stringifiedReport); | |
| 1202 | |
| 1203 RefPtr<EncodedFormData> report = | |
| 1204 EncodedFormData::create(stringifiedReport.utf8()); | |
| 1205 | |
| 1206 LocalFrame* frame = document->frame(); | |
| 1207 if (!frame) | |
| 1208 return; | |
| 1209 frame->localDOMWindow()->enqueueDocumentEvent( | |
| 1210 SecurityPolicyViolationEvent::create( | |
| 1211 EventTypeNames::securitypolicyviolation, violationData)); | |
| 1212 | |
| 1213 for (const String& endpoint : reportEndpoints) { | |
| 1214 // If we have a context frame we're dealing with 'frame-ancestors' and we | |
| 1215 // don't have our own execution context. Use the frame's document to | |
| 1216 // complete the endpoint URL, overriding its URL with the blocked document's | |
| 1217 // URL. | |
| 1218 DCHECK(!contextFrame || !m_executionContext); | |
| 1219 DCHECK(!contextFrame || | |
| 1220 equalIgnoringCase(effectiveDirective, FrameAncestors)); | |
| 1221 KURL url = | |
| 1222 contextFrame | |
| 1223 ? frame->document()->completeURLWithOverride(endpoint, blockedURL) | |
| 1224 : completeURL(endpoint); | |
| 1225 PingLoader::sendViolationReport( | |
| 1226 frame, url, report, PingLoader::ContentSecurityPolicyViolationReport); | |
| 1227 } | |
| 1228 } | |
| 1229 | |
| 1230 void ContentSecurityPolicy::reportMixedContent(const KURL& mixedURL, | |
| 1231 RedirectStatus redirectStatus) { | |
| 1232 for (const auto& policy : m_policies) | |
| 1233 policy->reportMixedContent(mixedURL, redirectStatus); | |
| 1234 } | |
| 1235 | |
| 1236 void ContentSecurityPolicy::reportInvalidReferrer(const String& invalidValue) { | |
| 1237 logToConsole( | |
| 1238 "The 'referrer' Content Security Policy directive has the invalid value " | |
| 1239 "\"" + | |
| 1240 invalidValue + | |
| 1241 "\". Valid values are \"no-referrer\", \"no-referrer-when-downgrade\", " | |
| 1242 "\"origin\", \"origin-when-cross-origin\", and \"unsafe-url\"."); | |
| 1243 } | |
| 1244 | |
| 1245 void ContentSecurityPolicy::reportReportOnlyInMeta(const String& header) { | |
| 1246 logToConsole("The report-only Content Security Policy '" + header + | |
| 1247 "' was delivered via a <meta> element, which is disallowed. The " | |
| 1248 "policy has been ignored."); | |
| 1249 } | |
| 1250 | |
| 1251 void ContentSecurityPolicy::reportMetaOutsideHead(const String& header) { | |
| 1252 logToConsole("The Content Security Policy '" + header + | |
| 1253 "' was delivered via a <meta> element outside the document's " | |
| 1254 "<head>, which is disallowed. The policy has been ignored."); | |
| 1255 } | |
| 1256 | |
| 1257 void ContentSecurityPolicy::reportValueForEmptyDirective(const String& name, | |
| 1258 const String& value) { | |
| 1259 logToConsole("The Content Security Policy directive '" + name + | |
| 1260 "' should be empty, but was delivered with a value of '" + | |
| 1261 value + | |
| 1262 "'. The directive has been applied, and the value ignored."); | |
| 1263 } | |
| 1264 | |
| 1265 void ContentSecurityPolicy::reportInvalidInReportOnly(const String& name) { | |
| 1266 logToConsole("The Content Security Policy directive '" + name + | |
| 1267 "' is ignored when delivered in a report-only policy."); | |
| 1268 } | |
| 1269 | |
| 1270 void ContentSecurityPolicy::reportInvalidDirectiveInMeta( | |
| 1271 const String& directive) { | |
| 1272 logToConsole( | |
| 1273 "Content Security Policies delivered via a <meta> element may not " | |
| 1274 "contain the " + | |
| 1275 directive + " directive."); | |
| 1276 } | |
| 1277 | |
| 1278 void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) { | |
| 1279 DEFINE_STATIC_LOCAL(String, allow, ("allow")); | |
| 1280 DEFINE_STATIC_LOCAL(String, options, ("options")); | |
| 1281 DEFINE_STATIC_LOCAL(String, policyURI, ("policy-uri")); | |
| 1282 DEFINE_STATIC_LOCAL( | |
| 1283 String, allowMessage, | |
| 1284 ("The 'allow' directive has been replaced with 'default-src'. Please use " | |
| 1285 "that directive instead, as 'allow' has no effect.")); | |
| 1286 DEFINE_STATIC_LOCAL( | |
| 1287 String, optionsMessage, | |
| 1288 ("The 'options' directive has been replaced with 'unsafe-inline' and " | |
| 1289 "'unsafe-eval' source expressions for the 'script-src' and 'style-src' " | |
| 1290 "directives. Please use those directives instead, as 'options' has no " | |
| 1291 "effect.")); | |
| 1292 DEFINE_STATIC_LOCAL(String, policyURIMessage, | |
| 1293 ("The 'policy-uri' directive has been removed from the " | |
| 1294 "specification. Please specify a complete policy via " | |
| 1295 "the Content-Security-Policy header.")); | |
| 1296 | |
| 1297 String message = | |
| 1298 "Unrecognized Content-Security-Policy directive '" + name + "'.\n"; | |
| 1299 MessageLevel level = ErrorMessageLevel; | |
| 1300 if (equalIgnoringCase(name, allow)) { | |
| 1301 message = allowMessage; | |
| 1302 } else if (equalIgnoringCase(name, options)) { | |
| 1303 message = optionsMessage; | |
| 1304 } else if (equalIgnoringCase(name, policyURI)) { | |
| 1305 message = policyURIMessage; | |
| 1306 } else if (isDirectiveName(name)) { | |
| 1307 message = "The Content-Security-Policy directive '" + name + | |
| 1308 "' is implemented behind a flag which is currently disabled.\n"; | |
| 1309 level = InfoMessageLevel; | |
| 1310 } | |
| 1311 | |
| 1312 logToConsole(message, level); | |
| 1313 } | |
| 1314 | |
| 1315 void ContentSecurityPolicy::reportDirectiveAsSourceExpression( | |
| 1316 const String& directiveName, | |
| 1317 const String& sourceExpression) { | |
| 1318 String message = "The Content Security Policy directive '" + directiveName + | |
| 1319 "' contains '" + sourceExpression + | |
| 1320 "' as a source expression. Did you mean '" + directiveName + | |
| 1321 " ...; " + sourceExpression + "...' (note the semicolon)?"; | |
| 1322 logToConsole(message); | |
| 1323 } | |
| 1324 | |
| 1325 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) { | |
| 1326 String message = | |
| 1327 "Ignoring duplicate Content-Security-Policy directive '" + name + "'.\n"; | |
| 1328 logToConsole(message); | |
| 1329 } | |
| 1330 | |
| 1331 void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) { | |
| 1332 String message; | |
| 1333 if (pluginType.isNull()) | |
| 1334 message = | |
| 1335 "'plugin-types' Content Security Policy directive is empty; all " | |
| 1336 "plugins will be blocked.\n"; | |
| 1337 else if (pluginType == "'none'") | |
| 1338 message = | |
| 1339 "Invalid plugin type in 'plugin-types' Content Security Policy " | |
| 1340 "directive: '" + | |
| 1341 pluginType + | |
| 1342 "'. Did you mean to set the object-src directive to 'none'?\n"; | |
| 1343 else | |
| 1344 message = | |
| 1345 "Invalid plugin type in 'plugin-types' Content Security Policy " | |
| 1346 "directive: '" + | |
| 1347 pluginType + "'.\n"; | |
| 1348 logToConsole(message); | |
| 1349 } | |
| 1350 | |
| 1351 void ContentSecurityPolicy::reportInvalidSandboxFlags( | |
| 1352 const String& invalidFlags) { | |
| 1353 logToConsole( | |
| 1354 "Error while parsing the 'sandbox' Content Security Policy directive: " + | |
| 1355 invalidFlags); | |
| 1356 } | |
| 1357 | |
| 1358 void ContentSecurityPolicy::reportInvalidReflectedXSS( | |
| 1359 const String& invalidValue) { | |
| 1360 logToConsole( | |
| 1361 "The 'reflected-xss' Content Security Policy directive has the invalid " | |
| 1362 "value \"" + | |
| 1363 invalidValue + | |
| 1364 "\". Valid values are \"allow\", \"filter\", and \"block\"."); | |
| 1365 } | |
| 1366 | |
| 1367 void ContentSecurityPolicy::reportInvalidRequireSRIForTokens( | |
| 1368 const String& invalidTokens) { | |
| 1369 logToConsole( | |
| 1370 "Error while parsing the 'require-sri-for' Content Security Policy " | |
| 1371 "directive: " + | |
| 1372 invalidTokens); | |
| 1373 } | |
| 1374 | |
| 1375 void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter( | |
| 1376 const String& directiveName, | |
| 1377 const String& value) { | |
| 1378 String message = "The value for Content Security Policy directive '" + | |
| 1379 directiveName + "' contains an invalid character: '" + | |
| 1380 value + | |
| 1381 "'. Non-whitespace characters outside ASCII 0x21-0x7E must " | |
| 1382 "be percent-encoded, as described in RFC 3986, section 2.1: " | |
| 1383 "http://tools.ietf.org/html/rfc3986#section-2.1."; | |
| 1384 logToConsole(message); | |
| 1385 } | |
| 1386 | |
| 1387 void ContentSecurityPolicy::reportInvalidPathCharacter( | |
| 1388 const String& directiveName, | |
| 1389 const String& value, | |
| 1390 const char invalidChar) { | |
| 1391 ASSERT(invalidChar == '#' || invalidChar == '?'); | |
| 1392 | |
| 1393 String ignoring = | |
| 1394 "The fragment identifier, including the '#', will be ignored."; | |
| 1395 if (invalidChar == '?') | |
| 1396 ignoring = "The query component, including the '?', will be ignored."; | |
| 1397 String message = "The source list for Content Security Policy directive '" + | |
| 1398 directiveName + | |
| 1399 "' contains a source with an invalid path: '" + value + | |
| 1400 "'. " + ignoring; | |
| 1401 logToConsole(message); | |
| 1402 } | |
| 1403 | |
| 1404 void ContentSecurityPolicy::reportInvalidSourceExpression( | |
| 1405 const String& directiveName, | |
| 1406 const String& source) { | |
| 1407 String message = "The source list for Content Security Policy directive '" + | |
| 1408 directiveName + "' contains an invalid source: '" + source + | |
| 1409 "'. It will be ignored."; | |
| 1410 if (equalIgnoringCase(source, "'none'")) | |
| 1411 message = message + | |
| 1412 " Note that 'none' has no effect unless it is the only " | |
| 1413 "expression in the source list."; | |
| 1414 logToConsole(message); | |
| 1415 } | |
| 1416 | |
| 1417 void ContentSecurityPolicy::reportMissingReportURI(const String& policy) { | |
| 1418 logToConsole("The Content Security Policy '" + policy + | |
| 1419 "' was delivered in report-only mode, but does not specify a " | |
| 1420 "'report-uri'; the policy will have no effect. Please either " | |
| 1421 "add a 'report-uri' directive, or deliver the policy via the " | |
| 1422 "'Content-Security-Policy' header."); | |
| 1423 } | |
| 1424 | |
| 1425 void ContentSecurityPolicy::logToConsole(const String& message, | |
| 1426 MessageLevel level) { | |
| 1427 logToConsole(ConsoleMessage::create(SecurityMessageSource, level, message)); | |
| 1428 } | |
| 1429 | |
| 1430 void ContentSecurityPolicy::logToConsole(ConsoleMessage* consoleMessage, | |
| 1431 LocalFrame* frame) { | |
| 1432 if (frame) | |
| 1433 frame->document()->addConsoleMessage(consoleMessage); | |
| 1434 else if (m_executionContext) | |
| 1435 m_executionContext->addConsoleMessage(consoleMessage); | |
| 1436 else | |
| 1437 m_consoleMessages.append(consoleMessage); | |
| 1438 } | |
| 1439 | |
| 1440 void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector( | |
| 1441 const String& directiveText) const { | |
| 1442 InspectorInstrumentation::scriptExecutionBlockedByCSP(m_executionContext, | |
| 1443 directiveText); | |
| 1444 } | |
| 1445 | |
| 1446 bool ContentSecurityPolicy::experimentalFeaturesEnabled() const { | |
| 1447 return RuntimeEnabledFeatures:: | |
| 1448 experimentalContentSecurityPolicyFeaturesEnabled(); | |
| 1449 } | |
| 1450 | |
| 1451 bool ContentSecurityPolicy::shouldSendCSPHeader(Resource::Type type) const { | |
| 1452 for (const auto& policy : m_policies) { | |
| 1453 if (policy->shouldSendCSPHeader(type)) | |
| 1454 return true; | |
| 1455 } | |
| 1456 return false; | |
| 1457 } | |
| 1458 | |
| 1459 bool ContentSecurityPolicy::urlMatchesSelf(const KURL& url) const { | |
| 1460 return m_selfSource->matches(url, RedirectStatus::NoRedirect); | |
| 1461 } | |
| 1462 | |
| 1463 bool ContentSecurityPolicy::protocolMatchesSelf(const KURL& url) const { | |
| 1464 if (equalIgnoringCase("http", m_selfProtocol)) | |
| 1465 return url.protocolIsInHTTPFamily(); | |
| 1466 return equalIgnoringCase(url.protocol(), m_selfProtocol); | |
| 1467 } | |
| 1468 | |
| 1469 bool ContentSecurityPolicy::selfMatchesInnerURL() const { | |
| 1470 // Due to backwards-compatibility concerns, we allow 'self' to match blob and | |
| 1471 // filesystem URLs if we're in a context that bypasses Content Security Policy | |
| 1472 // in the main world. | |
| 1473 // | |
| 1474 // TODO(mkwst): Revisit this once embedders have an opportunity to update | |
| 1475 // their extension models. | |
| 1476 return m_executionContext && | |
| 1477 SchemeRegistry::schemeShouldBypassContentSecurityPolicy( | |
| 1478 m_executionContext->getSecurityOrigin()->protocol()); | |
| 1479 } | |
| 1480 | |
| 1481 bool ContentSecurityPolicy::shouldBypassMainWorld( | |
| 1482 const ExecutionContext* context) { | |
| 1483 if (context && context->isDocument()) { | |
| 1484 const Document* document = toDocument(context); | |
| 1485 if (document->frame()) | |
| 1486 return document->frame()->script().shouldBypassMainWorldCSP(); | |
| 1487 } | |
| 1488 return false; | |
| 1489 } | |
| 1490 | |
| 1491 bool ContentSecurityPolicy::shouldSendViolationReport( | |
| 1492 const String& report) const { | |
| 1493 // Collisions have no security impact, so we can save space by storing only | |
| 1494 // the string's hash rather than the whole report. | |
| 1495 return !m_violationReportsSent.contains(report.impl()->hash()); | |
| 1496 } | |
| 1497 | |
| 1498 void ContentSecurityPolicy::didSendViolationReport(const String& report) { | |
| 1499 m_violationReportsSent.add(report.impl()->hash()); | |
| 1500 } | |
| 1501 | 1573 |
| 1502 } // namespace blink | 1574 } // namespace blink |
| OLD | NEW |