reporting_uploader.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. // Copyright 2016 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. #include "net/reporting/reporting_uploader.h"
  5. #include <string>
  6. #include <utility>
  7. #include <vector>
  8. #include "base/callback_helpers.h"
  9. #include "base/memory/raw_ptr.h"
  10. #include "base/strings/string_split.h"
  11. #include "base/strings/string_util.h"
  12. #include "net/base/elements_upload_data_stream.h"
  13. #include "net/base/isolation_info.h"
  14. #include "net/base/load_flags.h"
  15. #include "net/base/network_isolation_key.h"
  16. #include "net/base/upload_bytes_element_reader.h"
  17. #include "net/http/http_response_headers.h"
  18. #include "net/traffic_annotation/network_traffic_annotation.h"
  19. #include "net/url_request/redirect_info.h"
  20. #include "net/url_request/url_request_context.h"
  21. #include "url/gurl.h"
  22. #include "url/origin.h"
  23. namespace net {
  24. namespace {
  25. constexpr char kUploadContentType[] = "application/reports+json";
  26. constexpr net::NetworkTrafficAnnotationTag kReportUploadTrafficAnnotation =
  27. net::DefineNetworkTrafficAnnotation("reporting", R"(
  28. semantics {
  29. sender: "Reporting API"
  30. description:
  31. "The Reporting API reports various issues back to website owners "
  32. "to help them detect and fix problems."
  33. trigger:
  34. "Encountering issues. Examples of these issues are Content "
  35. "Security Policy violations and Interventions/Deprecations "
  36. "encountered. See draft of reporting spec here: "
  37. "https://wicg.github.io/reporting."
  38. data: "Details of the issue, depending on issue type."
  39. destination: OTHER
  40. }
  41. policy {
  42. cookies_allowed: NO
  43. setting: "This feature cannot be disabled by settings."
  44. policy_exception_justification: "Not implemented."
  45. })");
  46. // Returns true if |request| contains any of the |allowed_values| in a response
  47. // header field named |header|. |allowed_values| are expected to be lower-case
  48. // and the check is case-insensitive.
  49. bool HasHeaderValues(URLRequest* request,
  50. const std::string& header,
  51. const std::set<std::string>& allowed_values) {
  52. std::string response_headers;
  53. request->GetResponseHeaderByName(header, &response_headers);
  54. const std::vector<std::string> response_values =
  55. base::SplitString(base::ToLowerASCII(response_headers), ",",
  56. base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  57. for (const auto& value : response_values) {
  58. if (allowed_values.find(value) != allowed_values.end())
  59. return true;
  60. }
  61. return false;
  62. }
  63. ReportingUploader::Outcome ResponseCodeToOutcome(int response_code) {
  64. if (response_code >= 200 && response_code <= 299)
  65. return ReportingUploader::Outcome::SUCCESS;
  66. if (response_code == 410)
  67. return ReportingUploader::Outcome::REMOVE_ENDPOINT;
  68. return ReportingUploader::Outcome::FAILURE;
  69. }
  70. struct PendingUpload {
  71. enum State { CREATED, SENDING_PREFLIGHT, SENDING_PAYLOAD };
  72. PendingUpload(const url::Origin& report_origin,
  73. const GURL& url,
  74. const IsolationInfo& isolation_info,
  75. const std::string& json,
  76. int max_depth,
  77. ReportingUploader::UploadCallback callback)
  78. : report_origin(report_origin),
  79. url(url),
  80. isolation_info(isolation_info),
  81. payload_reader(UploadOwnedBytesElementReader::CreateWithString(json)),
  82. max_depth(max_depth),
  83. callback(std::move(callback)) {}
  84. void RunCallback(ReportingUploader::Outcome outcome) {
  85. std::move(callback).Run(outcome);
  86. }
  87. State state = CREATED;
  88. const url::Origin report_origin;
  89. const GURL url;
  90. const IsolationInfo isolation_info;
  91. std::unique_ptr<UploadElementReader> payload_reader;
  92. int max_depth;
  93. ReportingUploader::UploadCallback callback;
  94. std::unique_ptr<URLRequest> request;
  95. };
  96. class ReportingUploaderImpl : public ReportingUploader, URLRequest::Delegate {
  97. public:
  98. explicit ReportingUploaderImpl(const URLRequestContext* context)
  99. : context_(context) {
  100. DCHECK(context_);
  101. }
  102. ~ReportingUploaderImpl() override {
  103. for (auto& request_and_upload : uploads_) {
  104. auto& upload = request_and_upload.second;
  105. upload->RunCallback(Outcome::FAILURE);
  106. }
  107. }
  108. void StartUpload(const url::Origin& report_origin,
  109. const GURL& url,
  110. const IsolationInfo& isolation_info,
  111. const std::string& json,
  112. int max_depth,
  113. bool eligible_for_credentials,
  114. UploadCallback callback) override {
  115. auto upload =
  116. std::make_unique<PendingUpload>(report_origin, url, isolation_info,
  117. json, max_depth, std::move(callback));
  118. auto collector_origin = url::Origin::Create(url);
  119. if (collector_origin == report_origin) {
  120. // Skip the preflight check if the reports are being sent to the same
  121. // origin as the requests they describe.
  122. StartPayloadRequest(std::move(upload), eligible_for_credentials);
  123. } else {
  124. StartPreflightRequest(std::move(upload));
  125. }
  126. }
  127. void OnShutdown() override {
  128. // Cancels all pending uploads.
  129. uploads_.clear();
  130. }
  131. void StartPreflightRequest(std::unique_ptr<PendingUpload> upload) {
  132. DCHECK(upload->state == PendingUpload::CREATED);
  133. upload->state = PendingUpload::SENDING_PREFLIGHT;
  134. upload->request = context_->CreateRequest(upload->url, IDLE, this,
  135. kReportUploadTrafficAnnotation);
  136. upload->request->set_method("OPTIONS");
  137. upload->request->SetLoadFlags(LOAD_DISABLE_CACHE);
  138. upload->request->set_allow_credentials(false);
  139. upload->request->set_isolation_info(upload->isolation_info);
  140. upload->request->SetExtraRequestHeaderByName(
  141. HttpRequestHeaders::kOrigin, upload->report_origin.Serialize(), true);
  142. upload->request->SetExtraRequestHeaderByName(
  143. "Access-Control-Request-Method", "POST", true);
  144. upload->request->SetExtraRequestHeaderByName(
  145. "Access-Control-Request-Headers", "content-type", true);
  146. // Set the max_depth for this request, to cap how deep a stack of "reports
  147. // about reports" can get. (Without this, a Reporting policy that uploads
  148. // reports to the same origin can cause an infinite stack of reports about
  149. // reports.)
  150. upload->request->set_reporting_upload_depth(upload->max_depth + 1);
  151. URLRequest* raw_request = upload->request.get();
  152. uploads_[raw_request] = std::move(upload);
  153. raw_request->Start();
  154. }
  155. void StartPayloadRequest(std::unique_ptr<PendingUpload> upload,
  156. bool eligible_for_credentials) {
  157. DCHECK(upload->state == PendingUpload::CREATED ||
  158. upload->state == PendingUpload::SENDING_PREFLIGHT);
  159. upload->state = PendingUpload::SENDING_PAYLOAD;
  160. upload->request = context_->CreateRequest(upload->url, IDLE, this,
  161. kReportUploadTrafficAnnotation);
  162. upload->request->set_method("POST");
  163. upload->request->SetLoadFlags(LOAD_DISABLE_CACHE);
  164. // Credentials are sent for V1 reports, if the endpoint is same-origin with
  165. // the site generating the report (this will be set to false either by the
  166. // delivery agent determining that this is a V0 report, or by `StartUpload`
  167. // determining that this is a cross-origin case, and taking the CORS
  168. // preflight path).
  169. upload->request->set_allow_credentials(eligible_for_credentials);
  170. // The site for cookies is taken from the reporting source's IsolationInfo,
  171. // in the case of V1 reporting endpoints, and will be null for V0 reports.
  172. upload->request->set_site_for_cookies(
  173. upload->isolation_info.site_for_cookies());
  174. // Prior to using `isolation_info` directly here we built the
  175. // `upload->network_isolation_key` to create the set the `isolation_info`.
  176. // As experiments roll out to determine whether network partitions should be
  177. // double or triple keyed the isolation_info might have a null value for
  178. // `frame_origin`. Thus we should again get it from `network_isolation_key`
  179. // until we can trust `isolation_info::frame_origin`.
  180. upload->request->set_initiator(upload->report_origin);
  181. upload->request->set_isolation_info(upload->isolation_info);
  182. upload->request->SetExtraRequestHeaderByName(
  183. HttpRequestHeaders::kContentType, kUploadContentType, true);
  184. upload->request->set_upload(ElementsUploadDataStream::CreateWithReader(
  185. std::move(upload->payload_reader), 0));
  186. // Set the max_depth for this request, to cap how deep a stack of "reports
  187. // about reports" can get. (Without this, a Reporting policy that uploads
  188. // reports to the same origin can cause an infinite stack of reports about
  189. // reports.)
  190. upload->request->set_reporting_upload_depth(upload->max_depth + 1);
  191. URLRequest* raw_request = upload->request.get();
  192. uploads_[raw_request] = std::move(upload);
  193. raw_request->Start();
  194. }
  195. // URLRequest::Delegate implementation:
  196. void OnReceivedRedirect(URLRequest* request,
  197. const RedirectInfo& redirect_info,
  198. bool* defer_redirect) override {
  199. if (!redirect_info.new_url.SchemeIsCryptographic()) {
  200. request->Cancel();
  201. return;
  202. }
  203. }
  204. void OnAuthRequired(URLRequest* request,
  205. const AuthChallengeInfo& auth_info) override {
  206. request->Cancel();
  207. }
  208. void OnCertificateRequested(URLRequest* request,
  209. SSLCertRequestInfo* cert_request_info) override {
  210. request->Cancel();
  211. }
  212. void OnSSLCertificateError(URLRequest* request,
  213. int net_error,
  214. const SSLInfo& ssl_info,
  215. bool fatal) override {
  216. request->Cancel();
  217. }
  218. void OnResponseStarted(URLRequest* request, int net_error) override {
  219. // Grab Upload from map, and hold on to it in a local unique_ptr so it's
  220. // removed at the end of the method.
  221. auto it = uploads_.find(request);
  222. DCHECK(it != uploads_.end());
  223. std::unique_ptr<PendingUpload> upload = std::move(it->second);
  224. uploads_.erase(it);
  225. if (net_error != OK) {
  226. upload->RunCallback(ReportingUploader::Outcome::FAILURE);
  227. return;
  228. }
  229. // request->GetResponseCode() should work, but doesn't in the cases above
  230. // where the request was canceled, so get the response code by hand.
  231. // TODO(juliatuttle): Check if mmenke fixed this yet.
  232. HttpResponseHeaders* headers = request->response_headers();
  233. int response_code = headers ? headers->response_code() : 0;
  234. switch (upload->state) {
  235. case PendingUpload::SENDING_PREFLIGHT:
  236. HandlePreflightResponse(std::move(upload), response_code);
  237. break;
  238. case PendingUpload::SENDING_PAYLOAD:
  239. HandlePayloadResponse(std::move(upload), response_code);
  240. break;
  241. default:
  242. NOTREACHED();
  243. }
  244. }
  245. void HandlePreflightResponse(std::unique_ptr<PendingUpload> upload,
  246. int response_code) {
  247. // Check that the preflight succeeded: it must have an HTTP OK status code,
  248. // with the following headers:
  249. // - Access-Control-Allow-Origin: * or the report origin
  250. // - Access-Control-Allow-Headers: * or Content-Type
  251. // Note that * is allowed here as the credentials mode is never 'include'.
  252. // Access-Control-Allow-Methods is not checked, as the preflight is always
  253. // for a POST method, which is safelisted.
  254. URLRequest* request = upload->request.get();
  255. bool preflight_succeeded =
  256. (response_code >= 200 && response_code <= 299) &&
  257. HasHeaderValues(
  258. request, "Access-Control-Allow-Origin",
  259. {"*", base::ToLowerASCII(upload->report_origin.Serialize())}) &&
  260. HasHeaderValues(request, "Access-Control-Allow-Headers",
  261. {"*", "content-type"});
  262. if (!preflight_succeeded) {
  263. upload->RunCallback(ReportingUploader::Outcome::FAILURE);
  264. return;
  265. }
  266. // Any upload which required CORS should not receive credentials, as they
  267. // are sent to same-origin endpoints only.
  268. StartPayloadRequest(std::move(upload), /*eligible_for_credentials=*/false);
  269. }
  270. void HandlePayloadResponse(std::unique_ptr<PendingUpload> upload,
  271. int response_code) {
  272. upload->RunCallback(ResponseCodeToOutcome(response_code));
  273. }
  274. void OnReadCompleted(URLRequest* request, int bytes_read) override {
  275. // Reporting doesn't need anything in the body of the response, so it
  276. // doesn't read it, so it should never get OnReadCompleted calls.
  277. NOTREACHED();
  278. }
  279. int GetPendingUploadCountForTesting() const override {
  280. return uploads_.size();
  281. }
  282. private:
  283. raw_ptr<const URLRequestContext> context_;
  284. std::map<const URLRequest*, std::unique_ptr<PendingUpload>> uploads_;
  285. };
  286. } // namespace
  287. ReportingUploader::~ReportingUploader() = default;
  288. // static
  289. std::unique_ptr<ReportingUploader> ReportingUploader::Create(
  290. const URLRequestContext* context) {
  291. return std::make_unique<ReportingUploaderImpl>(context);
  292. }
  293. } // namespace net