chromium/net/cookies/cookie_monster.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Portions of this code based on Mozilla:
//   (netwerk/cookie/src/nsCookieService.cpp)
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Daniel Witte ([email protected])
 *   Michiel van Leeuwen ([email protected])
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif

#include "net/cookies/cookie_monster.h"

#include <functional>
#include <list>
#include <numeric>
#include <optional>
#include <set>
#include <string_view>
#include <utility>

#include "base/check_is_test.h"
#include "base/containers/flat_map.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/ranges/algorithm.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "net/base/isolation_info.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/base/schemeful_site.h"
#include "net/base/url_util.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_constants.h"
#include "net/cookies/cookie_monster_change_dispatcher.h"
#include "net/cookies/cookie_monster_netlog_params.h"
#include "net/cookies/cookie_partition_key.h"
#include "net/cookies/cookie_partition_key_collection.h"
#include "net/cookies/cookie_util.h"
#include "net/cookies/parsed_cookie.h"
#include "net/http/http_util.h"
#include "net/log/net_log.h"
#include "net/log/net_log_values.h"
#include "url/origin.h"
#include "url/third_party/mozilla/url_parse.h"
#include "url/url_canon.h"
#include "url/url_constants.h"

Time;
TimeTicks;
TimeRange;

// In steady state, most cookie requests can be satisfied by the in memory
// cookie monster store. If the cookie request cannot be satisfied by the in
// memory store, the relevant cookies must be fetched from the persistent
// store. The task is queued in CookieMonster::tasks_pending_ if it requires
// all cookies to be loaded from the backend, or tasks_pending_for_key_ if it
// only requires all cookies associated with an eTLD+1.
//
// On the browser critical paths (e.g. for loading initial web pages in a
// session restore) it may take too long to wait for the full load. If a cookie
// request is for a specific URL, DoCookieCallbackForURL is called, which
// triggers a priority load if the key is not loaded yet by calling
// PersistentCookieStore::LoadCookiesForKey. The request is queued in
// CookieMonster::tasks_pending_for_key_ and executed upon receiving
// notification of key load completion via CookieMonster::OnKeyLoaded(). If
// multiple requests for the same eTLD+1 are received before key load
// completion, only the first request calls
// PersistentCookieStore::LoadCookiesForKey, all subsequent requests are queued
// in CookieMonster::tasks_pending_for_key_ and executed upon receiving
// notification of key load completion triggered by the first request for the
// same eTLD+1.

static const int kDaysInTenYears =;
static const int kMinutesInTenYears =;

namespace {

// This enum is used to generate a histogramed bitmask measureing the types
// of stored cookies. Please do not reorder the list when adding new entries.
// New items MUST be added at the end of the list, just before
// COOKIE_TYPE_LAST_ENTRY;
// There will be 2^COOKIE_TYPE_LAST_ENTRY buckets in the linear histogram.
enum CookieType {};

void MaybeRunDeleteCallback(base::WeakPtr<net::CookieMonster> cookie_monster,
                            base::OnceClosure callback) {}

template <typename CB, typename... R>
void MaybeRunCookieCallback(base::OnceCallback<CB> callback, R&&... result) {}

// Anonymous and Fenced Frame uses a CookiePartitionKey with a nonce. In these
// contexts, access to unpartitioned cookie is not granted.
//
// This returns true if the |list| of key should include unpartitioned cookie in
// GetCookie...().
bool IncludeUnpartitionedCookies(
    const net::CookiePartitionKeyCollection& list) {}

size_t NameValueSizeBytes(const net::CanonicalCookie& cc) {}

size_t NumBytesInCookieMapForKey(
    const net::CookieMonster::CookieMap& cookie_map,
    const std::string& key) {}

size_t NumBytesInCookieItVector(
    const net::CookieMonster::CookieItVector& cookie_its) {}

void LogStoredCookieToUMA(const net::CanonicalCookie& cc,
                          const net::CookieAccessResult& access_result) {}

}  // namespace

