1 // Copyright 2017 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/reporting/reporting_service.h"
6
7 #include <optional>
8 #include <utility>
9
10 #include "base/feature_list.h"
11 #include "base/functional/bind.h"
12 #include "base/json/json_reader.h"
13 #include "base/logging.h"
14 #include "base/memory/raw_ptr.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/time/tick_clock.h"
17 #include "base/time/time.h"
18 #include "base/values.h"
19 #include "net/base/features.h"
20 #include "net/base/isolation_info.h"
21 #include "net/http/structured_headers.h"
22 #include "net/reporting/reporting_browsing_data_remover.h"
23 #include "net/reporting/reporting_cache.h"
24 #include "net/reporting/reporting_context.h"
25 #include "net/reporting/reporting_delegate.h"
26 #include "net/reporting/reporting_delivery_agent.h"
27 #include "net/reporting/reporting_header_parser.h"
28 #include "net/reporting/reporting_uploader.h"
29 #include "url/gurl.h"
30 #include "url/origin.h"
31
32 namespace net {
33
34 namespace {
35
36 constexpr int kMaxJsonSize = 16 * 1024;
37 constexpr int kMaxJsonDepth = 5;
38
39 // If constructed with a PersistentReportingStore, the first call to any of
40 // QueueReport(), ProcessHeader(), RemoveBrowsingData(), or
41 // RemoveAllBrowsingData() on a valid input will trigger a load from the store.
42 // Tasks are queued pending completion of loading from the store.
43 class ReportingServiceImpl : public ReportingService {
44 public:
ReportingServiceImpl(std::unique_ptr<ReportingContext> context)45 explicit ReportingServiceImpl(std::unique_ptr<ReportingContext> context)
46 : context_(std::move(context)) {
47 if (!context_->IsClientDataPersisted())
48 initialized_ = true;
49 }
50
51 ReportingServiceImpl(const ReportingServiceImpl&) = delete;
52 ReportingServiceImpl& operator=(const ReportingServiceImpl&) = delete;
53
54 // ReportingService implementation:
55
~ReportingServiceImpl()56 ~ReportingServiceImpl() override {
57 if (initialized_)
58 context_->cache()->Flush();
59 }
60
SetDocumentReportingEndpoints(const base::UnguessableToken & reporting_source,const url::Origin & origin,const IsolationInfo & isolation_info,const base::flat_map<std::string,std::string> & endpoints)61 void SetDocumentReportingEndpoints(
62 const base::UnguessableToken& reporting_source,
63 const url::Origin& origin,
64 const IsolationInfo& isolation_info,
65 const base::flat_map<std::string, std::string>& endpoints) override {
66 DCHECK(!reporting_source.is_empty());
67 DoOrBacklogTask(
68 base::BindOnce(&ReportingServiceImpl::DoSetDocumentReportingEndpoints,
69 base::Unretained(this), reporting_source, isolation_info,
70 FixupNetworkAnonymizationKey(
71 isolation_info.network_anonymization_key()),
72 origin, std::move(endpoints)));
73 }
74
SendReportsAndRemoveSource(const base::UnguessableToken & reporting_source)75 void SendReportsAndRemoveSource(
76 const base::UnguessableToken& reporting_source) override {
77 DCHECK(!reporting_source.is_empty());
78 context_->delivery_agent()->SendReportsForSource(reporting_source);
79 context_->cache()->SetExpiredSource(reporting_source);
80 }
81
QueueReport(const GURL & url,const std::optional<base::UnguessableToken> & reporting_source,const NetworkAnonymizationKey & network_anonymization_key,const std::string & user_agent,const std::string & group,const std::string & type,base::Value::Dict body,int depth)82 void QueueReport(
83 const GURL& url,
84 const std::optional<base::UnguessableToken>& reporting_source,
85 const NetworkAnonymizationKey& network_anonymization_key,
86 const std::string& user_agent,
87 const std::string& group,
88 const std::string& type,
89 base::Value::Dict body,
90 int depth) override {
91 DCHECK(context_);
92 DCHECK(context_->delegate());
93 // If |reporting_source| is provided, it must not be empty.
94 DCHECK(!(reporting_source.has_value() && reporting_source->is_empty()));
95
96 if (!context_->delegate()->CanQueueReport(url::Origin::Create(url)))
97 return;
98
99 // Strip username, password, and ref fragment from the URL.
100 GURL sanitized_url = url.GetAsReferrer();
101 if (!sanitized_url.is_valid())
102 return;
103
104 base::TimeTicks queued_ticks = context_->tick_clock().NowTicks();
105
106 // base::Unretained is safe because the callback is stored in
107 // |task_backlog_| which will not outlive |this|.
108 DoOrBacklogTask(
109 base::BindOnce(&ReportingServiceImpl::DoQueueReport,
110 base::Unretained(this), reporting_source,
111 FixupNetworkAnonymizationKey(network_anonymization_key),
112 std::move(sanitized_url), user_agent, group, type,
113 std::move(body), depth, queued_ticks));
114 }
115
ProcessReportToHeader(const url::Origin & origin,const NetworkAnonymizationKey & network_anonymization_key,const std::string & header_string)116 void ProcessReportToHeader(
117 const url::Origin& origin,
118 const NetworkAnonymizationKey& network_anonymization_key,
119 const std::string& header_string) override {
120 if (header_string.size() > kMaxJsonSize)
121 return;
122
123 std::optional<base::Value> header_value = base::JSONReader::Read(
124 "[" + header_string + "]", base::JSON_PARSE_RFC, kMaxJsonDepth);
125 if (!header_value)
126 return;
127
128 DVLOG(1) << "Received Reporting policy for " << origin;
129 DoOrBacklogTask(base::BindOnce(
130 &ReportingServiceImpl::DoProcessReportToHeader, base::Unretained(this),
131 FixupNetworkAnonymizationKey(network_anonymization_key), origin,
132 std::move(header_value).value()));
133 }
134
RemoveBrowsingData(uint64_t data_type_mask,const base::RepeatingCallback<bool (const url::Origin &)> & origin_filter)135 void RemoveBrowsingData(
136 uint64_t data_type_mask,
137 const base::RepeatingCallback<bool(const url::Origin&)>& origin_filter)
138 override {
139 DoOrBacklogTask(base::BindOnce(&ReportingServiceImpl::DoRemoveBrowsingData,
140 base::Unretained(this), data_type_mask,
141 origin_filter));
142 }
143
RemoveAllBrowsingData(uint64_t data_type_mask)144 void RemoveAllBrowsingData(uint64_t data_type_mask) override {
145 DoOrBacklogTask(
146 base::BindOnce(&ReportingServiceImpl::DoRemoveAllBrowsingData,
147 base::Unretained(this), data_type_mask));
148 }
149
OnShutdown()150 void OnShutdown() override {
151 shut_down_ = true;
152 context_->OnShutdown();
153 }
154
GetPolicy() const155 const ReportingPolicy& GetPolicy() const override {
156 return context_->policy();
157 }
158
StatusAsValue() const159 base::Value StatusAsValue() const override {
160 base::Value::Dict dict;
161 dict.Set("reportingEnabled", true);
162 dict.Set("clients", context_->cache()->GetClientsAsValue());
163 dict.Set("reports", context_->cache()->GetReportsAsValue());
164 return base::Value(std::move(dict));
165 }
166
GetReports() const167 std::vector<raw_ptr<const ReportingReport, VectorExperimental>> GetReports()
168 const override {
169 std::vector<raw_ptr<const net::ReportingReport, VectorExperimental>>
170 reports;
171 context_->cache()->GetReports(&reports);
172 return reports;
173 }
174
175 base::flat_map<url::Origin, std::vector<ReportingEndpoint>>
GetV1ReportingEndpointsByOrigin() const176 GetV1ReportingEndpointsByOrigin() const override {
177 return context_->cache()->GetV1ReportingEndpointsByOrigin();
178 }
179
AddReportingCacheObserver(ReportingCacheObserver * observer)180 void AddReportingCacheObserver(ReportingCacheObserver* observer) override {
181 context_->AddCacheObserver(observer);
182 }
183
RemoveReportingCacheObserver(ReportingCacheObserver * observer)184 void RemoveReportingCacheObserver(ReportingCacheObserver* observer) override {
185 context_->RemoveCacheObserver(observer);
186 }
187
GetContextForTesting() const188 ReportingContext* GetContextForTesting() const override {
189 return context_.get();
190 }
191
192 private:
DoOrBacklogTask(base::OnceClosure task)193 void DoOrBacklogTask(base::OnceClosure task) {
194 if (shut_down_)
195 return;
196
197 FetchAllClientsFromStoreIfNecessary();
198
199 if (!initialized_) {
200 task_backlog_.push_back(std::move(task));
201 return;
202 }
203
204 std::move(task).Run();
205 }
206
DoQueueReport(const std::optional<base::UnguessableToken> & reporting_source,const NetworkAnonymizationKey & network_anonymization_key,GURL sanitized_url,const std::string & user_agent,const std::string & group,const std::string & type,base::Value::Dict body,int depth,base::TimeTicks queued_ticks)207 void DoQueueReport(
208 const std::optional<base::UnguessableToken>& reporting_source,
209 const NetworkAnonymizationKey& network_anonymization_key,
210 GURL sanitized_url,
211 const std::string& user_agent,
212 const std::string& group,
213 const std::string& type,
214 base::Value::Dict body,
215 int depth,
216 base::TimeTicks queued_ticks) {
217 DCHECK(initialized_);
218 context_->cache()->AddReport(
219 reporting_source, network_anonymization_key, sanitized_url, user_agent,
220 group, type, std::move(body), depth, queued_ticks, 0 /* attempts */);
221 }
222
DoProcessReportToHeader(const NetworkAnonymizationKey & network_anonymization_key,const url::Origin & origin,const base::Value & header_value)223 void DoProcessReportToHeader(
224 const NetworkAnonymizationKey& network_anonymization_key,
225 const url::Origin& origin,
226 const base::Value& header_value) {
227 DCHECK(initialized_);
228 DCHECK(header_value.is_list());
229 ReportingHeaderParser::ParseReportToHeader(context_.get(),
230 network_anonymization_key,
231 origin, header_value.GetList());
232 }
233
DoSetDocumentReportingEndpoints(const base::UnguessableToken & reporting_source,const IsolationInfo & isolation_info,const NetworkAnonymizationKey & network_anonymization_key,const url::Origin & origin,base::flat_map<std::string,std::string> header_value)234 void DoSetDocumentReportingEndpoints(
235 const base::UnguessableToken& reporting_source,
236 const IsolationInfo& isolation_info,
237 const NetworkAnonymizationKey& network_anonymization_key,
238 const url::Origin& origin,
239 base::flat_map<std::string, std::string> header_value) {
240 DCHECK(initialized_);
241 ReportingHeaderParser::ProcessParsedReportingEndpointsHeader(
242 context_.get(), reporting_source, isolation_info,
243 network_anonymization_key, origin, std::move(header_value));
244 }
245
DoRemoveBrowsingData(uint64_t data_type_mask,const base::RepeatingCallback<bool (const url::Origin &)> & origin_filter)246 void DoRemoveBrowsingData(
247 uint64_t data_type_mask,
248 const base::RepeatingCallback<bool(const url::Origin&)>& origin_filter) {
249 DCHECK(initialized_);
250 ReportingBrowsingDataRemover::RemoveBrowsingData(
251 context_->cache(), data_type_mask, origin_filter);
252 }
253
DoRemoveAllBrowsingData(uint64_t data_type_mask)254 void DoRemoveAllBrowsingData(uint64_t data_type_mask) {
255 DCHECK(initialized_);
256 ReportingBrowsingDataRemover::RemoveAllBrowsingData(context_->cache(),
257 data_type_mask);
258 }
259
ExecuteBacklog()260 void ExecuteBacklog() {
261 DCHECK(initialized_);
262 DCHECK(context_);
263
264 if (shut_down_)
265 return;
266
267 for (base::OnceClosure& task : task_backlog_) {
268 std::move(task).Run();
269 }
270 task_backlog_.clear();
271 }
272
FetchAllClientsFromStoreIfNecessary()273 void FetchAllClientsFromStoreIfNecessary() {
274 if (!context_->IsClientDataPersisted() || started_loading_from_store_)
275 return;
276
277 started_loading_from_store_ = true;
278 FetchAllClientsFromStore();
279 }
280
FetchAllClientsFromStore()281 void FetchAllClientsFromStore() {
282 DCHECK(context_->IsClientDataPersisted());
283 DCHECK(!initialized_);
284
285 context_->store()->LoadReportingClients(base::BindOnce(
286 &ReportingServiceImpl::OnClientsLoaded, weak_factory_.GetWeakPtr()));
287 }
288
OnClientsLoaded(std::vector<ReportingEndpoint> loaded_endpoints,std::vector<CachedReportingEndpointGroup> loaded_endpoint_groups)289 void OnClientsLoaded(
290 std::vector<ReportingEndpoint> loaded_endpoints,
291 std::vector<CachedReportingEndpointGroup> loaded_endpoint_groups) {
292 initialized_ = true;
293 context_->cache()->AddClientsLoadedFromStore(
294 std::move(loaded_endpoints), std::move(loaded_endpoint_groups));
295 ExecuteBacklog();
296 }
297
298 // Returns either |network_anonymization_key| or an empty
299 // NetworkAnonymizationKey, based on |respect_network_anonymization_key_|.
300 // Should be used on all NetworkAnonymizationKeys passed in through public API
301 // calls.
FixupNetworkAnonymizationKey(const NetworkAnonymizationKey & network_anonymization_key)302 const NetworkAnonymizationKey& FixupNetworkAnonymizationKey(
303 const NetworkAnonymizationKey& network_anonymization_key) {
304 if (respect_network_anonymization_key_)
305 return network_anonymization_key;
306 return empty_nak_;
307 }
308
309 std::unique_ptr<ReportingContext> context_;
310 bool shut_down_ = false;
311 bool started_loading_from_store_ = false;
312 bool initialized_ = false;
313 std::vector<base::OnceClosure> task_backlog_;
314
315 bool respect_network_anonymization_key_ =
316 NetworkAnonymizationKey::IsPartitioningEnabled();
317
318 // Allows returning a NetworkAnonymizationKey by reference when
319 // |respect_network_anonymization_key_| is false.
320 NetworkAnonymizationKey empty_nak_;
321
322 base::WeakPtrFactory<ReportingServiceImpl> weak_factory_{this};
323 };
324
325 } // namespace
326
327 ReportingService::~ReportingService() = default;
328
329 // static
Create(const ReportingPolicy & policy,URLRequestContext * request_context,ReportingCache::PersistentReportingStore * store)330 std::unique_ptr<ReportingService> ReportingService::Create(
331 const ReportingPolicy& policy,
332 URLRequestContext* request_context,
333 ReportingCache::PersistentReportingStore* store) {
334 return std::make_unique<ReportingServiceImpl>(
335 ReportingContext::Create(policy, request_context, store));
336 }
337
338 // static
CreateForTesting(std::unique_ptr<ReportingContext> reporting_context)339 std::unique_ptr<ReportingService> ReportingService::CreateForTesting(
340 std::unique_ptr<ReportingContext> reporting_context) {
341 return std::make_unique<ReportingServiceImpl>(std::move(reporting_context));
342 }
343
StatusAsValue() const344 base::Value ReportingService::StatusAsValue() const {
345 NOTIMPLEMENTED();
346 return base::Value();
347 }
348
349 } // namespace net
350