| 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 |