namespace net {

// See comments at declaration of these variables in cookie_monster.h
// for details.
const size_t CookieMonster::kDomainMaxCookies =;
const size_t CookieMonster::kDomainPurgeCookies =;
const size_t CookieMonster::kMaxCookies =;
const size_t CookieMonster::kPurgeCookies =;

const size_t CookieMonster::kMaxDomainPurgedKeys =;

const size_t CookieMonster::kPerPartitionDomainMaxCookieBytes =;
const size_t CookieMonster::kPerPartitionDomainMaxCookies =;

const size_t CookieMonster::kDomainCookiesQuotaLow =;
const size_t CookieMonster::kDomainCookiesQuotaMedium =;
const size_t CookieMonster::kDomainCookiesQuotaHigh =;

const int CookieMonster::kSafeFromGlobalPurgeDays =;

namespace {

bool ContainsControlCharacter(const std::string& s) {}

CanonicalCookieVector;

// Default minimum delay after updating a cookie's LastAccessDate before we
// will update it again.
const int kDefaultAccessUpdateThresholdSeconds =;

// Comparator to sort cookies from highest creation date to lowest
// creation date.
struct OrderByCreationTimeDesc {};

bool LRACookieSorter(const CookieMonster::CookieMap::iterator& it1,
                     const CookieMonster::CookieMap::iterator& it2) {}

// For a CookieItVector iterator range [|it_begin|, |it_end|),
// sorts the first |num_sort| elements by LastAccessDate().
void SortLeastRecentlyAccessed(CookieMonster::CookieItVector::iterator it_begin,
                               CookieMonster::CookieItVector::iterator it_end,
                               size_t num_sort) {}

// Given a single cookie vector |cookie_its|, pushs all of the secure cookies in
// |cookie_its| into |secure_cookie_its| and all of the non-secure cookies into
// |non_secure_cookie_its|. Both |secure_cookie_its| and |non_secure_cookie_its|
// must be non-NULL.
void SplitCookieVectorIntoSecureAndNonSecure(
    const CookieMonster::CookieItVector& cookie_its,
    CookieMonster::CookieItVector* secure_cookie_its,
    CookieMonster::CookieItVector* non_secure_cookie_its) {}

bool LowerBoundAccessDateComparator(const CookieMonster::CookieMap::iterator it,
                                    const Time& access_date) {}

// For a CookieItVector iterator range [|it_begin|, |it_end|)
// from a CookieItVector sorted by LastAccessDate(), returns the
// first iterator with access date >= |access_date|, or cookie_its_end if this
// holds for all.
CookieMonster::CookieItVector::iterator LowerBoundAccessDate(
    const CookieMonster::CookieItVector::iterator its_begin,
    const CookieMonster::CookieItVector::iterator its_end,
    const Time& access_date) {}

// Mapping between DeletionCause and CookieChangeCause; the
// mapping also provides a boolean that specifies whether or not an
// OnCookieChange notification ought to be generated.
ChangeCausePair;
const ChangeCausePair kChangeCauseMapping[] =;

bool IsCookieEligibleForEviction(CookiePriority current_priority_level,
                                 bool protect_secure_cookies,
                                 const CanonicalCookie* cookie) {}

size_t CountCookiesForPossibleDeletion(
    CookiePriority priority,
    const CookieMonster::CookieItVector* cookies,
    bool protect_secure_cookies) {}

struct DeletionCookieLists {};

// Performs 2 tasks
// * Counts every cookie at the given `priority` in `cookies`. This is the
// return value.
// * Fills in the host & domain lists for `could_be_deleted` with every cookie
// of the given {secureness, priority} in `cookies`.
size_t CountCookiesAndGenerateListsForPossibleDeletion(
    CookiePriority priority,
    DeletionCookieLists& could_be_deleted,
    const CookieMonster::CookieItList* cookies,
    bool generate_for_secure) {}

// Records minutes until the expiration date of a cookie to the appropriate
// histogram. Only histograms cookies that have an expiration date (i.e. are
// persistent).
void HistogramExpirationDuration(const CanonicalCookie& cookie,
                                 base::Time creation_time) {}

}  // namespace

CookieMonster::CookieMonster(scoped_refptr<PersistentCookieStore> store,
                             NetLog* net_log)
    :{}

CookieMonster::CookieMonster(scoped_refptr<PersistentCookieStore> store,
                             base::TimeDelta last_access_threshold,
                             NetLog* net_log)
    :{}

// Asynchronous CookieMonster API

void CookieMonster::FlushStore(base::OnceClosure callback) {}

void CookieMonster::SetForceKeepSessionState() {}

void CookieMonster::SetAllCookiesAsync(const CookieList& list,
                                       SetCookiesCallback callback) {}

void CookieMonster::SetCanonicalCookieAsync(
    std::unique_ptr<CanonicalCookie> cookie,
    const GURL& source_url,
    const CookieOptions& options,
    SetCookiesCallback callback,
    std::optional<CookieAccessResult> cookie_access_result) {}

void CookieMonster::GetCookieListWithOptionsAsync(
    const GURL& url,
    const CookieOptions& options,
    const CookiePartitionKeyCollection& cookie_partition_key_collection,
    GetCookieListCallback callback) {}

void CookieMonster::GetAllCookiesAsync(GetAllCookiesCallback callback) {}

void CookieMonster::GetAllCookiesWithAccessSemanticsAsync(
    GetAllCookiesWithAccessSemanticsCallback callback) {}

void CookieMonster::DeleteCanonicalCookieAsync(const CanonicalCookie& cookie,
                                               DeleteCallback callback) {}

void CookieMonster::DeleteAllCreatedInTimeRangeAsync(
    const TimeRange& creation_range,
    DeleteCallback callback) {}

void CookieMonster::DeleteAllMatchingInfoAsync(CookieDeletionInfo delete_info,
                                               DeleteCallback callback) {}

void CookieMonster::DeleteSessionCookiesAsync(
    CookieStore::DeleteCallback callback) {}

void CookieMonster::DeleteMatchingCookiesAsync(
    CookieStore::DeletePredicate predicate,
    CookieStore::DeleteCallback callback) {}

void CookieMonster::SetCookieableSchemes(
    const std::vector<std::string>& schemes,
    SetCookieableSchemesCallback callback) {}

// This function must be called before the CookieMonster is used.
void CookieMonster::SetPersistSessionCookies(bool persist_session_cookies) {}

const char* const CookieMonster::kDefaultCookieableSchemes[] =;
const int CookieMonster::kDefaultCookieableSchemesCount =;

CookieChangeDispatcher& CookieMonster::GetChangeDispatcher() {}

CookieMonster::~CookieMonster() {}

// static
bool CookieMonster::CookieSorter(const CanonicalCookie* cc1,
                                 const CanonicalCookie* cc2) {}

void CookieMonster::GetAllCookies(GetAllCookiesCallback callback) {}

void CookieMonster::AttachAccessSemanticsListForCookieList(
    GetAllCookiesWithAccessSemanticsCallback callback,
    const CookieList& cookie_list) {}

void CookieMonster::GetCookieListWithOptions(
    const GURL& url,
    const CookieOptions& options,
    const CookiePartitionKeyCollection& cookie_partition_key_collection,
    GetCookieListCallback callback) {}

void CookieMonster::DeleteAllCreatedInTimeRange(const TimeRange& creation_range,
                                                DeleteCallback callback) {}

bool CookieMonster::MatchCookieDeletionInfo(
    const CookieDeletionInfo& delete_info,
    const net::CanonicalCookie& cookie) {}

void CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie,
                                          DeleteCallback callback) {}

