| // Copyright 2022 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/network_anonymization_key.h" |
| |
| #include "base/test/gtest_util.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/unguessable_token.h" |
| #include "base/values.h" |
| #include "net/base/features.h" |
| #include "net/base/schemeful_site.h" |
| #include "network_anonymization_key.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "url/gurl.h" |
| #include "url/url_util.h" |
| |
| namespace net { |
| |
| class NetworkAnonymizationKeyTest : public testing::Test { |
| protected: |
| const SchemefulSite kTestSiteA = SchemefulSite(GURL("http://a.test/")); |
| const SchemefulSite kTestSiteB = SchemefulSite(GURL("http://b.test/")); |
| const SchemefulSite kDataSite = SchemefulSite(GURL("data:foo")); |
| const base::UnguessableToken kNonce = base::UnguessableToken::Create(); |
| }; |
| |
| class NetworkAnonymizationKeyTestWithNikMode |
| : public NetworkAnonymizationKeyTest, |
| public testing::WithParamInterface<NetworkIsolationKey::Mode> { |
| public: |
| NetworkAnonymizationKeyTestWithNikMode() { |
| switch (GetParam()) { |
| case NetworkIsolationKey::Mode::kFrameSiteEnabled: |
| scoped_feature_list_.InitAndDisableFeature( |
| net::features::kEnableCrossSiteFlagNetworkIsolationKey); |
| break; |
| case NetworkIsolationKey::Mode::kCrossSiteFlagEnabled: |
| scoped_feature_list_.InitAndEnableFeature( |
| net::features::kEnableCrossSiteFlagNetworkIsolationKey); |
| break; |
| } |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| Tests, |
| NetworkAnonymizationKeyTestWithNikMode, |
| testing::ValuesIn({NetworkIsolationKey::Mode::kFrameSiteEnabled, |
| NetworkIsolationKey::Mode::kCrossSiteFlagEnabled}), |
| [](const testing::TestParamInfo<NetworkIsolationKey::Mode>& info) { |
| return info.param == NetworkIsolationKey::Mode::kFrameSiteEnabled |
| ? "NikFrameSiteEnabled" |
| : "NikCrossSiteFlagEnabled"; |
| }); |
| |
| TEST_P(NetworkAnonymizationKeyTestWithNikMode, CreateFromNetworkIsolationKey) { |
| SchemefulSite site_a = SchemefulSite(GURL("http://a.test/")); |
| SchemefulSite site_b = SchemefulSite(GURL("http://b.test/")); |
| SchemefulSite opaque = SchemefulSite(url::Origin()); |
| base::UnguessableToken nik_nonce = base::UnguessableToken::Create(); |
| |
| NetworkIsolationKey populated_cross_site_nik(site_a, site_b, nik_nonce); |
| NetworkIsolationKey populated_same_site_nik(site_a, site_a, nik_nonce); |
| NetworkIsolationKey populated_same_site_opaque_nik(opaque, opaque, nik_nonce); |
| NetworkIsolationKey empty_nik; |
| |
| NetworkAnonymizationKey nak_from_same_site_nik = |
| NetworkAnonymizationKey::CreateFromNetworkIsolationKey( |
| populated_same_site_nik); |
| NetworkAnonymizationKey nak_from_cross_site_nik = |
| NetworkAnonymizationKey::CreateFromNetworkIsolationKey( |
| populated_cross_site_nik); |
| NetworkAnonymizationKey nak_from_same_site_opaque_nik = |
| NetworkAnonymizationKey::CreateFromNetworkIsolationKey( |
| populated_same_site_opaque_nik); |
| NetworkAnonymizationKey nak_from_empty_nik = |
| NetworkAnonymizationKey::CreateFromNetworkIsolationKey(empty_nik); |
| |
| // NAKs created when there is no top frame site on the NIK should create an |
| // empty NAK. |
| EXPECT_TRUE(nak_from_empty_nik.IsEmpty()); |
| |
| // Top site should be populated correctly. |
| EXPECT_EQ(nak_from_same_site_nik.GetTopFrameSite(), site_a); |
| EXPECT_EQ(nak_from_cross_site_nik.GetTopFrameSite(), site_a); |
| EXPECT_EQ(nak_from_same_site_opaque_nik.GetTopFrameSite(), opaque); |
| |
| // Nonce should be populated correctly. |
| EXPECT_EQ(nak_from_same_site_nik.GetNonce(), nik_nonce); |
| EXPECT_EQ(nak_from_cross_site_nik.GetNonce(), nik_nonce); |
| EXPECT_EQ(nak_from_same_site_opaque_nik.GetNonce(), nik_nonce); |
| |
| // Is cross site boolean should be populated correctly. |
| EXPECT_TRUE(nak_from_same_site_nik.IsSameSite()); |
| EXPECT_TRUE(nak_from_cross_site_nik.IsCrossSite()); |
| EXPECT_TRUE(nak_from_same_site_opaque_nik.IsSameSite()); |
| |
| // Double-keyed + cross site bit NAKs created from different third party |
| // cross site contexts should be the different. |
| EXPECT_FALSE(nak_from_same_site_nik == nak_from_cross_site_nik); |
| } |
| |
| TEST_F(NetworkAnonymizationKeyTest, CreateSameSite) { |
| SchemefulSite site = SchemefulSite(GURL("http://a.test/")); |
| SchemefulSite opaque = SchemefulSite(url::Origin()); |
| NetworkAnonymizationKey key; |
| |
| key = NetworkAnonymizationKey::CreateSameSite(site); |
| EXPECT_EQ(key.GetTopFrameSite(), site); |
| EXPECT_FALSE(key.GetNonce().has_value()); |
| EXPECT_TRUE(key.IsSameSite()); |
| |
| key = NetworkAnonymizationKey::CreateSameSite(opaque); |
| EXPECT_EQ(key.GetTopFrameSite(), opaque); |
| EXPECT_FALSE(key.GetNonce().has_value()); |
| EXPECT_TRUE(key.IsSameSite()); |
| } |
| |
| TEST_F(NetworkAnonymizationKeyTest, CreateCrossSite) { |
| SchemefulSite site = SchemefulSite(GURL("http://a.test/")); |
| SchemefulSite opaque = SchemefulSite(url::Origin()); |
| NetworkAnonymizationKey key; |
| |
| key = NetworkAnonymizationKey::CreateCrossSite(site); |
| EXPECT_EQ(key.GetTopFrameSite(), site); |
| EXPECT_FALSE(key.GetNonce().has_value()); |
| EXPECT_TRUE(key.IsCrossSite()); |
| |
| key = NetworkAnonymizationKey::CreateCrossSite(opaque); |
| EXPECT_EQ(key.GetTopFrameSite(), opaque); |
| EXPECT_FALSE(key.GetNonce().has_value()); |
| EXPECT_TRUE(key.IsCrossSite()); |
| } |
| |
| TEST_F(NetworkAnonymizationKeyTest, CreateFromFrameSite) { |
| SchemefulSite site_a = SchemefulSite(GURL("http://a.test/")); |
| SchemefulSite site_b = SchemefulSite(GURL("http://b.test/")); |
| SchemefulSite opaque = SchemefulSite(url::Origin()); |
| base::UnguessableToken nonce = base::UnguessableToken::Create(); |
| |
| NetworkAnonymizationKey nak_from_same_site = |
| NetworkAnonymizationKey::CreateFromFrameSite(site_a, site_a, nonce); |
| NetworkAnonymizationKey nak_from_cross_site = |
| NetworkAnonymizationKey::CreateFromFrameSite(site_a, site_b, nonce); |
| NetworkAnonymizationKey nak_from_same_site_opaque = |
| NetworkAnonymizationKey::CreateFromFrameSite(opaque, opaque, nonce); |
| |
| // Top site should be populated correctly. |
| EXPECT_EQ(nak_from_same_site.GetTopFrameSite(), site_a); |
| EXPECT_EQ(nak_from_cross_site.GetTopFrameSite(), site_a); |
| EXPECT_EQ(nak_from_same_site_opaque.GetTopFrameSite(), opaque); |
| |
| // Nonce should be populated correctly. |
| EXPECT_EQ(nak_from_same_site.GetNonce(), nonce); |
| EXPECT_EQ(nak_from_cross_site.GetNonce(), nonce); |
| EXPECT_EQ(nak_from_same_site_opaque.GetNonce(), nonce); |
| |
| // Is cross site boolean should be populated correctly. |
| EXPECT_TRUE(nak_from_same_site.IsSameSite()); |
| EXPECT_TRUE(nak_from_cross_site.IsCrossSite()); |
| EXPECT_TRUE(nak_from_same_site_opaque.IsSameSite()); |
| |
| // Double-keyed + cross site bit NAKs created from different third party |
| // cross site contexts should be the different. |
| EXPECT_FALSE(nak_from_same_site == nak_from_cross_site); |
| } |
| |
| TEST_F(NetworkAnonymizationKeyTest, IsEmpty) { |
| NetworkAnonymizationKey empty_key; |
| NetworkAnonymizationKey populated_key = |
| NetworkAnonymizationKey::CreateFromParts(/*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/false, |
| /*nonce=*/absl::nullopt); |
| |
| EXPECT_TRUE(empty_key.IsEmpty()); |
| EXPECT_FALSE(populated_key.IsEmpty()); |
| } |
| |
| TEST_F(NetworkAnonymizationKeyTest, CreateTransient) { |
| NetworkAnonymizationKey transient_key1 = |
| NetworkAnonymizationKey::CreateTransient(); |
| NetworkAnonymizationKey transient_key2 = |
| NetworkAnonymizationKey::CreateTransient(); |
| |
| EXPECT_TRUE(transient_key1.IsTransient()); |
| EXPECT_TRUE(transient_key2.IsTransient()); |
| EXPECT_FALSE(transient_key1 == transient_key2); |
| } |
| |
| TEST_F(NetworkAnonymizationKeyTest, IsTransient) { |
| NetworkAnonymizationKey empty_key; |
| NetworkAnonymizationKey populated_key = |
| NetworkAnonymizationKey::CreateFromParts(/*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/false, |
| /*nonce=*/absl::nullopt); |
| NetworkAnonymizationKey data_top_frame_key = |
| NetworkAnonymizationKey::CreateFromParts(/*top_frame_site=*/kDataSite, |
| /*is_cross_site=*/false, |
| /*nonce=*/absl::nullopt); |
| NetworkAnonymizationKey populated_key_with_nonce = |
| NetworkAnonymizationKey::CreateFromParts( |
| /*top_frame_site=*/kTestSiteA, |
| /*is_cross_site*/ false, base::UnguessableToken::Create()); |
| NetworkAnonymizationKey data_frame_key = |
| NetworkAnonymizationKey::CreateFromParts(/*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/false, |
| /*nonce=*/absl::nullopt); |
| |
| NetworkAnonymizationKey from_create_transient = |
| NetworkAnonymizationKey::CreateTransient(); |
| |
| EXPECT_TRUE(empty_key.IsTransient()); |
| EXPECT_FALSE(populated_key.IsTransient()); |
| EXPECT_TRUE(data_top_frame_key.IsTransient()); |
| EXPECT_TRUE(populated_key_with_nonce.IsTransient()); |
| EXPECT_TRUE(from_create_transient.IsTransient()); |
| |
| NetworkAnonymizationKey populated_double_key = |
| NetworkAnonymizationKey::CreateFromParts(/*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/false, |
| /*nonce=*/absl::nullopt); |
| EXPECT_FALSE(data_frame_key.IsTransient()); |
| EXPECT_FALSE(populated_double_key.IsTransient()); |
| } |
| |
| TEST_F(NetworkAnonymizationKeyTest, IsFullyPopulated) { |
| NetworkAnonymizationKey empty_key; |
| NetworkAnonymizationKey populated_key = |
| NetworkAnonymizationKey::CreateFromParts(/*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/false, |
| /*nonce=*/absl::nullopt); |
| EXPECT_TRUE(populated_key.IsFullyPopulated()); |
| EXPECT_FALSE(empty_key.IsFullyPopulated()); |
| NetworkAnonymizationKey empty_frame_site_key = |
| NetworkAnonymizationKey::CreateFromParts(/*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/false, |
| /*nonce=*/absl::nullopt); |
| EXPECT_TRUE(empty_frame_site_key.IsFullyPopulated()); |
| } |
| |
| TEST_F(NetworkAnonymizationKeyTest, Getters) { |
| NetworkAnonymizationKey key = |
| NetworkAnonymizationKey::CreateFromParts(/*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/true, kNonce); |
| |
| EXPECT_EQ(key.GetTopFrameSite(), kTestSiteA); |
| EXPECT_EQ(key.GetNonce(), kNonce); |
| |
| EXPECT_TRUE(key.IsCrossSite()); |
| } |
| |
| TEST_F(NetworkAnonymizationKeyTest, ToDebugString) { |
| NetworkAnonymizationKey key = |
| NetworkAnonymizationKey::CreateFromParts(/*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/true, kNonce); |
| NetworkAnonymizationKey empty_key; |
| |
| // `is_cross_site` holds the value the key is created with. |
| std::string double_key_with_cross_site_flag_expected_string_value = |
| kTestSiteA.GetDebugString() + " cross_site (with nonce " + |
| kNonce.ToString() + ")"; |
| EXPECT_EQ(key.ToDebugString(), |
| double_key_with_cross_site_flag_expected_string_value); |
| EXPECT_EQ(empty_key.ToDebugString(), "null"); |
| } |
| |
| TEST_F(NetworkAnonymizationKeyTest, Equality) { |
| NetworkAnonymizationKey key = |
| NetworkAnonymizationKey::CreateFromParts(/*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/false, kNonce); |
| NetworkAnonymizationKey key_duplicate = |
| NetworkAnonymizationKey::CreateFromParts(/*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/false, kNonce); |
| EXPECT_TRUE(key == key_duplicate); |
| EXPECT_FALSE(key != key_duplicate); |
| EXPECT_FALSE(key < key_duplicate); |
| |
| NetworkAnonymizationKey key_cross_site = |
| NetworkAnonymizationKey::CreateFromParts(/*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/true, kNonce); |
| |
| // The `is_cross_site` flag changes the NAK. |
| EXPECT_FALSE(key == key_cross_site); |
| EXPECT_TRUE(key != key_cross_site); |
| EXPECT_TRUE(key < key_cross_site); |
| |
| NetworkAnonymizationKey key_no_nonce = |
| NetworkAnonymizationKey::CreateFromParts(/*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/false, |
| /*nonce=*/absl::nullopt); |
| EXPECT_FALSE(key == key_no_nonce); |
| EXPECT_TRUE(key != key_no_nonce); |
| EXPECT_FALSE(key < key_no_nonce); |
| |
| NetworkAnonymizationKey key_different_nonce = |
| NetworkAnonymizationKey::CreateFromParts( |
| /*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/false, |
| /*nonce=*/base::UnguessableToken::Create()); |
| EXPECT_FALSE(key == key_different_nonce); |
| EXPECT_TRUE(key != key_different_nonce); |
| |
| NetworkAnonymizationKey key_different_frame_site = |
| NetworkAnonymizationKey::CreateFromParts( |
| /*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/false, kNonce); |
| |
| EXPECT_TRUE(key == key_different_frame_site); |
| EXPECT_FALSE(key != key_different_frame_site); |
| EXPECT_FALSE(key < key_different_frame_site); |
| |
| NetworkAnonymizationKey key_different_top_level_site = |
| NetworkAnonymizationKey::CreateFromParts( |
| /*top_frame_site=*/kTestSiteB, |
| /*is_cross_site=*/false, kNonce); |
| EXPECT_FALSE(key == key_different_top_level_site); |
| EXPECT_TRUE(key != key_different_top_level_site); |
| EXPECT_TRUE(key < key_different_top_level_site); |
| |
| NetworkAnonymizationKey empty_key; |
| NetworkAnonymizationKey empty_key_duplicate; |
| EXPECT_TRUE(empty_key == empty_key_duplicate); |
| EXPECT_FALSE(empty_key != empty_key_duplicate); |
| EXPECT_FALSE(empty_key < empty_key_duplicate); |
| |
| EXPECT_FALSE(empty_key == key); |
| EXPECT_TRUE(empty_key != key); |
| EXPECT_TRUE(empty_key < key); |
| } |
| |
| TEST_F(NetworkAnonymizationKeyTest, ValueRoundTripCrossSite) { |
| const SchemefulSite kOpaqueSite = SchemefulSite(GURL("data:text/html,junk")); |
| NetworkAnonymizationKey original_key = |
| NetworkAnonymizationKey::CreateFromParts(/*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/true); |
| base::Value value; |
| ASSERT_TRUE(original_key.ToValue(&value)); |
| |
| // Fill initial value with opaque data, to make sure it's overwritten. |
| NetworkAnonymizationKey from_value_key = NetworkAnonymizationKey(); |
| EXPECT_TRUE(NetworkAnonymizationKey::FromValue(value, &from_value_key)); |
| EXPECT_EQ(original_key, from_value_key); |
| } |
| |
| TEST_F(NetworkAnonymizationKeyTest, ValueRoundTripSameSite) { |
| const SchemefulSite kOpaqueSite = SchemefulSite(GURL("data:text/html,junk")); |
| NetworkAnonymizationKey original_key = |
| NetworkAnonymizationKey::CreateFromParts(/*top_frame_site=*/kTestSiteA, |
| /*is_cross_site=*/false); |
| base::Value value; |
| ASSERT_TRUE(original_key.ToValue(&value)); |
| |
| // Fill initial value with opaque data, to make sure it's overwritten. |
| NetworkAnonymizationKey from_value_key = NetworkAnonymizationKey(); |
| EXPECT_TRUE(NetworkAnonymizationKey::FromValue(value, &from_value_key)); |
| EXPECT_EQ(original_key, from_value_key); |
| } |
| |
| TEST_F(NetworkAnonymizationKeyTest, TransientValueRoundTrip) { |
| const SchemefulSite kOpaqueSite = SchemefulSite(GURL("data:text/html,junk")); |
| NetworkAnonymizationKey original_key = |
| NetworkAnonymizationKey::CreateTransient(); |
| base::Value value; |
| ASSERT_FALSE(original_key.ToValue(&value)); |
| } |
| |
| TEST_F(NetworkAnonymizationKeyTest, EmptyValueRoundTrip) { |
| const SchemefulSite kOpaqueSite = SchemefulSite(GURL("data:text/html,junk")); |
| NetworkAnonymizationKey original_key; |
| base::Value value; |
| ASSERT_TRUE(original_key.ToValue(&value)); |
| |
| // Fill initial value with opaque data, to make sure it's overwritten. |
| NetworkAnonymizationKey from_value_key = NetworkAnonymizationKey(); |
| EXPECT_TRUE(NetworkAnonymizationKey::FromValue(value, &from_value_key)); |
| EXPECT_EQ(original_key, from_value_key); |
| } |
| |
| TEST(NetworkAnonymizationKeyFeatureShiftTest, |
| ValueRoundTripKeySchemeMissmatch) { |
| base::test::ScopedFeatureList scoped_feature_list_; |
| const SchemefulSite kOpaqueSite = SchemefulSite(GURL("data:text/html,junk")); |
| const SchemefulSite kTestSiteA = SchemefulSite(GURL("http://a.test/")); |
| const SchemefulSite kTestSiteB = SchemefulSite(GURL("http://b.test/")); |
| NetworkAnonymizationKey expected_failure_nak = NetworkAnonymizationKey(); |
| |
| // Create a cross site double key + cross site flag NetworkAnonymizationKey. |
| NetworkAnonymizationKey original_cross_site_double_key = |
| NetworkAnonymizationKey::CreateFromParts(kTestSiteA, false); |
| base::Value cross_site_double_key_value; |
| ASSERT_TRUE( |
| original_cross_site_double_key.ToValue(&cross_site_double_key_value)); |
| |
| // Check that deserializing a double keyed NetworkAnonymizationKey (a |
| // one-element list) fails, using the serialized site from |
| // `cross_site_double_key_value` to build it. |
| base::Value serialized_site = |
| cross_site_double_key_value.GetList()[0].Clone(); |
| base::Value::List double_key_list; |
| double_key_list.Append(serialized_site.Clone()); |
| base::Value double_key_value = base::Value(std::move(double_key_list)); |
| EXPECT_FALSE(NetworkAnonymizationKey::FromValue(double_key_value, |
| &expected_failure_nak)); |
| |
| // Check that deserializing a triple keyed value (a 2-element list |
| // containing two sites) fails. |
| base::Value::List triple_key_list; |
| triple_key_list.Append(serialized_site.Clone()); |
| triple_key_list.Append(std::move(serialized_site)); |
| base::Value triple_key_value = base::Value(std::move(triple_key_list)); |
| EXPECT_FALSE(NetworkAnonymizationKey::FromValue(triple_key_value, |
| &expected_failure_nak)); |
| |
| // Convert the successful value back to a NAK and verify. |
| NetworkAnonymizationKey from_value_cross_site_double_key = |
| NetworkAnonymizationKey(); |
| EXPECT_TRUE(NetworkAnonymizationKey::FromValue( |
| cross_site_double_key_value, &from_value_cross_site_double_key)); |
| EXPECT_EQ(original_cross_site_double_key, from_value_cross_site_double_key); |
| } |
| |
| } // namespace net |