blob: 222a92e72884a785a16420f5da3820e93896c265 [file] [log] [blame]
juliatuttle1690bc62017-03-29 17:16:021// Copyright 2017 The Chromium Authors. All rights reserved.
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/reporting/reporting_header_parser.h"
6
7#include <string>
Lily Chenefb6fcf2019-04-19 04:17:548#include <utility>
9#include <vector>
juliatuttle1690bc62017-03-29 17:16:0210
Julia Tuttleec467a5f2018-02-22 20:22:4511#include "base/bind.h"
juliatuttle1690bc62017-03-29 17:16:0212#include "base/json/json_reader.h"
13#include "base/logging.h"
juliatuttle667c0bb2017-07-06 15:17:1314#include "base/metrics/histogram_macros.h"
juliatuttle1690bc62017-03-29 17:16:0215#include "base/time/time.h"
16#include "base/values.h"
17#include "net/reporting/reporting_cache.h"
juliatuttleee4b55e2017-04-07 17:09:4518#include "net/reporting/reporting_context.h"
juliatuttle587548912017-05-23 14:17:2119#include "net/reporting/reporting_delegate.h"
Lily Chenfc92ff42019-05-06 22:59:1020#include "net/reporting/reporting_endpoint.h"
juliatuttle1690bc62017-03-29 17:16:0221
22namespace net {
23
24namespace {
25
Douglas Creager134b52e2018-11-09 18:00:1426using HeaderEndpointGroupOutcome =
27 ReportingHeaderParser::HeaderEndpointGroupOutcome;
28using HeaderEndpointOutcome = ReportingHeaderParser::HeaderEndpointOutcome;
29using HeaderOutcome = ReportingHeaderParser::HeaderOutcome;
juliatuttle667c0bb2017-07-06 15:17:1330
31void RecordHeaderOutcome(HeaderOutcome outcome) {
Douglas Creager134b52e2018-11-09 18:00:1432 UMA_HISTOGRAM_ENUMERATION(ReportingHeaderParser::kHeaderOutcomeHistogram,
33 outcome, HeaderOutcome::MAX);
juliatuttle667c0bb2017-07-06 15:17:1334}
35
Douglas Creagerf0db63a2018-02-28 17:50:2336void RecordHeaderEndpointGroupOutcome(HeaderEndpointGroupOutcome outcome) {
Douglas Creager134b52e2018-11-09 18:00:1437 UMA_HISTOGRAM_ENUMERATION(
38 ReportingHeaderParser::kHeaderEndpointGroupOutcomeHistogram, outcome,
39 HeaderEndpointGroupOutcome::MAX);
Douglas Creagerf0db63a2018-02-28 17:50:2340}
41
juliatuttle667c0bb2017-07-06 15:17:1342void RecordHeaderEndpointOutcome(HeaderEndpointOutcome outcome) {
Douglas Creager134b52e2018-11-09 18:00:1443 UMA_HISTOGRAM_ENUMERATION(
44 ReportingHeaderParser::kHeaderEndpointOutcomeHistogram, outcome,
45 HeaderEndpointOutcome::MAX);
juliatuttle667c0bb2017-07-06 15:17:1346}
47
juliatuttle1690bc62017-03-29 17:16:0248const char kUrlKey[] = "url";
Douglas Creagerbca64422018-06-18 13:54:4249const char kIncludeSubdomainsKey[] = "include_subdomains";
Douglas Creagerf0db63a2018-02-28 17:50:2350const char kEndpointsKey[] = "endpoints";
juliatuttle1690bc62017-03-29 17:16:0251const char kGroupKey[] = "group";
Lily Chenefb6fcf2019-04-19 04:17:5452const char kDefaultGroupName[] = "default";
Douglas Creagerbca64422018-06-18 13:54:4253const char kMaxAgeKey[] = "max_age";
Julia Tuttled56350d2017-12-07 19:11:1754const char kPriorityKey[] = "priority";
55const char kWeightKey[] = "weight";
juliatuttle1690bc62017-03-29 17:16:0256
Julia Tuttle443a0a682017-12-04 16:16:2657// Processes a single endpoint tuple received in a Report-To header.
58//
59// |origin| is the origin that sent the Report-To header.
60//
61// |value| is the parsed JSON value of the endpoint tuple.
62//
63// |*endpoint_out| will contain the endpoint URL parsed out of the tuple.
Lily Chenefb6fcf2019-04-19 04:17:5464HeaderEndpointOutcome ProcessEndpoint(
65 ReportingDelegate* delegate,
66 const url::Origin& origin,
67 const base::Value& value,
Lily Chenfc92ff42019-05-06 22:59:1068 ReportingEndpoint::EndpointInfo* endpoint_info_out) {
juliatuttle1690bc62017-03-29 17:16:0269 const base::DictionaryValue* dict = nullptr;
70 if (!value.GetAsDictionary(&dict))
juliatuttle667c0bb2017-07-06 15:17:1371 return HeaderEndpointOutcome::DISCARDED_NOT_DICTIONARY;
juliatuttle1690bc62017-03-29 17:16:0272 DCHECK(dict);
73
74 std::string endpoint_url_string;
juliatuttle667c0bb2017-07-06 15:17:1375 if (!dict->HasKey(kUrlKey))
Douglas Creagerf0db63a2018-02-28 17:50:2376 return HeaderEndpointOutcome::DISCARDED_URL_MISSING;
juliatuttle1690bc62017-03-29 17:16:0277 if (!dict->GetString(kUrlKey, &endpoint_url_string))
Douglas Creagerf0db63a2018-02-28 17:50:2378 return HeaderEndpointOutcome::DISCARDED_URL_NOT_STRING;
juliatuttle1690bc62017-03-29 17:16:0279
80 GURL endpoint_url(endpoint_url_string);
81 if (!endpoint_url.is_valid())
Douglas Creagerf0db63a2018-02-28 17:50:2382 return HeaderEndpointOutcome::DISCARDED_URL_INVALID;
juliatuttle1690bc62017-03-29 17:16:0283 if (!endpoint_url.SchemeIsCryptographic())
Douglas Creagerf0db63a2018-02-28 17:50:2384 return HeaderEndpointOutcome::DISCARDED_URL_INSECURE;
Lily Chenefb6fcf2019-04-19 04:17:5485 endpoint_info_out->url = std::move(endpoint_url);
juliatuttle1690bc62017-03-29 17:16:0286
Lily Chenfc92ff42019-05-06 22:59:1087 int priority = ReportingEndpoint::EndpointInfo::kDefaultPriority;
Julia Tuttled56350d2017-12-07 19:11:1788 if (dict->HasKey(kPriorityKey) && !dict->GetInteger(kPriorityKey, &priority))
89 return HeaderEndpointOutcome::DISCARDED_PRIORITY_NOT_INTEGER;
Lily Chenefb6fcf2019-04-19 04:17:5490 if (priority < 0)
91 return HeaderEndpointOutcome::DISCARDED_PRIORITY_NEGATIVE;
92 endpoint_info_out->priority = priority;
Julia Tuttled56350d2017-12-07 19:11:1793
Lily Chenfc92ff42019-05-06 22:59:1094 int weight = ReportingEndpoint::EndpointInfo::kDefaultWeight;
Julia Tuttled56350d2017-12-07 19:11:1795 if (dict->HasKey(kWeightKey) && !dict->GetInteger(kWeightKey, &weight))
96 return HeaderEndpointOutcome::DISCARDED_WEIGHT_NOT_INTEGER;
Lily Chenefb6fcf2019-04-19 04:17:5497 if (weight < 0)
98 return HeaderEndpointOutcome::DISCARDED_WEIGHT_NEGATIVE;
99 endpoint_info_out->weight = weight;
juliatuttle667c0bb2017-07-06 15:17:13100
juliatuttle667c0bb2017-07-06 15:17:13101 if (!delegate->CanSetClient(origin, endpoint_url))
102 return HeaderEndpointOutcome::SET_REJECTED_BY_DELEGATE;
103
juliatuttle667c0bb2017-07-06 15:17:13104 return HeaderEndpointOutcome::SET;
juliatuttle587548912017-05-23 14:17:21105}
106
Douglas Creagerf0db63a2018-02-28 17:50:23107// Processes a single endpoint group tuple received in a Report-To header.
108//
109// |origin| is the origin that sent the Report-To header.
110//
111// |value| is the parsed JSON value of the endpoint group tuple.
Lily Chenefb6fcf2019-04-19 04:17:54112HeaderEndpointGroupOutcome ProcessEndpointGroup(
113 ReportingDelegate* delegate,
114 ReportingCache* cache,
115 const url::Origin& origin,
116 const base::Value& value,
117 ReportingEndpointGroup* parsed_endpoint_group_out) {
Douglas Creagerf0db63a2018-02-28 17:50:23118 const base::DictionaryValue* dict = nullptr;
119 if (!value.GetAsDictionary(&dict))
120 return HeaderEndpointGroupOutcome::DISCARDED_NOT_DICTIONARY;
121 DCHECK(dict);
122
Lily Chenefb6fcf2019-04-19 04:17:54123 std::string group_name = kDefaultGroupName;
124 if (dict->HasKey(kGroupKey) && !dict->GetString(kGroupKey, &group_name))
Douglas Creagerf0db63a2018-02-28 17:50:23125 return HeaderEndpointGroupOutcome::DISCARDED_GROUP_NOT_STRING;
Lily Chenefb6fcf2019-04-19 04:17:54126 parsed_endpoint_group_out->name = std::move(group_name);
Douglas Creagerf0db63a2018-02-28 17:50:23127
128 int ttl_sec = -1;
129 if (!dict->HasKey(kMaxAgeKey))
130 return HeaderEndpointGroupOutcome::DISCARDED_TTL_MISSING;
131 if (!dict->GetInteger(kMaxAgeKey, &ttl_sec))
132 return HeaderEndpointGroupOutcome::DISCARDED_TTL_NOT_INTEGER;
133 if (ttl_sec < 0)
134 return HeaderEndpointGroupOutcome::DISCARDED_TTL_NEGATIVE;
Lily Chenefb6fcf2019-04-19 04:17:54135 // max_age: 0 signifies removal of the endpoint group.
136 if (ttl_sec == 0) {
137 cache->RemoveEndpointGroup(origin, group_name);
138 return HeaderEndpointGroupOutcome::REMOVED_TTL_ZERO;
139 }
140 parsed_endpoint_group_out->ttl = base::TimeDelta::FromSeconds(ttl_sec);
Douglas Creagerf0db63a2018-02-28 17:50:23141
Douglas Creagerf0db63a2018-02-28 17:50:23142 bool subdomains_bool = false;
143 if (dict->HasKey(kIncludeSubdomainsKey) &&
144 dict->GetBoolean(kIncludeSubdomainsKey, &subdomains_bool) &&
145 subdomains_bool == true) {
Lily Chenefb6fcf2019-04-19 04:17:54146 parsed_endpoint_group_out->include_subdomains = OriginSubdomains::INCLUDE;
Douglas Creagerf0db63a2018-02-28 17:50:23147 }
148
149 const base::ListValue* endpoint_list = nullptr;
150 if (!dict->HasKey(kEndpointsKey))
151 return HeaderEndpointGroupOutcome::DISCARDED_ENDPOINTS_MISSING;
152 if (!dict->GetList(kEndpointsKey, &endpoint_list))
153 return HeaderEndpointGroupOutcome::DISCARDED_ENDPOINTS_NOT_LIST;
154
Lily Chenfc92ff42019-05-06 22:59:10155 std::vector<ReportingEndpoint::EndpointInfo> endpoints;
Lily Chenefb6fcf2019-04-19 04:17:54156
Douglas Creagerf0db63a2018-02-28 17:50:23157 for (size_t i = 0; i < endpoint_list->GetSize(); i++) {
158 const base::Value* endpoint = nullptr;
159 bool got_endpoint = endpoint_list->Get(i, &endpoint);
160 DCHECK(got_endpoint);
Lily Chenefb6fcf2019-04-19 04:17:54161
Lily Chenfc92ff42019-05-06 22:59:10162 ReportingEndpoint::EndpointInfo parsed_endpoint;
Douglas Creagerf0db63a2018-02-28 17:50:23163
164 HeaderEndpointOutcome outcome =
Lily Chenefb6fcf2019-04-19 04:17:54165 ProcessEndpoint(delegate, origin, *endpoint, &parsed_endpoint);
166
167 if (outcome == HeaderEndpointOutcome::SET)
168 endpoints.push_back(std::move(parsed_endpoint));
169
Douglas Creagerf0db63a2018-02-28 17:50:23170 RecordHeaderEndpointOutcome(outcome);
171 }
172
Lily Chenefb6fcf2019-04-19 04:17:54173 // Remove the group if it is empty.
174 if (endpoints.empty()) {
175 cache->RemoveEndpointGroup(origin, group_name);
176 return HeaderEndpointGroupOutcome::REMOVED_EMPTY;
177 }
178
179 parsed_endpoint_group_out->endpoints = std::move(endpoints);
180
Douglas Creagerf0db63a2018-02-28 17:50:23181 return HeaderEndpointGroupOutcome::PARSED;
182}
183
juliatuttle587548912017-05-23 14:17:21184} // namespace
185
186// static
Douglas Creager134b52e2018-11-09 18:00:14187const char ReportingHeaderParser::kHeaderOutcomeHistogram[] =
188 "Net.Reporting.HeaderOutcome";
189
190// static
191const char ReportingHeaderParser::kHeaderEndpointGroupOutcomeHistogram[] =
192 "Net.Reporting.HeaderEndpointGroupOutcome";
193
194// static
195const char ReportingHeaderParser::kHeaderEndpointOutcomeHistogram[] =
196 "Net.Reporting.HeaderEndpointOutcome";
197
198// static
juliatuttle667c0bb2017-07-06 15:17:13199void ReportingHeaderParser::RecordHeaderDiscardedForNoReportingService() {
200 RecordHeaderOutcome(HeaderOutcome::DISCARDED_NO_REPORTING_SERVICE);
201}
202
203// static
204void ReportingHeaderParser::RecordHeaderDiscardedForInvalidSSLInfo() {
205 RecordHeaderOutcome(HeaderOutcome::DISCARDED_INVALID_SSL_INFO);
206}
207
208// static
209void ReportingHeaderParser::RecordHeaderDiscardedForCertStatusError() {
210 RecordHeaderOutcome(HeaderOutcome::DISCARDED_CERT_STATUS_ERROR);
211}
212
213// static
Julia Tuttleef19cb52018-03-16 16:58:35214void ReportingHeaderParser::RecordHeaderDiscardedForJsonInvalid() {
215 RecordHeaderOutcome(HeaderOutcome::DISCARDED_JSON_INVALID);
216}
217
218// static
219void ReportingHeaderParser::RecordHeaderDiscardedForJsonTooBig() {
220 RecordHeaderOutcome(HeaderOutcome::DISCARDED_JSON_TOO_BIG);
Julia Tuttleec467a5f2018-02-22 20:22:45221}
222
223// static
juliatuttle587548912017-05-23 14:17:21224void ReportingHeaderParser::ParseHeader(ReportingContext* context,
225 const GURL& url,
Julia Tuttleec467a5f2018-02-22 20:22:45226 std::unique_ptr<base::Value> value) {
juliatuttle587548912017-05-23 14:17:21227 DCHECK(url.SchemeIsCryptographic());
228
Douglas Creagerf0db63a2018-02-28 17:50:23229 const base::ListValue* group_list = nullptr;
230 bool is_list = value->GetAsList(&group_list);
juliatuttle587548912017-05-23 14:17:21231 DCHECK(is_list);
232
233 ReportingDelegate* delegate = context->delegate();
234 ReportingCache* cache = context->cache();
Julia Tuttle443a0a682017-12-04 16:16:26235
236 url::Origin origin = url::Origin::Create(url);
237
Lily Chenefb6fcf2019-04-19 04:17:54238 std::vector<ReportingEndpointGroup> parsed_header;
Julia Tuttle443a0a682017-12-04 16:16:26239
Douglas Creagerf0db63a2018-02-28 17:50:23240 for (size_t i = 0; i < group_list->GetSize(); i++) {
Lily Chenefb6fcf2019-04-19 04:17:54241 const base::Value* group_value = nullptr;
242 bool got_group = group_list->Get(i, &group_value);
Douglas Creagerf0db63a2018-02-28 17:50:23243 DCHECK(got_group);
Lily Chenefb6fcf2019-04-19 04:17:54244 ReportingEndpointGroup parsed_endpoint_group;
Douglas Creagerf0db63a2018-02-28 17:50:23245 HeaderEndpointGroupOutcome outcome = ProcessEndpointGroup(
Lily Chenefb6fcf2019-04-19 04:17:54246 delegate, cache, origin, *group_value, &parsed_endpoint_group);
Douglas Creagerf0db63a2018-02-28 17:50:23247 RecordHeaderEndpointGroupOutcome(outcome);
Lily Chenefb6fcf2019-04-19 04:17:54248 if (outcome == HeaderEndpointGroupOutcome::PARSED)
249 parsed_header.push_back(std::move(parsed_endpoint_group));
Julia Tuttle443a0a682017-12-04 16:16:26250 }
251
Lily Chenefb6fcf2019-04-19 04:17:54252 // Remove the client if it has no valid endpoint groups.
253 if (parsed_header.empty()) {
254 cache->RemoveClient(origin);
255 RecordHeaderOutcome(HeaderOutcome::REMOVED_EMPTY);
256 return;
juliatuttle1690bc62017-03-29 17:16:02257 }
Julia Tuttleefe2fae42018-03-30 15:22:31258
Lily Chenefb6fcf2019-04-19 04:17:54259 cache->OnParsedHeader(origin, std::move(parsed_header));
Julia Tuttleefe2fae42018-03-30 15:22:31260 RecordHeaderOutcome(HeaderOutcome::PARSED);
juliatuttle1690bc62017-03-29 17:16:02261}
262
263} // namespace net