void CookieMonster::DeleteMatchingCookies(DeletePredicate predicate,
                                          DeletionCause cause,
                                          DeleteCallback callback) {}

void CookieMonster::MarkCookieStoreAsInitialized() {}

void CookieMonster::FetchAllCookiesIfNecessary() {}

void CookieMonster::FetchAllCookies() {}

void CookieMonster::OnLoaded(
    TimeTicks beginning_time,
    std::vector<std::unique_ptr<CanonicalCookie>> cookies) {}

void CookieMonster::OnKeyLoaded(
    const std::string& key,
    std::vector<std::unique_ptr<CanonicalCookie>> cookies) {}

void CookieMonster::StoreLoadedCookies(
    std::vector<std::unique_ptr<CanonicalCookie>> cookies) {}

void CookieMonster::InvokeQueue() {}

void CookieMonster::EnsureCookiesMapIsValid() {}

// Our strategy to find duplicates is:
// (1) Build a map from cookie unique key to
//     {list of cookies with this signature, sorted by creation time}.
// (2) For each list with more than 1 entry, keep the cookie having the
//     most recent creation time, and delete the others.
//
void CookieMonster::TrimDuplicateCookiesForKey(
    const std::string& key,
    CookieMap::iterator begin,
    CookieMap::iterator end,
    std::optional<PartitionedCookieMap::iterator> cookie_partition_it) {}

