File: async_check_tracker.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (202 lines) | stat: -rw-r--r-- 8,766 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_ASYNC_CHECK_TRACKER_H_
#define COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_ASYNC_CHECK_TRACKER_H_

#include <memory>

#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/time/time.h"
#include "components/safe_browsing/content/browser/url_checker_holder.h"
#include "components/security_interstitials/core/unsafe_resource.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"

namespace safe_browsing {

class BaseUIManager;

// AsyncCheckTracker is responsible for:
// * Manage the lifetime of any `UrlCheckerHolder` that is not able to
// complete before BrowserUrlLoaderThrottle::WillProcessResponse is called.
// * Trigger a warning based on the result from `UrlCheckerHolder` if the
// check is completed between BrowserUrlLoaderThrottle::WillProcessResponse and
// WebContentsObserver::DidFinishNavigation. If the check is completed before
// WillProcessResponse, SafeBrowsingNavigationThrottle will trigger the warning.
// If the check is completed after DidFinishNavigation,
// BaseUIManager::DisplayBlockingPage will trigger the warning.
// * Track and provide the status of navigation that is associated with
// UnsafeResource. Other classes can add themselves as an observer and get
// notified when certain events happen.
// This class should only be called on the UI thread.
class AsyncCheckTracker
    : public content::WebContentsUserData<AsyncCheckTracker>,
      public content::WebContentsObserver {
 public:
  // Interface for observing events on AsyncCheckTracker.
  class Observer : public base::CheckedObserver {
   public:
    // Called when a SB check is completed by AsyncCheckTracker. This is not
    // called if the check was not handled by AsyncCheckTracker (i.e. the check
    // is completed before the response body is processed, which is the majority
    // of cases).
    virtual void OnAsyncSafeBrowsingCheckCompleted() {}
    // Notify the observers to unsubscribe before AsyncCheckTracker is
    // destructed.
    virtual void OnAsyncSafeBrowsingCheckTrackerDestructed() {}
  };

  // Returns true if the main frame load is pending (i.e. the navigation has not
  // yet committed). Note that a main frame hit may not be pending, eg. 1)
  // client side detection happens after the load is committed, or 2) async Safe
  // Browsing check is enabled.
  // Caveat: This class only tracks committed navigation ids for a
  // certain period, so this function may not return the correct result if the
  // navigation associated with the `resource` is too old.
  static bool IsMainPageResourceLoadPending(
      const security_interstitials::UnsafeResource& resource);

  static bool IsMainPageLoadPending(
      const security_interstitials::UnsafeResourceLocator& rfh_locator,
      const std::optional<int64_t>& navigation_id,
      safe_browsing::SBThreatType threat_type);

  // Returns the timestamp when the navigation associated with `resource` is
  // committed. Returns nullopt if the navigation has not committed.
  // Caveat: This class only tracks committed navigation ids for a
  // certain period, so this function may not return the correct result if the
  // navigation associated with the `resource` is too old.
  static std::optional<base::TimeTicks> GetBlockedPageCommittedTimestamp(
      const security_interstitials::UnsafeResource& resource);

  // Returns whether the platform is eligible for its sync checker to check the
  // allowlist first. Only return true if
  //   * The allowlist check is significantly faster than the local blocklist
  //     check on this platform. AND
  //   * As a risk mitigation, the async checker should still fall back to local
  //     blocklist check if the URL matches the allowlist.
  static bool IsPlatformEligibleForSyncCheckerCheckAllowlist();

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

  ~AsyncCheckTracker() override;

  // Takes ownership of `checker`.
  void TransferUrlChecker(std::unique_ptr<UrlCheckerHolder> checker);

  // Called by `UrlCheckerHolder` or `BrowserURLLoaderThrottle`, when the check
  // completes.
  void PendingCheckerCompleted(int64_t navigation_id,
                               UrlCheckerHolder::OnCompleteCheckResult result);

  // Returns whether navigation is pending.
  bool IsNavigationPending(int64_t navigation_id);

  // Returns the time when the navigation is committed. Returns nullopt if the
  // navigation has not yet committed.
  std::optional<base::TimeTicks> GetNavigationCommittedTimestamp(
      int64_t navigation_id);

  // Returns whether the additional sync checker should check the allowlist
  // first.
  // Checking the allowlist first can reduce the loading latency caused by Safe
  // Browsing on certain platforms.
  bool should_sync_checker_check_allowlist() {
    return should_sync_checker_check_allowlist_;
  }

  // content::WebContentsObserver methods:
  void DidFinishNavigation(content::NavigationHandle* handle) override;

  void AddObserver(Observer* observer);
  void RemoveObserver(Observer* observer);

  size_t PendingCheckersSizeForTesting();

  void SetNavigationTimestampsSizeThresholdForTesting(size_t threshold);

  base::WeakPtr<AsyncCheckTracker> GetWeakPtr();

 private:
  friend class content::WebContentsUserData<AsyncCheckTracker>;
  friend class SBBrowserUrlLoaderThrottleTestBase;
  friend class AsyncCheckTrackerTest;
  friend class SafeBrowsingBlockingPageTestHelper;

  AsyncCheckTracker(content::WebContents* web_contents,
                    scoped_refptr<BaseUIManager> ui_manager,
                    bool should_sync_checker_check_allowlist);

  // Deletes the pending checker in `pending_checkers_` that is keyed by
  // `navigation_id`. Does nothing if `navigation_id` is not found.
  void MaybeDeleteChecker(int64_t navigation_id);

  // Deletes all pending checkers in `pending_checkers_` except the checker that
  // is keyed by `excluded_navigation_id`.
  void DeletePendingCheckers(std::optional<int64_t> excluded_navigation_id);

  // Deletes expired timestamps to avoid `committed_navigation_timestamps_`
  // getting too large.
  void DeleteExpiredNavigationTimestamps();

  // Displays an interstitial if there is unsafe resource associated with
  // `redirect_chain` and `navigation_id`.
  void MaybeDisplayBlockingPage(const std::vector<GURL>& redirect_chain,
                                int64_t navigation_id);

  // Displays an interstitial on `resource`.
  void DisplayBlockingPage(security_interstitials::UnsafeResource resource);

  // Sets callback to be used once all checkers are completed. Used only for
  // tests.
  void SetOnAllCheckersCompletedForTesting(base::OnceClosure callback);

  // May call |on_all_checkers_completed_callback_for_testing_| if there are no
  // |pending_checkers_| remaining.
  void MaybeCallOnAllCheckersCompletedCallback();

  // Used to display a warning.
  scoped_refptr<BaseUIManager> ui_manager_;

  // Pending Safe Browsing checkers on the current page, keyed by the
  // navigation_id.
  base::flat_map<int64_t, std::unique_ptr<UrlCheckerHolder>> pending_checkers_;

  // Set to true if interstitial should be shown after DidFinishNavigation is
  // called. Reset to false after interstitial is triggered.
  bool show_interstitial_after_finish_navigation_ = false;

  // Time when a navigation is committed, keyed by the navigation_id.
  // Whether a navigation id is in the map can be used to determine if a
  // navigation has committed. The time when the navigation has committed is
  // used for logging metrics.
  base::flat_map<int64_t, base::TimeTicks> committed_navigation_timestamps_;

  // The threshold that will trigger a cleanup on
  // `committed_navigation_timestamps_`. Overridden in tests.
  size_t navigation_timestamps_size_threshold_;

  // Whether sync checker should check the allowlist first.
  const bool should_sync_checker_check_allowlist_ = false;

  // A list of observers that are interested in events from this class.
  base::ObserverList<Observer> observers_;

  // Callback that is called once all checkers are completed. Used only for
  // tests.
  base::OnceClosure on_all_checkers_completed_callback_for_testing_;

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

  WEB_CONTENTS_USER_DATA_KEY_DECL();
};

}  // namespace safe_browsing

#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_ASYNC_CHECK_TRACKER_H_