File: script_injection_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 (197 lines) | stat: -rw-r--r-- 8,302 bytes parent folder | download | duplicates (6)
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
// 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 EXTENSIONS_BROWSER_SCRIPT_INJECTION_TRACKER_H_
#define EXTENSIONS_BROWSER_SCRIPT_INJECTION_TRACKER_H_

#include <optional>

#include "base/debug/crash_logging.h"
#include "base/types/pass_key.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/mojom/context_type.mojom-forward.h"
#include "extensions/common/mojom/host_id.mojom-forward.h"
#include "url/gurl.h"

namespace content {
class BrowserContext;
class NavigationHandle;
class RenderFrameHost;
class RenderProcessHost;
}  // namespace content

namespace extensions {

class ActiveTabPermissionGranter;
class Extension;
class ExtensionWebContentsObserver;
class UserScriptLoader;
class PermissionsUpdater;
class RequestContentScript;
class ScriptExecutor;

// Class for
// 1) observing when an extension script (content script or user script) gets
//    injected into a process,
// 2) checking if an extension script (content script or user script) was ever
//    injected into a given process.
//
// WARNING: False positives might happen.  This class is primarily meant to help
// make security decisions.  This focus means that it is known and
// working-as-intended that false positives might happen - in some scenarios the
// tracker might report that a content script was injected, when it actually
// wasn't (e.g. because the tracker might not have access to all the
// renderer-side information used to decide whether to run a content script).
//
// WARNING: This class ignores cases that don't currently need IPC verification:
// - CSS content scripts (only JavaScript content scripts are tracked)
// - WebUI content scripts (only content scripts injected by extensions are
//   tracked)
//
// This class may only be used on the UI thread.
class ScriptInjectionTracker {
 public:
  // The type of script being executed. We make this distinction because these
  // scripts have different privileges associated with them.
  // Note that this is similar, but not identical, to `mojom::ExecutionWorld`,
  // which refers to the world in which a script will be executed. Technically,
  // content scripts can choose to execute in the main world, but would still be
  // considered ScriptType::kContentScript.
  // TODO(crbug.com/40055126): The above is true (and how this class has
  // historically tracked injections), but if a script only executes in the main
  // world, it won't have content script bindings or be associated with a
  // mojom::ContextType::kContentScript. Should we just not track those, or
  // track them separately? The injection world can be determined dynamically by
  // looking at `UserScript::execution_world` for persistent scripts and
  // `mojom::JSInjection::world` for one-time scripts.
  enum class ScriptType {
    kContentScript,
    kUserScript,
  };

  // Only static methods.
  ScriptInjectionTracker() = delete;

  // Answers whether the `process` has ever in the past run a content script
  // from an extension with the given `extension_id`.
  static bool DidProcessRunContentScriptFromExtension(
      const content::RenderProcessHost& process,
      const ExtensionId& extension_id);

  // Answers whether the `process` has ever in the past run a user script from
  // an extension with the given `extension_id`.
  static bool DidProcessRunUserScriptFromExtension(
      const content::RenderProcessHost& process,
      const ExtensionId& extension_id);

  // Returns all the IDs for extensions that have ever in the past run a content
  // script in `process`.
  static ExtensionIdSet GetExtensionsThatRanContentScriptsInProcess(
      const content::RenderProcessHost& process);

  // The few methods below are called by ExtensionWebContentsObserver to notify
  // ScriptInjectionTracker about various events.  The methods correspond
  // directly to methods of content::WebContentsObserver with the same names.
  static void ReadyToCommitNavigation(
      base::PassKey<ExtensionWebContentsObserver> pass_key,
      content::NavigationHandle* navigation);
  static void DidFinishNavigation(
      base::PassKey<ExtensionWebContentsObserver> pass_key,
      content::NavigationHandle* navigation);

