1*6777b538SAndroid Build Coastguard Worker // Copyright 2024 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 #include "net/http/http_cookie_indices.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include "net/cookies/parsed_cookie.h"
8*6777b538SAndroid Build Coastguard Worker #include "net/http/http_response_headers.h"
9*6777b538SAndroid Build Coastguard Worker #include "net/http/structured_headers.h"
10*6777b538SAndroid Build Coastguard Worker
11*6777b538SAndroid Build Coastguard Worker namespace net {
12*6777b538SAndroid Build Coastguard Worker
13*6777b538SAndroid Build Coastguard Worker namespace {
14*6777b538SAndroid Build Coastguard Worker constexpr std::string_view kCookieIndicesHeader = "Cookie-Indices";
15*6777b538SAndroid Build Coastguard Worker } // namespace
16*6777b538SAndroid Build Coastguard Worker
ParseCookieIndices(const HttpResponseHeaders & headers)17*6777b538SAndroid Build Coastguard Worker std::optional<std::vector<std::string>> ParseCookieIndices(
18*6777b538SAndroid Build Coastguard Worker const HttpResponseHeaders& headers) {
19*6777b538SAndroid Build Coastguard Worker std::string normalized_header;
20*6777b538SAndroid Build Coastguard Worker if (!headers.GetNormalizedHeader(kCookieIndicesHeader, &normalized_header)) {
21*6777b538SAndroid Build Coastguard Worker return std::nullopt;
22*6777b538SAndroid Build Coastguard Worker }
23*6777b538SAndroid Build Coastguard Worker
24*6777b538SAndroid Build Coastguard Worker std::optional<net::structured_headers::List> list =
25*6777b538SAndroid Build Coastguard Worker structured_headers::ParseList(normalized_header);
26*6777b538SAndroid Build Coastguard Worker if (!list.has_value()) {
27*6777b538SAndroid Build Coastguard Worker return std::nullopt;
28*6777b538SAndroid Build Coastguard Worker }
29*6777b538SAndroid Build Coastguard Worker
30*6777b538SAndroid Build Coastguard Worker std::vector<std::string> cookie_names;
31*6777b538SAndroid Build Coastguard Worker cookie_names.reserve(list->size());
32*6777b538SAndroid Build Coastguard Worker for (const structured_headers::ParameterizedMember& member : *list) {
33*6777b538SAndroid Build Coastguard Worker if (member.member_is_inner_list) {
34*6777b538SAndroid Build Coastguard Worker // Inner list not permitted here.
35*6777b538SAndroid Build Coastguard Worker return std::nullopt;
36*6777b538SAndroid Build Coastguard Worker }
37*6777b538SAndroid Build Coastguard Worker
38*6777b538SAndroid Build Coastguard Worker const structured_headers::ParameterizedItem& item = member.member[0];
39*6777b538SAndroid Build Coastguard Worker if (!item.item.is_string()) {
40*6777b538SAndroid Build Coastguard Worker // Non-string items are not permitted here.
41*6777b538SAndroid Build Coastguard Worker return std::nullopt;
42*6777b538SAndroid Build Coastguard Worker }
43*6777b538SAndroid Build Coastguard Worker
44*6777b538SAndroid Build Coastguard Worker // There are basically three sets of requirements that are interesting here.
45*6777b538SAndroid Build Coastguard Worker //
46*6777b538SAndroid Build Coastguard Worker // 1. Cookie names Chromium considers valid, given by:
47*6777b538SAndroid Build Coastguard Worker // cookie-name = *cookie-name-octet
48*6777b538SAndroid Build Coastguard Worker // cookie-name-octet = %x20-3A / %x3C / %x3E-7E / %x80-FF
49*6777b538SAndroid Build Coastguard Worker // ; octets excluding CTLs, ";", and "="
50*6777b538SAndroid Build Coastguard Worker // See |net::ParsedCookie::IsValidCookieName|.
51*6777b538SAndroid Build Coastguard Worker //
52*6777b538SAndroid Build Coastguard Worker // 2. Cookie names RFC 6265 considers valid, given by:
53*6777b538SAndroid Build Coastguard Worker // cookie-name = token
54*6777b538SAndroid Build Coastguard Worker // token = 1*<any CHAR except CTLs or separators>
55*6777b538SAndroid Build Coastguard Worker // separators = "(" | ")" | "<" | ">" | "@"
56*6777b538SAndroid Build Coastguard Worker // | "," | ";" | ":" | "\" | <">
57*6777b538SAndroid Build Coastguard Worker // | "/" | "[" | "]" | "?" | "="
58*6777b538SAndroid Build Coastguard Worker // | "{" | "}" | SP | HT
59*6777b538SAndroid Build Coastguard Worker // CHAR = <any US-ASCII character (octets 0 - 127)>
60*6777b538SAndroid Build Coastguard Worker // CTL = <any US-ASCII control character
61*6777b538SAndroid Build Coastguard Worker // (octets 0 - 31) and DEL (127)>
62*6777b538SAndroid Build Coastguard Worker //
63*6777b538SAndroid Build Coastguard Worker // 3. Valid RFC 8941 structured field strings, whose values are given by:
64*6777b538SAndroid Build Coastguard Worker // string-value = *( %x20-7E )
65*6777b538SAndroid Build Coastguard Worker //
66*6777b538SAndroid Build Coastguard Worker // While all RFC 6265 valid cookie names are valid structured field strings,
67*6777b538SAndroid Build Coastguard Worker // Chromium accepts cookies whose names can nonetheless not be spelled here.
68*6777b538SAndroid Build Coastguard Worker // For example, cookie names outside 7-bit ASCII cannot be specified.
69*6777b538SAndroid Build Coastguard Worker //
70*6777b538SAndroid Build Coastguard Worker // Nor is every structured field string a valid cookie name, since it may
71*6777b538SAndroid Build Coastguard Worker // contain a ";" or "=" character (or several other characters excluded by
72*6777b538SAndroid Build Coastguard Worker // RFC 6265 in addition to Chromium). In the interest of interoperability,
73*6777b538SAndroid Build Coastguard Worker // those are expressly rejected.
74*6777b538SAndroid Build Coastguard Worker const std::string& name = item.item.GetString();
75*6777b538SAndroid Build Coastguard Worker if (name.find_first_of("()<>@,;:\\\"/[]?={} \t") != std::string::npos) {
76*6777b538SAndroid Build Coastguard Worker // This is one of those structured field strings that is not a valid
77*6777b538SAndroid Build Coastguard Worker // cookie name according to RFC 6265.
78*6777b538SAndroid Build Coastguard Worker // TODO(crbug.com/328628231): Watch mnot/I-D#346 to see if a different
79*6777b538SAndroid Build Coastguard Worker // behavior is agreed on.
80*6777b538SAndroid Build Coastguard Worker continue;
81*6777b538SAndroid Build Coastguard Worker }
82*6777b538SAndroid Build Coastguard Worker CHECK(ParsedCookie::IsValidCookieName(name))
83*6777b538SAndroid Build Coastguard Worker << "invalid cookie name \"" << name << "\"";
84*6777b538SAndroid Build Coastguard Worker cookie_names.push_back(name);
85*6777b538SAndroid Build Coastguard Worker }
86*6777b538SAndroid Build Coastguard Worker return cookie_names;
87*6777b538SAndroid Build Coastguard Worker }
88*6777b538SAndroid Build Coastguard Worker
89*6777b538SAndroid Build Coastguard Worker } // namespace net
90