| // Copyright (c) 2012 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. |
| |
| #ifndef NET_COOKIES_COOKIE_STORE_UNITTEST_H_ |
| #define NET_COOKIES_COOKIE_STORE_UNITTEST_H_ |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/location.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/message_loop/message_loop_current.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/string_tokenizer.h" |
| #include "base/threading/thread.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "net/cookies/canonical_cookie.h" |
| #include "net/cookies/cookie_store.h" |
| #include "net/cookies/cookie_store_test_callbacks.h" |
| #include "net/cookies/cookie_store_test_helpers.h" |
| #include "net/cookies/test_cookie_access_delegate.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| #if defined(OS_IOS) |
| #include "base/ios/ios_util.h" |
| #endif |
| |
| // This file declares unittest templates that can be used to test common |
| // behavior of any CookieStore implementation. |
| // See cookie_monster_unittest.cc for an example of an implementation. |
| |
| namespace net { |
| |
| using base::Thread; |
| using TimeRange = CookieDeletionInfo::TimeRange; |
| |
| const int kTimeout = 1000; |
| |
| const char kValidCookieLine[] = "A=B; path=/"; |
| |
| // The CookieStoreTestTraits must have the following members: |
| // struct CookieStoreTestTraits { |
| // // Factory function. Will be called at most once per test. |
| // static std::unique_ptr<CookieStore> Create(); |
| // |
| // // Drains the run loop(s) used to deliver cookie change notifications. |
| // static void DeliverChangeNotifications(); |
| // |
| // // The cookie store supports cookies with the exclude_httponly() option. |
| // static const bool supports_http_only; |
| // |
| // // The cookie store is able to make the difference between the ".com" |
| // // and the "com" domains. |
| // static const bool supports_non_dotted_domains; |
| // |
| // // The cookie store does not fold domains with trailing dots (so "com." and |
| // "com" are different domains). |
| // static const bool preserves_trailing_dots; |
| // |
| // // The cookie store rejects cookies for invalid schemes such as ftp. |
| // static const bool filters_schemes; |
| // |
| // // The cookie store has a bug happening when a path is a substring of |
| // // another. |
| // static const bool has_path_prefix_bug; |
| // |
| // // The cookie store forbids setting a cookie with an empty name. |
| // static const bool forbids_setting_empty_name; |
| // |
| // // The cookie store supports global tracking of cookie changes (i.e. |
| // // calls to CookieStore::AddCallbackForAllChanges()). |
| // static const bool supports_global_cookie_tracking; |
| // |
| // // The cookie store supports tracking of cookie changes for an URL (i.e. |
| // // calls to CookieStore::AddCallbackForUrl()). |
| // static const bool supports_url_cookie_tracking; |
| // |
| // // The cookie store supports tracking of named cookie changes (i.e. |
| // // calls to CookieStore::AddCallbackForCookie()). |
| // static const bool supports_named_cookie_tracking; |
| // |
| // // The cookie store supports more than one callback per cookie change type. |
| // static const bool supports_multiple_tracking_callbacks; |
| // |
| // // The cookie store correctly distinguishes between OVERWRITE and EXPLICIT |
| // // (deletion) change causes. |
| // static const bool has_exact_change_cause; |
| // |
| // // The cookie store is guaranteed to deliver cookie changes in the order |
| // // in which calls were issued. This only applies to changes coming from |
| // // _different_ calls. If a call results in a cookie overwrite, the deletion |
| // // change must still be issued before the insertion change. |
| // static const bool has_exact_change_ordering; |
| // |
| // // Time to wait between two cookie insertions to ensure that cookies have |
| // // different creation times. |
| // static const int creation_time_granularity_in_ms; |
| // |
| // // The cookie store supports setting a CookieAccessDelegate and using it to |
| // // get the access semantics for each cookie via |
| // // CookieStore::GetAllCookiesWithAccessSemanticsAsync(). |
| // // If this is not supported, all access semantics will show up as UNKNOWN. |
| // static const bool supports_cookie_access_semantics; |
| // }; |
| |
| template <class CookieStoreTestTraits> |
| class CookieStoreTest : public testing::Test { |
| protected: |
| CookieStoreTest() |
| : http_www_foo_("http://www.foo.com"), |
| http_bar_foo_("http://bar.foo.com"), |
| http_www_bar_("http://www.bar.com"), |
| https_www_foo_("https://www.foo.com"), |
| ftp_foo_("ftp://ftp.foo.com/"), |
| ws_www_foo_("ws://www.foo.com"), |
| wss_www_foo_("wss://www.foo.com"), |
| www_foo_foo_("http://www.foo.com/foo"), |
| www_foo_bar_("http://www.foo.com/bar"), |
| http_baz_com_("http://baz.com"), |
| http_bar_com_("http://bar.com") { |
| // This test may be used outside of the net test suite, and thus may not |
| // have a message loop. |
| if (!base::MessageLoopCurrent::Get()) |
| message_loop_.reset(new base::MessageLoop); |
| } |
| |
| // Helper methods for the asynchronous Cookie Store API that call the |
| // asynchronous method and then pump the loop until the callback is invoked, |
| // finally returning the value. |
| // TODO(chlily): Consolidate some of these. |
| |
| std::string GetCookies(CookieStore* cs, const GURL& url) { |
| DCHECK(cs); |
| CookieOptions options; |
| if (!CookieStoreTestTraits::supports_http_only) |
| options.set_include_httponly(); |
| options.set_same_site_cookie_context( |
| net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); |
| return GetCookiesWithOptions(cs, url, options); |
| } |
| |
| std::string GetCookiesWithOptions(CookieStore* cs, |
| const GURL& url, |
| const CookieOptions& options) { |
| return CanonicalCookie::BuildCookieLine( |
| GetCookieListWithOptions(cs, url, options)); |
| } |
| |
| CookieList GetCookieListWithOptions(CookieStore* cs, |
| const GURL& url, |
| const CookieOptions& options) { |
| DCHECK(cs); |
| GetCookieListCallback callback; |
| cs->GetCookieListWithOptionsAsync(url, options, callback.MakeCallback()); |
| callback.WaitUntilDone(); |
| return callback.cookies(); |
| } |
| |
| // This does not update the access time on the cookies. |
| CookieList GetAllCookiesForURL(CookieStore* cs, const GURL& url) { |
| return GetCookieListWithOptions(cs, url, CookieOptions::MakeAllInclusive()); |
| } |
| |
| // This does not update the access time on the cookies. |
| CookieStatusList GetExcludedCookiesForURL(CookieStore* cs, const GURL& url) { |
| DCHECK(cs); |
| GetCookieListCallback callback; |
| CookieOptions options = CookieOptions::MakeAllInclusive(); |
| options.set_return_excluded_cookies(); |
| cs->GetCookieListWithOptionsAsync(url, options, callback.MakeCallback()); |
| callback.WaitUntilDone(); |
| return callback.excluded_cookies(); |
| } |
| |
| CookieList GetAllCookies(CookieStore* cs) { |
| DCHECK(cs); |
| GetAllCookiesCallback callback; |
| cs->GetAllCookiesAsync(callback.MakeCallback()); |
| callback.WaitUntilDone(); |
| return callback.cookies(); |
| } |
| |
| bool CreateAndSetCookie( |
| CookieStore* cs, |
| const GURL& url, |
| const std::string& cookie_line, |
| const CookieOptions& options, |
| base::Optional<base::Time> server_time = base::nullopt) { |
| auto cookie = CanonicalCookie::Create(url, cookie_line, base::Time::Now(), |
| server_time); |
| if (!cookie) |
| return false; |
| DCHECK(cs); |
| ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> callback; |
| cs->SetCanonicalCookieAsync(std::move(cookie), url.scheme(), options, |
| callback.MakeCallback()); |
| callback.WaitUntilDone(); |
| return callback.result().IsInclude(); |
| } |
| |
| bool SetCanonicalCookie(CookieStore* cs, |
| std::unique_ptr<CanonicalCookie> cookie, |
| std::string source_scheme, |
| bool can_modify_httponly) { |
| DCHECK(cs); |
| ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> callback; |
| CookieOptions options; |
| if (can_modify_httponly) |
| options.set_include_httponly(); |
| options.set_same_site_cookie_context( |
| net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); |
| cs->SetCanonicalCookieAsync(std::move(cookie), std::move(source_scheme), |
| options, callback.MakeCallback()); |
| callback.WaitUntilDone(); |
| return callback.result().IsInclude(); |
| } |
| |
| bool SetCookieWithServerTime(CookieStore* cs, |
| const GURL& url, |
| const std::string& cookie_line, |
| const base::Time& server_time) { |
| CookieOptions options; |
| if (!CookieStoreTestTraits::supports_http_only) |
| options.set_include_httponly(); |
| options.set_same_site_cookie_context( |
| net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); |
| return CreateAndSetCookie(cs, url, cookie_line, options, |
| base::make_optional(server_time)); |
| } |
| |
| bool SetCookie(CookieStore* cs, |
| const GURL& url, |
| const std::string& cookie_line) { |
| CookieOptions options; |
| if (!CookieStoreTestTraits::supports_http_only) |
| options.set_include_httponly(); |
| options.set_same_site_cookie_context( |
| net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); |
| return CreateAndSetCookie(cs, url, cookie_line, options); |
| } |
| |
| CanonicalCookie::CookieInclusionStatus CreateAndSetCookieReturnStatus( |
| CookieStore* cs, |
| const GURL& url, |
| const std::string& cookie_line) { |
| CanonicalCookie::CookieInclusionStatus create_status; |
| auto cookie = CanonicalCookie::Create(url, cookie_line, base::Time::Now(), |
| base::nullopt /* server_time */, |
| &create_status); |
| if (!cookie) |
| return create_status; |
| |
| CookieOptions options; |
| if (!CookieStoreTestTraits::supports_http_only) |
| options.set_include_httponly(); |
| // Allow setting SameSite cookies. |
| options.set_same_site_cookie_context( |
| net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); |
| |
| DCHECK(cs); |
| ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> callback; |
| cs->SetCanonicalCookieAsync(std::move(cookie), url.scheme(), options, |
| callback.MakeCallback()); |
| callback.WaitUntilDone(); |
| return callback.result(); |
| } |
| |
| CanonicalCookie::CookieInclusionStatus SetCanonicalCookieReturnStatus( |
| CookieStore* cs, |
| std::unique_ptr<CanonicalCookie> cookie, |
| std::string source_scheme, |
| bool can_modify_httponly) { |
| DCHECK(cs); |
| ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> callback; |
| CookieOptions options; |
| if (can_modify_httponly) |
| options.set_include_httponly(); |
| options.set_same_site_cookie_context( |
| net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); |
| cs->SetCanonicalCookieAsync(std::move(cookie), std::move(source_scheme), |
| options, callback.MakeCallback()); |
| callback.WaitUntilDone(); |
| return callback.result(); |
| } |
| |
| uint32_t DeleteCanonicalCookie(CookieStore* cs, |
| const CanonicalCookie& cookie) { |
| DCHECK(cs); |
| ResultSavingCookieCallback<uint32_t> callback; |
| cs->DeleteCanonicalCookieAsync(cookie, callback.MakeCallback()); |
| callback.WaitUntilDone(); |
| return callback.result(); |
| } |
| |
| uint32_t DeleteCreatedInTimeRange(CookieStore* cs, |
| const TimeRange& creation_range) { |
| DCHECK(cs); |
| ResultSavingCookieCallback<uint32_t> callback; |
| cs->DeleteAllCreatedInTimeRangeAsync(creation_range, |
| callback.MakeCallback()); |
| callback.WaitUntilDone(); |
| return callback.result(); |
| } |
| |
| uint32_t DeleteAllCreatedInTimeRange(CookieStore* cs, |
| CookieDeletionInfo delete_info) { |
| DCHECK(cs); |
| ResultSavingCookieCallback<uint32_t> callback; |
| cs->DeleteAllMatchingInfoAsync(std::move(delete_info), |
| callback.MakeCallback()); |
| callback.WaitUntilDone(); |
| return callback.result(); |
| } |
| |
| uint32_t DeleteSessionCookies(CookieStore* cs) { |
| DCHECK(cs); |
| ResultSavingCookieCallback<uint32_t> callback; |
| cs->DeleteSessionCookiesAsync(callback.MakeCallback()); |
| callback.WaitUntilDone(); |
| return callback.result(); |
| } |
| |
| uint32_t DeleteAll(CookieStore* cs) { |
| DCHECK(cs); |
| ResultSavingCookieCallback<uint32_t> callback; |
| cs->DeleteAllAsync(callback.MakeCallback()); |
| callback.WaitUntilDone(); |
| return callback.result(); |
| } |
| |
| bool FindAndDeleteCookie(CookieStore* cs, |
| const std::string& domain, |
| const std::string& name) { |
| for (auto& cookie : this->GetAllCookies(cs)) { |
| if (cookie.Domain() == domain && cookie.Name() == name) |
| return this->DeleteCanonicalCookie(cs, cookie); |
| } |
| |
| return false; |
| } |
| |
| // Returns the CookieStore for the test - each test only uses one CookieStore. |
| CookieStore* GetCookieStore() { |
| if (!cookie_store_) |
| cookie_store_ = CookieStoreTestTraits::Create(); |
| return cookie_store_.get(); |
| } |
| |
| // Resets CookieStore. |
| void ResetCookieStore() { cookie_store_.reset(); } |
| |
| // Compares two cookie lines. |
| void MatchCookieLines(const std::string& line1, const std::string& line2) { |
| EXPECT_EQ(TokenizeCookieLine(line1), TokenizeCookieLine(line2)); |
| } |
| |
| // Check the cookie line by polling until equality or a timeout is reached. |
| void MatchCookieLineWithTimeout(CookieStore* cs, |
| const GURL& url, |
| const std::string& line) { |
| std::string cookies = GetCookies(cs, url); |
| bool matched = (TokenizeCookieLine(line) == TokenizeCookieLine(cookies)); |
| base::Time polling_end_date = base::Time::Now() + |
| base::TimeDelta::FromMilliseconds( |
| CookieStoreTestTraits::creation_time_granularity_in_ms); |
| |
| while (!matched && base::Time::Now() <= polling_end_date) { |
| base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); |
| cookies = GetCookies(cs, url); |
| matched = (TokenizeCookieLine(line) == TokenizeCookieLine(cookies)); |
| } |
| |
| EXPECT_TRUE(matched) << "\"" << cookies |
| << "\" does not match \"" << line << "\""; |
| } |
| |
| const CookieURLHelper http_www_foo_; |
| const CookieURLHelper http_bar_foo_; |
| const CookieURLHelper http_www_bar_; |
| const CookieURLHelper https_www_foo_; |
| const CookieURLHelper ftp_foo_; |
| const CookieURLHelper ws_www_foo_; |
| const CookieURLHelper wss_www_foo_; |
| const CookieURLHelper www_foo_foo_; |
| const CookieURLHelper www_foo_bar_; |
| const CookieURLHelper http_baz_com_; |
| const CookieURLHelper http_bar_com_; |
| |
| std::unique_ptr<base::MessageLoop> message_loop_; |
| |
| private: |
| // Returns a set of strings of type "name=value". Fails in case of duplicate. |
| std::set<std::string> TokenizeCookieLine(const std::string& line) { |
| std::set<std::string> tokens; |
| base::StringTokenizer tokenizer(line, " ;"); |
| while (tokenizer.GetNext()) |
| EXPECT_TRUE(tokens.insert(tokenizer.token()).second); |
| return tokens; |
| } |
| |
| std::unique_ptr<CookieStore> cookie_store_; |
| }; |
| TYPED_TEST_SUITE_P(CookieStoreTest); |
| |
| TYPED_TEST_P(CookieStoreTest, FilterTest) { |
| CookieStore* cs = this->GetCookieStore(); |
| |
| base::Time two_hours_ago = base::Time::Now() - base::TimeDelta::FromHours(2); |
| base::Time one_hour_ago = base::Time::Now() - base::TimeDelta::FromHours(1); |
| base::Time one_hour_from_now = |
| base::Time::Now() + base::TimeDelta::FromHours(1); |
| |
| std::unique_ptr<CanonicalCookie> cc(CanonicalCookie::CreateSanitizedCookie( |
| this->www_foo_foo_.url(), "A", "B", std::string(), "/foo", one_hour_ago, |
| one_hour_from_now, base::Time(), false, false, |
| CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT)); |
| ASSERT_TRUE(cc); |
| EXPECT_TRUE(this->SetCanonicalCookie(cs, std::move(cc), "https", |
| true /*modify_httponly*/)); |
| |
| // Note that for the creation time to be set exactly, without modification, |
| // it must be different from the one set by the line above. |
| cc = CanonicalCookie::CreateSanitizedCookie( |
| this->www_foo_bar_.url(), "C", "D", this->www_foo_bar_.domain(), "/bar", |
| two_hours_ago, base::Time(), one_hour_ago, false, true, |
| CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT); |
| ASSERT_TRUE(cc); |
| EXPECT_TRUE(this->SetCanonicalCookie(cs, std::move(cc), "https", |
| true /*modify_httponly*/)); |
| |
| // Because of strict secure cookies, it should not be possible to create |
| // a secure cookie with an HTTP URL. |
| |
| cc = CanonicalCookie::CreateSanitizedCookie( |
| this->http_www_foo_.url(), "E", "F", std::string(), std::string(), |
| base::Time(), base::Time(), base::Time(), true, false, |
| CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT); |
| ASSERT_FALSE(cc); |
| |
| cc = CanonicalCookie::CreateSanitizedCookie( |
| this->https_www_foo_.url(), "E", "F", std::string(), std::string(), |
| base::Time(), base::Time(), base::Time(), true, false, |
| CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT); |
| ASSERT_TRUE(cc); |
| EXPECT_TRUE(this->SetCanonicalCookie(cs, std::move(cc), "https", |
| true /*modify_httponly*/)); |
| |
| // Get all the cookies for a given URL, regardless of properties. This 'get()' |
| // operation shouldn't update the access time, as the test checks that the |
| // access time is set properly upon creation. Updating the access time would |
| // make that difficult. |
| CookieList cookies = this->GetAllCookiesForURL(cs, this->www_foo_foo_.url()); |
| CookieList::iterator it = cookies.begin(); |
| |
| ASSERT_TRUE(it != cookies.end()); |
| EXPECT_EQ("A", it->Name()); |
| EXPECT_EQ("B", it->Value()); |
| EXPECT_EQ(this->www_foo_foo_.host(), it->Domain()); |
| EXPECT_EQ("/foo", it->Path()); |
| EXPECT_EQ(one_hour_ago, it->CreationDate()); |
| EXPECT_TRUE(it->IsPersistent()); |
| // Expect expiration date is in the right range. Some cookie implementations |
| // may not record it with millisecond accuracy. |
| EXPECT_LE((one_hour_from_now - it->ExpiryDate()).magnitude().InSeconds(), 5); |
| // Some CookieStores don't store last access date. |
| if (!it->LastAccessDate().is_null()) |
| EXPECT_EQ(one_hour_ago, it->LastAccessDate()); |
| EXPECT_FALSE(it->IsSecure()); |
| EXPECT_FALSE(it->IsHttpOnly()); |
| |
| ASSERT_TRUE(++it == cookies.end()); |
| |
| // Verify that the cookie was set as 'httponly' by passing in a CookieOptions |
| // that excludes them and getting an empty result. |
| if (TypeParam::supports_http_only) { |
| net::CookieOptions options; |
| options.set_same_site_cookie_context( |
| net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); |
| cookies = |
| this->GetCookieListWithOptions(cs, this->www_foo_bar_.url(), options); |
| it = cookies.begin(); |
| ASSERT_TRUE(it == cookies.end()); |
| } |
| |
| // Get the cookie using the wide open |options|: |
| cookies = this->GetAllCookiesForURL(cs, this->www_foo_bar_.url()); |
| it = cookies.begin(); |
| |
| ASSERT_TRUE(it != cookies.end()); |
| EXPECT_EQ("C", it->Name()); |
| EXPECT_EQ("D", it->Value()); |
| EXPECT_EQ(this->www_foo_bar_.Format(".%D"), it->Domain()); |
| EXPECT_EQ("/bar", it->Path()); |
| EXPECT_EQ(two_hours_ago, it->CreationDate()); |
| EXPECT_FALSE(it->IsPersistent()); |
| // Some CookieStores don't store last access date. |
| if (!it->LastAccessDate().is_null()) |
| EXPECT_EQ(one_hour_ago, it->LastAccessDate()); |
| EXPECT_FALSE(it->IsSecure()); |
| EXPECT_TRUE(it->IsHttpOnly()); |
| |
| EXPECT_TRUE(++it == cookies.end()); |
| |
| cookies = this->GetAllCookiesForURL(cs, this->https_www_foo_.url()); |
| it = cookies.begin(); |
| |
| ASSERT_TRUE(it != cookies.end()); |
| EXPECT_EQ("E", it->Name()); |
| EXPECT_EQ("F", it->Value()); |
| EXPECT_EQ("/", it->Path()); |
| EXPECT_EQ(this->https_www_foo_.host(), it->Domain()); |
| // Cookie should have its creation time set, and be in a reasonable range. |
| EXPECT_LE((base::Time::Now() - it->CreationDate()).magnitude().InMinutes(), |
| 2); |
| EXPECT_FALSE(it->IsPersistent()); |
| // Some CookieStores don't store last access date. |
| if (!it->LastAccessDate().is_null()) |
| EXPECT_EQ(it->CreationDate(), it->LastAccessDate()); |
| EXPECT_TRUE(it->IsSecure()); |
| EXPECT_FALSE(it->IsHttpOnly()); |
| |
| EXPECT_TRUE(++it == cookies.end()); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { |
| CookieStore* cs = this->GetCookieStore(); |
| |
| base::Time two_hours_ago = base::Time::Now() - base::TimeDelta::FromHours(2); |
| base::Time one_hour_ago = base::Time::Now() - base::TimeDelta::FromHours(1); |
| base::Time one_hour_from_now = |
| base::Time::Now() + base::TimeDelta::FromHours(1); |
| |
| std::string foo_foo_host(this->www_foo_foo_.url().host()); |
| std::string foo_bar_domain(this->www_foo_bar_.domain()); |
| std::string http_foo_host(this->http_www_foo_.url().host()); |
| std::string https_foo_host(this->https_www_foo_.url().host()); |
| |
| EXPECT_TRUE(this->SetCanonicalCookie( |
| cs, |
| std::make_unique<CanonicalCookie>( |
| "A", "B", foo_foo_host, "/foo", one_hour_ago, one_hour_from_now, |
| base::Time(), false /* secure */, false /* httponly */, |
| CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT), |
| "http", true)); |
| // Note that for the creation time to be set exactly, without modification, |
| // it must be different from the one set by the line above. |
| EXPECT_TRUE(this->SetCanonicalCookie( |
| cs, |
| std::make_unique<CanonicalCookie>( |
| "C", "D", "." + foo_bar_domain, "/bar", two_hours_ago, base::Time(), |
| one_hour_ago, false, true, CookieSameSite::NO_RESTRICTION, |
| COOKIE_PRIORITY_DEFAULT), |
| "http", true)); |
| |
| // A secure source is required for setting secure cookies. |
| EXPECT_TRUE( |
| this->SetCanonicalCookieReturnStatus( |
| cs, |
| std::make_unique<CanonicalCookie>( |
| "E", "F", http_foo_host, "/", base::Time(), base::Time(), |
| base::Time(), true, false, CookieSameSite::NO_RESTRICTION, |
| COOKIE_PRIORITY_DEFAULT), |
| "http", true) |
| .HasExclusionReason( |
| CanonicalCookie::CookieInclusionStatus::EXCLUDE_SECURE_ONLY)); |
| |
| // A Secure cookie can be created from an insecure URL, but is rejected upon |
| // setting. |
| CanonicalCookie::CookieInclusionStatus status; |
| auto cookie = CanonicalCookie::Create( |
| this->http_www_foo_.url(), "foo=1; Secure", base::Time::Now(), |
| base::nullopt /* server_time */, &status); |
| EXPECT_TRUE(cookie->IsSecure()); |
| EXPECT_TRUE(status.IsInclude()); |
| EXPECT_TRUE( |
| this->SetCanonicalCookieReturnStatus(cs, std::move(cookie), "http", true) |
| .HasExclusionReason( |
| CanonicalCookie::CookieInclusionStatus::EXCLUDE_SECURE_ONLY)); |
| |
| // A secure source is also required for overwriting secure cookies. Writing |
| // a secure cookie then overwriting it from a non-secure source should fail. |
| EXPECT_TRUE(this->SetCanonicalCookie( |
| cs, |
| std::make_unique<CanonicalCookie>( |
| "E", "F", http_foo_host, "/", base::Time(), base::Time(), |
| base::Time(), true /* secure */, false /* httponly */, |
| CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT), |
| "https", true /* modify_http_only */)); |
| |
| EXPECT_TRUE( |
| this->SetCanonicalCookieReturnStatus( |
| cs, |
| std::make_unique<CanonicalCookie>( |
| "E", "F", http_foo_host, "/", base::Time(), base::Time(), |
| base::Time(), true /* secure */, false /* httponly */, |
| CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT), |
| "http", true /* modify_http_only */) |
| .HasExclusionReason( |
| CanonicalCookie::CookieInclusionStatus::EXCLUDE_SECURE_ONLY)); |
| |
| if (TypeParam::supports_http_only) { |
| // Permission to modify http only cookies is required to set an |
| // httponly cookie. |
| EXPECT_TRUE( |
| this->SetCanonicalCookieReturnStatus( |
| cs, |
| std::make_unique<CanonicalCookie>( |
| "G", "H", http_foo_host, "/unique", base::Time(), |
| base::Time(), base::Time(), false /* secure */, |
| true /* httponly */, CookieSameSite::NO_RESTRICTION, |
| COOKIE_PRIORITY_DEFAULT), |
| "http", false /* modify_http_only */) |
| .HasExclusionReason( |
| CanonicalCookie::CookieInclusionStatus::EXCLUDE_HTTP_ONLY)); |
| |
| // A HttpOnly cookie can be created, but is rejected |
| // upon setting if the options do not specify include_httponly. |
| CanonicalCookie::CookieInclusionStatus create_status; |
| auto c = CanonicalCookie::Create( |
| this->http_www_foo_.url(), "bar=1; HttpOnly", base::Time::Now(), |
| base::nullopt /* server_time */, &create_status); |
| EXPECT_TRUE(c->IsHttpOnly()); |
| EXPECT_TRUE(create_status.IsInclude()); |
| EXPECT_TRUE( |
| this->SetCanonicalCookieReturnStatus(cs, std::move(c), "http", |
| false /* can_modify_httponly */) |
| .HasExclusionReason( |
| CanonicalCookie::CookieInclusionStatus::EXCLUDE_HTTP_ONLY)); |
| |
| // Permission to modify httponly cookies is also required to overwrite |
| // an httponly cookie. |
| EXPECT_TRUE(this->SetCanonicalCookie( |
| cs, |
| std::make_unique<CanonicalCookie>( |
| "G", "H", http_foo_host, "/unique", base::Time(), base::Time(), |
| base::Time(), false /* secure */, true /* httponly */, |
| CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT), |
| "http", true /* modify_http_only */)); |
| |
| EXPECT_TRUE( |
| this->SetCanonicalCookieReturnStatus( |
| cs, |
| std::make_unique<CanonicalCookie>( |
| "G", "H", http_foo_host, "/unique", base::Time(), |
| base::Time(), base::Time(), false /* secure */, |
| true /* httponly */, CookieSameSite::NO_RESTRICTION, |
| COOKIE_PRIORITY_DEFAULT), |
| "http", false /* modify_http_only */) |
| .HasExclusionReason( |
| CanonicalCookie::CookieInclusionStatus::EXCLUDE_HTTP_ONLY)); |
| } else { |
| // Leave store in same state as if the above tests had been run. |
| EXPECT_TRUE(this->SetCanonicalCookie( |
| cs, |
| std::make_unique<CanonicalCookie>( |
| "G", "H", http_foo_host, "/unique", base::Time(), base::Time(), |
| base::Time(), false /* secure */, true /* httponly */, |
| CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT), |
| "http", true /* modify_http_only */)); |
| } |
| |
| // Get all the cookies for a given URL, regardless of properties. This 'get()' |
| // operation shouldn't update the access time, as the test checks that the |
| // access time is set properly upon creation. Updating the access time would |
| // make that difficult. |
| CookieList cookies = this->GetAllCookiesForURL(cs, this->www_foo_foo_.url()); |
| CookieList::iterator it = cookies.begin(); |
| |
| ASSERT_EQ(1u, cookies.size()); |
| EXPECT_EQ("A", it->Name()); |
| EXPECT_EQ("B", it->Value()); |
| EXPECT_EQ(this->www_foo_foo_.host(), it->Domain()); |
| EXPECT_EQ("/foo", it->Path()); |
| EXPECT_EQ(one_hour_ago, it->CreationDate()); |
| EXPECT_TRUE(it->IsPersistent()); |
| // Expect expiration date is in the right range. Some cookie implementations |
| // may not record it with millisecond accuracy. |
| EXPECT_LE((one_hour_from_now - it->ExpiryDate()).magnitude().InSeconds(), 5); |
| // Some CookieStores don't store last access date. |
| if (!it->LastAccessDate().is_null()) |
| EXPECT_EQ(one_hour_ago, it->LastAccessDate()); |
| EXPECT_FALSE(it->IsSecure()); |
| EXPECT_FALSE(it->IsHttpOnly()); |
| |
| // Get the cookie using the wide open |options|: |
| cookies = this->GetAllCookiesForURL(cs, this->www_foo_bar_.url()); |
| ASSERT_EQ(1u, cookies.size()); |
| it = cookies.begin(); |
| |
| EXPECT_EQ("C", it->Name()); |
| EXPECT_EQ("D", it->Value()); |
| EXPECT_EQ(this->www_foo_bar_.Format(".%D"), it->Domain()); |
| EXPECT_EQ("/bar", it->Path()); |
| EXPECT_EQ(two_hours_ago, it->CreationDate()); |
| EXPECT_FALSE(it->IsPersistent()); |
| // Some CookieStores don't store last access date. |
| if (!it->LastAccessDate().is_null()) |
| EXPECT_EQ(one_hour_ago, it->LastAccessDate()); |
| EXPECT_FALSE(it->IsSecure()); |
| EXPECT_TRUE(it->IsHttpOnly()); |
| |
| cookies = this->GetAllCookiesForURL(cs, this->https_www_foo_.url()); |
| ASSERT_EQ(1u, cookies.size()); |
| it = cookies.begin(); |
| |
| EXPECT_EQ("E", it->Name()); |
| EXPECT_EQ("F", it->Value()); |
| EXPECT_EQ("/", it->Path()); |
| EXPECT_EQ(this->https_www_foo_.host(), it->Domain()); |
| // Cookie should have its creation time set, and be in a reasonable range. |
| EXPECT_LE((base::Time::Now() - it->CreationDate()).magnitude().InMinutes(), |
| 2); |
| EXPECT_FALSE(it->IsPersistent()); |
| // Some CookieStores don't store last access date. |
| if (!it->LastAccessDate().is_null()) |
| EXPECT_EQ(it->CreationDate(), it->LastAccessDate()); |
| EXPECT_TRUE(it->IsSecure()); |
| EXPECT_FALSE(it->IsHttpOnly()); |
| } |
| |
| // Test enforcement around setting secure cookies. |
| TYPED_TEST_P(CookieStoreTest, SecureEnforcement) { |
| CookieStore* cs = this->GetCookieStore(); |
| GURL http_url(this->http_www_foo_.url()); |
| std::string http_domain(http_url.host()); |
| GURL https_url(this->https_www_foo_.url()); |
| std::string https_domain(https_url.host()); |
| |
| // Confirm that setting the secure attribute from an insecure source fails, |
| // but the other combinations work. |
| EXPECT_FALSE(this->SetCanonicalCookie( |
| cs, |
| std::make_unique<CanonicalCookie>( |
| "A", "B", http_domain, "/", base::Time::Now(), base::Time(), |
| base::Time(), true, false, CookieSameSite::STRICT_MODE, |
| COOKIE_PRIORITY_DEFAULT), |
| "http", true /*modify_httponly*/)); |
| EXPECT_TRUE(this->SetCanonicalCookie( |
| cs, |
| std::make_unique<CanonicalCookie>( |
| "A", "B", http_domain, "/", base::Time::Now(), base::Time(), |
| base::Time(), true, false, CookieSameSite::STRICT_MODE, |
| COOKIE_PRIORITY_DEFAULT), |
| "https", true /*modify_httponly*/)); |
| EXPECT_TRUE(this->SetCanonicalCookie( |
| cs, |
| std::make_unique<CanonicalCookie>( |
| "A", "B", http_domain, "/", base::Time::Now(), base::Time(), |
| base::Time(), false, false, CookieSameSite::STRICT_MODE, |
| COOKIE_PRIORITY_DEFAULT), |
| "https", true /*modify_httponly*/)); |
| EXPECT_TRUE(this->SetCanonicalCookie( |
| cs, |
| std::make_unique<CanonicalCookie>( |
| "A", "B", http_domain, "/", base::Time::Now(), base::Time(), |
| base::Time(), false, false, CookieSameSite::STRICT_MODE, |
| COOKIE_PRIORITY_DEFAULT), |
| "http", true /*modify_httponly*/)); |
| } |
| |
| // The iOS networking stack uses the iOS cookie parser, which we do not |
| // control. While it is spec-compliant, that does not match the practical |
| // behavior of most UAs in some cases, which we try to replicate. See |
| // https://crbug.com/638389 for more information. |
| TYPED_TEST_P(CookieStoreTest, EmptyKeyTest) { |
| #if !defined(OS_IOS) |
| CookieStore* cs = this->GetCookieStore(); |
| |
| GURL url1("http://foo1.bar.com"); |
| EXPECT_TRUE(this->SetCookie(cs, url1, "foo")); |
| EXPECT_EQ("foo", this->GetCookies(cs, url1)); |
| |
| // Regression tests for https://crbug.com/601786 |
| GURL url2("http://foo2.bar.com"); |
| EXPECT_TRUE(this->SetCookie(cs, url2, "foo")); |
| EXPECT_TRUE(this->SetCookie(cs, url2, "\t")); |
| EXPECT_EQ("", this->GetCookies(cs, url2)); |
| |
| GURL url3("http://foo3.bar.com"); |
| EXPECT_TRUE(this->SetCookie(cs, url3, "foo")); |
| EXPECT_TRUE(this->SetCookie(cs, url3, "=")); |
| EXPECT_EQ("", this->GetCookies(cs, url3)); |
| |
| GURL url4("http://foo4.bar.com"); |
| EXPECT_TRUE(this->SetCookie(cs, url4, "foo")); |
| EXPECT_TRUE(this->SetCookie(cs, url4, "")); |
| EXPECT_EQ("", this->GetCookies(cs, url4)); |
| |
| GURL url5("http://foo5.bar.com"); |
| EXPECT_TRUE(this->SetCookie(cs, url5, "foo")); |
| EXPECT_TRUE(this->SetCookie(cs, url5, "; bar")); |
| EXPECT_EQ("", this->GetCookies(cs, url5)); |
| |
| GURL url6("http://foo6.bar.com"); |
| EXPECT_TRUE(this->SetCookie(cs, url6, "foo")); |
| EXPECT_TRUE(this->SetCookie(cs, url6, " ")); |
| EXPECT_EQ("", this->GetCookies(cs, url6)); |
| #endif |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, DomainTest) { |
| CookieStore* cs = this->GetCookieStore(); |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); |
| this->MatchCookieLines("A=B", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), |
| this->http_www_foo_.Format("C=D; domain=.%D"))); |
| this->MatchCookieLines("A=B; C=D", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| |
| // Verify that A=B was set as a host cookie rather than a domain |
| // cookie -- should not be accessible from a sub sub-domain. |
| this->MatchCookieLines( |
| "C=D", this->GetCookies( |
| cs, GURL(this->http_www_foo_.Format("http://foo.www.%D")))); |
| |
| // Test and make sure we find domain cookies on the same domain. |
| EXPECT_TRUE( |
| this->SetCookie(cs, this->http_www_foo_.url(), |
| this->http_www_foo_.Format("E=F; domain=.www.%D"))); |
| this->MatchCookieLines("A=B; C=D; E=F", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| |
| // Test setting a domain= that doesn't start w/ a dot, should |
| // treat it as a domain cookie, as if there was a pre-pended dot. |
| EXPECT_TRUE( |
| this->SetCookie(cs, this->http_www_foo_.url(), |
| this->http_www_foo_.Format("G=H; domain=www.%D"))); |
| this->MatchCookieLines("A=B; C=D; E=F; G=H", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| |
| // Test domain enforcement, should fail on a sub-domain or something too deep. |
| EXPECT_FALSE(this->SetCookie(cs, this->http_www_foo_.url(), |
| this->http_www_foo_.Format("I=J; domain=.%R"))); |
| this->MatchCookieLines( |
| std::string(), |
| this->GetCookies(cs, GURL(this->http_www_foo_.Format("http://a.%R")))); |
| EXPECT_FALSE( |
| this->SetCookie(cs, this->http_www_foo_.url(), |
| this->http_www_foo_.Format("K=L; domain=.bla.www.%D"))); |
| this->MatchCookieLines( |
| "C=D; E=F; G=H", |
| this->GetCookies(cs, |
| GURL(this->http_www_foo_.Format("http://bla.www.%D")))); |
| this->MatchCookieLines("A=B; C=D; E=F; G=H", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| } |
| |
| // FireFox recognizes domains containing trailing periods as valid. |
| // IE and Safari do not. Assert the expected policy here. |
| TYPED_TEST_P(CookieStoreTest, DomainWithTrailingDotTest) { |
| CookieStore* cs = this->GetCookieStore(); |
| // These two cases fail because the url, http://www.foo.com, does not match |
| // the domain given in the cookie line (due to the trailing dots), so the |
| // cookie is not created. |
| EXPECT_FALSE(this->SetCookie(cs, this->http_www_foo_.url(), |
| "a=1; domain=.www.foo.com.")); |
| EXPECT_FALSE(this->SetCookie(cs, this->http_www_foo_.url(), |
| "b=2; domain=.www.foo.com..")); |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| |
| GURL url_with_dot("http://www.foo.com."); |
| GURL url_with_double_dot("http://www.foo.com.."); |
| |
| // This succeeds because the urls match. |
| EXPECT_TRUE(this->SetCookie(cs, url_with_dot, "a=1; domain=.www.foo.com.")); |
| // This fails because two trailing dots are not allowed, so the cookie is not |
| // created. |
| EXPECT_FALSE( |
| this->SetCookie(cs, url_with_double_dot, "b=2; domain=.www.foo.com..")); |
| |
| if (TypeParam::preserves_trailing_dots) { |
| // If the CookieStore preserves trailing dots, then .www.foo.com is not |
| // considered the same as .www.foo.com. |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| this->MatchCookieLines("a=1", this->GetCookies(cs, url_with_dot)); |
| } else { |
| // If the CookieStore does not preserve trailing dots, the domains will both |
| // be folded into one. |
| this->MatchCookieLines("a=1", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| this->MatchCookieLines("a=1", this->GetCookies(cs, url_with_dot)); |
| } |
| } |
| |
| // Test that cookies can bet set on higher level domains. |
| TYPED_TEST_P(CookieStoreTest, ValidSubdomainTest) { |
| CookieStore* cs = this->GetCookieStore(); |
| GURL url_abcd("http://a.b.c.d.com"); |
| GURL url_bcd("http://b.c.d.com"); |
| GURL url_cd("http://c.d.com"); |
| GURL url_d("http://d.com"); |
| |
| EXPECT_TRUE(this->SetCookie(cs, url_abcd, "a=1; domain=.a.b.c.d.com")); |
| EXPECT_TRUE(this->SetCookie(cs, url_abcd, "b=2; domain=.b.c.d.com")); |
| EXPECT_TRUE(this->SetCookie(cs, url_abcd, "c=3; domain=.c.d.com")); |
| EXPECT_TRUE(this->SetCookie(cs, url_abcd, "d=4; domain=.d.com")); |
| |
| this->MatchCookieLines("a=1; b=2; c=3; d=4", this->GetCookies(cs, url_abcd)); |
| this->MatchCookieLines("b=2; c=3; d=4", this->GetCookies(cs, url_bcd)); |
| this->MatchCookieLines("c=3; d=4", this->GetCookies(cs, url_cd)); |
| this->MatchCookieLines("d=4", this->GetCookies(cs, url_d)); |
| |
| // Check that the same cookie can exist on different sub-domains. |
| EXPECT_TRUE(this->SetCookie(cs, url_bcd, "X=bcd; domain=.b.c.d.com")); |
| EXPECT_TRUE(this->SetCookie(cs, url_bcd, "X=cd; domain=.c.d.com")); |
| this->MatchCookieLines("b=2; c=3; d=4; X=bcd; X=cd", |
| this->GetCookies(cs, url_bcd)); |
| this->MatchCookieLines("c=3; d=4; X=cd", this->GetCookies(cs, url_cd)); |
| } |
| |
| // Test that setting a cookie which specifies an invalid domain has |
| // no side-effect. An invalid domain in this context is one which does |
| // not match the originating domain. |
| TYPED_TEST_P(CookieStoreTest, InvalidDomainTest) { |
| CookieStore* cs = this->GetCookieStore(); |
| GURL url_foobar("http://foo.bar.com"); |
| |
| // More specific sub-domain than allowed. |
| EXPECT_FALSE(this->SetCookie(cs, url_foobar, "a=1; domain=.yo.foo.bar.com")); |
| |
| // The iOS networking stack uses the iOS cookie parser, which we do not |
| // control. Its handling of multiple domain= values in cookie string varies |
| // depending on iOS version. See https://crbug.com/639167 |
| #if !defined(OS_IOS) |
| // Regression test for https://crbug.com/601786 |
| EXPECT_FALSE( |
| this->SetCookie(cs, url_foobar, "a=1; domain=.yo.foo.bar.com; domain=")); |
| #endif // !defined(OS_IOS) |
| |
| EXPECT_FALSE(this->SetCookie(cs, url_foobar, "b=2; domain=.foo.com")); |
| EXPECT_FALSE(this->SetCookie(cs, url_foobar, "c=3; domain=.bar.foo.com")); |
| |
| // Different TLD, but the rest is a substring. |
| EXPECT_FALSE(this->SetCookie(cs, url_foobar, "d=4; domain=.foo.bar.com.net")); |
| |
| // A substring that isn't really a parent domain. |
| EXPECT_FALSE(this->SetCookie(cs, url_foobar, "e=5; domain=ar.com")); |
| |
| // Completely invalid domains: |
| EXPECT_FALSE(this->SetCookie(cs, url_foobar, "f=6; domain=.")); |
| EXPECT_FALSE(this->SetCookie(cs, url_foobar, "g=7; domain=/")); |
| EXPECT_FALSE( |
| this->SetCookie(cs, url_foobar, "h=8; domain=http://foo.bar.com")); |
| EXPECT_FALSE(this->SetCookie(cs, url_foobar, "i=9; domain=..foo.bar.com")); |
| EXPECT_FALSE(this->SetCookie(cs, url_foobar, "j=10; domain=..bar.com")); |
| |
| // Make sure there isn't something quirky in the domain canonicalization |
| // that supports full URL semantics. |
| EXPECT_FALSE( |
| this->SetCookie(cs, url_foobar, "k=11; domain=.foo.bar.com?blah")); |
| EXPECT_FALSE( |
| this->SetCookie(cs, url_foobar, "l=12; domain=.foo.bar.com/blah")); |
| EXPECT_FALSE(this->SetCookie(cs, url_foobar, "m=13; domain=.foo.bar.com:80")); |
| EXPECT_FALSE(this->SetCookie(cs, url_foobar, "n=14; domain=.foo.bar.com:")); |
| EXPECT_FALSE( |
| this->SetCookie(cs, url_foobar, "o=15; domain=.foo.bar.com#sup")); |
| |
| this->MatchCookieLines(std::string(), this->GetCookies(cs, url_foobar)); |
| } |
| |
| // Make sure the cookie code hasn't gotten its subdomain string handling |
| // reversed, missed a suffix check, etc. It's important here that the two |
| // hosts below have the same domain + registry. |
| TYPED_TEST_P(CookieStoreTest, InvalidDomainSameDomainAndRegistry) { |
| CookieStore* cs = this->GetCookieStore(); |
| GURL url_foocom("http://foo.com.com"); |
| EXPECT_FALSE(this->SetCookie(cs, url_foocom, "a=1; domain=.foo.com.com.com")); |
| this->MatchCookieLines(std::string(), this->GetCookies(cs, url_foocom)); |
| } |
| |
| // Setting the domain without a dot on a parent domain should add a domain |
| // cookie. |
| TYPED_TEST_P(CookieStoreTest, DomainWithoutLeadingDotParentDomain) { |
| CookieStore* cs = this->GetCookieStore(); |
| GURL url_hosted("http://manage.hosted.filefront.com"); |
| GURL url_filefront("http://www.filefront.com"); |
| EXPECT_TRUE(this->SetCookie(cs, url_hosted, "sawAd=1; domain=filefront.com")); |
| this->MatchCookieLines("sawAd=1", this->GetCookies(cs, url_hosted)); |
| this->MatchCookieLines("sawAd=1", this->GetCookies(cs, url_filefront)); |
| } |
| |
| // Even when the specified domain matches the domain of the URL exactly, treat |
| // it as setting a domain cookie. |
| TYPED_TEST_P(CookieStoreTest, DomainWithoutLeadingDotSameDomain) { |
| CookieStore* cs = this->GetCookieStore(); |
| GURL url("http://www.foo.com"); |
| EXPECT_TRUE(this->SetCookie(cs, url, "a=1; domain=www.foo.com")); |
| this->MatchCookieLines("a=1", this->GetCookies(cs, url)); |
| this->MatchCookieLines("a=1", |
| this->GetCookies(cs, GURL("http://sub.www.foo.com"))); |
| this->MatchCookieLines( |
| std::string(), this->GetCookies(cs, GURL("http://something-else.com"))); |
| } |
| |
| // Test that the domain specified in cookie string is treated case-insensitive |
| TYPED_TEST_P(CookieStoreTest, CaseInsensitiveDomainTest) { |
| CookieStore* cs = this->GetCookieStore(); |
| GURL url("http://www.foo.com"); |
| EXPECT_TRUE(this->SetCookie(cs, url, "a=1; domain=.FOO.COM")); |
| EXPECT_TRUE(this->SetCookie(cs, url, "b=2; domain=.wWw.fOO.cOM")); |
| this->MatchCookieLines("a=1; b=2", this->GetCookies(cs, url)); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, TestIpAddress) { |
| GURL url_ip("http://1.2.3.4/weee"); |
| CookieStore* cs = this->GetCookieStore(); |
| EXPECT_TRUE(this->SetCookie(cs, url_ip, kValidCookieLine)); |
| this->MatchCookieLines("A=B", this->GetCookies(cs, url_ip)); |
| } |
| |
| // IP addresses should not be able to set domain cookies. |
| TYPED_TEST_P(CookieStoreTest, TestIpAddressNoDomainCookies) { |
| GURL url_ip("http://1.2.3.4/weee"); |
| CookieStore* cs = this->GetCookieStore(); |
| EXPECT_FALSE(this->SetCookie(cs, url_ip, "b=2; domain=.1.2.3.4")); |
| EXPECT_FALSE(this->SetCookie(cs, url_ip, "c=3; domain=.3.4")); |
| this->MatchCookieLines(std::string(), this->GetCookies(cs, url_ip)); |
| // It should be allowed to set a cookie if domain= matches the IP address |
| // exactly. This matches IE/Firefox, even though it seems a bit wrong. |
| EXPECT_FALSE(this->SetCookie(cs, url_ip, "b=2; domain=1.2.3.3")); |
| this->MatchCookieLines(std::string(), this->GetCookies(cs, url_ip)); |
| EXPECT_TRUE(this->SetCookie(cs, url_ip, "b=2; domain=1.2.3.4")); |
| this->MatchCookieLines("b=2", this->GetCookies(cs, url_ip)); |
| } |
| |
| // Test a TLD setting cookies on itself. |
| TYPED_TEST_P(CookieStoreTest, TestTLD) { |
| if (!TypeParam::supports_non_dotted_domains) |
| return; |
| CookieStore* cs = this->GetCookieStore(); |
| GURL url("http://com/"); |
| |
| // Allow setting on "com", (but only as a host cookie). |
| EXPECT_TRUE(this->SetCookie(cs, url, "a=1")); |
| // Domain cookies can't be set. |
| EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.com")); |
| // Exact matches between the domain attribute and the host are treated as |
| // host cookies, not domain cookies. |
| EXPECT_TRUE(this->SetCookie(cs, url, "c=3; domain=com")); |
| |
| this->MatchCookieLines("a=1; c=3", this->GetCookies(cs, url)); |
| |
| // Make sure they don't show up for a normal .com, they should be host, |
| // domain, cookies. |
| this->MatchCookieLines( |
| std::string(), |
| this->GetCookies(cs, GURL("http://hopefully-no-cookies.com/"))); |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, GURL("http://.com/"))); |
| } |
| |
| // http://com. should be treated the same as http://com. |
| TYPED_TEST_P(CookieStoreTest, TestTLDWithTerminalDot) { |
| CookieStore* cs = this->GetCookieStore(); |
| GURL url("http://com./index.html"); |
| EXPECT_TRUE(this->SetCookie(cs, url, "a=1")); |
| EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.com.")); |
| this->MatchCookieLines("a=1", this->GetCookies(cs, url)); |
| this->MatchCookieLines( |
| std::string(), |
| this->GetCookies(cs, GURL("http://hopefully-no-cookies.com./"))); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, TestSubdomainSettingCookiesOnUnknownTLD) { |
| CookieStore* cs = this->GetCookieStore(); |
| GURL url("http://a.b"); |
| EXPECT_FALSE(this->SetCookie(cs, url, "a=1; domain=.b")); |
| EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=b")); |
| this->MatchCookieLines(std::string(), this->GetCookies(cs, url)); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, TestSubdomainSettingCookiesOnKnownTLD) { |
| CookieStore* cs = this->GetCookieStore(); |
| GURL url("http://foo.com"); |
| EXPECT_FALSE(this->SetCookie(cs, url, "a=1; domain=.com")); |
| EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=com")); |
| this->MatchCookieLines(std::string(), this->GetCookies(cs, url)); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, TestSubdomainSettingCookiesOnKnownDottedTLD) { |
| CookieStore* cs = this->GetCookieStore(); |
| GURL url("http://foo.co.uk"); |
| EXPECT_FALSE(this->SetCookie(cs, url, "a=1; domain=.co.uk")); |
| EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.uk")); |
| this->MatchCookieLines(std::string(), this->GetCookies(cs, url)); |
| this->MatchCookieLines( |
| std::string(), this->GetCookies(cs, GURL("http://something-else.co.uk"))); |
| this->MatchCookieLines( |
| std::string(), this->GetCookies(cs, GURL("http://something-else.uk"))); |
| } |
| |
| // Intranet URLs should only be able to set host cookies. |
| TYPED_TEST_P(CookieStoreTest, TestSettingCookiesOnUnknownTLD) { |
| CookieStore* cs = this->GetCookieStore(); |
| GURL url("http://b"); |
| EXPECT_TRUE(this->SetCookie(cs, url, "a=1")); |
| EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.b")); |
| this->MatchCookieLines("a=1", this->GetCookies(cs, url)); |
| } |
| |
| // Exact matches between the domain attribute and an intranet host are |
| // treated as host cookies, not domain cookies. |
| TYPED_TEST_P(CookieStoreTest, TestSettingCookiesWithHostDomainOnUnknownTLD) { |
| if (!TypeParam::supports_non_dotted_domains) |
| return; |
| CookieStore* cs = this->GetCookieStore(); |
| GURL url("http://b"); |
| EXPECT_TRUE(this->SetCookie(cs, url, "a=1; domain=b")); |
| |
| this->MatchCookieLines("a=1", this->GetCookies(cs, url)); |
| |
| // Make sure it doesn't show up for an intranet subdomain, it should be |
| // a host, not domain, cookie. |
| this->MatchCookieLines( |
| std::string(), |
| this->GetCookies(cs, GURL("http://hopefully-no-cookies.b/"))); |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, GURL("http://.b/"))); |
| } |
| |
| // Test reading/writing cookies when the domain ends with a period, |
| // as in "www.foo.com." |
| TYPED_TEST_P(CookieStoreTest, TestHostEndsWithDot) { |
| CookieStore* cs = this->GetCookieStore(); |
| GURL url("http://www.foo.com"); |
| GURL url_with_dot("http://www.foo.com."); |
| EXPECT_TRUE(this->SetCookie(cs, url, "a=1")); |
| this->MatchCookieLines("a=1", this->GetCookies(cs, url)); |
| // This fails because the url does not match the domain, so the cookie cannot |
| // be created. |
| EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.www.foo.com.")); |
| this->MatchCookieLines("a=1", this->GetCookies(cs, url)); |
| // This cookie can be created because the url matches the domain, and it can |
| // be set, but the get-cookie result differs depending on whether the |
| // CookieStore preserves trailing dots. |
| EXPECT_TRUE(this->SetCookie(cs, url_with_dot, "b=2; domain=.foo.com.")); |
| |
| // Do not share cookie space with the dot version of domain. |
| // Note: this is not what FireFox does, but it _is_ what IE+Safari do. |
| if (TypeParam::preserves_trailing_dots) { |
| this->MatchCookieLines("a=1", this->GetCookies(cs, url)); |
| this->MatchCookieLines("b=2", this->GetCookies(cs, url_with_dot)); |
| } else { |
| this->MatchCookieLines("a=1 b=2", this->GetCookies(cs, url)); |
| this->MatchCookieLines("a=1 b=2", this->GetCookies(cs, url_with_dot)); |
| } |
| |
| // Make sure there weren't any side effects. |
| this->MatchCookieLines( |
| std::string(), |
| this->GetCookies(cs, GURL("http://hopefully-no-cookies.com/"))); |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, GURL("http://.com/"))); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, InvalidScheme) { |
| if (!TypeParam::filters_schemes) |
| return; |
| |
| CookieStore* cs = this->GetCookieStore(); |
| EXPECT_FALSE(this->SetCookie(cs, this->ftp_foo_.url(), kValidCookieLine)); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, InvalidScheme_Read) { |
| if (!TypeParam::filters_schemes) |
| return; |
| |
| const std::string kValidDomainCookieLine = |
| this->http_www_foo_.Format("A=B; path=/; domain=%D"); |
| |
| CookieStore* cs = this->GetCookieStore(); |
| EXPECT_TRUE( |
| this->SetCookie(cs, this->http_www_foo_.url(), kValidDomainCookieLine)); |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, this->ftp_foo_.url())); |
| EXPECT_EQ(0U, |
| this->GetCookieListWithOptions(cs, this->ftp_foo_.url(), |
| CookieOptions::MakeAllInclusive()) |
| .size()); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, PathTest) { |
| CookieStore* cs = this->GetCookieStore(); |
| std::string url("http://www.foo.com"); |
| EXPECT_TRUE(this->SetCookie(cs, GURL(url), "A=B; path=/wee")); |
| this->MatchCookieLines("A=B", this->GetCookies(cs, GURL(url + "/wee"))); |
| this->MatchCookieLines("A=B", this->GetCookies(cs, GURL(url + "/wee/"))); |
| this->MatchCookieLines("A=B", this->GetCookies(cs, GURL(url + "/wee/war"))); |
| this->MatchCookieLines( |
| "A=B", this->GetCookies(cs, GURL(url + "/wee/war/more/more"))); |
| if (!TypeParam::has_path_prefix_bug) |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, GURL(url + "/weehee"))); |
| this->MatchCookieLines(std::string(), this->GetCookies(cs, GURL(url + "/"))); |
| |
| // If we add a 0 length path, it should default to / |
| EXPECT_TRUE(this->SetCookie(cs, GURL(url), "A=C; path=")); |
| this->MatchCookieLines("A=B; A=C", this->GetCookies(cs, GURL(url + "/wee"))); |
| this->MatchCookieLines("A=C", this->GetCookies(cs, GURL(url + "/"))); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, EmptyExpires) { |
| CookieStore* cs = this->GetCookieStore(); |
| CookieOptions options; |
| if (!TypeParam::supports_http_only) |
| options.set_include_httponly(); |
| options.set_same_site_cookie_context( |
| net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); |
| GURL url("http://www7.ipdl.inpit.go.jp/Tokujitu/tjkta.ipdl?N0000=108"); |
| std::string set_cookie_line = |
| "ACSTM=20130308043820420042; path=/; domain=ipdl.inpit.go.jp; Expires="; |
| std::string cookie_line = "ACSTM=20130308043820420042"; |
| |
| this->CreateAndSetCookie(cs, url, set_cookie_line, options); |
| this->MatchCookieLines(cookie_line, |
| this->GetCookiesWithOptions(cs, url, options)); |
| |
| base::Optional<base::Time> server_time = |
| base::make_optional(base::Time::Now() - base::TimeDelta::FromHours(1)); |
| this->CreateAndSetCookie(cs, url, set_cookie_line, options, server_time); |
| this->MatchCookieLines(cookie_line, |
| this->GetCookiesWithOptions(cs, url, options)); |
| |
| server_time = base::Time::Now() + base::TimeDelta::FromHours(1); |
| this->CreateAndSetCookie(cs, url, set_cookie_line, options, server_time); |
| this->MatchCookieLines(cookie_line, |
| this->GetCookiesWithOptions(cs, url, options)); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, HttpOnlyTest) { |
| if (!TypeParam::supports_http_only) |
| return; |
| |
| CookieStore* cs = this->GetCookieStore(); |
| CookieOptions options; |
| options.set_include_httponly(); |
| options.set_same_site_cookie_context( |
| net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); |
| |
| // Create a httponly cookie. |
| EXPECT_TRUE(this->CreateAndSetCookie(cs, this->http_www_foo_.url(), |
| "A=B; httponly", options)); |
| |
| // Check httponly read protection. |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| this->MatchCookieLines("A=B", this->GetCookiesWithOptions( |
| cs, this->http_www_foo_.url(), options)); |
| |
| // Check httponly overwrite protection. |
| EXPECT_FALSE(this->SetCookie(cs, this->http_www_foo_.url(), "A=C")); |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| this->MatchCookieLines("A=B", this->GetCookiesWithOptions( |
| cs, this->http_www_foo_.url(), options)); |
| EXPECT_TRUE( |
| this->CreateAndSetCookie(cs, this->http_www_foo_.url(), "A=C", options)); |
| this->MatchCookieLines("A=C", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| |
| // Check httponly create protection. |
| EXPECT_FALSE(this->SetCookie(cs, this->http_www_foo_.url(), "B=A; httponly")); |
| this->MatchCookieLines("A=C", this->GetCookiesWithOptions( |
| cs, this->http_www_foo_.url(), options)); |
| EXPECT_TRUE(this->CreateAndSetCookie(cs, this->http_www_foo_.url(), |
| "B=A; httponly", options)); |
| this->MatchCookieLines( |
| "A=C; B=A", |
| this->GetCookiesWithOptions(cs, this->http_www_foo_.url(), options)); |
| this->MatchCookieLines("A=C", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, TestCookieDeletion) { |
| CookieStore* cs = this->GetCookieStore(); |
| |
| // Create a session cookie. |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), kValidCookieLine)); |
| this->MatchCookieLines("A=B", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| // Delete it via Max-Age. |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), |
| std::string(kValidCookieLine) + "; max-age=0")); |
| this->MatchCookieLineWithTimeout(cs, this->http_www_foo_.url(), |
| std::string()); |
| |
| // Create a session cookie. |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), kValidCookieLine)); |
| this->MatchCookieLines("A=B", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| // Delete it via Expires. |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), |
| std::string(kValidCookieLine) + |
| "; expires=Mon, 18-Apr-1977 22:50:13 GMT")); |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| |
| // Create a persistent cookie. |
| EXPECT_TRUE(this->SetCookie( |
| cs, this->http_www_foo_.url(), |
| std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT")); |
| |
| this->MatchCookieLines("A=B", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| // Delete it via Max-Age. |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), |
| std::string(kValidCookieLine) + "; max-age=0")); |
| this->MatchCookieLineWithTimeout(cs, this->http_www_foo_.url(), |
| std::string()); |
| |
| // Create a persistent cookie. |
| EXPECT_TRUE(this->SetCookie( |
| cs, this->http_www_foo_.url(), |
| std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT")); |
| this->MatchCookieLines("A=B", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| // Delete it via Expires. |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), |
| std::string(kValidCookieLine) + |
| "; expires=Mon, 18-Apr-1977 22:50:13 GMT")); |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| |
| // Create a persistent cookie. |
| EXPECT_TRUE(this->SetCookie( |
| cs, this->http_www_foo_.url(), |
| std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT")); |
| this->MatchCookieLines("A=B", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| // Check that it is not deleted with significant enough clock skew. |
| base::Time server_time; |
| EXPECT_TRUE(base::Time::FromString("Sun, 17-Apr-1977 22:50:13 GMT", |
| &server_time)); |
| EXPECT_TRUE(this->SetCookieWithServerTime( |
| cs, this->http_www_foo_.url(), |
| std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-1977 22:50:13 GMT", |
| server_time)); |
| this->MatchCookieLines("A=B", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| |
| // Create a persistent cookie. |
| EXPECT_TRUE(this->SetCookie( |
| cs, this->http_www_foo_.url(), |
| std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT")); |
| this->MatchCookieLines("A=B", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| // Delete it via Expires, with a unix epoch of 0. |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), |
| std::string(kValidCookieLine) + |
| "; expires=Thu, 1-Jan-1970 00:00:00 GMT")); |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, TestDeleteAll) { |
| CookieStore* cs = this->GetCookieStore(); |
| |
| // Set a session cookie. |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), kValidCookieLine)); |
| EXPECT_EQ("A=B", this->GetCookies(cs, this->http_www_foo_.url())); |
| |
| // Set a persistent cookie. |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), |
| "C=D; expires=Mon, 18-Apr-22 22:50:13 GMT")); |
| |
| EXPECT_EQ(2u, this->GetAllCookies(cs).size()); |
| |
| // Delete both, and make sure it works |
| EXPECT_EQ(2u, this->DeleteAll(cs)); |
| EXPECT_EQ(0u, this->GetAllCookies(cs).size()); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, TestDeleteAllCreatedInTimeRange) { |
| CookieStore* cs = this->GetCookieStore(); |
| const base::Time last_month = base::Time::Now() - |
| base::TimeDelta::FromDays(30); |
| const base::Time last_minute = base::Time::Now() - |
| base::TimeDelta::FromMinutes(1); |
| const base::Time next_minute = base::Time::Now() + |
| base::TimeDelta::FromMinutes(1); |
| const base::Time next_month = base::Time::Now() + |
| base::TimeDelta::FromDays(30); |
| |
| // Add a cookie. |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); |
| // Check that the cookie is in the store. |
| this->MatchCookieLines("A=B", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| |
| // Remove cookies in empty intervals. |
| EXPECT_EQ(0u, this->DeleteCreatedInTimeRange( |
| cs, TimeRange(last_month, last_minute))); |
| EXPECT_EQ(0u, this->DeleteCreatedInTimeRange( |
| cs, TimeRange(next_minute, next_month))); |
| // Check that the cookie is still there. |
| this->MatchCookieLines("A=B", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| |
| // Remove the cookie with an interval defined by two dates. |
| EXPECT_EQ(1u, this->DeleteCreatedInTimeRange( |
| cs, TimeRange(last_minute, next_minute))); |
| // Check that the cookie disappeared. |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| |
| // Add another cookie. |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); |
| // Check that the cookie is in the store. |
| this->MatchCookieLines("C=D", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| |
| // Remove the cookie with a null ending time. |
| EXPECT_EQ(1u, this->DeleteCreatedInTimeRange( |
| cs, TimeRange(last_minute, base::Time()))); |
| // Check that the cookie disappeared. |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, TestDeleteAllWithInfo) { |
| CookieStore* cs = this->GetCookieStore(); |
| base::Time now = base::Time::Now(); |
| base::Time last_month = base::Time::Now() - base::TimeDelta::FromDays(30); |
| base::Time last_minute = base::Time::Now() - base::TimeDelta::FromMinutes(1); |
| |
| // These 3 cookies match the time range and host. |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "Y=Z")); |
| EXPECT_TRUE(this->SetCookie(cs, this->https_www_foo_.url(), "E=B")); |
| |
| // Delete cookies. |
| CookieDeletionInfo delete_info(now, base::Time::Max()); |
| delete_info.value_for_testing = "B"; |
| EXPECT_EQ(2u, // Deletes A=B, E=B |
| this->DeleteAllCreatedInTimeRange(cs, std::move(delete_info))); |
| |
| // Check that we deleted the right ones. |
| this->MatchCookieLines("C=D;Y=Z", |
| this->GetCookies(cs, this->https_www_foo_.url())); |
| |
| // Finally, check that we don't delete cookies when our time range is off. |
| delete_info = CookieDeletionInfo(last_month, last_minute); |
| delete_info.value_for_testing = "D"; |
| EXPECT_EQ(0u, this->DeleteAllCreatedInTimeRange(cs, std::move(delete_info))); |
| this->MatchCookieLines("C=D;Y=Z", |
| this->GetCookies(cs, this->https_www_foo_.url())); |
| // Same thing, but with a good time range. |
| delete_info = CookieDeletionInfo(now, base::Time::Max()); |
| delete_info.value_for_testing = "D"; |
| EXPECT_EQ(1u, this->DeleteAllCreatedInTimeRange(cs, std::move(delete_info))); |
| this->MatchCookieLines("Y=Z", |
| this->GetCookies(cs, this->https_www_foo_.url())); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, TestSecure) { |
| CookieStore* cs = this->GetCookieStore(); |
| |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); |
| this->MatchCookieLines("A=B", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| this->MatchCookieLines("A=B", |
| this->GetCookies(cs, this->https_www_foo_.url())); |
| |
| EXPECT_TRUE(this->SetCookie(cs, this->https_www_foo_.url(), "A=B; secure")); |
| // The secure should overwrite the non-secure. |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| this->MatchCookieLines("A=B", |
| this->GetCookies(cs, this->https_www_foo_.url())); |
| |
| EXPECT_TRUE(this->SetCookie(cs, this->https_www_foo_.url(), "D=E; secure")); |
| this->MatchCookieLines(std::string(), |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| this->MatchCookieLines("A=B; D=E", |
| this->GetCookies(cs, this->https_www_foo_.url())); |
| |
| EXPECT_TRUE(this->SetCookie(cs, this->https_www_foo_.url(), "A=B")); |
| // The non-secure should overwrite the secure. |
| this->MatchCookieLines("A=B", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| this->MatchCookieLines("D=E; A=B", |
| this->GetCookies(cs, this->https_www_foo_.url())); |
| } |
| |
| // Formerly NetUtilTest.CookieTest back when we used wininet's cookie handling. |
| TYPED_TEST_P(CookieStoreTest, NetUtilCookieTest) { |
| const GURL test_url("http://mojo.jojo.foo.com/"); |
| |
| CookieStore* cs = this->GetCookieStore(); |
| |
| EXPECT_TRUE(this->SetCookie(cs, test_url, "foo=bar")); |
| std::string value = this->GetCookies(cs, test_url); |
| this->MatchCookieLines("foo=bar", value); |
| |
| // test that we can retrieve all cookies: |
| EXPECT_TRUE(this->SetCookie(cs, test_url, "x=1")); |
| EXPECT_TRUE(this->SetCookie(cs, test_url, "y=2")); |
| |
| std::string result = this->GetCookies(cs, test_url); |
| EXPECT_FALSE(result.empty()); |
| EXPECT_NE(result.find("x=1"), std::string::npos) << result; |
| EXPECT_NE(result.find("y=2"), std::string::npos) << result; |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, OverwritePersistentCookie) { |
| GURL url_foo("http://www.foo.com/"); |
| GURL url_chromium("http://chromium.org"); |
| CookieStore* cs = this->GetCookieStore(); |
| |
| // Insert a cookie "a" for path "/path1" |
| EXPECT_TRUE(this->SetCookie(cs, url_foo, |
| "a=val1; path=/path1; " |
| "expires=Mon, 18-Apr-22 22:50:13 GMT")); |
| |
| // Insert a cookie "b" for path "/path1" |
| EXPECT_TRUE(this->SetCookie(cs, url_foo, |
| "b=val1; path=/path1; " |
| "expires=Mon, 18-Apr-22 22:50:14 GMT")); |
| |
| // Insert a cookie "b" for path "/path1", that is httponly. This should |
| // overwrite the non-http-only version. |
| CookieOptions allow_httponly; |
| allow_httponly.set_include_httponly(); |
| allow_httponly.set_same_site_cookie_context( |
| net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); |
| EXPECT_TRUE(this->CreateAndSetCookie(cs, url_foo, |
| "b=val2; path=/path1; httponly; " |
| "expires=Mon, 18-Apr-22 22:50:14 GMT", |
| allow_httponly)); |
| |
| // Insert a cookie "a" for path "/path1". This should overwrite. |
| EXPECT_TRUE(this->SetCookie(cs, url_foo, |
| "a=val33; path=/path1; " |
| "expires=Mon, 18-Apr-22 22:50:14 GMT")); |
| |
| // Insert a cookie "a" for path "/path2". This should NOT overwrite |
| // cookie "a", since the path is different. |
| EXPECT_TRUE(this->SetCookie(cs, url_foo, |
| "a=val9; path=/path2; " |
| "expires=Mon, 18-Apr-22 22:50:14 GMT")); |
| |
| // Insert a cookie "a" for path "/path1", but this time for "chromium.org". |
| // Although the name and path match, the hostnames do not, so shouldn't |
| // overwrite. |
| EXPECT_TRUE(this->SetCookie(cs, url_chromium, |
| "a=val99; path=/path1; " |
| "expires=Mon, 18-Apr-22 22:50:14 GMT")); |
| |
| if (TypeParam::supports_http_only) { |
| this->MatchCookieLines( |
| "a=val33", this->GetCookies(cs, GURL("http://www.foo.com/path1"))); |
| } else { |
| this->MatchCookieLines( |
| "a=val33; b=val2", |
| this->GetCookies(cs, GURL("http://www.foo.com/path1"))); |
| } |
| this->MatchCookieLines( |
| "a=val9", this->GetCookies(cs, GURL("http://www.foo.com/path2"))); |
| this->MatchCookieLines( |
| "a=val99", this->GetCookies(cs, GURL("http://chromium.org/path1"))); |
| } |
| |
| // Note that accepting an empty name is contrary to spec; see |
| // https://tools.ietf.org/html/rfc6265#section-4.1.1. However, we do it |
| // for web compatibility; see http://inikulin.github.io/cookie-compat/ |
| // (specifically the "foo" and "=a" tests). This test is present in Chromium |
| // so that a flag is raised if this behavior is changed. |
| // On IOS we use the system cookie store which has Safari's behavior, so |
| // the test is skipped. |
| TYPED_TEST_P(CookieStoreTest, EmptyName) { |
| if (TypeParam::forbids_setting_empty_name) |
| return; |
| |
| GURL url_foo("http://www.foo.com/"); |
| CookieStore* cs = this->GetCookieStore(); |
| |
| CookieOptions options; |
| if (!TypeParam::supports_http_only) |
| options.set_include_httponly(); |
| options.set_same_site_cookie_context( |
| net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); |
| |
| EXPECT_TRUE(this->CreateAndSetCookie(cs, url_foo, "a", options)); |
| CookieList list = this->GetAllCookiesForURL(cs, url_foo); |
| EXPECT_EQ(1u, list.size()); |
| EXPECT_EQ("", list[0].Name()); |
| EXPECT_EQ("a", list[0].Value()); |
| EXPECT_EQ(1u, this->DeleteAll(cs)); |
| |
| EXPECT_TRUE(this->CreateAndSetCookie(cs, url_foo, "=b", options)); |
| list = this->GetAllCookiesForURL(cs, url_foo); |
| EXPECT_EQ(1u, list.size()); |
| EXPECT_EQ("", list[0].Name()); |
| EXPECT_EQ("b", list[0].Value()); |
| EXPECT_EQ(1u, this->DeleteAll(cs)); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, CookieOrdering) { |
| // Put a random set of cookies into a store and make sure they're returned in |
| // the right order. |
| // Cookies should be sorted by path length and creation time, as per RFC6265. |
| CookieStore* cs = this->GetCookieStore(); |
| EXPECT_TRUE( |
| this->SetCookie(cs, GURL("http://d.c.b.a.foo.com/aa/x.html"), "c=1")); |
| EXPECT_TRUE(this->SetCookie(cs, GURL("http://b.a.foo.com/aa/bb/cc/x.html"), |
| "d=1; domain=b.a.foo.com")); |
| base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds( |
| TypeParam::creation_time_granularity_in_ms)); |
| EXPECT_TRUE(this->SetCookie(cs, GURL("http://b.a.foo.com/aa/bb/cc/x.html"), |
| "a=4; domain=b.a.foo.com")); |
| base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds( |
| TypeParam::creation_time_granularity_in_ms)); |
| EXPECT_TRUE(this->SetCookie(cs, GURL("http://c.b.a.foo.com/aa/bb/cc/x.html"), |
| "e=1; domain=c.b.a.foo.com")); |
| EXPECT_TRUE( |
| this->SetCookie(cs, GURL("http://d.c.b.a.foo.com/aa/bb/x.html"), "b=1")); |
| EXPECT_TRUE(this->SetCookie(cs, GURL("http://news.bbc.co.uk/midpath/x.html"), |
| "g=10")); |
| EXPECT_EQ("d=1; a=4; e=1; b=1; c=1", |
| this->GetCookies(cs, GURL("http://d.c.b.a.foo.com/aa/bb/cc/dd"))); |
| |
| CookieOptions options; |
| options.set_same_site_cookie_context( |
| net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); |
| |
| CookieList cookies = this->GetCookieListWithOptions( |
| cs, GURL("http://d.c.b.a.foo.com/aa/bb/cc/dd"), options); |
| CookieList::const_iterator it = cookies.begin(); |
| |
| ASSERT_TRUE(it != cookies.end()); |
| EXPECT_EQ("d", it->Name()); |
| |
| ASSERT_TRUE(++it != cookies.end()); |
| EXPECT_EQ("a", it->Name()); |
| |
| ASSERT_TRUE(++it != cookies.end()); |
| EXPECT_EQ("e", it->Name()); |
| |
| ASSERT_TRUE(++it != cookies.end()); |
| EXPECT_EQ("b", it->Name()); |
| |
| ASSERT_TRUE(++it != cookies.end()); |
| EXPECT_EQ("c", it->Name()); |
| |
| EXPECT_TRUE(++it == cookies.end()); |
| } |
| |
| // Check that GetAllCookiesAsync returns cookies from multiple domains, in the |
| // correct order. |
| TYPED_TEST_P(CookieStoreTest, GetAllCookiesAsync) { |
| CookieStore* cs = this->GetCookieStore(); |
| |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B; path=/a")); |
| EXPECT_TRUE(this->SetCookie(cs, this->http_baz_com_.url(), "C=D;/")); |
| EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "E=F; path=/bar")); |
| |
| // Check cookies for url. |
| CookieList cookies = this->GetAllCookies(cs); |
| CookieList::const_iterator it = cookies.begin(); |
| |
| ASSERT_TRUE(it != cookies.end()); |
| EXPECT_EQ(this->http_bar_com_.host(), it->Domain()); |
| EXPECT_EQ("/bar", it->Path()); |
| EXPECT_EQ("E", it->Name()); |
| EXPECT_EQ("F", it->Value()); |
| |
| ASSERT_TRUE(++it != cookies.end()); |
| EXPECT_EQ(this->http_www_foo_.host(), it->Domain()); |
| EXPECT_EQ("/a", it->Path()); |
| EXPECT_EQ("A", it->Name()); |
| EXPECT_EQ("B", it->Value()); |
| |
| ASSERT_TRUE(++it != cookies.end()); |
| EXPECT_EQ(this->http_baz_com_.host(), it->Domain()); |
| EXPECT_EQ("/", it->Path()); |
| EXPECT_EQ("C", it->Name()); |
| EXPECT_EQ("D", it->Value()); |
| |
| ASSERT_TRUE(++it == cookies.end()); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, GetAllCookiesWithAccessSemanticsAsync) { |
| CookieStore* cs = this->GetCookieStore(); |
| auto access_delegate = std::make_unique<TestCookieAccessDelegate>(); |
| TestCookieAccessDelegate* test_delegate = access_delegate.get(); |
| // if !supports_cookie_access_semantics, the delegate will be stored but will |
| // not be used. |
| cs->SetCookieAccessDelegate(std::move(access_delegate)); |
| |
| test_delegate->SetExpectationForCookieDomain("domain1.test", |
| CookieAccessSemantics::LEGACY); |
| test_delegate->SetExpectationForCookieDomain( |
| "domain2.test", CookieAccessSemantics::NONLEGACY); |
| test_delegate->SetExpectationForCookieDomain("domain3.test", |
| CookieAccessSemantics::UNKNOWN); |
| |
| this->CreateAndSetCookie(cs, GURL("http://domain1.test"), "cookie=1", |
| CookieOptions::MakeAllInclusive()); |
| this->CreateAndSetCookie(cs, GURL("http://domain2.test"), "cookie=1", |
| CookieOptions::MakeAllInclusive()); |
| this->CreateAndSetCookie(cs, GURL("http://domain3.test"), "cookie=1", |
| CookieOptions::MakeAllInclusive()); |
| this->CreateAndSetCookie(cs, GURL("http://domain4.test"), "cookie=1", |
| CookieOptions::MakeAllInclusive()); |
| |
| GetAllCookiesWithAccessSemanticsCallback callback; |
| cs->GetAllCookiesWithAccessSemanticsAsync(callback.MakeCallback()); |
| callback.WaitUntilDone(); |
| EXPECT_TRUE(callback.was_run()); |
| |
| EXPECT_EQ(callback.cookies().size(), callback.access_semantics_list().size()); |
| EXPECT_EQ(4u, callback.access_semantics_list().size()); |
| EXPECT_EQ("domain1.test", callback.cookies()[0].Domain()); |
| EXPECT_EQ("domain2.test", callback.cookies()[1].Domain()); |
| EXPECT_EQ("domain3.test", callback.cookies()[2].Domain()); |
| EXPECT_EQ("domain4.test", callback.cookies()[3].Domain()); |
| |
| if (!TypeParam::supports_cookie_access_semantics) { |
| for (CookieAccessSemantics semantics : callback.access_semantics_list()) { |
| EXPECT_EQ(CookieAccessSemantics::UNKNOWN, semantics); |
| } |
| } else { |
| EXPECT_EQ(CookieAccessSemantics::LEGACY, |
| callback.access_semantics_list()[0]); |
| EXPECT_EQ(CookieAccessSemantics::NONLEGACY, |
| callback.access_semantics_list()[1]); |
| EXPECT_EQ(CookieAccessSemantics::UNKNOWN, |
| callback.access_semantics_list()[2]); |
| EXPECT_EQ(CookieAccessSemantics::UNKNOWN, |
| callback.access_semantics_list()[3]); |
| } |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, DeleteCanonicalCookieAsync) { |
| CookieStore* cs = this->GetCookieStore(); |
| |
| // Set two cookies with the same name, and make sure both are set. |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B;Path=/foo")); |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=C;Path=/bar")); |
| EXPECT_EQ(2u, this->GetAllCookies(cs).size()); |
| EXPECT_EQ("A=B", this->GetCookies(cs, this->www_foo_foo_.url())); |
| EXPECT_EQ("A=C", this->GetCookies(cs, this->www_foo_bar_.url())); |
| |
| // Delete the "/foo" cookie, and make sure only it was deleted. |
| CookieList cookies = this->GetCookieListWithOptions( |
| cs, this->www_foo_foo_.url(), CookieOptions::MakeAllInclusive()); |
| ASSERT_EQ(1u, cookies.size()); |
| EXPECT_EQ(1u, this->DeleteCanonicalCookie(cs, cookies[0])); |
| EXPECT_EQ(1u, this->GetAllCookies(cs).size()); |
| EXPECT_EQ("", this->GetCookies(cs, this->www_foo_foo_.url())); |
| EXPECT_EQ("A=C", this->GetCookies(cs, this->www_foo_bar_.url())); |
| |
| // Deleting the "/foo" cookie again should fail. |
| EXPECT_EQ(0u, this->DeleteCanonicalCookie(cs, cookies[0])); |
| |
| // Try to delete the "/bar" cookie after overwriting it with a new cookie. |
| cookies = this->GetCookieListWithOptions(cs, this->www_foo_bar_.url(), |
| CookieOptions::MakeAllInclusive()); |
| ASSERT_EQ(1u, cookies.size()); |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=D;Path=/bar")); |
| EXPECT_EQ(0u, this->DeleteCanonicalCookie(cs, cookies[0])); |
| EXPECT_EQ(1u, this->GetAllCookies(cs).size()); |
| EXPECT_EQ("A=D", this->GetCookies(cs, this->www_foo_bar_.url())); |
| |
| // Delete the new "/bar" cookie. |
| cookies = this->GetCookieListWithOptions(cs, this->www_foo_bar_.url(), |
| CookieOptions::MakeAllInclusive()); |
| ASSERT_EQ(1u, cookies.size()); |
| EXPECT_EQ(1u, this->DeleteCanonicalCookie(cs, cookies[0])); |
| EXPECT_EQ(0u, this->GetAllCookies(cs).size()); |
| EXPECT_EQ("", this->GetCookies(cs, this->www_foo_bar_.url())); |
| } |
| |
| TYPED_TEST_P(CookieStoreTest, DeleteSessionCookie) { |
| CookieStore* cs = this->GetCookieStore(); |
| // Create a session cookie and a persistent cookie. |
| EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), |
| std::string(kValidCookieLine))); |
| EXPECT_TRUE(this->SetCookie( |
| cs, this->http_www_foo_.url(), |
| this->http_www_foo_.Format("C=D; path=/; domain=%D;" |
| "expires=Mon, 18-Apr-22 22:50:13 GMT"))); |
| this->MatchCookieLines("A=B; C=D", |
| this->GetCookies(cs, this->http_www_foo_.url())); |
| // Delete the session cookie. |
| this->DeleteSessionCookies(cs); |
| // Check that the session cookie has been deleted but not the persistent one. |
| EXPECT_EQ("C=D", this->GetCookies(cs, this->http_www_foo_.url())); |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(CookieStoreTest, |
| FilterTest, |
| SetCanonicalCookieTest, |
| SecureEnforcement, |
| EmptyKeyTest, |
| DomainTest, |
| DomainWithTrailingDotTest, |
| ValidSubdomainTest, |
| InvalidDomainTest, |
| InvalidDomainSameDomainAndRegistry, |
| DomainWithoutLeadingDotParentDomain, |
| DomainWithoutLeadingDotSameDomain, |
| CaseInsensitiveDomainTest, |
| TestIpAddress, |
| TestIpAddressNoDomainCookies, |
| TestTLD, |
| TestTLDWithTerminalDot, |
| TestSubdomainSettingCookiesOnUnknownTLD, |
| TestSubdomainSettingCookiesOnKnownTLD, |
| TestSubdomainSettingCookiesOnKnownDottedTLD, |
| TestSettingCookiesOnUnknownTLD, |
| TestSettingCookiesWithHostDomainOnUnknownTLD, |
| TestHostEndsWithDot, |
| InvalidScheme, |
| InvalidScheme_Read, |
| PathTest, |
| EmptyExpires, |
| HttpOnlyTest, |
| TestCookieDeletion, |
| TestDeleteAll, |
| TestDeleteAllCreatedInTimeRange, |
| TestDeleteAllWithInfo, |
| TestSecure, |
| NetUtilCookieTest, |
| OverwritePersistentCookie, |
| EmptyName, |
| CookieOrdering, |
| GetAllCookiesAsync, |
| GetAllCookiesWithAccessSemanticsAsync, |
| DeleteCanonicalCookieAsync, |
| DeleteSessionCookie); |
| |
| } // namespace net |
| |
| #endif // NET_COOKIES_COOKIE_STORE_UNITTEST_H_ |