xref: /aosp_15_r20/external/cronet/net/cookies/parsed_cookie.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2012 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker // Portions of this code based on Mozilla:
6*6777b538SAndroid Build Coastguard Worker //   (netwerk/cookie/src/nsCookieService.cpp)
7*6777b538SAndroid Build Coastguard Worker /* ***** BEGIN LICENSE BLOCK *****
8*6777b538SAndroid Build Coastguard Worker  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
9*6777b538SAndroid Build Coastguard Worker  *
10*6777b538SAndroid Build Coastguard Worker  * The contents of this file are subject to the Mozilla Public License Version
11*6777b538SAndroid Build Coastguard Worker  * 1.1 (the "License"); you may not use this file except in compliance with
12*6777b538SAndroid Build Coastguard Worker  * the License. You may obtain a copy of the License at
13*6777b538SAndroid Build Coastguard Worker  * http://www.mozilla.org/MPL/
14*6777b538SAndroid Build Coastguard Worker  *
15*6777b538SAndroid Build Coastguard Worker  * Software distributed under the License is distributed on an "AS IS" basis,
16*6777b538SAndroid Build Coastguard Worker  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
17*6777b538SAndroid Build Coastguard Worker  * for the specific language governing rights and limitations under the
18*6777b538SAndroid Build Coastguard Worker  * License.
19*6777b538SAndroid Build Coastguard Worker  *
20*6777b538SAndroid Build Coastguard Worker  * The Original Code is mozilla.org code.
21*6777b538SAndroid Build Coastguard Worker  *
22*6777b538SAndroid Build Coastguard Worker  * The Initial Developer of the Original Code is
23*6777b538SAndroid Build Coastguard Worker  * Netscape Communications Corporation.
24*6777b538SAndroid Build Coastguard Worker  * Portions created by the Initial Developer are Copyright (C) 2003
25*6777b538SAndroid Build Coastguard Worker  * the Initial Developer. All Rights Reserved.
26*6777b538SAndroid Build Coastguard Worker  *
27*6777b538SAndroid Build Coastguard Worker  * Contributor(s):
28*6777b538SAndroid Build Coastguard Worker  *   Daniel Witte ([email protected])
29*6777b538SAndroid Build Coastguard Worker  *   Michiel van Leeuwen ([email protected])
30*6777b538SAndroid Build Coastguard Worker  *
31*6777b538SAndroid Build Coastguard Worker  * Alternatively, the contents of this file may be used under the terms of
32*6777b538SAndroid Build Coastguard Worker  * either the GNU General Public License Version 2 or later (the "GPL"), or
33*6777b538SAndroid Build Coastguard Worker  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
34*6777b538SAndroid Build Coastguard Worker  * in which case the provisions of the GPL or the LGPL are applicable instead
35*6777b538SAndroid Build Coastguard Worker  * of those above. If you wish to allow use of your version of this file only
36*6777b538SAndroid Build Coastguard Worker  * under the terms of either the GPL or the LGPL, and not to allow others to
37*6777b538SAndroid Build Coastguard Worker  * use your version of this file under the terms of the MPL, indicate your
38*6777b538SAndroid Build Coastguard Worker  * decision by deleting the provisions above and replace them with the notice
39*6777b538SAndroid Build Coastguard Worker  * and other provisions required by the GPL or the LGPL. If you do not delete
40*6777b538SAndroid Build Coastguard Worker  * the provisions above, a recipient may use your version of this file under
41*6777b538SAndroid Build Coastguard Worker  * the terms of any one of the MPL, the GPL or the LGPL.
42*6777b538SAndroid Build Coastguard Worker  *
43*6777b538SAndroid Build Coastguard Worker  * ***** END LICENSE BLOCK ***** */
44*6777b538SAndroid Build Coastguard Worker 
45*6777b538SAndroid Build Coastguard Worker #include "net/cookies/parsed_cookie.h"
46*6777b538SAndroid Build Coastguard Worker 
47*6777b538SAndroid Build Coastguard Worker #include "base/logging.h"
48*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_macros.h"
49*6777b538SAndroid Build Coastguard Worker #include "base/numerics/checked_math.h"
50*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_util.h"
51*6777b538SAndroid Build Coastguard Worker #include "net/base/features.h"
52*6777b538SAndroid Build Coastguard Worker #include "net/cookies/cookie_constants.h"
53*6777b538SAndroid Build Coastguard Worker #include "net/cookies/cookie_inclusion_status.h"
54*6777b538SAndroid Build Coastguard Worker #include "net/http/http_util.h"
55*6777b538SAndroid Build Coastguard Worker 
56*6777b538SAndroid Build Coastguard Worker namespace {
57*6777b538SAndroid Build Coastguard Worker 
58*6777b538SAndroid Build Coastguard Worker const char kPathTokenName[] = "path";
59*6777b538SAndroid Build Coastguard Worker const char kDomainTokenName[] = "domain";
60*6777b538SAndroid Build Coastguard Worker const char kExpiresTokenName[] = "expires";
61*6777b538SAndroid Build Coastguard Worker const char kMaxAgeTokenName[] = "max-age";
62*6777b538SAndroid Build Coastguard Worker const char kSecureTokenName[] = "secure";
63*6777b538SAndroid Build Coastguard Worker const char kHttpOnlyTokenName[] = "httponly";
64*6777b538SAndroid Build Coastguard Worker const char kSameSiteTokenName[] = "samesite";
65*6777b538SAndroid Build Coastguard Worker const char kPriorityTokenName[] = "priority";
66*6777b538SAndroid Build Coastguard Worker const char kPartitionedTokenName[] = "partitioned";
67*6777b538SAndroid Build Coastguard Worker 
68*6777b538SAndroid Build Coastguard Worker const char kTerminator[] = "\n\r\0";
69*6777b538SAndroid Build Coastguard Worker const int kTerminatorLen = sizeof(kTerminator) - 1;
70*6777b538SAndroid Build Coastguard Worker const char kWhitespace[] = " \t";
71*6777b538SAndroid Build Coastguard Worker const char kValueSeparator = ';';
72*6777b538SAndroid Build Coastguard Worker const char kTokenSeparator[] = ";=";
73*6777b538SAndroid Build Coastguard Worker 
74*6777b538SAndroid Build Coastguard Worker // Returns true if |c| occurs in |chars|
75*6777b538SAndroid Build Coastguard Worker // TODO(erikwright): maybe make this take an iterator, could check for end also?
CharIsA(const char c,const char * chars)76*6777b538SAndroid Build Coastguard Worker inline bool CharIsA(const char c, const char* chars) {
77*6777b538SAndroid Build Coastguard Worker   return strchr(chars, c) != nullptr;
78*6777b538SAndroid Build Coastguard Worker }
79*6777b538SAndroid Build Coastguard Worker 
80*6777b538SAndroid Build Coastguard Worker // Seek the iterator to the first occurrence of |character|.
81*6777b538SAndroid Build Coastguard Worker // Returns true if it hits the end, false otherwise.
SeekToCharacter(std::string::const_iterator * it,const std::string::const_iterator & end,const char character)82*6777b538SAndroid Build Coastguard Worker inline bool SeekToCharacter(std::string::const_iterator* it,
83*6777b538SAndroid Build Coastguard Worker                             const std::string::const_iterator& end,
84*6777b538SAndroid Build Coastguard Worker                             const char character) {
85*6777b538SAndroid Build Coastguard Worker   for (; *it != end && **it != character; ++(*it)) {
86*6777b538SAndroid Build Coastguard Worker   }
87*6777b538SAndroid Build Coastguard Worker   return *it == end;
88*6777b538SAndroid Build Coastguard Worker }
89*6777b538SAndroid Build Coastguard Worker 
90*6777b538SAndroid Build Coastguard Worker // Seek the iterator to the first occurrence of a character in |chars|.
91*6777b538SAndroid Build Coastguard Worker // Returns true if it hit the end, false otherwise.
SeekTo(std::string::const_iterator * it,const std::string::const_iterator & end,const char * chars)92*6777b538SAndroid Build Coastguard Worker inline bool SeekTo(std::string::const_iterator* it,
93*6777b538SAndroid Build Coastguard Worker                    const std::string::const_iterator& end,
94*6777b538SAndroid Build Coastguard Worker                    const char* chars) {
95*6777b538SAndroid Build Coastguard Worker   for (; *it != end && !CharIsA(**it, chars); ++(*it)) {
96*6777b538SAndroid Build Coastguard Worker   }
97*6777b538SAndroid Build Coastguard Worker   return *it == end;
98*6777b538SAndroid Build Coastguard Worker }
99*6777b538SAndroid Build Coastguard Worker // Seek the iterator to the first occurrence of a character not in |chars|.
100*6777b538SAndroid Build Coastguard Worker // Returns true if it hit the end, false otherwise.
SeekPast(std::string::const_iterator * it,const std::string::const_iterator & end,const char * chars)101*6777b538SAndroid Build Coastguard Worker inline bool SeekPast(std::string::const_iterator* it,
102*6777b538SAndroid Build Coastguard Worker                      const std::string::const_iterator& end,
103*6777b538SAndroid Build Coastguard Worker                      const char* chars) {
104*6777b538SAndroid Build Coastguard Worker   for (; *it != end && CharIsA(**it, chars); ++(*it)) {
105*6777b538SAndroid Build Coastguard Worker   }
106*6777b538SAndroid Build Coastguard Worker   return *it == end;
107*6777b538SAndroid Build Coastguard Worker }
SeekBackPast(std::string::const_iterator * it,const std::string::const_iterator & end,const char * chars)108*6777b538SAndroid Build Coastguard Worker inline bool SeekBackPast(std::string::const_iterator* it,
109*6777b538SAndroid Build Coastguard Worker                          const std::string::const_iterator& end,
110*6777b538SAndroid Build Coastguard Worker                          const char* chars) {
111*6777b538SAndroid Build Coastguard Worker   for (; *it != end && CharIsA(**it, chars); --(*it)) {
112*6777b538SAndroid Build Coastguard Worker   }
113*6777b538SAndroid Build Coastguard Worker   return *it == end;
114*6777b538SAndroid Build Coastguard Worker }
115*6777b538SAndroid Build Coastguard Worker 
116*6777b538SAndroid Build Coastguard Worker // Returns the string piece within |value| that is a valid cookie value.
ValidStringPieceForValue(const std::string & value)117*6777b538SAndroid Build Coastguard Worker std::string_view ValidStringPieceForValue(const std::string& value) {
118*6777b538SAndroid Build Coastguard Worker   std::string::const_iterator it = value.begin();
119*6777b538SAndroid Build Coastguard Worker   std::string::const_iterator end =
120*6777b538SAndroid Build Coastguard Worker       net::ParsedCookie::FindFirstTerminator(value);
121*6777b538SAndroid Build Coastguard Worker   std::string::const_iterator value_start;
122*6777b538SAndroid Build Coastguard Worker   std::string::const_iterator value_end;
123*6777b538SAndroid Build Coastguard Worker 
124*6777b538SAndroid Build Coastguard Worker   net::ParsedCookie::ParseValue(&it, end, &value_start, &value_end);
125*6777b538SAndroid Build Coastguard Worker 
126*6777b538SAndroid Build Coastguard Worker   return base::MakeStringPiece(value_start, value_end);
127*6777b538SAndroid Build Coastguard Worker }
128*6777b538SAndroid Build Coastguard Worker 
129*6777b538SAndroid Build Coastguard Worker }  // namespace
130*6777b538SAndroid Build Coastguard Worker 
131*6777b538SAndroid Build Coastguard Worker namespace net {
132*6777b538SAndroid Build Coastguard Worker 
ParsedCookie(const std::string & cookie_line,bool block_truncated,CookieInclusionStatus * status_out)133*6777b538SAndroid Build Coastguard Worker ParsedCookie::ParsedCookie(const std::string& cookie_line,
134*6777b538SAndroid Build Coastguard Worker                            bool block_truncated,
135*6777b538SAndroid Build Coastguard Worker                            CookieInclusionStatus* status_out) {
136*6777b538SAndroid Build Coastguard Worker   // Put a pointer on the stack so the rest of the function can assign to it if
137*6777b538SAndroid Build Coastguard Worker   // the default nullptr is passed in.
138*6777b538SAndroid Build Coastguard Worker   CookieInclusionStatus blank_status;
139*6777b538SAndroid Build Coastguard Worker   if (status_out == nullptr) {
140*6777b538SAndroid Build Coastguard Worker     status_out = &blank_status;
141*6777b538SAndroid Build Coastguard Worker   }
142*6777b538SAndroid Build Coastguard Worker   *status_out = CookieInclusionStatus();
143*6777b538SAndroid Build Coastguard Worker 
144*6777b538SAndroid Build Coastguard Worker   ParseTokenValuePairs(cookie_line, block_truncated, *status_out);
145*6777b538SAndroid Build Coastguard Worker   if (IsValid()) {
146*6777b538SAndroid Build Coastguard Worker     SetupAttributes();
147*6777b538SAndroid Build Coastguard Worker   } else {
148*6777b538SAndroid Build Coastguard Worker     // Status should indicate exclusion if the resulting ParsedCookie is
149*6777b538SAndroid Build Coastguard Worker     // invalid.
150*6777b538SAndroid Build Coastguard Worker     CHECK(!status_out->IsInclude());
151*6777b538SAndroid Build Coastguard Worker   }
152*6777b538SAndroid Build Coastguard Worker }
153*6777b538SAndroid Build Coastguard Worker 
154*6777b538SAndroid Build Coastguard Worker ParsedCookie::~ParsedCookie() = default;
155*6777b538SAndroid Build Coastguard Worker 
IsValid() const156*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::IsValid() const {
157*6777b538SAndroid Build Coastguard Worker   return !pairs_.empty();
158*6777b538SAndroid Build Coastguard Worker }
159*6777b538SAndroid Build Coastguard Worker 
SameSite(CookieSameSiteString * samesite_string) const160*6777b538SAndroid Build Coastguard Worker CookieSameSite ParsedCookie::SameSite(
161*6777b538SAndroid Build Coastguard Worker     CookieSameSiteString* samesite_string) const {
162*6777b538SAndroid Build Coastguard Worker   CookieSameSite samesite = CookieSameSite::UNSPECIFIED;
163*6777b538SAndroid Build Coastguard Worker   if (same_site_index_ != 0) {
164*6777b538SAndroid Build Coastguard Worker     samesite = StringToCookieSameSite(pairs_[same_site_index_].second,
165*6777b538SAndroid Build Coastguard Worker                                       samesite_string);
166*6777b538SAndroid Build Coastguard Worker   } else if (samesite_string) {
167*6777b538SAndroid Build Coastguard Worker     *samesite_string = CookieSameSiteString::kUnspecified;
168*6777b538SAndroid Build Coastguard Worker   }
169*6777b538SAndroid Build Coastguard Worker   return samesite;
170*6777b538SAndroid Build Coastguard Worker }
171*6777b538SAndroid Build Coastguard Worker 
Priority() const172*6777b538SAndroid Build Coastguard Worker CookiePriority ParsedCookie::Priority() const {
173*6777b538SAndroid Build Coastguard Worker   return (priority_index_ == 0)
174*6777b538SAndroid Build Coastguard Worker              ? COOKIE_PRIORITY_DEFAULT
175*6777b538SAndroid Build Coastguard Worker              : StringToCookiePriority(pairs_[priority_index_].second);
176*6777b538SAndroid Build Coastguard Worker }
177*6777b538SAndroid Build Coastguard Worker 
SetName(const std::string & name)178*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::SetName(const std::string& name) {
179*6777b538SAndroid Build Coastguard Worker   const std::string& value = pairs_.empty() ? "" : pairs_[0].second;
180*6777b538SAndroid Build Coastguard Worker 
181*6777b538SAndroid Build Coastguard Worker   // Ensure there are no invalid characters in `name`. This should be done
182*6777b538SAndroid Build Coastguard Worker   // before calling ParseTokenString because we want terminating characters
183*6777b538SAndroid Build Coastguard Worker   // ('\r', '\n', and '\0') and '=' in `name` to cause a rejection instead of
184*6777b538SAndroid Build Coastguard Worker   // truncation.
185*6777b538SAndroid Build Coastguard Worker   // TODO(crbug.com/1233602) Once we change logic more broadly to reject
186*6777b538SAndroid Build Coastguard Worker   // cookies containing these characters, we should be able to simplify this
187*6777b538SAndroid Build Coastguard Worker   // logic since IsValidCookieNameValuePair() also calls IsValidCookieName().
188*6777b538SAndroid Build Coastguard Worker   // Also, this check will currently fail if `name` has a tab character in the
189*6777b538SAndroid Build Coastguard Worker   // leading or trailing whitespace, which is inconsistent with what happens
190*6777b538SAndroid Build Coastguard Worker   // when parsing a cookie line in the constructor (but the old logic for
191*6777b538SAndroid Build Coastguard Worker   // SetName() behaved this way as well).
192*6777b538SAndroid Build Coastguard Worker   if (!IsValidCookieName(name)) {
193*6777b538SAndroid Build Coastguard Worker     return false;
194*6777b538SAndroid Build Coastguard Worker   }
195*6777b538SAndroid Build Coastguard Worker 
196*6777b538SAndroid Build Coastguard Worker   // Use the same whitespace trimming code as the constructor.
197*6777b538SAndroid Build Coastguard Worker   const std::string& parsed_name = ParseTokenString(name);
198*6777b538SAndroid Build Coastguard Worker 
199*6777b538SAndroid Build Coastguard Worker   if (!IsValidCookieNameValuePair(parsed_name, value)) {
200*6777b538SAndroid Build Coastguard Worker     return false;
201*6777b538SAndroid Build Coastguard Worker   }
202*6777b538SAndroid Build Coastguard Worker 
203*6777b538SAndroid Build Coastguard Worker   if (pairs_.empty())
204*6777b538SAndroid Build Coastguard Worker     pairs_.emplace_back("", "");
205*6777b538SAndroid Build Coastguard Worker   pairs_[0].first = parsed_name;
206*6777b538SAndroid Build Coastguard Worker 
207*6777b538SAndroid Build Coastguard Worker   return true;
208*6777b538SAndroid Build Coastguard Worker }
209*6777b538SAndroid Build Coastguard Worker 
SetValue(const std::string & value)210*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::SetValue(const std::string& value) {
211*6777b538SAndroid Build Coastguard Worker   const std::string& name = pairs_.empty() ? "" : pairs_[0].first;
212*6777b538SAndroid Build Coastguard Worker 
213*6777b538SAndroid Build Coastguard Worker   // Ensure there are no invalid characters in `value`. This should be done
214*6777b538SAndroid Build Coastguard Worker   // before calling ParseValueString because we want terminating characters
215*6777b538SAndroid Build Coastguard Worker   // ('\r', '\n', and '\0') in `value` to cause a rejection instead of
216*6777b538SAndroid Build Coastguard Worker   // truncation.
217*6777b538SAndroid Build Coastguard Worker   // TODO(crbug.com/1233602) Once we change logic more broadly to reject
218*6777b538SAndroid Build Coastguard Worker   // cookies containing these characters, we should be able to simplify this
219*6777b538SAndroid Build Coastguard Worker   // logic since IsValidCookieNameValuePair() also calls IsValidCookieValue().
220*6777b538SAndroid Build Coastguard Worker   // Also, this check will currently fail if `value` has a tab character in
221*6777b538SAndroid Build Coastguard Worker   // the leading or trailing whitespace, which is inconsistent with what
222*6777b538SAndroid Build Coastguard Worker   // happens when parsing a cookie line in the constructor (but the old logic
223*6777b538SAndroid Build Coastguard Worker   // for SetValue() behaved this way as well).
224*6777b538SAndroid Build Coastguard Worker   if (!IsValidCookieValue(value)) {
225*6777b538SAndroid Build Coastguard Worker     return false;
226*6777b538SAndroid Build Coastguard Worker   }
227*6777b538SAndroid Build Coastguard Worker 
228*6777b538SAndroid Build Coastguard Worker   // Use the same whitespace trimming code as the constructor.
229*6777b538SAndroid Build Coastguard Worker   const std::string& parsed_value = ParseValueString(value);
230*6777b538SAndroid Build Coastguard Worker 
231*6777b538SAndroid Build Coastguard Worker   if (!IsValidCookieNameValuePair(name, parsed_value)) {
232*6777b538SAndroid Build Coastguard Worker     return false;
233*6777b538SAndroid Build Coastguard Worker   }
234*6777b538SAndroid Build Coastguard Worker   if (pairs_.empty())
235*6777b538SAndroid Build Coastguard Worker     pairs_.emplace_back("", "");
236*6777b538SAndroid Build Coastguard Worker   pairs_[0].second = parsed_value;
237*6777b538SAndroid Build Coastguard Worker 
238*6777b538SAndroid Build Coastguard Worker   return true;
239*6777b538SAndroid Build Coastguard Worker }
240*6777b538SAndroid Build Coastguard Worker 
SetPath(const std::string & path)241*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::SetPath(const std::string& path) {
242*6777b538SAndroid Build Coastguard Worker   return SetString(&path_index_, kPathTokenName, path);
243*6777b538SAndroid Build Coastguard Worker }
244*6777b538SAndroid Build Coastguard Worker 
SetDomain(const std::string & domain)245*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::SetDomain(const std::string& domain) {
246*6777b538SAndroid Build Coastguard Worker   return SetString(&domain_index_, kDomainTokenName, domain);
247*6777b538SAndroid Build Coastguard Worker }
248*6777b538SAndroid Build Coastguard Worker 
SetExpires(const std::string & expires)249*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::SetExpires(const std::string& expires) {
250*6777b538SAndroid Build Coastguard Worker   return SetString(&expires_index_, kExpiresTokenName, expires);
251*6777b538SAndroid Build Coastguard Worker }
252*6777b538SAndroid Build Coastguard Worker 
SetMaxAge(const std::string & maxage)253*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::SetMaxAge(const std::string& maxage) {
254*6777b538SAndroid Build Coastguard Worker   return SetString(&maxage_index_, kMaxAgeTokenName, maxage);
255*6777b538SAndroid Build Coastguard Worker }
256*6777b538SAndroid Build Coastguard Worker 
SetIsSecure(bool is_secure)257*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::SetIsSecure(bool is_secure) {
258*6777b538SAndroid Build Coastguard Worker   return SetBool(&secure_index_, kSecureTokenName, is_secure);
259*6777b538SAndroid Build Coastguard Worker }
260*6777b538SAndroid Build Coastguard Worker 
SetIsHttpOnly(bool is_http_only)261*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::SetIsHttpOnly(bool is_http_only) {
262*6777b538SAndroid Build Coastguard Worker   return SetBool(&httponly_index_, kHttpOnlyTokenName, is_http_only);
263*6777b538SAndroid Build Coastguard Worker }
264*6777b538SAndroid Build Coastguard Worker 
SetSameSite(const std::string & same_site)265*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::SetSameSite(const std::string& same_site) {
266*6777b538SAndroid Build Coastguard Worker   return SetString(&same_site_index_, kSameSiteTokenName, same_site);
267*6777b538SAndroid Build Coastguard Worker }
268*6777b538SAndroid Build Coastguard Worker 
SetPriority(const std::string & priority)269*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::SetPriority(const std::string& priority) {
270*6777b538SAndroid Build Coastguard Worker   return SetString(&priority_index_, kPriorityTokenName, priority);
271*6777b538SAndroid Build Coastguard Worker }
272*6777b538SAndroid Build Coastguard Worker 
SetIsPartitioned(bool is_partitioned)273*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::SetIsPartitioned(bool is_partitioned) {
274*6777b538SAndroid Build Coastguard Worker   return SetBool(&partitioned_index_, kPartitionedTokenName, is_partitioned);
275*6777b538SAndroid Build Coastguard Worker }
276*6777b538SAndroid Build Coastguard Worker 
ToCookieLine() const277*6777b538SAndroid Build Coastguard Worker std::string ParsedCookie::ToCookieLine() const {
278*6777b538SAndroid Build Coastguard Worker   std::string out;
279*6777b538SAndroid Build Coastguard Worker   for (auto it = pairs_.begin(); it != pairs_.end(); ++it) {
280*6777b538SAndroid Build Coastguard Worker     if (!out.empty())
281*6777b538SAndroid Build Coastguard Worker       out.append("; ");
282*6777b538SAndroid Build Coastguard Worker     out.append(it->first);
283*6777b538SAndroid Build Coastguard Worker     // Determine whether to emit the pair's value component. We should always
284*6777b538SAndroid Build Coastguard Worker     // print it for the first pair(see crbug.com/977619). After the first pair,
285*6777b538SAndroid Build Coastguard Worker     // we need to consider whether the name component is a special token.
286*6777b538SAndroid Build Coastguard Worker     if (it == pairs_.begin() ||
287*6777b538SAndroid Build Coastguard Worker         (it->first != kSecureTokenName && it->first != kHttpOnlyTokenName &&
288*6777b538SAndroid Build Coastguard Worker          it->first != kPartitionedTokenName)) {
289*6777b538SAndroid Build Coastguard Worker       out.append("=");
290*6777b538SAndroid Build Coastguard Worker       out.append(it->second);
291*6777b538SAndroid Build Coastguard Worker     }
292*6777b538SAndroid Build Coastguard Worker   }
293*6777b538SAndroid Build Coastguard Worker   return out;
294*6777b538SAndroid Build Coastguard Worker }
295*6777b538SAndroid Build Coastguard Worker 
296*6777b538SAndroid Build Coastguard Worker // static
FindFirstTerminator(const std::string & s)297*6777b538SAndroid Build Coastguard Worker std::string::const_iterator ParsedCookie::FindFirstTerminator(
298*6777b538SAndroid Build Coastguard Worker     const std::string& s) {
299*6777b538SAndroid Build Coastguard Worker   std::string::const_iterator end = s.end();
300*6777b538SAndroid Build Coastguard Worker   size_t term_pos = s.find_first_of(std::string(kTerminator, kTerminatorLen));
301*6777b538SAndroid Build Coastguard Worker   if (term_pos != std::string::npos) {
302*6777b538SAndroid Build Coastguard Worker     // We found a character we should treat as an end of string.
303*6777b538SAndroid Build Coastguard Worker     end = s.begin() + term_pos;
304*6777b538SAndroid Build Coastguard Worker   }
305*6777b538SAndroid Build Coastguard Worker   return end;
306*6777b538SAndroid Build Coastguard Worker }
307*6777b538SAndroid Build Coastguard Worker 
308*6777b538SAndroid Build Coastguard Worker // static
ParseToken(std::string::const_iterator * it,const std::string::const_iterator & end,std::string::const_iterator * token_start,std::string::const_iterator * token_end)309*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::ParseToken(std::string::const_iterator* it,
310*6777b538SAndroid Build Coastguard Worker                               const std::string::const_iterator& end,
311*6777b538SAndroid Build Coastguard Worker                               std::string::const_iterator* token_start,
312*6777b538SAndroid Build Coastguard Worker                               std::string::const_iterator* token_end) {
313*6777b538SAndroid Build Coastguard Worker   DCHECK(it && token_start && token_end);
314*6777b538SAndroid Build Coastguard Worker   std::string::const_iterator token_real_end;
315*6777b538SAndroid Build Coastguard Worker 
316*6777b538SAndroid Build Coastguard Worker   // Seek past any whitespace before the "token" (the name).
317*6777b538SAndroid Build Coastguard Worker   // token_start should point at the first character in the token
318*6777b538SAndroid Build Coastguard Worker   if (SeekPast(it, end, kWhitespace))
319*6777b538SAndroid Build Coastguard Worker     return false;  // No token, whitespace or empty.
320*6777b538SAndroid Build Coastguard Worker   *token_start = *it;
321*6777b538SAndroid Build Coastguard Worker 
322*6777b538SAndroid Build Coastguard Worker   // Seek over the token, to the token separator.
323*6777b538SAndroid Build Coastguard Worker   // token_real_end should point at the token separator, i.e. '='.
324*6777b538SAndroid Build Coastguard Worker   // If it == end after the seek, we probably have a token-value.
325*6777b538SAndroid Build Coastguard Worker   SeekTo(it, end, kTokenSeparator);
326*6777b538SAndroid Build Coastguard Worker   token_real_end = *it;
327*6777b538SAndroid Build Coastguard Worker 
328*6777b538SAndroid Build Coastguard Worker   // Ignore any whitespace between the token and the token separator.
329*6777b538SAndroid Build Coastguard Worker   // token_end should point after the last interesting token character,
330*6777b538SAndroid Build Coastguard Worker   // pointing at either whitespace, or at '=' (and equal to token_real_end).
331*6777b538SAndroid Build Coastguard Worker   if (*it != *token_start) {  // We could have an empty token name.
332*6777b538SAndroid Build Coastguard Worker     --(*it);                  // Go back before the token separator.
333*6777b538SAndroid Build Coastguard Worker     // Skip over any whitespace to the first non-whitespace character.
334*6777b538SAndroid Build Coastguard Worker     SeekBackPast(it, *token_start, kWhitespace);
335*6777b538SAndroid Build Coastguard Worker     // Point after it.
336*6777b538SAndroid Build Coastguard Worker     ++(*it);
337*6777b538SAndroid Build Coastguard Worker   }
338*6777b538SAndroid Build Coastguard Worker   *token_end = *it;
339*6777b538SAndroid Build Coastguard Worker 
340*6777b538SAndroid Build Coastguard Worker   // Seek us back to the end of the token.
341*6777b538SAndroid Build Coastguard Worker   *it = token_real_end;
342*6777b538SAndroid Build Coastguard Worker   return true;
343*6777b538SAndroid Build Coastguard Worker }
344*6777b538SAndroid Build Coastguard Worker 
345*6777b538SAndroid Build Coastguard Worker // static
ParseValue(std::string::const_iterator * it,const std::string::const_iterator & end,std::string::const_iterator * value_start,std::string::const_iterator * value_end)346*6777b538SAndroid Build Coastguard Worker void ParsedCookie::ParseValue(std::string::const_iterator* it,
347*6777b538SAndroid Build Coastguard Worker                               const std::string::const_iterator& end,
348*6777b538SAndroid Build Coastguard Worker                               std::string::const_iterator* value_start,
349*6777b538SAndroid Build Coastguard Worker                               std::string::const_iterator* value_end) {
350*6777b538SAndroid Build Coastguard Worker   DCHECK(it && value_start && value_end);
351*6777b538SAndroid Build Coastguard Worker 
352*6777b538SAndroid Build Coastguard Worker   // Seek past any whitespace that might be in-between the token and value.
353*6777b538SAndroid Build Coastguard Worker   SeekPast(it, end, kWhitespace);
354*6777b538SAndroid Build Coastguard Worker   // value_start should point at the first character of the value.
355*6777b538SAndroid Build Coastguard Worker   *value_start = *it;
356*6777b538SAndroid Build Coastguard Worker 
357*6777b538SAndroid Build Coastguard Worker   // Just look for ';' to terminate ('=' allowed).
358*6777b538SAndroid Build Coastguard Worker   // We can hit the end, maybe they didn't terminate.
359*6777b538SAndroid Build Coastguard Worker   SeekToCharacter(it, end, kValueSeparator);
360*6777b538SAndroid Build Coastguard Worker 
361*6777b538SAndroid Build Coastguard Worker   // Will point at the ; separator or the end.
362*6777b538SAndroid Build Coastguard Worker   *value_end = *it;
363*6777b538SAndroid Build Coastguard Worker 
364*6777b538SAndroid Build Coastguard Worker   // Ignore any unwanted whitespace after the value.
365*6777b538SAndroid Build Coastguard Worker   if (*value_end != *value_start) {  // Could have an empty value
366*6777b538SAndroid Build Coastguard Worker     --(*value_end);
367*6777b538SAndroid Build Coastguard Worker     // Skip over any whitespace to the first non-whitespace character.
368*6777b538SAndroid Build Coastguard Worker     SeekBackPast(value_end, *value_start, kWhitespace);
369*6777b538SAndroid Build Coastguard Worker     // Point after it.
370*6777b538SAndroid Build Coastguard Worker     ++(*value_end);
371*6777b538SAndroid Build Coastguard Worker   }
372*6777b538SAndroid Build Coastguard Worker }
373*6777b538SAndroid Build Coastguard Worker 
374*6777b538SAndroid Build Coastguard Worker // static
ParseTokenString(const std::string & token)375*6777b538SAndroid Build Coastguard Worker std::string ParsedCookie::ParseTokenString(const std::string& token) {
376*6777b538SAndroid Build Coastguard Worker   std::string::const_iterator it = token.begin();
377*6777b538SAndroid Build Coastguard Worker   std::string::const_iterator end = FindFirstTerminator(token);
378*6777b538SAndroid Build Coastguard Worker 
379*6777b538SAndroid Build Coastguard Worker   std::string::const_iterator token_start, token_end;
380*6777b538SAndroid Build Coastguard Worker   if (ParseToken(&it, end, &token_start, &token_end))
381*6777b538SAndroid Build Coastguard Worker     return std::string(token_start, token_end);
382*6777b538SAndroid Build Coastguard Worker   return std::string();
383*6777b538SAndroid Build Coastguard Worker }
384*6777b538SAndroid Build Coastguard Worker 
385*6777b538SAndroid Build Coastguard Worker // static
ParseValueString(const std::string & value)386*6777b538SAndroid Build Coastguard Worker std::string ParsedCookie::ParseValueString(const std::string& value) {
387*6777b538SAndroid Build Coastguard Worker   return std::string(ValidStringPieceForValue(value));
388*6777b538SAndroid Build Coastguard Worker }
389*6777b538SAndroid Build Coastguard Worker 
390*6777b538SAndroid Build Coastguard Worker // static
ValueMatchesParsedValue(const std::string & value)391*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::ValueMatchesParsedValue(const std::string& value) {
392*6777b538SAndroid Build Coastguard Worker   // ValidStringPieceForValue() returns a valid substring of |value|.
393*6777b538SAndroid Build Coastguard Worker   // If |value| can be fully parsed the result will have the same length
394*6777b538SAndroid Build Coastguard Worker   // as |value|.
395*6777b538SAndroid Build Coastguard Worker   return ValidStringPieceForValue(value).length() == value.length();
396*6777b538SAndroid Build Coastguard Worker }
397*6777b538SAndroid Build Coastguard Worker 
398*6777b538SAndroid Build Coastguard Worker // static
IsValidCookieName(const std::string & name)399*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::IsValidCookieName(const std::string& name) {
400*6777b538SAndroid Build Coastguard Worker   // IsValidCookieName() returns whether a string matches the following
401*6777b538SAndroid Build Coastguard Worker   // grammar:
402*6777b538SAndroid Build Coastguard Worker   //
403*6777b538SAndroid Build Coastguard Worker   // cookie-name       = *cookie-name-octet
404*6777b538SAndroid Build Coastguard Worker   // cookie-name-octet = %x20-3A / %x3C / %x3E-7E / %x80-FF
405*6777b538SAndroid Build Coastguard Worker   //                       ; octets excluding CTLs, ";", and "="
406*6777b538SAndroid Build Coastguard Worker   //
407*6777b538SAndroid Build Coastguard Worker   // This can be used to determine whether cookie names and cookie attribute
408*6777b538SAndroid Build Coastguard Worker   // names contain any invalid characters.
409*6777b538SAndroid Build Coastguard Worker   //
410*6777b538SAndroid Build Coastguard Worker   // Note that RFC6265bis section 4.1.1 suggests a stricter grammar for
411*6777b538SAndroid Build Coastguard Worker   // parsing cookie names, but we choose to allow a wider range of characters
412*6777b538SAndroid Build Coastguard Worker   // than what's allowed by that grammar (while still conforming to the
413*6777b538SAndroid Build Coastguard Worker   // requirements of the parsing algorithm defined in section 5.2).
414*6777b538SAndroid Build Coastguard Worker   //
415*6777b538SAndroid Build Coastguard Worker   // For reference, see:
416*6777b538SAndroid Build Coastguard Worker   //  - https://crbug.com/238041
417*6777b538SAndroid Build Coastguard Worker   for (char i : name) {
418*6777b538SAndroid Build Coastguard Worker     if (HttpUtil::IsControlChar(i) || i == ';' || i == '=')
419*6777b538SAndroid Build Coastguard Worker       return false;
420*6777b538SAndroid Build Coastguard Worker   }
421*6777b538SAndroid Build Coastguard Worker   return true;
422*6777b538SAndroid Build Coastguard Worker }
423*6777b538SAndroid Build Coastguard Worker 
424*6777b538SAndroid Build Coastguard Worker // static
IsValidCookieValue(const std::string & value)425*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::IsValidCookieValue(const std::string& value) {
426*6777b538SAndroid Build Coastguard Worker   // IsValidCookieValue() returns whether a string matches the following
427*6777b538SAndroid Build Coastguard Worker   // grammar:
428*6777b538SAndroid Build Coastguard Worker   //
429*6777b538SAndroid Build Coastguard Worker   // cookie-value       = *cookie-value-octet
430*6777b538SAndroid Build Coastguard Worker   // cookie-value-octet = %x20-3A / %x3C-7E / %x80-FF
431*6777b538SAndroid Build Coastguard Worker   //                       ; octets excluding CTLs and ";"
432*6777b538SAndroid Build Coastguard Worker   //
433*6777b538SAndroid Build Coastguard Worker   // This can be used to determine whether cookie values contain any invalid
434*6777b538SAndroid Build Coastguard Worker   // characters.
435*6777b538SAndroid Build Coastguard Worker   //
436*6777b538SAndroid Build Coastguard Worker   // Note that RFC6265bis section 4.1.1 suggests a stricter grammar for
437*6777b538SAndroid Build Coastguard Worker   // parsing cookie values, but we choose to allow a wider range of characters
438*6777b538SAndroid Build Coastguard Worker   // than what's allowed by that grammar (while still conforming to the
439*6777b538SAndroid Build Coastguard Worker   // requirements of the parsing algorithm defined in section 5.2).
440*6777b538SAndroid Build Coastguard Worker   //
441*6777b538SAndroid Build Coastguard Worker   // For reference, see:
442*6777b538SAndroid Build Coastguard Worker   //  - https://crbug.com/238041
443*6777b538SAndroid Build Coastguard Worker   for (char i : value) {
444*6777b538SAndroid Build Coastguard Worker     if (HttpUtil::IsControlChar(i) || i == ';')
445*6777b538SAndroid Build Coastguard Worker       return false;
446*6777b538SAndroid Build Coastguard Worker   }
447*6777b538SAndroid Build Coastguard Worker   return true;
448*6777b538SAndroid Build Coastguard Worker }
449*6777b538SAndroid Build Coastguard Worker 
450*6777b538SAndroid Build Coastguard Worker // static
CookieAttributeValueHasValidCharSet(const std::string & value)451*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::CookieAttributeValueHasValidCharSet(
452*6777b538SAndroid Build Coastguard Worker     const std::string& value) {
453*6777b538SAndroid Build Coastguard Worker   // A cookie attribute value has the same character set restrictions as cookie
454*6777b538SAndroid Build Coastguard Worker   // values, so re-use the validation function for that.
455*6777b538SAndroid Build Coastguard Worker   return IsValidCookieValue(value);
456*6777b538SAndroid Build Coastguard Worker }
457*6777b538SAndroid Build Coastguard Worker 
458*6777b538SAndroid Build Coastguard Worker // static
CookieAttributeValueHasValidSize(const std::string & value)459*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::CookieAttributeValueHasValidSize(const std::string& value) {
460*6777b538SAndroid Build Coastguard Worker   return (value.size() <= kMaxCookieAttributeValueSize);
461*6777b538SAndroid Build Coastguard Worker }
462*6777b538SAndroid Build Coastguard Worker 
463*6777b538SAndroid Build Coastguard Worker // static
IsValidCookieNameValuePair(const std::string & name,const std::string & value,CookieInclusionStatus * status_out)464*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::IsValidCookieNameValuePair(
465*6777b538SAndroid Build Coastguard Worker     const std::string& name,
466*6777b538SAndroid Build Coastguard Worker     const std::string& value,
467*6777b538SAndroid Build Coastguard Worker     CookieInclusionStatus* status_out) {
468*6777b538SAndroid Build Coastguard Worker   // Ignore cookies with neither name nor value.
469*6777b538SAndroid Build Coastguard Worker   if (name.empty() && value.empty()) {
470*6777b538SAndroid Build Coastguard Worker     if (status_out != nullptr) {
471*6777b538SAndroid Build Coastguard Worker       status_out->AddExclusionReason(
472*6777b538SAndroid Build Coastguard Worker           CookieInclusionStatus::EXCLUDE_NO_COOKIE_CONTENT);
473*6777b538SAndroid Build Coastguard Worker     }
474*6777b538SAndroid Build Coastguard Worker     // TODO(crbug.com/1228815) Note - if the exclusion reasons change to no
475*6777b538SAndroid Build Coastguard Worker     // longer be the same, we'll need to not return right away and evaluate all
476*6777b538SAndroid Build Coastguard Worker     // of the checks.
477*6777b538SAndroid Build Coastguard Worker     return false;
478*6777b538SAndroid Build Coastguard Worker   }
479*6777b538SAndroid Build Coastguard Worker 
480*6777b538SAndroid Build Coastguard Worker   // Enforce a length limit for name + value per RFC6265bis.
481*6777b538SAndroid Build Coastguard Worker   base::CheckedNumeric<size_t> name_value_pair_size = name.size();
482*6777b538SAndroid Build Coastguard Worker   name_value_pair_size += value.size();
483*6777b538SAndroid Build Coastguard Worker   if (!name_value_pair_size.IsValid() ||
484*6777b538SAndroid Build Coastguard Worker       (name_value_pair_size.ValueOrDie() > kMaxCookieNamePlusValueSize)) {
485*6777b538SAndroid Build Coastguard Worker     if (status_out != nullptr) {
486*6777b538SAndroid Build Coastguard Worker       status_out->AddExclusionReason(
487*6777b538SAndroid Build Coastguard Worker           CookieInclusionStatus::EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE);
488*6777b538SAndroid Build Coastguard Worker     }
489*6777b538SAndroid Build Coastguard Worker     return false;
490*6777b538SAndroid Build Coastguard Worker   }
491*6777b538SAndroid Build Coastguard Worker 
492*6777b538SAndroid Build Coastguard Worker   // Ignore Set-Cookie directives containing control characters. See
493*6777b538SAndroid Build Coastguard Worker   // http://crbug.com/238041.
494*6777b538SAndroid Build Coastguard Worker   if (!IsValidCookieName(name) || !IsValidCookieValue(value)) {
495*6777b538SAndroid Build Coastguard Worker     if (status_out != nullptr) {
496*6777b538SAndroid Build Coastguard Worker       status_out->AddExclusionReason(
497*6777b538SAndroid Build Coastguard Worker           CookieInclusionStatus::EXCLUDE_DISALLOWED_CHARACTER);
498*6777b538SAndroid Build Coastguard Worker     }
499*6777b538SAndroid Build Coastguard Worker     return false;
500*6777b538SAndroid Build Coastguard Worker   }
501*6777b538SAndroid Build Coastguard Worker   return true;
502*6777b538SAndroid Build Coastguard Worker }
503*6777b538SAndroid Build Coastguard Worker 
504*6777b538SAndroid Build Coastguard Worker // Parse all token/value pairs and populate pairs_.
ParseTokenValuePairs(const std::string & cookie_line,bool block_truncated,CookieInclusionStatus & status_out)505*6777b538SAndroid Build Coastguard Worker void ParsedCookie::ParseTokenValuePairs(const std::string& cookie_line,
506*6777b538SAndroid Build Coastguard Worker                                         bool block_truncated,
507*6777b538SAndroid Build Coastguard Worker                                         CookieInclusionStatus& status_out) {
508*6777b538SAndroid Build Coastguard Worker   pairs_.clear();
509*6777b538SAndroid Build Coastguard Worker 
510*6777b538SAndroid Build Coastguard Worker   // Ok, here we go.  We should be expecting to be starting somewhere
511*6777b538SAndroid Build Coastguard Worker   // before the cookie line, not including any header name...
512*6777b538SAndroid Build Coastguard Worker   std::string::const_iterator start = cookie_line.begin();
513*6777b538SAndroid Build Coastguard Worker   std::string::const_iterator it = start;
514*6777b538SAndroid Build Coastguard Worker 
515*6777b538SAndroid Build Coastguard Worker   // TODO(erikwright): Make sure we're stripping \r\n in the network code.
516*6777b538SAndroid Build Coastguard Worker   // Then we can log any unexpected terminators.
517*6777b538SAndroid Build Coastguard Worker   std::string::const_iterator end = FindFirstTerminator(cookie_line);
518*6777b538SAndroid Build Coastguard Worker 
519*6777b538SAndroid Build Coastguard Worker   // For metrics on truncating character presence in the cookie line.
520*6777b538SAndroid Build Coastguard Worker   if (end < cookie_line.end()) {
521*6777b538SAndroid Build Coastguard Worker     switch (*end) {
522*6777b538SAndroid Build Coastguard Worker       case '\0':
523*6777b538SAndroid Build Coastguard Worker         truncating_char_in_cookie_string_type_ =
524*6777b538SAndroid Build Coastguard Worker             TruncatingCharacterInCookieStringType::kTruncatingCharNull;
525*6777b538SAndroid Build Coastguard Worker         break;
526*6777b538SAndroid Build Coastguard Worker       case '\r':
527*6777b538SAndroid Build Coastguard Worker         truncating_char_in_cookie_string_type_ =
528*6777b538SAndroid Build Coastguard Worker             TruncatingCharacterInCookieStringType::kTruncatingCharNewline;
529*6777b538SAndroid Build Coastguard Worker         break;
530*6777b538SAndroid Build Coastguard Worker       case '\n':
531*6777b538SAndroid Build Coastguard Worker         truncating_char_in_cookie_string_type_ =
532*6777b538SAndroid Build Coastguard Worker             TruncatingCharacterInCookieStringType::kTruncatingCharLineFeed;
533*6777b538SAndroid Build Coastguard Worker         break;
534*6777b538SAndroid Build Coastguard Worker       default:
535*6777b538SAndroid Build Coastguard Worker         NOTREACHED();
536*6777b538SAndroid Build Coastguard Worker     }
537*6777b538SAndroid Build Coastguard Worker     if (block_truncated &&
538*6777b538SAndroid Build Coastguard Worker         base::FeatureList::IsEnabled(net::features::kBlockTruncatedCookies)) {
539*6777b538SAndroid Build Coastguard Worker       status_out.AddExclusionReason(
540*6777b538SAndroid Build Coastguard Worker           CookieInclusionStatus::EXCLUDE_DISALLOWED_CHARACTER);
541*6777b538SAndroid Build Coastguard Worker       return;
542*6777b538SAndroid Build Coastguard Worker     }
543*6777b538SAndroid Build Coastguard Worker   }
544*6777b538SAndroid Build Coastguard Worker 
545*6777b538SAndroid Build Coastguard Worker   // Exit early for an empty cookie string.
546*6777b538SAndroid Build Coastguard Worker   if (it == end) {
547*6777b538SAndroid Build Coastguard Worker     status_out.AddExclusionReason(
548*6777b538SAndroid Build Coastguard Worker         CookieInclusionStatus::EXCLUDE_NO_COOKIE_CONTENT);
549*6777b538SAndroid Build Coastguard Worker     return;
550*6777b538SAndroid Build Coastguard Worker   }
551*6777b538SAndroid Build Coastguard Worker 
552*6777b538SAndroid Build Coastguard Worker   for (int pair_num = 0; it != end; ++pair_num) {
553*6777b538SAndroid Build Coastguard Worker     TokenValuePair pair;
554*6777b538SAndroid Build Coastguard Worker 
555*6777b538SAndroid Build Coastguard Worker     std::string::const_iterator token_start, token_end;
556*6777b538SAndroid Build Coastguard Worker     if (!ParseToken(&it, end, &token_start, &token_end)) {
557*6777b538SAndroid Build Coastguard Worker       // Allow first token to be treated as empty-key if unparsable
558*6777b538SAndroid Build Coastguard Worker       if (pair_num != 0)
559*6777b538SAndroid Build Coastguard Worker         break;
560*6777b538SAndroid Build Coastguard Worker 
561*6777b538SAndroid Build Coastguard Worker       // If parsing failed, start the value parsing at the very beginning.
562*6777b538SAndroid Build Coastguard Worker       token_start = start;
563*6777b538SAndroid Build Coastguard Worker     }
564*6777b538SAndroid Build Coastguard Worker 
565*6777b538SAndroid Build Coastguard Worker     if (it == end || *it != '=') {
566*6777b538SAndroid Build Coastguard Worker       // We have a token-value, we didn't have any token name.
567*6777b538SAndroid Build Coastguard Worker       if (pair_num == 0) {
568*6777b538SAndroid Build Coastguard Worker         // For the first time around, we want to treat single values
569*6777b538SAndroid Build Coastguard Worker         // as a value with an empty name. (Mozilla bug 169091).
570*6777b538SAndroid Build Coastguard Worker         // IE seems to also have this behavior, ex "AAA", and "AAA=10" will
571*6777b538SAndroid Build Coastguard Worker         // set 2 different cookies, and setting "BBB" will then replace "AAA".
572*6777b538SAndroid Build Coastguard Worker         pair.first = "";
573*6777b538SAndroid Build Coastguard Worker         // Rewind to the beginning of what we thought was the token name,
574*6777b538SAndroid Build Coastguard Worker         // and let it get parsed as a value.
575*6777b538SAndroid Build Coastguard Worker         it = token_start;
576*6777b538SAndroid Build Coastguard Worker       } else {
577*6777b538SAndroid Build Coastguard Worker         // Any not-first attribute we want to treat a value as a
578*6777b538SAndroid Build Coastguard Worker         // name with an empty value...  This is so something like
579*6777b538SAndroid Build Coastguard Worker         // "secure;" will get parsed as a Token name, and not a value.
580*6777b538SAndroid Build Coastguard Worker         pair.first = std::string(token_start, token_end);
581*6777b538SAndroid Build Coastguard Worker       }
582*6777b538SAndroid Build Coastguard Worker     } else {
583*6777b538SAndroid Build Coastguard Worker       // We have a TOKEN=VALUE.
584*6777b538SAndroid Build Coastguard Worker       pair.first = std::string(token_start, token_end);
585*6777b538SAndroid Build Coastguard Worker       ++it;  // Skip past the '='.
586*6777b538SAndroid Build Coastguard Worker     }
587*6777b538SAndroid Build Coastguard Worker 
588*6777b538SAndroid Build Coastguard Worker     // OK, now try to parse a value.
589*6777b538SAndroid Build Coastguard Worker     std::string::const_iterator value_start, value_end;
590*6777b538SAndroid Build Coastguard Worker     ParseValue(&it, end, &value_start, &value_end);
591*6777b538SAndroid Build Coastguard Worker 
592*6777b538SAndroid Build Coastguard Worker     // OK, we're finished with a Token/Value.
593*6777b538SAndroid Build Coastguard Worker     pair.second = std::string(value_start, value_end);
594*6777b538SAndroid Build Coastguard Worker 
595*6777b538SAndroid Build Coastguard Worker     // For metrics, check if either the name or value contain an internal HTAB
596*6777b538SAndroid Build Coastguard Worker     // (0x9). That is, not leading or trailing.
597*6777b538SAndroid Build Coastguard Worker     if (pair_num == 0 &&
598*6777b538SAndroid Build Coastguard Worker         (pair.first.find_first_of("\t") != std::string::npos ||
599*6777b538SAndroid Build Coastguard Worker          pair.second.find_first_of("\t") != std::string::npos)) {
600*6777b538SAndroid Build Coastguard Worker       internal_htab_ = true;
601*6777b538SAndroid Build Coastguard Worker     }
602*6777b538SAndroid Build Coastguard Worker 
603*6777b538SAndroid Build Coastguard Worker     bool ignore_pair = false;
604*6777b538SAndroid Build Coastguard Worker     if (pair_num == 0) {
605*6777b538SAndroid Build Coastguard Worker       if (!IsValidCookieNameValuePair(pair.first, pair.second, &status_out)) {
606*6777b538SAndroid Build Coastguard Worker         pairs_.clear();
607*6777b538SAndroid Build Coastguard Worker         break;
608*6777b538SAndroid Build Coastguard Worker       }
609*6777b538SAndroid Build Coastguard Worker     } else {
610*6777b538SAndroid Build Coastguard Worker       // From RFC2109: "Attributes (names) (attr) are case-insensitive."
611*6777b538SAndroid Build Coastguard Worker       pair.first = base::ToLowerASCII(pair.first);
612*6777b538SAndroid Build Coastguard Worker 
613*6777b538SAndroid Build Coastguard Worker       // Attribute names have the same character set limitations as cookie
614*6777b538SAndroid Build Coastguard Worker       // names, but only a handful of values are allowed. We don't check that
615*6777b538SAndroid Build Coastguard Worker       // this attribute name is one of the allowed ones here, so just re-use
616*6777b538SAndroid Build Coastguard Worker       // the cookie name check.
617*6777b538SAndroid Build Coastguard Worker       if (!IsValidCookieName(pair.first)) {
618*6777b538SAndroid Build Coastguard Worker         status_out.AddExclusionReason(
619*6777b538SAndroid Build Coastguard Worker             CookieInclusionStatus::EXCLUDE_DISALLOWED_CHARACTER);
620*6777b538SAndroid Build Coastguard Worker         pairs_.clear();
621*6777b538SAndroid Build Coastguard Worker         break;
622*6777b538SAndroid Build Coastguard Worker       }
623*6777b538SAndroid Build Coastguard Worker 
624*6777b538SAndroid Build Coastguard Worker       if (!CookieAttributeValueHasValidCharSet(pair.second)) {
625*6777b538SAndroid Build Coastguard Worker         // If the attribute value contains invalid characters, the whole
626*6777b538SAndroid Build Coastguard Worker         // cookie should be ignored.
627*6777b538SAndroid Build Coastguard Worker         status_out.AddExclusionReason(
628*6777b538SAndroid Build Coastguard Worker             CookieInclusionStatus::EXCLUDE_DISALLOWED_CHARACTER);
629*6777b538SAndroid Build Coastguard Worker         pairs_.clear();
630*6777b538SAndroid Build Coastguard Worker         break;
631*6777b538SAndroid Build Coastguard Worker       }
632*6777b538SAndroid Build Coastguard Worker 
633*6777b538SAndroid Build Coastguard Worker       if (!CookieAttributeValueHasValidSize(pair.second)) {
634*6777b538SAndroid Build Coastguard Worker         // If the attribute value is too large, it should be ignored.
635*6777b538SAndroid Build Coastguard Worker         ignore_pair = true;
636*6777b538SAndroid Build Coastguard Worker         status_out.AddWarningReason(
637*6777b538SAndroid Build Coastguard Worker             CookieInclusionStatus::WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE);
638*6777b538SAndroid Build Coastguard Worker       }
639*6777b538SAndroid Build Coastguard Worker     }
640*6777b538SAndroid Build Coastguard Worker 
641*6777b538SAndroid Build Coastguard Worker     if (!ignore_pair) {
642*6777b538SAndroid Build Coastguard Worker       pairs_.push_back(pair);
643*6777b538SAndroid Build Coastguard Worker     }
644*6777b538SAndroid Build Coastguard Worker 
645*6777b538SAndroid Build Coastguard Worker     // We've processed a token/value pair, we're either at the end of
646*6777b538SAndroid Build Coastguard Worker     // the string or a ValueSeparator like ';', which we want to skip.
647*6777b538SAndroid Build Coastguard Worker     if (it != end)
648*6777b538SAndroid Build Coastguard Worker       ++it;
649*6777b538SAndroid Build Coastguard Worker   }
650*6777b538SAndroid Build Coastguard Worker }
651*6777b538SAndroid Build Coastguard Worker 
SetupAttributes()652*6777b538SAndroid Build Coastguard Worker void ParsedCookie::SetupAttributes() {
653*6777b538SAndroid Build Coastguard Worker   // We skip over the first token/value, the user supplied one.
654*6777b538SAndroid Build Coastguard Worker   for (size_t i = 1; i < pairs_.size(); ++i) {
655*6777b538SAndroid Build Coastguard Worker     if (pairs_[i].first == kPathTokenName) {
656*6777b538SAndroid Build Coastguard Worker       path_index_ = i;
657*6777b538SAndroid Build Coastguard Worker     } else if (pairs_[i].first == kDomainTokenName) {
658*6777b538SAndroid Build Coastguard Worker       domain_index_ = i;
659*6777b538SAndroid Build Coastguard Worker     } else if (pairs_[i].first == kExpiresTokenName) {
660*6777b538SAndroid Build Coastguard Worker       expires_index_ = i;
661*6777b538SAndroid Build Coastguard Worker     } else if (pairs_[i].first == kMaxAgeTokenName) {
662*6777b538SAndroid Build Coastguard Worker       maxage_index_ = i;
663*6777b538SAndroid Build Coastguard Worker     } else if (pairs_[i].first == kSecureTokenName) {
664*6777b538SAndroid Build Coastguard Worker       secure_index_ = i;
665*6777b538SAndroid Build Coastguard Worker     } else if (pairs_[i].first == kHttpOnlyTokenName) {
666*6777b538SAndroid Build Coastguard Worker       httponly_index_ = i;
667*6777b538SAndroid Build Coastguard Worker     } else if (pairs_[i].first == kSameSiteTokenName) {
668*6777b538SAndroid Build Coastguard Worker       same_site_index_ = i;
669*6777b538SAndroid Build Coastguard Worker     } else if (pairs_[i].first == kPriorityTokenName) {
670*6777b538SAndroid Build Coastguard Worker       priority_index_ = i;
671*6777b538SAndroid Build Coastguard Worker     } else if (pairs_[i].first == kPartitionedTokenName) {
672*6777b538SAndroid Build Coastguard Worker       partitioned_index_ = i;
673*6777b538SAndroid Build Coastguard Worker     } else {
674*6777b538SAndroid Build Coastguard Worker       /* some attribute we don't know or don't care about. */
675*6777b538SAndroid Build Coastguard Worker     }
676*6777b538SAndroid Build Coastguard Worker   }
677*6777b538SAndroid Build Coastguard Worker }
678*6777b538SAndroid Build Coastguard Worker 
SetString(size_t * index,const std::string & key,const std::string & untrusted_value)679*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::SetString(size_t* index,
680*6777b538SAndroid Build Coastguard Worker                              const std::string& key,
681*6777b538SAndroid Build Coastguard Worker                              const std::string& untrusted_value) {
682*6777b538SAndroid Build Coastguard Worker   // This function should do equivalent input validation to the
683*6777b538SAndroid Build Coastguard Worker   // constructor. Otherwise, the Set* functions can put this ParsedCookie in a
684*6777b538SAndroid Build Coastguard Worker   // state where parsing the output of ToCookieLine() produces a different
685*6777b538SAndroid Build Coastguard Worker   // ParsedCookie.
686*6777b538SAndroid Build Coastguard Worker   //
687*6777b538SAndroid Build Coastguard Worker   // Without input validation, invoking pc.SetPath(" baz ") would result in
688*6777b538SAndroid Build Coastguard Worker   // pc.ToCookieLine() == "path= baz ". Parsing the "path= baz " string would
689*6777b538SAndroid Build Coastguard Worker   // produce a cookie with "path" attribute equal to "baz" (no spaces). We
690*6777b538SAndroid Build Coastguard Worker   // should not produce cookie lines that parse to different key/value pairs!
691*6777b538SAndroid Build Coastguard Worker 
692*6777b538SAndroid Build Coastguard Worker   // Inputs containing invalid characters or attribute value strings that are
693*6777b538SAndroid Build Coastguard Worker   // too large should be ignored. Note that we check the attribute value size
694*6777b538SAndroid Build Coastguard Worker   // after removing leading and trailing whitespace.
695*6777b538SAndroid Build Coastguard Worker   if (!CookieAttributeValueHasValidCharSet(untrusted_value))
696*6777b538SAndroid Build Coastguard Worker     return false;
697*6777b538SAndroid Build Coastguard Worker 
698*6777b538SAndroid Build Coastguard Worker   // Use the same whitespace trimming code as the constructor.
699*6777b538SAndroid Build Coastguard Worker   const std::string parsed_value = ParseValueString(untrusted_value);
700*6777b538SAndroid Build Coastguard Worker 
701*6777b538SAndroid Build Coastguard Worker   if (!CookieAttributeValueHasValidSize(parsed_value))
702*6777b538SAndroid Build Coastguard Worker     return false;
703*6777b538SAndroid Build Coastguard Worker 
704*6777b538SAndroid Build Coastguard Worker   if (parsed_value.empty()) {
705*6777b538SAndroid Build Coastguard Worker     ClearAttributePair(*index);
706*6777b538SAndroid Build Coastguard Worker     return true;
707*6777b538SAndroid Build Coastguard Worker   } else {
708*6777b538SAndroid Build Coastguard Worker     return SetAttributePair(index, key, parsed_value);
709*6777b538SAndroid Build Coastguard Worker   }
710*6777b538SAndroid Build Coastguard Worker }
711*6777b538SAndroid Build Coastguard Worker 
SetBool(size_t * index,const std::string & key,bool value)712*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::SetBool(size_t* index, const std::string& key, bool value) {
713*6777b538SAndroid Build Coastguard Worker   if (!value) {
714*6777b538SAndroid Build Coastguard Worker     ClearAttributePair(*index);
715*6777b538SAndroid Build Coastguard Worker     return true;
716*6777b538SAndroid Build Coastguard Worker   } else {
717*6777b538SAndroid Build Coastguard Worker     return SetAttributePair(index, key, std::string());
718*6777b538SAndroid Build Coastguard Worker   }
719*6777b538SAndroid Build Coastguard Worker }
720*6777b538SAndroid Build Coastguard Worker 
SetAttributePair(size_t * index,const std::string & key,const std::string & value)721*6777b538SAndroid Build Coastguard Worker bool ParsedCookie::SetAttributePair(size_t* index,
722*6777b538SAndroid Build Coastguard Worker                                     const std::string& key,
723*6777b538SAndroid Build Coastguard Worker                                     const std::string& value) {
724*6777b538SAndroid Build Coastguard Worker   if (!HttpUtil::IsToken(key))
725*6777b538SAndroid Build Coastguard Worker     return false;
726*6777b538SAndroid Build Coastguard Worker   if (!IsValid())
727*6777b538SAndroid Build Coastguard Worker     return false;
728*6777b538SAndroid Build Coastguard Worker   if (*index) {
729*6777b538SAndroid Build Coastguard Worker     pairs_[*index].second = value;
730*6777b538SAndroid Build Coastguard Worker   } else {
731*6777b538SAndroid Build Coastguard Worker     pairs_.emplace_back(key, value);
732*6777b538SAndroid Build Coastguard Worker     *index = pairs_.size() - 1;
733*6777b538SAndroid Build Coastguard Worker   }
734*6777b538SAndroid Build Coastguard Worker   return true;
735*6777b538SAndroid Build Coastguard Worker }
736*6777b538SAndroid Build Coastguard Worker 
ClearAttributePair(size_t index)737*6777b538SAndroid Build Coastguard Worker void ParsedCookie::ClearAttributePair(size_t index) {
738*6777b538SAndroid Build Coastguard Worker   // The first pair (name/value of cookie at pairs_[0]) cannot be cleared.
739*6777b538SAndroid Build Coastguard Worker   // Cookie attributes that don't have a value at the moment, are
740*6777b538SAndroid Build Coastguard Worker   // represented with an index being equal to 0.
741*6777b538SAndroid Build Coastguard Worker   if (index == 0)
742*6777b538SAndroid Build Coastguard Worker     return;
743*6777b538SAndroid Build Coastguard Worker 
744*6777b538SAndroid Build Coastguard Worker   size_t* indexes[] = {
745*6777b538SAndroid Build Coastguard Worker       &path_index_,      &domain_index_,   &expires_index_,
746*6777b538SAndroid Build Coastguard Worker       &maxage_index_,    &secure_index_,   &httponly_index_,
747*6777b538SAndroid Build Coastguard Worker       &same_site_index_, &priority_index_, &partitioned_index_};
748*6777b538SAndroid Build Coastguard Worker   for (size_t* attribute_index : indexes) {
749*6777b538SAndroid Build Coastguard Worker     if (*attribute_index == index)
750*6777b538SAndroid Build Coastguard Worker       *attribute_index = 0;
751*6777b538SAndroid Build Coastguard Worker     else if (*attribute_index > index)
752*6777b538SAndroid Build Coastguard Worker       --(*attribute_index);
753*6777b538SAndroid Build Coastguard Worker   }
754*6777b538SAndroid Build Coastguard Worker   pairs_.erase(pairs_.begin() + index);
755*6777b538SAndroid Build Coastguard Worker }
756*6777b538SAndroid Build Coastguard Worker 
757*6777b538SAndroid Build Coastguard Worker }  // namespace net
758