  // Called before ExtensionMsg_ExecuteCode is sent to a renderer process
  // (typically when handling chrome.tabs.executeScript or a similar API call).
  //
  // The caller needs to ensure that if `host_id.type() == HostID::EXTENSIONS`,
  // then the extension with the given `host_id` exists and is enabled.
  static void WillExecuteCode(base::PassKey<ScriptExecutor> pass_key,
                              ScriptType script_type,
                              content::RenderFrameHost* frame,
                              const mojom::HostID& host_id);

  // Called before `extensions::mojom::LocalFrame::ExecuteDeclarativeScript` is
  // invoked in a renderer process (e.g. when handling RequestContentScript
  // action of the `chrome.declarativeContent` API).
  static void WillExecuteCode(base::PassKey<RequestContentScript> pass_key,
                              content::RenderFrameHost* frame,
                              const Extension& extension);

  // Called before renderer is notified of new tab permissions.
  static void WillGrantActiveTab(
      base::PassKey<ActiveTabPermissionGranter> pass_key,
      const Extension& extension,
      content::RenderProcessHost& process);

  // Called right after the given renderer `process` is notified about new
  // scripts.
  static void DidUpdateScriptsInRenderer(
      base::PassKey<UserScriptLoader> pass_key,
      const mojom::HostID& host_id,
      content::RenderProcessHost& process);

  // Called right after the given renderer `process` is notified about
  // permission updates.
  static void DidUpdatePermissionsInRenderer(
      base::PassKey<PermissionsUpdater> pass_key,
      const Extension& extension,
      content::RenderProcessHost& process);

 private:
  using PassKey = base::PassKey<ScriptInjectionTracker>;

  // See the doc comment of DoStaticContentScriptsMatchForTesting in the .cc
  // file.
  friend class ContentScriptMatchingBrowserTest;
  static bool DoStaticContentScriptsMatchForTesting(
      const Extension& extension,
      content::RenderFrameHost* frame,
      const GURL& url);
};

namespace debug {

// Helper for adding a set of `ScriptInjectionTracker`-related crash keys.
//
// For example, the `extension_registry_status` crash key will log if the
// affected extension has been enabled, and the
// `do_static_content_scripts_match` crash key will log if the tracker thinks
// that the affected frame matches the content script URL patterns from the
// extension manifest.  Search for the `Get...CrashKey` functions in the `.cc`
// file for a comprehensive, up-to-date list of the generated crash keys and
// of their names.
class ScopedScriptInjectionTrackerFailureCrashKeys {
 public:
  ScopedScriptInjectionTrackerFailureCrashKeys(
      content::BrowserContext& browser_context,
      const ExtensionId& extension_id);
  ScopedScriptInjectionTrackerFailureCrashKeys(content::RenderFrameHost& frame,
                                               const ExtensionId& extension_id);
  ~ScopedScriptInjectionTrackerFailureCrashKeys();

 private:
  base::debug::ScopedCrashKeyString registry_status_crash_key_;
  base::debug::ScopedCrashKeyString is_incognito_crash_key_;

  std::optional<base::debug::ScopedCrashKeyString>
      last_committed_origin_crash_key_;
  std::optional<base::debug::ScopedCrashKeyString>
      last_committed_url_crash_key_;
  std::optional<base::debug::ScopedCrashKeyString> lifecycle_state_crash_key_;
  std::optional<base::debug::ScopedCrashKeyString> is_guest_crash_key_;

  std::optional<base::debug::ScopedCrashKeyString>
      do_web_view_scripts_match_crash_key_;
  std::optional<base::debug::ScopedCrashKeyString>
      do_static_content_scripts_match_crash_key_;
  std::optional<base::debug::ScopedCrashKeyString>
      do_dynamic_content_scripts_match_crash_key_;
  std::optional<base::debug::ScopedCrashKeyString>
      do_user_scripts_match_crash_key_;
};

}  // namespace debug

}  // namespace extensions

#endif  // EXTENSIONS_BROWSER_SCRIPT_INJECTION_TRACKER_H_