std::vector<CanonicalCookie*>
CookieMonster::FindCookiesForRegistryControlledHost(
    const GURL& url,
    CookieMap* cookie_map,
    CookieMonster::PartitionedCookieMap::iterator* partition_it) {}

std::vector<CanonicalCookie*>
CookieMonster::FindPartitionedCookiesForRegistryControlledHost(
    const CookiePartitionKey& cookie_partition_key,
    const GURL& url) {}

void CookieMonster::FilterCookiesWithOptions(
    const GURL url,
    const CookieOptions options,
    std::vector<CanonicalCookie*>* cookie_ptrs,
    CookieAccessResultList* included_cookies,
    CookieAccessResultList* excluded_cookies) {}

void CookieMonster::MaybeDeleteEquivalentCookieAndUpdateStatus(
    const std::string& key,
    const CanonicalCookie& cookie_being_set,
    bool allowed_to_set_secure_cookie,
    bool skip_httponly,
    bool already_expired,
    base::Time* creation_date_to_inherit,
    CookieInclusionStatus* status,
    std::optional<PartitionedCookieMap::iterator> cookie_partition_it) {}

CookieMonster::CookieMap::iterator CookieMonster::InternalInsertCookie(
    const std::string& key,
    std::unique_ptr<CanonicalCookie> cc,
    bool sync_to_store,
    const CookieAccessResult& access_result,
    bool dispatch_change) {}

bool CookieMonster::ShouldUpdatePersistentStore(CanonicalCookie* cc) {}

CookieMonster::PartitionedCookieMapIterators
CookieMonster::InternalInsertPartitionedCookie(
    std::string key,
    std::unique_ptr<CanonicalCookie> cc,
    bool sync_to_store,
    const CookieAccessResult& access_result,
    bool dispatch_change) {}

void CookieMonster::SetCanonicalCookie(
    std::unique_ptr<CanonicalCookie> cc,
    const GURL& source_url,
    const CookieOptions& options,
    SetCookiesCallback callback,
    std::optional<CookieAccessResult> cookie_access_result) {}

void CookieMonster::SetAllCookies(CookieList list,
                                  SetCookiesCallback callback) {}

void CookieMonster::InternalUpdateCookieAccessTime(CanonicalCookie* cc,
                                                   const Time& current) {}

// InternalDeleteCookies must not invalidate iterators other than the one being
// deleted.
void CookieMonster::InternalDeleteCookie(CookieMap::iterator it,
                                         bool sync_to_store,
                                         DeletionCause deletion_cause) {}

void CookieMonster::InternalDeletePartitionedCookie(
    PartitionedCookieMap::iterator partition_it,
    CookieMap::iterator cookie_it,
    bool sync_to_store,
    DeletionCause deletion_cause) {}

// Domain expiry behavior is unchanged by key/expiry scheme (the
// meaning of the key is different, but that's not visible to this routine).
size_t CookieMonster::GarbageCollect(const Time& current,
                                     const std::string& key) {}

size_t CookieMonster::GarbageCollectPartitionedCookies(
    const base::Time& current,
    const CookiePartitionKey& cookie_partition_key,
    const std::string& key) {}

size_t CookieMonster::PurgeLeastRecentMatches(CookieItVector* cookies,
                                              CookiePriority priority,
                                              size_t to_protect,
                                              size_t purge_goal,
                                              bool protect_secure_cookies) {}

