OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/frame/csp/CSPDirectiveList.h" | 5 #include "core/frame/csp/CSPDirectiveList.h" |
6 | 6 |
7 #include "core/frame/csp/ContentSecurityPolicy.h" | 7 #include "core/frame/csp/ContentSecurityPolicy.h" |
8 #include "core/frame/csp/SourceListDirective.h" | 8 #include "core/frame/csp/SourceListDirective.h" |
9 #include "platform/loader/fetch/ResourceRequest.h" | 9 #include "platform/loader/fetch/ResourceRequest.h" |
10 #include "platform/network/ContentSecurityPolicyParsers.h" | 10 #include "platform/network/ContentSecurityPolicyParsers.h" |
(...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
400 directiveList = | 400 directiveList = |
401 createList(test.list, ContentSecurityPolicyHeaderTypeEnforce); | 401 createList(test.list, ContentSecurityPolicyHeaderTypeEnforce); |
402 EXPECT_EQ( | 402 EXPECT_EQ( |
403 test.expected, | 403 test.expected, |
404 directiveList->allowRequestWithoutIntegrity( | 404 directiveList->allowRequestWithoutIntegrity( |
405 test.context, resource, ResourceRequest::RedirectStatus::NoRedirect, | 405 test.context, resource, ResourceRequest::RedirectStatus::NoRedirect, |
406 SecurityViolationReportingPolicy::SuppressReporting)); | 406 SecurityViolationReportingPolicy::SuppressReporting)); |
407 } | 407 } |
408 } | 408 } |
409 | 409 |
410 TEST_F(CSPDirectiveListTest, workerSrc) { | 410 TEST_F(CSPDirectiveListTest, WorkerSrc) { |
411 struct TestCase { | 411 struct TestCase { |
412 const char* list; | 412 const char* list; |
413 bool allowed; | 413 bool allowed; |
414 } cases[] = { | 414 } cases[] = { |
415 {"worker-src 'none'", false}, | 415 {"worker-src 'none'", false}, |
416 {"worker-src http://not.example.test", false}, | 416 {"worker-src http://not.example.test", false}, |
417 {"worker-src https://example.test", true}, | 417 {"worker-src https://example.test", true}, |
418 {"default-src *; worker-src 'none'", false}, | 418 {"default-src *; worker-src 'none'", false}, |
419 {"default-src *; worker-src http://not.example.test", false}, | 419 {"default-src *; worker-src http://not.example.test", false}, |
420 {"default-src *; worker-src https://example.test", true}, | 420 {"default-src *; worker-src https://example.test", true}, |
421 {"child-src *; worker-src 'none'", false}, | 421 {"script-src *; worker-src 'none'", false}, |
422 {"child-src *; worker-src http://not.example.test", false}, | 422 {"script-src *; worker-src http://not.example.test", false}, |
423 {"child-src *; worker-src https://example.test", true}, | 423 {"script-src *; worker-src https://example.test", true}, |
424 {"default-src *; child-src *; worker-src 'none'", false}, | 424 {"default-src *; script-src *; worker-src 'none'", false}, |
425 {"default-src *; child-src *; worker-src http://not.example.test", false}, | 425 {"default-src *; script-src *; worker-src http://not.example.test", |
426 {"default-src *; child-src *; worker-src https://example.test", true}, | 426 false}, |
| 427 {"default-src *; script-src *; worker-src https://example.test", true}, |
427 | 428 |
428 // Fallback to child-src. | 429 // Fallback to script-src. |
429 {"child-src 'none'", false}, | 430 {"script-src 'none'", false}, |
430 {"child-src http://not.example.test", false}, | 431 {"script-src http://not.example.test", false}, |
431 {"child-src https://example.test", true}, | 432 {"script-src https://example.test", true}, |
432 {"default-src *; child-src 'none'", false}, | 433 {"default-src *; script-src 'none'", false}, |
433 {"default-src *; child-src http://not.example.test", false}, | 434 {"default-src *; script-src http://not.example.test", false}, |
434 {"default-src *; child-src https://example.test", true}, | 435 {"default-src *; script-src https://example.test", true}, |
435 | 436 |
436 // Fallback to default-src. | 437 // Fallback to default-src. |
437 {"default-src 'none'", false}, | 438 {"default-src 'none'", false}, |
438 {"default-src http://not.example.test", false}, | 439 {"default-src http://not.example.test", false}, |
439 {"default-src https://example.test", true}, | 440 {"default-src https://example.test", true}, |
440 }; | 441 }; |
441 | 442 |
442 for (const auto& test : cases) { | 443 for (const auto& test : cases) { |
443 SCOPED_TRACE(test.list); | 444 SCOPED_TRACE(test.list); |
444 KURL resource = KURL(KURL(), "https://example.test/worker.js"); | 445 KURL resource = KURL(KURL(), "https://example.test/worker.js"); |
445 Member<CSPDirectiveList> directiveList = | 446 Member<CSPDirectiveList> directiveList = |
446 createList(test.list, ContentSecurityPolicyHeaderTypeEnforce); | 447 createList(test.list, ContentSecurityPolicyHeaderTypeEnforce); |
447 EXPECT_EQ(test.allowed, | 448 EXPECT_EQ(test.allowed, |
448 directiveList->allowWorkerFromSource( | 449 directiveList->allowWorkerFromSource( |
449 resource, ResourceRequest::RedirectStatus::NoRedirect, | 450 resource, ResourceRequest::RedirectStatus::NoRedirect, |
450 SecurityViolationReportingPolicy::SuppressReporting)); | 451 SecurityViolationReportingPolicy::SuppressReporting)); |
451 } | 452 } |
452 } | 453 } |
453 | 454 |
| 455 TEST_F(CSPDirectiveListTest, WorkerSrcChildSrcFallback) { |
| 456 // TODO(mkwst): Remove this test once we remove the temporary fallback |
| 457 // behavior. https://crbug.com/662930 |
| 458 struct TestCase { |
| 459 const char* list; |
| 460 bool allowed; |
| 461 } cases[] = { |
| 462 // When 'worker-src' is not present, 'child-src' can allow a worker when |
| 463 // present. |
| 464 {"child-src https://example.test", true}, |
| 465 {"child-src https://not-example.test", true}, |
| 466 {"script-src https://example.test", true}, |
| 467 {"script-src https://not-example.test", false}, |
| 468 {"child-src https://example.test; script-src https://example.test", true}, |
| 469 {"child-src https://example.test; script-src https://not-example.test", |
| 470 true}, |
| 471 {"child-src https://not-example.test; script-src https://example.test", |
| 472 true}, |
| 473 {"child-src https://not-example.test; script-src " |
| 474 "https://not-example.test", |
| 475 false}, |
| 476 |
| 477 // If 'worker-src' is present, 'child-src' will not allow a worker. |
| 478 {"worker-src https://example.test; child-src https://example.test", true}, |
| 479 {"worker-src https://example.test; child-src https://not-example.test", |
| 480 true}, |
| 481 {"worker-src https://not-example.test; child-src https://example.test", |
| 482 false}, |
| 483 {"worker-src https://not-example.test; child-src " |
| 484 "https://not-example.test", |
| 485 false}, |
| 486 }; |
| 487 |
| 488 for (const auto& test : cases) { |
| 489 SCOPED_TRACE(test.list); |
| 490 KURL resource = KURL(KURL(), "https://example.test/worker.js"); |
| 491 Member<CSPDirectiveList> directiveList = |
| 492 createList(test.list, ContentSecurityPolicyHeaderTypeEnforce); |
| 493 EXPECT_EQ(test.allowed, |
| 494 directiveList->allowWorkerFromSource( |
| 495 resource, ResourceRequest::RedirectStatus::NoRedirect, |
| 496 SecurityViolationReportingPolicy::SuppressReporting)); |
| 497 } |
| 498 } |
| 499 |
454 TEST_F(CSPDirectiveListTest, SubsumesBasedOnCSPSourcesOnly) { | 500 TEST_F(CSPDirectiveListTest, SubsumesBasedOnCSPSourcesOnly) { |
455 CSPDirectiveList* A = createList( | 501 CSPDirectiveList* A = createList( |
456 "script-src http://*.one.com; img-src https://one.com " | 502 "script-src http://*.one.com; img-src https://one.com " |
457 "http://two.com/imgs/", | 503 "http://two.com/imgs/", |
458 ContentSecurityPolicyHeaderTypeEnforce); | 504 ContentSecurityPolicyHeaderTypeEnforce); |
459 | 505 |
460 struct TestCase { | 506 struct TestCase { |
461 const std::vector<const char*> policies; | 507 const std::vector<const char*> policies; |
462 bool expected; | 508 bool expected; |
463 bool expectedFirstPolicyOpposite; | 509 bool expectedFirstPolicyOpposite; |
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
710 HeapVector<Member<CSPDirectiveList>> listB; | 756 HeapVector<Member<CSPDirectiveList>> listB; |
711 for (const auto& policyB : test.policiesB) | 757 for (const auto& policyB : test.policiesB) |
712 listB.push_back( | 758 listB.push_back( |
713 createList(policyB, ContentSecurityPolicyHeaderTypeEnforce)); | 759 createList(policyB, ContentSecurityPolicyHeaderTypeEnforce)); |
714 | 760 |
715 EXPECT_EQ(test.expected, A->subsumes(listB)); | 761 EXPECT_EQ(test.expected, A->subsumes(listB)); |
716 } | 762 } |
717 } | 763 } |
718 | 764 |
719 TEST_F(CSPDirectiveListTest, OperativeDirectiveGivenType) { | 765 TEST_F(CSPDirectiveListTest, OperativeDirectiveGivenType) { |
720 enum DefaultBehaviour { Default, NoDefault, ChildAndDefault }; | 766 enum DefaultBehaviour { |
| 767 Default, |
| 768 NoDefault, |
| 769 ChildAndDefault, |
| 770 ScriptAndDefault |
| 771 }; |
721 | 772 |
722 struct TestCase { | 773 struct TestCase { |
723 ContentSecurityPolicy::DirectiveType directive; | 774 ContentSecurityPolicy::DirectiveType directive; |
724 const DefaultBehaviour type; | 775 const DefaultBehaviour type; |
725 } cases[] = { | 776 } cases[] = { |
726 // Directives with default directive. | 777 // Directives with default directive. |
727 {ContentSecurityPolicy::DirectiveType::ChildSrc, Default}, | 778 {ContentSecurityPolicy::DirectiveType::ChildSrc, Default}, |
728 {ContentSecurityPolicy::DirectiveType::ConnectSrc, Default}, | 779 {ContentSecurityPolicy::DirectiveType::ConnectSrc, Default}, |
729 {ContentSecurityPolicy::DirectiveType::FontSrc, Default}, | 780 {ContentSecurityPolicy::DirectiveType::FontSrc, Default}, |
730 {ContentSecurityPolicy::DirectiveType::ImgSrc, Default}, | 781 {ContentSecurityPolicy::DirectiveType::ImgSrc, Default}, |
731 {ContentSecurityPolicy::DirectiveType::ManifestSrc, Default}, | 782 {ContentSecurityPolicy::DirectiveType::ManifestSrc, Default}, |
732 {ContentSecurityPolicy::DirectiveType::MediaSrc, Default}, | 783 {ContentSecurityPolicy::DirectiveType::MediaSrc, Default}, |
733 {ContentSecurityPolicy::DirectiveType::ObjectSrc, Default}, | 784 {ContentSecurityPolicy::DirectiveType::ObjectSrc, Default}, |
734 {ContentSecurityPolicy::DirectiveType::ScriptSrc, Default}, | 785 {ContentSecurityPolicy::DirectiveType::ScriptSrc, Default}, |
735 {ContentSecurityPolicy::DirectiveType::StyleSrc, Default}, | 786 {ContentSecurityPolicy::DirectiveType::StyleSrc, Default}, |
736 // Directives with no default directive. | 787 // Directives with no default directive. |
737 {ContentSecurityPolicy::DirectiveType::BaseURI, NoDefault}, | 788 {ContentSecurityPolicy::DirectiveType::BaseURI, NoDefault}, |
738 {ContentSecurityPolicy::DirectiveType::DefaultSrc, NoDefault}, | 789 {ContentSecurityPolicy::DirectiveType::DefaultSrc, NoDefault}, |
739 {ContentSecurityPolicy::DirectiveType::FrameAncestors, NoDefault}, | 790 {ContentSecurityPolicy::DirectiveType::FrameAncestors, NoDefault}, |
740 {ContentSecurityPolicy::DirectiveType::FormAction, NoDefault}, | 791 {ContentSecurityPolicy::DirectiveType::FormAction, NoDefault}, |
741 // Directive with multiple default directives. | 792 // Directive with multiple default directives. |
742 {ContentSecurityPolicy::DirectiveType::FrameSrc, ChildAndDefault}, | 793 {ContentSecurityPolicy::DirectiveType::FrameSrc, ChildAndDefault}, |
743 {ContentSecurityPolicy::DirectiveType::WorkerSrc, ChildAndDefault}, | 794 {ContentSecurityPolicy::DirectiveType::WorkerSrc, ScriptAndDefault}, |
744 }; | 795 }; |
745 | 796 |
746 // Initial set-up. | 797 // Initial set-up. |
747 std::stringstream allDirectives; | 798 std::stringstream allDirectives; |
748 for (const auto& test : cases) { | 799 for (const auto& test : cases) { |
749 const char* name = ContentSecurityPolicy::getDirectiveName(test.directive); | 800 const char* name = ContentSecurityPolicy::getDirectiveName(test.directive); |
750 allDirectives << name << " http://" << name << ".com; "; | 801 allDirectives << name << " http://" << name << ".com; "; |
751 } | 802 } |
752 CSPDirectiveList* allDirectivesList = createList( | 803 CSPDirectiveList* allDirectivesList = createList( |
753 allDirectives.str().c_str(), ContentSecurityPolicyHeaderTypeEnforce); | 804 allDirectives.str().c_str(), ContentSecurityPolicyHeaderTypeEnforce); |
754 CSPDirectiveList* empty = | 805 CSPDirectiveList* empty = |
755 createList("", ContentSecurityPolicyHeaderTypeEnforce); | 806 createList("", ContentSecurityPolicyHeaderTypeEnforce); |
756 | 807 |
757 for (const auto& test : cases) { | 808 for (const auto& test : cases) { |
758 const char* name = ContentSecurityPolicy::getDirectiveName(test.directive); | 809 const char* name = ContentSecurityPolicy::getDirectiveName(test.directive); |
759 // When CSPDirectiveList is empty, then `null` should be returned for any | 810 // When CSPDirectiveList is empty, then `null` should be returned for any |
760 // type. | 811 // type. |
761 EXPECT_FALSE(empty->operativeDirective(test.directive)); | 812 EXPECT_FALSE(empty->operativeDirective(test.directive)); |
762 | 813 |
763 // When all directives present, then given a type that directive value | 814 // When all directives present, then given a type that directive value |
764 // should be returned. | 815 // should be returned. |
765 HeapVector<Member<CSPSource>> sources = | 816 HeapVector<Member<CSPSource>> sources = |
766 allDirectivesList->operativeDirective(test.directive)->m_list; | 817 allDirectivesList->operativeDirective(test.directive)->m_list; |
767 EXPECT_EQ(sources.size(), 1u); | 818 EXPECT_EQ(sources.size(), 1u); |
768 EXPECT_TRUE(sources[0]->m_host.startsWith(name)); | 819 EXPECT_TRUE(sources[0]->m_host.startsWith(name)); |
769 | 820 |
770 std::stringstream allExceptThis; | 821 std::stringstream allExceptThis; |
771 std::stringstream allExceptChildSrcAndThis; | 822 std::stringstream allExceptChildSrcAndThis; |
| 823 std::stringstream allExceptScriptSrcAndThis; |
772 for (const auto& subtest : cases) { | 824 for (const auto& subtest : cases) { |
773 if (subtest.directive == test.directive) | 825 if (subtest.directive == test.directive) |
774 continue; | 826 continue; |
775 const char* directiveName = | 827 const char* directiveName = |
776 ContentSecurityPolicy::getDirectiveName(subtest.directive); | 828 ContentSecurityPolicy::getDirectiveName(subtest.directive); |
777 allExceptThis << directiveName << " http://" << directiveName << ".com; "; | 829 allExceptThis << directiveName << " http://" << directiveName << ".com; "; |
778 if (subtest.directive != ContentSecurityPolicy::DirectiveType::ChildSrc) { | 830 if (subtest.directive != ContentSecurityPolicy::DirectiveType::ChildSrc) { |
779 allExceptChildSrcAndThis << directiveName << " http://" << directiveName | 831 allExceptChildSrcAndThis << directiveName << " http://" << directiveName |
780 << ".com; "; | 832 << ".com; "; |
781 } | 833 } |
| 834 if (subtest.directive != |
| 835 ContentSecurityPolicy::DirectiveType::ScriptSrc) { |
| 836 allExceptScriptSrcAndThis << directiveName << " http://" |
| 837 << directiveName << ".com; "; |
| 838 } |
782 } | 839 } |
783 CSPDirectiveList* allExceptThisList = createList( | 840 CSPDirectiveList* allExceptThisList = createList( |
784 allExceptThis.str().c_str(), ContentSecurityPolicyHeaderTypeEnforce); | 841 allExceptThis.str().c_str(), ContentSecurityPolicyHeaderTypeEnforce); |
785 CSPDirectiveList* allExceptChildSrcAndThisList = | 842 CSPDirectiveList* allExceptChildSrcAndThisList = |
786 createList(allExceptChildSrcAndThis.str().c_str(), | 843 createList(allExceptChildSrcAndThis.str().c_str(), |
787 ContentSecurityPolicyHeaderTypeEnforce); | 844 ContentSecurityPolicyHeaderTypeEnforce); |
| 845 CSPDirectiveList* allExceptScriptSrcAndThisList = |
| 846 createList(allExceptScriptSrcAndThis.str().c_str(), |
| 847 ContentSecurityPolicyHeaderTypeEnforce); |
788 | 848 |
789 switch (test.type) { | 849 switch (test.type) { |
790 case Default: | 850 case Default: |
791 sources = allExceptThisList->operativeDirective(test.directive)->m_list; | 851 sources = allExceptThisList->operativeDirective(test.directive)->m_list; |
792 EXPECT_EQ(sources.size(), 1u); | 852 EXPECT_EQ(sources.size(), 1u); |
793 EXPECT_EQ(sources[0]->m_host, "default-src.com"); | 853 EXPECT_EQ(sources[0]->m_host, "default-src.com"); |
794 break; | 854 break; |
795 case NoDefault: | 855 case NoDefault: |
796 EXPECT_FALSE(allExceptThisList->operativeDirective(test.directive)); | 856 EXPECT_FALSE(allExceptThisList->operativeDirective(test.directive)); |
797 break; | 857 break; |
798 case ChildAndDefault: | 858 case ChildAndDefault: |
799 sources = allExceptThisList->operativeDirective(test.directive)->m_list; | 859 sources = allExceptThisList->operativeDirective(test.directive)->m_list; |
800 EXPECT_EQ(sources.size(), 1u); | 860 EXPECT_EQ(sources.size(), 1u); |
801 EXPECT_EQ(sources[0]->m_host, "child-src.com"); | 861 EXPECT_EQ(sources[0]->m_host, "child-src.com"); |
802 sources = | 862 sources = |
803 allExceptChildSrcAndThisList->operativeDirective(test.directive) | 863 allExceptChildSrcAndThisList->operativeDirective(test.directive) |
804 ->m_list; | 864 ->m_list; |
805 EXPECT_EQ(sources.size(), 1u); | 865 EXPECT_EQ(sources.size(), 1u); |
806 EXPECT_EQ(sources[0]->m_host, "default-src.com"); | 866 EXPECT_EQ(sources[0]->m_host, "default-src.com"); |
807 break; | 867 break; |
| 868 case ScriptAndDefault: |
| 869 sources = allExceptThisList->operativeDirective(test.directive)->m_list; |
| 870 EXPECT_EQ(sources.size(), 1u); |
| 871 EXPECT_EQ(sources[0]->m_host, "script-src.com"); |
| 872 sources = |
| 873 allExceptScriptSrcAndThisList->operativeDirective(test.directive) |
| 874 ->m_list; |
| 875 EXPECT_EQ(sources.size(), 1u); |
| 876 EXPECT_EQ(sources[0]->m_host, "default-src.com"); |
| 877 break; |
808 } | 878 } |
809 } | 879 } |
810 } | 880 } |
811 | 881 |
812 TEST_F(CSPDirectiveListTest, GetSourceVector) { | 882 TEST_F(CSPDirectiveListTest, GetSourceVector) { |
813 const std::vector<const char*> policies = { | 883 const std::vector<const char*> policies = { |
814 // Policy 1 | 884 // Policy 1 |
815 "default-src https://default-src.com", | 885 "default-src https://default-src.com", |
816 // Policy 2 | 886 // Policy 2 |
817 "child-src http://child-src.com", | 887 "child-src http://child-src.com", |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
952 CSPDirectiveList::getSourceVector(test.directive, policyVector).size(), | 1022 CSPDirectiveList::getSourceVector(test.directive, policyVector).size(), |
953 udpatedTotal); | 1023 udpatedTotal); |
954 EXPECT_EQ(CSPDirectiveList::getSourceVector( | 1024 EXPECT_EQ(CSPDirectiveList::getSourceVector( |
955 ContentSecurityPolicy::DirectiveType::ChildSrc, policyVector) | 1025 ContentSecurityPolicy::DirectiveType::ChildSrc, policyVector) |
956 .size(), | 1026 .size(), |
957 expectedChildSrc); | 1027 expectedChildSrc); |
958 } | 1028 } |
959 } | 1029 } |
960 | 1030 |
961 } // namespace blink | 1031 } // namespace blink |
OLD | NEW |