| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| |
| #include "base/command_line.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "chrome/browser/policy/policy_test_utils.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "components/policy/core/common/policy_map.h" |
| #include "components/policy/policy_constants.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/test_utils.h" |
| #include "net/base/features.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace policy { |
| |
| namespace { |
| const char kURL[] = "http://example.com"; |
| } // namespace |
| |
| // Test fixture that enables (if param is true) and disables (if param is false) |
| // SameSite-by-default, Cookies-without-SameSite-must-be-Secure, and Schemeful |
| // Same-Site to test the policies that override those features, under both |
| // conditions. |
| class SameSiteCookiesPolicyTest : public PolicyTest, |
| public ::testing::WithParamInterface<bool> { |
| public: |
| SameSiteCookiesPolicyTest() { |
| std::vector<base::Feature> samesite_features = { |
| net::features::kSameSiteByDefaultCookies, |
| net::features::kCookiesWithoutSameSiteMustBeSecure, |
| net::features::kSchemefulSameSite}; |
| if (AreSameSiteFeaturesEnabled()) { |
| feature_list_.InitWithFeatures(samesite_features /* enabled */, {}); |
| } else { |
| feature_list_.InitWithFeatures({}, samesite_features /* disabled */); |
| } |
| } |
| |
| ~SameSiteCookiesPolicyTest() = default; |
| |
| protected: |
| bool AreSameSiteFeaturesEnabled() { return GetParam(); } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(SameSiteCookiesPolicyTest, |
| DefaultLegacyCookieAccessSettingIsAllow) { |
| PolicyMap policies; |
| // Set a policy to allow Legacy access for all cookies. |
| SetPolicy(&policies, key::kLegacySameSiteCookieBehaviorEnabled, |
| base::Value(1)); |
| UpdateProviderPolicy(policies); |
| |
| GURL url(kURL); |
| Profile* profile = browser()->profile(); |
| |
| // No cookies at startup |
| ASSERT_TRUE(content::GetCookies(profile, url).empty()); |
| |
| // Set a cookie from a same-site context. The cookie does not specify |
| // SameSite, so it may default to Lax if the SameSite features are enabled. |
| // Since the context used is same-site, it should always work. |
| EXPECT_TRUE(content::SetCookie(profile, url, "samesite-unspecified=1", |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::SAME_SITE_LAX))); |
| EXPECT_EQ("samesite-unspecified=1", content::GetCookies(profile, url)); |
| |
| // Overwrite the cookie from a cross-site context. Because we have a policy |
| // that allows Legacy access for all domains, this will work even if the |
| // SameSite features are enabled. (It works regardless, if they are disabled.) |
| EXPECT_TRUE(content::SetCookie( |
| profile, url, "samesite-unspecified=2", |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE))); |
| // Cookie has the new value because we were able to successfully overwrite it. |
| EXPECT_EQ("samesite-unspecified=2", content::GetCookies(profile, url)); |
| // Fetching the cookies from a cross-site context also works because of the |
| // policy. |
| EXPECT_EQ("samesite-unspecified=2", |
| content::GetCookies(profile, url, |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::CROSS_SITE))); |
| |
| // When Schemeful Same-Site is enabled a context downgrade to an insufficient |
| // context should still be allowed with legacy access. This'll always work if |
| // Schemeful Same-Site is disabled because the schemeless context is Lax |
| // which is sufficient. |
| EXPECT_TRUE(content::SetCookie( |
| profile, url, "samesite-lax=1; SameSite=Lax", |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX, |
| net::CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE))); |
| // Similarly when we try to get the cookie. |
| EXPECT_THAT( |
| content::GetCookies(profile, url, |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::SAME_SITE_LAX, |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::CROSS_SITE)), |
| testing::HasSubstr("samesite-lax=1")); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameSiteCookiesPolicyTest, |
| DefaultLegacyCookieAccessSettingIsBlock) { |
| PolicyMap policies; |
| // Set a policy to block Legacy access for all cookies. |
| SetPolicy(&policies, key::kLegacySameSiteCookieBehaviorEnabled, |
| base::Value(2)); |
| UpdateProviderPolicy(policies); |
| |
| GURL url(kURL); |
| Profile* profile = browser()->profile(); |
| |
| // No cookies at startup |
| ASSERT_TRUE(content::GetCookies(profile, url).empty()); |
| |
| // Set a cookie from a same-site context. The cookie does not specify |
| // SameSite, so it may default to Lax if the SameSite features are enabled. |
| // Since the context used is same-site, it should always work. |
| EXPECT_TRUE(content::SetCookie(profile, url, "samesite-unspecified=1", |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::SAME_SITE_LAX))); |
| EXPECT_EQ("samesite-unspecified=1", content::GetCookies(profile, url)); |
| |
| // Overwrite the cookie from a cross-site context. Because we have a policy |
| // that blocks Legacy access for all domains, this will not work even if the |
| // SameSite features are disabled. (It doesn't work regardless, if they are |
| // enabled.) |
| EXPECT_FALSE(content::SetCookie( |
| profile, url, "samesite-unspecified=2", |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE))); |
| // Cookie still has the previous value because re-setting it failed. |
| EXPECT_EQ("samesite-unspecified=1", content::GetCookies(profile, url)); |
| // Fetching the unspecified-samesite cookie from a cross-site context does not |
| // work because of the policy. |
| EXPECT_EQ("", |
| content::GetCookies(profile, url, |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::CROSS_SITE))); |
| |
| // When Schemeful Same-Site is enabled a context downgrade to an insufficient |
| // context should always be blocked. If Schemeful Same-Site is disabled then |
| // this shouldn't be blocked. |
| // Similarly when we try to get the cookie. |
| if (AreSameSiteFeaturesEnabled()) { |
| EXPECT_FALSE( |
| content::SetCookie(profile, url, "samesite-lax=1; SameSite=Lax", |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::SAME_SITE_LAX, |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::CROSS_SITE))); |
| // We should be able to get the cookie which was previously added. |
| EXPECT_EQ("samesite-unspecified=1", content::GetCookies(profile, url)); |
| // But no cookies should be returned for a downgrade to an insufficient |
| // context, since SameSite-by-default is active which requires a minimum of |
| // a Lax context. |
| EXPECT_EQ( |
| "", content::GetCookies(profile, url, |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::SAME_SITE_LAX, |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::CROSS_SITE))); |
| } else { |
| EXPECT_TRUE( |
| content::SetCookie(profile, url, "samesite-lax=1; SameSite=Lax", |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::SAME_SITE_LAX, |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::CROSS_SITE))); |
| EXPECT_THAT( |
| content::GetCookies(profile, url, |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::SAME_SITE_LAX, |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::CROSS_SITE)), |
| testing::HasSubstr("samesite-lax=1")); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameSiteCookiesPolicyTest, |
| AllowLegacyCookieAccessForDomain) { |
| GURL legacy_allowed_domain_url(kURL); |
| GURL other_domain_url("http://other-domain.example"); |
| |
| // Set a policy to allow Legacy cookie access for one domain only. |
| base::Value policy_value(base::Value::Type::LIST); |
| policy_value.Append(legacy_allowed_domain_url.host()); |
| |
| PolicyMap policies; |
| // Set a policy to allow Legacy access for the given domain only. |
| SetPolicy(&policies, key::kLegacySameSiteCookieBehaviorEnabledForDomainList, |
| std::move(policy_value)); |
| UpdateProviderPolicy(policies); |
| |
| Profile* profile = browser()->profile(); |
| |
| // No cookies at startup |
| ASSERT_TRUE(content::GetCookies(profile, legacy_allowed_domain_url).empty()); |
| ASSERT_TRUE(content::GetCookies(profile, other_domain_url).empty()); |
| |
| // Set a cookie from a same-site context. The cookie does not specify |
| // SameSite, so it may default to Lax if the SameSite features are enabled. |
| // Since the context used is same-site, it should always work. |
| EXPECT_TRUE(content::SetCookie(profile, legacy_allowed_domain_url, |
| "samesite-unspecified=1", |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::SAME_SITE_LAX))); |
| EXPECT_EQ("samesite-unspecified=1", |
| content::GetCookies(profile, legacy_allowed_domain_url)); |
| // Do the same on the other domain... |
| EXPECT_TRUE(content::SetCookie(profile, other_domain_url, |
| "samesite-unspecified=1", |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::SAME_SITE_LAX))); |
| EXPECT_EQ("samesite-unspecified=1", |
| content::GetCookies(profile, other_domain_url)); |
| |
| // Overwrite the cookie from a cross-site context. Because we have a policy |
| // that allows Legacy access for one domain but not the other, this will work |
| // on the policy-specified domain even if SameSite features are enabled, but |
| // it will not work for the other domain. (It works regardless, if they are |
| // disabled.) |
| EXPECT_TRUE(content::SetCookie( |
| profile, legacy_allowed_domain_url, "samesite-unspecified=2", |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE))); |
| EXPECT_EQ("samesite-unspecified=2", |
| content::GetCookies(profile, legacy_allowed_domain_url)); |
| EXPECT_EQ("samesite-unspecified=2", |
| content::GetCookies(profile, legacy_allowed_domain_url, |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::CROSS_SITE))); |
| // When Schemeful Same-Site is enabled a context downgrade to an insufficient |
| // context should still be allowed with legacy access. This'll always work if |
| // Schemeful Same-Site is disabled because the schemeless context is Lax |
| // which is sufficient. |
| EXPECT_TRUE(content::SetCookie( |
| profile, legacy_allowed_domain_url, "samesite-lax=1; SameSite=Lax", |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX, |
| net::CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE))); |
| // Similarly when we try to get the cookie. |
| EXPECT_THAT( |
| content::GetCookies(profile, legacy_allowed_domain_url, |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::SAME_SITE_LAX, |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::CROSS_SITE)), |
| testing::HasSubstr("samesite-lax=1")); |
| |
| // For the domain that is not Legacy by policy, we expect it to work only if |
| // the SameSite features are disabled. |
| if (AreSameSiteFeaturesEnabled()) { |
| EXPECT_FALSE( |
| content::SetCookie(profile, other_domain_url, "samesite-unspecified=2", |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::CROSS_SITE))); |
| EXPECT_EQ("samesite-unspecified=1", |
| content::GetCookies(profile, other_domain_url)); |
| EXPECT_EQ( |
| "", content::GetCookies(profile, other_domain_url, |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::CROSS_SITE))); |
| EXPECT_FALSE(content::SetCookie( |
| profile, other_domain_url, "samesite-lax=1; SameSite=Lax", |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext::ContextType:: |
| SAME_SITE_LAX, |
| net::CookieOptions::SameSiteCookieContext::ContextType:: |
| CROSS_SITE))); |
| // We should be able to get the cookie which was previously added. |
| EXPECT_EQ("samesite-unspecified=1", |
| content::GetCookies(profile, other_domain_url)); |
| // But no cookies should be returned for a downgrade to an insufficient |
| // context, since SameSite-by-default is active which requires a minimum of |
| // a Lax context. |
| EXPECT_EQ( |
| "", content::GetCookies(profile, other_domain_url, |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::SAME_SITE_LAX, |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::CROSS_SITE))); |
| } else { |
| EXPECT_TRUE( |
| content::SetCookie(profile, other_domain_url, "samesite-unspecified=2", |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::CROSS_SITE))); |
| EXPECT_EQ("samesite-unspecified=2", |
| content::GetCookies(profile, other_domain_url)); |
| EXPECT_EQ( |
| "samesite-unspecified=2", |
| content::GetCookies(profile, other_domain_url, |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::CROSS_SITE))); |
| |
| EXPECT_TRUE(content::SetCookie( |
| profile, other_domain_url, "samesite-lax=1; SameSite=Lax", |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext::ContextType:: |
| SAME_SITE_LAX, |
| net::CookieOptions::SameSiteCookieContext::ContextType:: |
| CROSS_SITE))); |
| EXPECT_THAT( |
| content::GetCookies(profile, other_domain_url, |
| net::CookieOptions::SameSiteCookieContext( |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::SAME_SITE_LAX, |
| net::CookieOptions::SameSiteCookieContext:: |
| ContextType::CROSS_SITE)), |
| testing::HasSubstr("samesite-lax=1")); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| SameSiteCookiesPolicyTest, |
| ::testing::Bool()); |
| |
| } // namespace policy |