// Copyright 2020 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/base/isolation_info.h" #include #include #include "base/strings/strcat.h" #include "base/test/gtest_util.h" #include "base/test/scoped_feature_list.h" #include "base/unguessable_token.h" #include "isolation_info.h" #include "net/base/features.h" #include "net/base/network_anonymization_key.h" #include "net/base/network_isolation_key.h" #include "net/base/schemeful_site.h" #include "net/cookies/site_for_cookies.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" #include "url/origin.h" #include "url/url_util.h" namespace net { namespace { class IsolationInfoTest : public testing::Test, public testing::WithParamInterface { public: void SetUp() override { switch (GetParam()) { case net::NetworkIsolationKey::Mode::kFrameSiteEnabled: scoped_feature_list_.InitWithFeatures( {}, {net::features::kEnableCrossSiteFlagNetworkIsolationKey, net::features::kEnableFrameSiteSharedOpaqueNetworkIsolationKey}); break; case net::NetworkIsolationKey::Mode::kFrameSiteWithSharedOpaqueEnabled: scoped_feature_list_.InitWithFeatures( {net::features::kEnableFrameSiteSharedOpaqueNetworkIsolationKey}, { net::features::kEnableCrossSiteFlagNetworkIsolationKey, }); break; case net::NetworkIsolationKey::Mode::kCrossSiteFlagEnabled: scoped_feature_list_.InitWithFeatures( {net::features::kEnableCrossSiteFlagNetworkIsolationKey}, {net::features::kEnableFrameSiteSharedOpaqueNetworkIsolationKey}); break; } } const url::Origin kOrigin1 = url::Origin::Create(GURL("https://a.foo.test")); const url::Origin kSite1 = url::Origin::Create(GURL("https://foo.test")); const url::Origin kOrigin2 = url::Origin::Create(GURL("https://b.bar.test")); const url::Origin kSite2 = url::Origin::Create(GURL("https://bar.test")); const url::Origin kOrigin3 = url::Origin::Create(GURL("https://c.baz.test")); const url::Origin kOpaqueOrigin; const base::UnguessableToken kNonce1 = base::UnguessableToken::Create(); const base::UnguessableToken kNonce2 = base::UnguessableToken::Create(); private: base::test::ScopedFeatureList scoped_feature_list_; }; INSTANTIATE_TEST_SUITE_P( Tests, IsolationInfoTest, testing::ValuesIn( {NetworkIsolationKey::Mode::kFrameSiteEnabled, NetworkIsolationKey::Mode::kCrossSiteFlagEnabled, NetworkIsolationKey::Mode::kFrameSiteWithSharedOpaqueEnabled}), [](const testing::TestParamInfo& info) { switch (info.param) { case NetworkIsolationKey::Mode::kFrameSiteEnabled: return "FrameSiteEnabled"; case NetworkIsolationKey::Mode::kCrossSiteFlagEnabled: return "CrossSiteFlagEnabled"; case NetworkIsolationKey::Mode::kFrameSiteWithSharedOpaqueEnabled: return "FrameSiteSharedOpaqueEnabled"; } }); void DuplicateAndCompare(const IsolationInfo& isolation_info) { std::optional duplicate_isolation_info = IsolationInfo::CreateIfConsistent( isolation_info.request_type(), isolation_info.top_frame_origin(), isolation_info.frame_origin(), isolation_info.site_for_cookies(), isolation_info.nonce()); ASSERT_TRUE(duplicate_isolation_info); EXPECT_TRUE(isolation_info.IsEqualForTesting(*duplicate_isolation_info)); } TEST_P(IsolationInfoTest, DebugString) { IsolationInfo isolation_info = IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kOrigin1, kOrigin2, SiteForCookies::FromOrigin(kOrigin1), kNonce1); std::vector parts; parts.push_back( "request_type: kMainFrame; top_frame_origin: https://a.foo.test; "); parts.push_back("frame_origin: https://b.bar.test; "); parts.push_back("network_anonymization_key: "); parts.push_back(isolation_info.network_anonymization_key().ToDebugString()); parts.push_back("; network_isolation_key: "); parts.push_back(isolation_info.network_isolation_key().ToDebugString()); parts.push_back("; nonce: "); parts.push_back(isolation_info.nonce().value().ToString()); parts.push_back( "; site_for_cookies: SiteForCookies: {site=https://foo.test; " "schemefully_same=true}"); EXPECT_EQ(isolation_info.DebugString(), base::StrCat(parts)); } TEST_P(IsolationInfoTest, CreateNetworkAnonymizationKeyForIsolationInfo) { IsolationInfo isolation_info = IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kOrigin1, kOrigin2, SiteForCookies::FromOrigin(kOrigin1), kNonce1); NetworkAnonymizationKey nak = isolation_info.CreateNetworkAnonymizationKeyForIsolationInfo( kOrigin1, kOrigin2, kNonce1); IsolationInfo same_site_isolation_info = IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kOrigin1, kOrigin1, SiteForCookies::FromOrigin(kOrigin1), kNonce1); // Top frame should be populated regardless of scheme. EXPECT_EQ(nak.GetTopFrameSite(), SchemefulSite(kOrigin1)); EXPECT_EQ(isolation_info.top_frame_origin(), kOrigin1); EXPECT_EQ(isolation_info.network_anonymization_key().GetTopFrameSite(), SchemefulSite(kOrigin1)); // Nonce should be empty regardless of scheme EXPECT_EQ(nak.GetNonce().value(), kNonce1); EXPECT_EQ(isolation_info.network_anonymization_key().GetNonce().value(), kNonce1); EXPECT_EQ(isolation_info.nonce().value(), kNonce1); // Triple-keyed IsolationInfo + double-keyed + cross site bit // NetworkAnonymizationKey case. EXPECT_EQ(isolation_info.frame_origin(), kOrigin2); EXPECT_TRUE(isolation_info.network_anonymization_key().IsCrossSite()); EXPECT_TRUE( same_site_isolation_info.network_anonymization_key().IsSameSite()); } // A 2.5-keyed NAK created with two identical opaque origins should be // same-site. TEST_P(IsolationInfoTest, CreateNetworkAnonymizationKeyForIsolationInfoOpaque) { url::Origin opaque; IsolationInfo isolation_info = IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, opaque, opaque, SiteForCookies::FromOrigin(opaque), kNonce1); NetworkAnonymizationKey nak = isolation_info.CreateNetworkAnonymizationKeyForIsolationInfo( opaque, opaque, kNonce1); EXPECT_TRUE(nak.IsSameSite()); url::Origin opaque2; nak = isolation_info.CreateNetworkAnonymizationKeyForIsolationInfo( opaque, opaque2, kNonce1); EXPECT_TRUE(nak.IsCrossSite()); } TEST_P(IsolationInfoTest, RequestTypeMainFrame) { IsolationInfo isolation_info = IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, kOrigin1, kOrigin1, SiteForCookies::FromOrigin(kOrigin1)); EXPECT_EQ(IsolationInfo::RequestType::kMainFrame, isolation_info.request_type()); EXPECT_EQ(kOrigin1, isolation_info.top_frame_origin()); EXPECT_EQ(kOrigin1, isolation_info.frame_origin()); switch (NetworkIsolationKey::GetMode()) { case NetworkIsolationKey::Mode::kFrameSiteEnabled: case NetworkIsolationKey::Mode::kFrameSiteWithSharedOpaqueEnabled: EXPECT_EQ("https://foo.test https://foo.test", isolation_info.network_isolation_key().ToCacheKeyString()); break; case NetworkIsolationKey::Mode::kCrossSiteFlagEnabled: EXPECT_EQ("https://foo.test _0", isolation_info.network_isolation_key().ToCacheKeyString()); break; } EXPECT_TRUE(isolation_info.network_isolation_key().IsFullyPopulated()); EXPECT_FALSE(isolation_info.network_isolation_key().IsTransient()); EXPECT_TRUE( isolation_info.site_for_cookies().IsFirstParty(kOrigin1.GetURL())); EXPECT_FALSE(isolation_info.nonce().has_value()); DuplicateAndCompare(isolation_info); IsolationInfo redirected_isolation_info = isolation_info.CreateForRedirect(kOrigin3); EXPECT_EQ(IsolationInfo::RequestType::kMainFrame, redirected_isolation_info.request_type()); EXPECT_EQ(kOrigin3, redirected_isolation_info.top_frame_origin()); EXPECT_EQ(kOrigin3, redirected_isolation_info.frame_origin()); EXPECT_TRUE( redirected_isolation_info.network_isolation_key().IsFullyPopulated()); EXPECT_FALSE(redirected_isolation_info.network_isolation_key().IsTransient()); switch (NetworkIsolationKey::GetMode()) { case NetworkIsolationKey::Mode::kFrameSiteEnabled: case NetworkIsolationKey::Mode::kFrameSiteWithSharedOpaqueEnabled: EXPECT_EQ( "https://baz.test https://baz.test", redirected_isolation_info.network_isolation_key().ToCacheKeyString()); break; case NetworkIsolationKey::Mode::kCrossSiteFlagEnabled: EXPECT_EQ( "https://baz.test _0", redirected_isolation_info.network_isolation_key().ToCacheKeyString()); break; } EXPECT_TRUE(redirected_isolation_info.site_for_cookies().IsFirstParty( kOrigin3.GetURL())); EXPECT_FALSE(redirected_isolation_info.nonce().has_value()); } TEST_P(IsolationInfoTest, RequestTypeSubFrame) { IsolationInfo isolation_info = IsolationInfo::Create(IsolationInfo::RequestType::kSubFrame, kOrigin1, kOrigin2, SiteForCookies::FromOrigin(kOrigin1)); EXPECT_EQ(IsolationInfo::RequestType::kSubFrame, isolation_info.request_type()); EXPECT_EQ(kOrigin1, isolation_info.top_frame_origin()); EXPECT_EQ(kOrigin2, isolation_info.frame_origin()); switch (NetworkIsolationKey::GetMode()) { case NetworkIsolationKey::Mode::kFrameSiteEnabled: case NetworkIsolationKey::Mode::kFrameSiteWithSharedOpaqueEnabled: EXPECT_EQ("https://foo.test https://bar.test", isolation_info.network_isolation_key().ToCacheKeyString()); break; case NetworkIsolationKey::Mode::kCrossSiteFlagEnabled: EXPECT_EQ("https://foo.test _1", isolation_info.network_isolation_key().ToCacheKeyString()); break; } EXPECT_TRUE(isolation_info.network_isolation_key().IsFullyPopulated()); EXPECT_FALSE(isolation_info.network_isolation_key().IsTransient()); EXPECT_TRUE( isolation_info.site_for_cookies().IsFirstParty(kOrigin1.GetURL())); EXPECT_FALSE(isolation_info.nonce().has_value()); DuplicateAndCompare(isolation_info); IsolationInfo redirected_isolation_info = isolation_info.CreateForRedirect(kOrigin3); EXPECT_EQ(IsolationInfo::RequestType::kSubFrame, redirected_isolation_info.request_type()); EXPECT_EQ(kOrigin1, redirected_isolation_info.top_frame_origin()); EXPECT_EQ(kOrigin3, redirected_isolation_info.frame_origin()); switch (NetworkIsolationKey::GetMode()) { case NetworkIsolationKey::Mode::kFrameSiteEnabled: case NetworkIsolationKey::Mode::kFrameSiteWithSharedOpaqueEnabled: EXPECT_EQ( "https://foo.test https://baz.test", redirected_isolation_info.network_isolation_key().ToCacheKeyString()); break; case NetworkIsolationKey::Mode::kCrossSiteFlagEnabled: EXPECT_EQ( "https://foo.test _1", redirected_isolation_info.network_isolation_key().ToCacheKeyString()); break; } EXPECT_TRUE( redirected_isolation_info.network_isolation_key().IsFullyPopulated()); EXPECT_FALSE(redirected_isolation_info.network_isolation_key().IsTransient()); EXPECT_TRUE(redirected_isolation_info.site_for_cookies().IsFirstParty( kOrigin1.GetURL())); EXPECT_FALSE(redirected_isolation_info.nonce().has_value()); } TEST_P(IsolationInfoTest, RequestTypeMainFrameWithNonce) { IsolationInfo isolation_info = IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kOrigin1, kOrigin1, SiteForCookies::FromOrigin(kOrigin1), kNonce1); EXPECT_EQ(IsolationInfo::RequestType::kMainFrame, isolation_info.request_type()); EXPECT_EQ(kOrigin1, isolation_info.top_frame_origin()); EXPECT_EQ(kOrigin1, isolation_info.frame_origin()); EXPECT_TRUE(isolation_info.network_isolation_key().IsFullyPopulated()); EXPECT_TRUE(isolation_info.network_isolation_key().IsTransient()); EXPECT_EQ(std::nullopt, isolation_info.network_isolation_key().ToCacheKeyString()); EXPECT_TRUE( isolation_info.site_for_cookies().IsFirstParty(kOrigin1.GetURL())); EXPECT_EQ(kNonce1, isolation_info.nonce().value()); DuplicateAndCompare(isolation_info); IsolationInfo redirected_isolation_info = isolation_info.CreateForRedirect(kOrigin3); EXPECT_EQ(IsolationInfo::RequestType::kMainFrame, redirected_isolation_info.request_type()); EXPECT_EQ(kOrigin3, redirected_isolation_info.top_frame_origin()); EXPECT_EQ(kOrigin3, redirected_isolation_info.frame_origin()); EXPECT_TRUE( redirected_isolation_info.network_isolation_key().IsFullyPopulated()); EXPECT_TRUE(redirected_isolation_info.network_isolation_key().IsTransient()); EXPECT_EQ( std::nullopt, redirected_isolation_info.network_isolation_key().ToCacheKeyString()); EXPECT_TRUE(redirected_isolation_info.site_for_cookies().IsFirstParty( kOrigin3.GetURL())); EXPECT_EQ(kNonce1, redirected_isolation_info.nonce().value()); } TEST_P(IsolationInfoTest, RequestTypeSubFrameWithNonce) { IsolationInfo isolation_info = IsolationInfo::Create( IsolationInfo::RequestType::kSubFrame, kOrigin1, kOrigin2, SiteForCookies::FromOrigin(kOrigin1), kNonce1); EXPECT_EQ(IsolationInfo::RequestType::kSubFrame, isolation_info.request_type()); EXPECT_EQ(kOrigin1, isolation_info.top_frame_origin()); EXPECT_EQ(kOrigin2, isolation_info.frame_origin()); EXPECT_TRUE(isolation_info.network_isolation_key().IsFullyPopulated()); EXPECT_TRUE(isolation_info.network_isolation_key().IsTransient()); EXPECT_EQ(std::nullopt, isolation_info.network_isolation_key().ToCacheKeyString()); EXPECT_TRUE( isolation_info.site_for_cookies().IsFirstParty(kOrigin1.GetURL())); EXPECT_EQ(kNonce1, isolation_info.nonce().value()); DuplicateAndCompare(isolation_info); IsolationInfo redirected_isolation_info = isolation_info.CreateForRedirect(kOrigin3); EXPECT_EQ(IsolationInfo::RequestType::kSubFrame, redirected_isolation_info.request_type()); EXPECT_EQ(kOrigin1, redirected_isolation_info.top_frame_origin()); EXPECT_EQ(kOrigin3, redirected_isolation_info.frame_origin()); EXPECT_TRUE( redirected_isolation_info.network_isolation_key().IsFullyPopulated()); EXPECT_TRUE(redirected_isolation_info.network_isolation_key().IsTransient()); EXPECT_EQ( std::nullopt, redirected_isolation_info.network_isolation_key().ToCacheKeyString()); EXPECT_TRUE(redirected_isolation_info.site_for_cookies().IsFirstParty( kOrigin1.GetURL())); EXPECT_EQ(kNonce1, redirected_isolation_info.nonce().value()); } TEST_P(IsolationInfoTest, RequestTypeOther) { IsolationInfo isolation_info; EXPECT_EQ(IsolationInfo::RequestType::kOther, isolation_info.request_type()); EXPECT_FALSE(isolation_info.top_frame_origin()); EXPECT_FALSE(isolation_info.frame_origin()); EXPECT_TRUE(isolation_info.network_isolation_key().IsEmpty()); EXPECT_TRUE(isolation_info.site_for_cookies().IsNull()); EXPECT_FALSE(isolation_info.nonce()); DuplicateAndCompare(isolation_info); IsolationInfo redirected_isolation_info = isolation_info.CreateForRedirect(kOrigin3); EXPECT_TRUE(isolation_info.IsEqualForTesting(redirected_isolation_info)); } TEST_P(IsolationInfoTest, RequestTypeOtherWithSiteForCookies) { IsolationInfo isolation_info = IsolationInfo::Create(IsolationInfo::RequestType::kOther, kOrigin1, kOrigin1, SiteForCookies::FromOrigin(kOrigin1)); EXPECT_EQ(IsolationInfo::RequestType::kOther, isolation_info.request_type()); EXPECT_EQ(kOrigin1, isolation_info.top_frame_origin()); EXPECT_EQ(kOrigin1, isolation_info.frame_origin()); switch (NetworkIsolationKey::GetMode()) { case NetworkIsolationKey::Mode::kFrameSiteEnabled: case NetworkIsolationKey::Mode::kFrameSiteWithSharedOpaqueEnabled: EXPECT_EQ("https://foo.test https://foo.test", isolation_info.network_isolation_key().ToCacheKeyString()); break; case NetworkIsolationKey::Mode::kCrossSiteFlagEnabled: EXPECT_EQ("https://foo.test _0", isolation_info.network_isolation_key().ToCacheKeyString()); break; } EXPECT_TRUE(isolation_info.network_isolation_key().IsFullyPopulated()); EXPECT_FALSE(isolation_info.network_isolation_key().IsTransient()); EXPECT_TRUE( isolation_info.site_for_cookies().IsFirstParty(kOrigin1.GetURL())); EXPECT_FALSE(isolation_info.nonce()); DuplicateAndCompare(isolation_info); IsolationInfo redirected_isolation_info = isolation_info.CreateForRedirect(kOrigin3); EXPECT_TRUE(isolation_info.IsEqualForTesting(redirected_isolation_info)); } // Test case of a subresource for cross-site subframe (which has an empty // site-for-cookies). TEST_P(IsolationInfoTest, RequestTypeOtherWithEmptySiteForCookies) { IsolationInfo isolation_info = IsolationInfo::Create( IsolationInfo::RequestType::kOther, kOrigin1, kOrigin2, SiteForCookies()); EXPECT_EQ(IsolationInfo::RequestType::kOther, isolation_info.request_type()); EXPECT_EQ(kOrigin1, isolation_info.top_frame_origin()); EXPECT_EQ(kOrigin2, isolation_info.frame_origin()); switch (NetworkIsolationKey::GetMode()) { case NetworkIsolationKey::Mode::kFrameSiteEnabled: case NetworkIsolationKey::Mode::kFrameSiteWithSharedOpaqueEnabled: EXPECT_EQ("https://foo.test https://bar.test", isolation_info.network_isolation_key().ToCacheKeyString()); break; case NetworkIsolationKey::Mode::kCrossSiteFlagEnabled: EXPECT_EQ("https://foo.test _1", isolation_info.network_isolation_key().ToCacheKeyString()); break; } EXPECT_TRUE(isolation_info.network_isolation_key().IsFullyPopulated()); EXPECT_FALSE(isolation_info.network_isolation_key().IsTransient()); EXPECT_TRUE(isolation_info.site_for_cookies().IsNull()); EXPECT_FALSE(isolation_info.nonce()); DuplicateAndCompare(isolation_info); IsolationInfo redirected_isolation_info = isolation_info.CreateForRedirect(kOrigin3); EXPECT_TRUE(isolation_info.IsEqualForTesting(redirected_isolation_info)); } TEST_P(IsolationInfoTest, CreateTransient) { IsolationInfo isolation_info = IsolationInfo::CreateTransient(); EXPECT_EQ(IsolationInfo::RequestType::kOther, isolation_info.request_type()); EXPECT_TRUE(isolation_info.top_frame_origin()->opaque()); EXPECT_TRUE(isolation_info.frame_origin()->opaque()); EXPECT_TRUE(isolation_info.network_isolation_key().IsFullyPopulated()); EXPECT_TRUE(isolation_info.network_isolation_key().IsTransient()); EXPECT_TRUE(isolation_info.site_for_cookies().IsNull()); EXPECT_FALSE(isolation_info.nonce()); DuplicateAndCompare(isolation_info); IsolationInfo redirected_isolation_info = isolation_info.CreateForRedirect(kOrigin3); EXPECT_TRUE(isolation_info.IsEqualForTesting(redirected_isolation_info)); } TEST_P(IsolationInfoTest, CreateTransientWithNonce) { IsolationInfo isolation_info = IsolationInfo::CreateTransientWithNonce(kNonce1); EXPECT_EQ(IsolationInfo::RequestType::kOther, isolation_info.request_type()); EXPECT_TRUE(isolation_info.top_frame_origin()->opaque()); EXPECT_TRUE(isolation_info.frame_origin()->opaque()); EXPECT_TRUE(isolation_info.network_isolation_key().IsFullyPopulated()); EXPECT_TRUE(isolation_info.network_isolation_key().IsTransient()); EXPECT_TRUE(isolation_info.site_for_cookies().IsNull()); ASSERT_TRUE(isolation_info.nonce().has_value()); EXPECT_EQ(isolation_info.nonce().value(), kNonce1); DuplicateAndCompare(isolation_info); IsolationInfo redirected_isolation_info = isolation_info.CreateForRedirect(kOrigin3); EXPECT_TRUE(isolation_info.IsEqualForTesting(redirected_isolation_info)); IsolationInfo new_info_same_nonce = IsolationInfo::CreateTransientWithNonce(kNonce1); ASSERT_TRUE(new_info_same_nonce.nonce().has_value()); EXPECT_EQ(new_info_same_nonce.nonce().value(), kNonce1); // The new NIK is distinct from the first one because it uses a new opaque // origin, even if the nonce is the same. EXPECT_NE(isolation_info.network_isolation_key(), new_info_same_nonce.network_isolation_key()); } TEST_P(IsolationInfoTest, CreateForInternalRequest) { IsolationInfo isolation_info = IsolationInfo::CreateForInternalRequest(kOrigin1); EXPECT_EQ(IsolationInfo::RequestType::kOther, isolation_info.request_type()); EXPECT_EQ(kOrigin1, isolation_info.top_frame_origin()); EXPECT_EQ(kOrigin1, isolation_info.frame_origin()); switch (NetworkIsolationKey::GetMode()) { case NetworkIsolationKey::Mode::kFrameSiteEnabled: case NetworkIsolationKey::Mode::kFrameSiteWithSharedOpaqueEnabled: EXPECT_EQ("https://foo.test https://foo.test", isolation_info.network_isolation_key().ToCacheKeyString()); break; case NetworkIsolationKey::Mode::kCrossSiteFlagEnabled: EXPECT_EQ("https://foo.test _0", isolation_info.network_isolation_key().ToCacheKeyString()); break; } EXPECT_TRUE(isolation_info.network_isolation_key().IsFullyPopulated()); EXPECT_FALSE(isolation_info.network_isolation_key().IsTransient()); EXPECT_TRUE( isolation_info.site_for_cookies().IsFirstParty(kOrigin1.GetURL())); EXPECT_FALSE(isolation_info.nonce()); DuplicateAndCompare(isolation_info); IsolationInfo redirected_isolation_info = isolation_info.CreateForRedirect(kOrigin3); EXPECT_TRUE(isolation_info.IsEqualForTesting(redirected_isolation_info)); } // Test that in the UpdateNothing case, the SiteForCookies does not have to // match the frame origin, unlike in the HTTP/HTTPS case. TEST_P(IsolationInfoTest, CustomSchemeRequestTypeOther) { // Have to register the scheme, or url::Origin::Create() will return an // opaque origin. url::ScopedSchemeRegistryForTests scoped_registry; url::AddStandardScheme("foo", url::SCHEME_WITH_HOST); const GURL kCustomOriginUrl = GURL("foo://a.foo.com"); const url::Origin kCustomOrigin = url::Origin::Create(kCustomOriginUrl); IsolationInfo isolation_info = IsolationInfo::Create( IsolationInfo::RequestType::kOther, kCustomOrigin, kOrigin1, SiteForCookies::FromOrigin(kCustomOrigin)); EXPECT_EQ(IsolationInfo::RequestType::kOther, isolation_info.request_type()); EXPECT_EQ(kCustomOrigin, isolation_info.top_frame_origin()); EXPECT_EQ(kOrigin1, isolation_info.frame_origin()); switch (NetworkIsolationKey::GetMode()) { case NetworkIsolationKey::Mode::kFrameSiteEnabled: case NetworkIsolationKey::Mode::kFrameSiteWithSharedOpaqueEnabled: EXPECT_EQ("foo://a.foo.com https://foo.test", isolation_info.network_isolation_key().ToCacheKeyString()); break; case NetworkIsolationKey::Mode::kCrossSiteFlagEnabled: EXPECT_EQ("foo://a.foo.com _1", isolation_info.network_isolation_key().ToCacheKeyString()); break; } EXPECT_TRUE(isolation_info.network_isolation_key().IsFullyPopulated()); EXPECT_FALSE(isolation_info.network_isolation_key().IsTransient()); EXPECT_TRUE(isolation_info.site_for_cookies().IsFirstParty(kCustomOriginUrl)); EXPECT_FALSE(isolation_info.nonce()); DuplicateAndCompare(isolation_info); IsolationInfo redirected_isolation_info = isolation_info.CreateForRedirect(kOrigin2); EXPECT_TRUE(isolation_info.IsEqualForTesting(redirected_isolation_info)); } // Success cases are covered by other tests, so only need a separate test to // cover the failure cases. TEST_P(IsolationInfoTest, CreateIfConsistentFails) { // Main frames with inconsistent SiteForCookies. EXPECT_FALSE(IsolationInfo::CreateIfConsistent( IsolationInfo::RequestType::kMainFrame, kOrigin1, kOrigin1, SiteForCookies::FromOrigin(kOrigin2))); EXPECT_FALSE(IsolationInfo::CreateIfConsistent( IsolationInfo::RequestType::kMainFrame, kOpaqueOrigin, kOpaqueOrigin, SiteForCookies::FromOrigin(kOrigin1))); // Sub frame with inconsistent SiteForCookies. EXPECT_FALSE(IsolationInfo::CreateIfConsistent( IsolationInfo::RequestType::kSubFrame, kOrigin1, kOrigin2, SiteForCookies::FromOrigin(kOrigin2))); // Sub resources with inconsistent SiteForCookies. EXPECT_FALSE(IsolationInfo::CreateIfConsistent( IsolationInfo::RequestType::kOther, kOrigin1, kOrigin2, SiteForCookies::FromOrigin(kOrigin2))); // Correctly have empty/non-empty origins: EXPECT_TRUE(IsolationInfo::CreateIfConsistent( IsolationInfo::RequestType::kOther, std::nullopt, std::nullopt, SiteForCookies())); // Incorrectly have empty/non-empty origins: EXPECT_FALSE(IsolationInfo::CreateIfConsistent( IsolationInfo::RequestType::kOther, std::nullopt, kOrigin1, SiteForCookies())); EXPECT_FALSE(IsolationInfo::CreateIfConsistent( IsolationInfo::RequestType::kSubFrame, std::nullopt, kOrigin2, SiteForCookies())); // Empty frame origins are incorrect. EXPECT_FALSE(IsolationInfo::CreateIfConsistent( IsolationInfo::RequestType::kOther, kOrigin1, std::nullopt, SiteForCookies())); EXPECT_FALSE(IsolationInfo::CreateIfConsistent( IsolationInfo::RequestType::kSubFrame, kOrigin1, std::nullopt, SiteForCookies())); EXPECT_FALSE(IsolationInfo::CreateIfConsistent( IsolationInfo::RequestType::kMainFrame, kOrigin1, std::nullopt, SiteForCookies::FromOrigin(kOrigin1))); EXPECT_FALSE(IsolationInfo::CreateIfConsistent( IsolationInfo::RequestType::kOther, kOrigin1, kOrigin2, SiteForCookies::FromOrigin(kOrigin1))); // No origins with non-null SiteForCookies. EXPECT_FALSE(IsolationInfo::CreateIfConsistent( IsolationInfo::RequestType::kOther, std::nullopt, std::nullopt, SiteForCookies::FromOrigin(kOrigin1))); // No origins with non-null nonce. EXPECT_FALSE(IsolationInfo::CreateIfConsistent( IsolationInfo::RequestType::kOther, std::nullopt, std::nullopt, SiteForCookies(), kNonce1)); } TEST_P(IsolationInfoTest, Serialization) { EXPECT_FALSE(IsolationInfo::Deserialize("")); EXPECT_FALSE(IsolationInfo::Deserialize("garbage")); const IsolationInfo kPositiveTestCases[] = { IsolationInfo::Create(IsolationInfo::RequestType::kSubFrame, kOrigin1, kOrigin2, SiteForCookies::FromOrigin(kOrigin1)), // Null party context IsolationInfo::Create(IsolationInfo::RequestType::kSubFrame, kOrigin1, kOrigin2, SiteForCookies::FromOrigin(kOrigin1)), // Empty party context IsolationInfo::Create(IsolationInfo::RequestType::kSubFrame, kOrigin1, kOrigin2, SiteForCookies::FromOrigin(kOrigin1)), // Multiple party context entries. IsolationInfo::Create(IsolationInfo::RequestType::kSubFrame, kOrigin1, kOrigin2, SiteForCookies::FromOrigin(kOrigin1)), // Without SiteForCookies IsolationInfo::Create(IsolationInfo::RequestType::kSubFrame, kOrigin1, kOrigin2, SiteForCookies()), // Request type kOther IsolationInfo::Create(IsolationInfo::RequestType::kOther, kOrigin1, kOrigin1, SiteForCookies::FromOrigin(kOrigin1)), // Request type kMainframe IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, kOrigin1, kOrigin1, SiteForCookies::FromOrigin(kOrigin1)), }; for (const auto& info : kPositiveTestCases) { auto rt = IsolationInfo::Deserialize(info.Serialize()); ASSERT_TRUE(rt); EXPECT_TRUE(rt->IsEqualForTesting(info)); } const IsolationInfo kNegativeTestCases[] = { IsolationInfo::CreateTransient(), // With nonce (i.e transient). IsolationInfo::Create(IsolationInfo::RequestType::kSubFrame, kOrigin1, kOrigin2, SiteForCookies::FromOrigin(kOrigin1), kNonce1), }; for (const auto& info : kNegativeTestCases) { EXPECT_TRUE(info.Serialize().empty()); } const IsolationInfo kNegativeWhenTripleKeyEnabledTestCases[] = { // With an opaque frame origin. When the NIK is triple-keyed, the opaque // frame site will cause it to be considered transient and fail to // serialize. When triple-keying is disabled, a boolean is used in place // of the frame site, so the NIK won't be considered transient anymore. // This will cause the IsolationInfo to be serialized, except that it // doesn't serialize opaque origins with the nonce, so upon // deserialization the recreated IsolationInfo will have a frame site // with a different nonce (i.e. a different opaque origin). IsolationInfo::Create(IsolationInfo::RequestType::kSubFrame, kOrigin1, url::Origin(), SiteForCookies::FromOrigin(kOrigin1)), }; for (const auto& info : kNegativeWhenTripleKeyEnabledTestCases) { switch (NetworkIsolationKey::GetMode()) { case NetworkIsolationKey::Mode::kFrameSiteEnabled: EXPECT_TRUE(info.Serialize().empty()); break; case NetworkIsolationKey::Mode::kCrossSiteFlagEnabled: case NetworkIsolationKey::Mode::kFrameSiteWithSharedOpaqueEnabled: auto rt = IsolationInfo::Deserialize(info.Serialize()); ASSERT_TRUE(rt); // See comment above for why this check fails. EXPECT_FALSE(rt->IsEqualForTesting(info)); EXPECT_TRUE(rt->frame_origin()->opaque()); EXPECT_NE(rt->frame_origin(), info.frame_origin()); break; } } } } // namespace } // namespace net