size_t CookieMonster::PurgeLeastRecentMatchesForOBC(
    CookieItList* cookies,
    CookiePriority priority,
    size_t to_protect,
    size_t purge_goal,
    bool delete_secure_cookies) {}

size_t CookieMonster::GarbageCollectExpired(const Time& current,
                                            const CookieMapItPair& itpair,
                                            CookieItVector* cookie_its) {}

size_t CookieMonster::GarbageCollectExpiredPartitionedCookies(
    const Time& current,
    const PartitionedCookieMap::iterator& cookie_partition_it,
    const CookieMapItPair& itpair,
    CookieItVector* cookie_its) {}

void CookieMonster::GarbageCollectAllExpiredPartitionedCookies(
    const Time& current) {}

size_t CookieMonster::GarbageCollectDeleteRange(
    const Time& current,
    DeletionCause cause,
    CookieItVector::iterator it_begin,
    CookieItVector::iterator it_end) {}

size_t CookieMonster::GarbageCollectLeastRecentlyAccessed(
    const base::Time& current,
    const base::Time& safe_date,
    size_t purge_goal,
    CookieItVector cookie_its,
    base::Time* earliest_time) {}

// A wrapper around registry_controlled_domains::GetDomainAndRegistry
// to make clear we're creating a key for our local map or for the persistent
// store's use. Here and in FindCookiesForRegistryControlledHost() are the only
// two places where we need to conditionalize based on key type.
//
// Note that this key algorithm explicitly ignores the scheme.  This is
// because when we're entering cookies into the map from the backing store,
// we in general won't have the scheme at that point.
// In practical terms, this means that file cookies will be stored
// in the map either by an empty string or by UNC name (and will be
// limited by kMaxCookiesPerHost), and extension cookies will be stored
// based on the single extension id, as the extension id won't have the
// form of a DNS host and hence GetKey() will return it unchanged.
//
// Arguably the right thing to do here is to make the key
// algorithm dependent on the scheme, and make sure that the scheme is
// available everywhere the key must be obtained (specfically at backing
// store load time).  This would require either changing the backing store
// database schema to include the scheme (far more trouble than it's worth), or
// separating out file cookies into their own CookieMonster instance and
// thus restricting each scheme to a single cookie monster (which might
// be worth it, but is still too much trouble to solve what is currently a
// non-problem).
//
// static
std::string CookieMonster::GetKey(std::string_view domain) {}

bool CookieMonster::HasCookieableScheme(const GURL& url) {}

CookieAccessSemantics CookieMonster::GetAccessSemanticsForCookie(
    const CanonicalCookie& cookie) const {}

// Test to see if stats should be recorded, and record them if so.
// The goal here is to get sampling for the average browser-hour of
// activity.  We won't take samples when the web isn't being surfed,
// and when the web is being surfed, we'll take samples about every
// kRecordStatisticsIntervalSeconds.
// last_statistic_record_time_ is initialized to Now() rather than null
// in the constructor so that we won't take statistics right after
// startup, to avoid bias from browsers that are started but not used.
void CookieMonster::RecordPeriodicStats(const base::Time& current_time) {}

bool CookieMonster::DoRecordPeriodicStats() {}

void CookieMonster::RecordPeriodicFirstPartySetsStats(
    base::flat_map<SchemefulSite, FirstPartySetEntry> sets) const {}

void CookieMonster::DoCookieCallback(base::OnceClosure callback) {}

void CookieMonster::DoCookieCallbackForURL(base::OnceClosure callback,
                                           const GURL& url) {}

void CookieMonster::DoCookieCallbackForHostOrDomain(
    base::OnceClosure callback,
    std::string_view host_or_domain) {}

CookieMonster::CookieSentToSamePort
CookieMonster::IsCookieSentToSamePortThatSetIt(
    const GURL& destination,
    int source_port,
    CookieSourceScheme source_scheme) {}

std::optional<bool> CookieMonster::SiteHasCookieInOtherPartition(
    const net::SchemefulSite& site,
    const std::optional<CookiePartitionKey>& partition_key) const {}

}  // namespace net