• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/http/http_cookie_indices.h"
6 
7 #include <functional>
8 
9 #include "base/containers/span.h"
10 #include "base/pickle.h"
11 #include "base/ranges/algorithm.h"
12 #include "crypto/sha2.h"
13 #include "net/cookies/parsed_cookie.h"
14 #include "net/http/http_response_headers.h"
15 #include "net/http/structured_headers.h"
16 
17 namespace net {
18 
19 namespace {
20 constexpr std::string_view kCookieIndicesHeader = "Cookie-Indices";
21 }  // namespace
22 
ParseCookieIndices(const HttpResponseHeaders & headers)23 std::optional<std::vector<std::string>> ParseCookieIndices(
24     const HttpResponseHeaders& headers) {
25   std::optional<std::string> normalized_header =
26       headers.GetNormalizedHeader(kCookieIndicesHeader);
27   if (!normalized_header) {
28     return std::nullopt;
29   }
30 
31   std::optional<structured_headers::List> list =
32       structured_headers::ParseList(*normalized_header);
33   if (!list.has_value()) {
34     return std::nullopt;
35   }
36 
37   std::vector<std::string> cookie_names;
38   cookie_names.reserve(list->size());
39   for (const structured_headers::ParameterizedMember& member : *list) {
40     if (member.member_is_inner_list) {
41       // Inner list not permitted here.
42       return std::nullopt;
43     }
44 
45     const structured_headers::ParameterizedItem& item = member.member[0];
46     if (!item.item.is_string()) {
47       // Non-string items are not permitted here.
48       return std::nullopt;
49     }
50 
51     // There are basically three sets of requirements that are interesting here.
52     //
53     // 1. Cookie names Chromium considers valid, given by:
54     //      cookie-name       = *cookie-name-octet
55     //      cookie-name-octet = %x20-3A / %x3C / %x3E-7E / %x80-FF
56     //                          ; octets excluding CTLs, ";", and "="
57     //    See |ParsedCookie::IsValidCookieName|.
58     //
59     // 2. Cookie names RFC 6265 considers valid, given by:
60     //      cookie-name = token
61     //      token       = 1*<any CHAR except CTLs or separators>
62     //      separators  = "(" | ")" | "<" | ">" | "@"
63     //                  | "," | ";" | ":" | "\" | <">
64     //                  | "/" | "[" | "]" | "?" | "="
65     //                  | "{" | "}" | SP | HT
66     //      CHAR        = <any US-ASCII character (octets 0 - 127)>
67     //      CTL         = <any US-ASCII control character
68     //                    (octets 0 - 31) and DEL (127)>
69     //
70     // 3. Valid RFC 8941 structured field strings, whose values are given by:
71     //      string-value   = *( %x20-7E )
72     //
73     // While all RFC 6265 valid cookie names are valid structured field strings,
74     // Chromium accepts cookies whose names can nonetheless not be spelled here.
75     // For example, cookie names outside 7-bit ASCII cannot be specified.
76     //
77     // Nor is every structured field string a valid cookie name, since it may
78     // contain a ";" or "=" character (or several other characters excluded by
79     // RFC 6265 in addition to Chromium). In the interest of interoperability,
80     // those are expressly rejected.
81     const std::string& name = item.item.GetString();
82     if (name.find_first_of("()<>@,;:\\\"/[]?={} \t") != std::string::npos) {
83       // This is one of those structured field strings that is not a valid
84       // cookie name according to RFC 6265.
85       // TODO(crbug.com/328628231): Watch mnot/I-D#346 to see if a different
86       // behavior is agreed on.
87       continue;
88     }
89     CHECK(ParsedCookie::IsValidCookieName(name))
90         << "invalid cookie name \"" << name << "\"";
91     cookie_names.push_back(name);
92   }
93   return cookie_names;
94 }
95 
HashCookieIndices(base::span<const std::string> cookie_indices,base::span<const std::pair<std::string,std::string>> cookies)96 CookieIndicesHash HashCookieIndices(
97     base::span<const std::string> cookie_indices,
98     base::span<const std::pair<std::string, std::string>> cookies) {
99   CHECK(base::ranges::adjacent_find(cookie_indices, std::greater_equal<>()) ==
100         cookie_indices.end())
101       << "cookie indices must be sorted and unique";
102 
103   std::vector<std::pair<std::string_view, std::string_view>> cookies_sorted(
104       cookies.begin(), cookies.end());
105   base::ranges::sort(cookies_sorted);
106 
107   base::Pickle pickle;
108   auto cookies_it = cookies_sorted.begin();
109   for (const std::string& cookie_name : cookie_indices) {
110     while (cookies_it != cookies_sorted.end() &&
111            cookies_it->first < cookie_name) {
112       ++cookies_it;
113     }
114     while (cookies_it != cookies_sorted.end() &&
115            cookies_it->first == cookie_name) {
116       pickle.WriteBool(true);
117       pickle.WriteString(cookies_it->second);
118       ++cookies_it;
119     }
120     pickle.WriteBool(false);
121   }
122   return crypto::SHA256Hash(pickle.payload_bytes());
123 }
124 
125 }  // namespace net
126