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 |