910e62b5创建于 1月15日历史提交
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef SERVICES_NETWORK_SCT_AUDITING_SCT_AUDITING_HANDLER_H_
#define SERVICES_NETWORK_SCT_AUDITING_SCT_AUDITING_HANDLER_H_

#include <memory>
#include <optional>

#include "base/containers/lru_cache.h"
#include "base/files/important_file_writer.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/timer/timer.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/backoff_entry.h"
#include "net/base/hash_value.h"
#include "net/cert/signed_certificate_timestamp_and_status.h"
#include "services/network/public/mojom/network_context.mojom-shared.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/sct_auditing/sct_auditing_cache.h"
#include "services/network/sct_auditing/sct_auditing_reporter.h"

namespace sct_auditing {
class SCTClientReport;
}  // namespace sct_auditing

namespace network {

class NetworkContext;

// SCTAuditingHandler owns SCT auditing reports for a specific NetworkContext.
// Each SCTAuditingHandler is owned by its matching NetworkContext. The
// SCTAuditingHandler is also responsible for persisting pending auditing
// reports to disk and loading them back on browser startup.
//
// Note: Persisted reports only repopulate the SCTAuditingHandler's
// `pending_reports_` cache, and *do not* repopulate the SCTAuditingCache's
// deduplication cache.
class COMPONENT_EXPORT(NETWORK_SERVICE) SCTAuditingHandler
    : public base::ImportantFileWriter::DataSerializer {
 public:
  SCTAuditingHandler(NetworkContext* context,
                     const base::FilePath& persistence_path,
                     size_t cache_size = 1024);
  ~SCTAuditingHandler() override;

  SCTAuditingHandler(const SCTAuditingHandler&) = delete;
  SCTAuditingHandler& operator=(const SCTAuditingHandler&) = delete;

  // Creates an SCT auditing report for the given
  // |signed_certificate_timestamps|, storing it in the SCTAuditingCache if
  // eligible. If the report passes the criteria and gets randomly selected for
  // sampling, enqueues the report to be sent to the server.
  void MaybeEnqueueReport(
      const net::HostPortPair& host_port_pair,
      const net::X509Certificate* validated_certificate_chain,
      const net::SignedCertificateTimestampAndStatusList&
          signed_certificate_timestamps);

  // base::ImportantFileWriter::DataSerializer:
  //
  // Serializes `pending_reporters_` into `*output`. Returns true if all
  // reporters were serialized correctly.
  //
  // The serialization format is a JSON list-of-dicts of the form:
  //   [
  //       {
  //          "reporter_key": <serialized HashValue>,
  //          "leaf_hash": <leaf hash as a string>,
  //          "backoff_entry": <serialized BackoffEntry>,
  //          "report": <serialized SCTClientReport>,
  //       }
  //   ]
  //
  // Each entry in the dictionary includes sufficient information to deserialize
  // and recreate the entries in the SCTAuditingHandler's pending reporters set.
  std::optional<std::string> SerializeData() override;

  void DeserializeData(const std::string& serialized);

  void OnStartupFinished();

  // Creates a new SCTAuditingReporter for the report and adds it to this
  // SCTAuditingHandler's pending reporters set. After creating the reporter,
  // this will call SCTAuditingReporter::Start() to initiate sending the report.
  // If the report is a hashdance report, |leaf_hash| should be set to the
  // Merkle tree leaf hash of a randomly selected SCT.
  // Optionally takes in a BackoffEntry and a bool for whether the report has
  // already been counted towards the max-reports limit, for recreating reporter
  // state from persisted storage.
  void AddReporter(
      net::HashValue reporter_key,
      std::unique_ptr<sct_auditing::SCTClientReport> report,
      std::optional<SCTAuditingReporter::SCTHashdanceMetadata> sct_metadata,
      std::unique_ptr<net::BackoffEntry> backoff_entry = nullptr,
      bool already_counted = false);

  // Loads serialized reports from `serialized` and creates a new
  // SCTAuditingReporter for each (if a reporter for that report does not yet
  // exist). This results in the set of loaded reports being merged with any
  // existing pending reports in the SCTAuditingCache.
  // Returns true if all entries were parsed and deserialized correctly.
  // If data does not deserialize correctly, this drops the entries rather than
  // trying to recover. This means that reports currently (and intentionally,
  // for simplicity) do no persist over format/version changes.
  void OnReportsLoadedFromDisk(const std::string& serialized);

  // Clears the set of pending reporters for this SCTAuditingHandler.
  void ClearPendingReports(base::OnceClosure callback);

  base::LRUCache<net::HashValue, std::unique_ptr<SCTAuditingReporter>>*
  GetPendingReportersForTesting() {
    return &pending_reporters_;
  }

  void SetMode(mojom::SCTAuditingMode mode);
  bool is_enabled() {
    return mode_ == mojom::SCTAuditingMode::kEnhancedSafeBrowsingReporting;
  }

  void SetURLLoaderFactoryForTesting(
      mojo::PendingRemote<mojom::URLLoaderFactory> factory) {
    url_loader_factory_.reset();
    url_loader_factory_.Bind(std::move(factory));
  }

  base::ImportantFileWriter* GetFileWriterForTesting() { return writer_.get(); }

  void set_hwm_metrics_period_for_testing(base::TimeDelta hwm_metrics_period) {
    hwm_metrics_period_ = hwm_metrics_period;
  }

  base::WeakPtr<SCTAuditingHandler> GetWeakPtr();

 private:
  void OnReporterStateUpdated();
  void OnReporterFinished(net::HashValue reporter_key);

  void ReportHWMMetrics();

  mojom::URLLoaderFactory* GetURLLoaderFactory();

  // The NetworkContext which owns this SCTAuditingHandler.
  raw_ptr<NetworkContext> owner_network_context_;

  // The pending reporters set is an LRUCache, so that the total number of
  // pending reporters can be capped. The LRUCache means that reporters will be
  // evicted (and canceled) oldest first. If a new report is triggered for the
  // same SCTs it will get deduplicated if a previous report is still pending,
  // but the last-seen time will be updated.
  base::LRUCache<net::HashValue, std::unique_ptr<SCTAuditingReporter>>
      pending_reporters_;
  // Tracks high-water-mark of `pending_reporters_.size()`.
  size_t pending_reporters_size_hwm_ = 0;

  mojom::SCTAuditingMode mode_ = mojom::SCTAuditingMode::kDisabled;
  base::RepeatingTimer histogram_timer_;

  // Helper for safely writing data to disk.
  std::unique_ptr<base::ImportantFileWriter> writer_;

  // Used to send reports.
  mojo::Remote<mojom::URLLoaderFactory> url_loader_factory_;

  base::FilePath persistence_path_;
  scoped_refptr<base::SequencedTaskRunner> foreground_runner_;
  scoped_refptr<base::SequencedTaskRunner> background_runner_;
  bool is_after_startup_ = false;
  bool persisted_reports_read_ = false;

  // How often the high-water-mark metrics get logged.
  base::TimeDelta hwm_metrics_period_ = base::Hours(1);

  base::WeakPtrFactory<SCTAuditingHandler> weak_factory_{this};
};

}  // namespace network

#endif  // SERVICES_NETWORK_SCT_AUDITING_SCT_AUDITING_HANDLER_H_