@metamask-previews/phishing-controller 6.0.1-preview.f71e42f → 7.0.0-preview.26b130a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [7.0.0]
10
+ ### Changed
11
+ - **BREAKING:** Migrate `PhishingController` to BaseControllerV2 ([#1705](https://github.com/MetaMask/core/pull/1705))
12
+ - `PhishingController` now expects a `messenger` option (and corresponding type `PhishingControllerMessenger` is now available)
13
+ - The constructor takes a single argument, an options bag, instead of three arguments
14
+ - The `disabled` configuration is no longer supported
15
+ - Update TypeScript to v4.8.x ([#1718](https://github.com/MetaMask/core/pull/1718))
16
+
17
+ ## [6.0.2]
18
+ ### Changed
19
+ - Bump dependency on `@metamask/controller-utils` to ^5.0.0
20
+
9
21
  ## [6.0.1]
10
22
  ### Changed
11
23
  - Bump dependency on `@metamask/base-controller` to ^3.2.1
@@ -61,7 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
61
73
  ## [1.1.1]
62
74
  ### Changed
63
75
  - Rename this repository to `core` ([#1031](https://github.com/MetaMask/controllers/pull/1031))
64
- - Update `@metamask/controller-utils` package ([#1041](https://github.com/MetaMask/controllers/pull/1041))
76
+ - Update `@metamask/controller-utils` package ([#1041](https://github.com/MetaMask/controllers/pull/1041))
65
77
 
66
78
  ## [1.1.0]
67
79
  ### Added
@@ -80,7 +92,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
80
92
 
81
93
  All changes listed after this point were applied to this package following the monorepo conversion.
82
94
 
83
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@6.0.1...HEAD
95
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@7.0.0...HEAD
96
+ [7.0.0]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@6.0.2...@metamask/phishing-controller@7.0.0
97
+ [6.0.2]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@6.0.1...@metamask/phishing-controller@6.0.2
84
98
  [6.0.1]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@6.0.0...@metamask/phishing-controller@6.0.1
85
99
  [6.0.0]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@5.0.0...@metamask/phishing-controller@6.0.0
86
100
  [5.0.0]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@4.0.0...@metamask/phishing-controller@5.0.0
@@ -1,5 +1,12 @@
1
- import type { BaseConfig, BaseState } from '@metamask/base-controller';
2
- import { BaseController } from '@metamask/base-controller';
1
+ import type { RestrictedControllerMessenger } from '@metamask/base-controller';
2
+ import { BaseControllerV2 as BaseController } from '@metamask/base-controller';
3
+ export declare const PHISHING_CONFIG_BASE_URL = "https://phishing-detection.metafi.codefi.network";
4
+ export declare const METAMASK_STALELIST_FILE = "/v1/stalelist";
5
+ export declare const METAMASK_HOTLIST_DIFF_FILE = "/v1/diffsSince";
6
+ export declare const HOTLIST_REFRESH_INTERVAL: number;
7
+ export declare const STALELIST_REFRESH_INTERVAL: number;
8
+ export declare const METAMASK_STALELIST_URL: string;
9
+ export declare const METAMASK_HOTLIST_DIFF_URL: string;
3
10
  /**
4
11
  * @type ListTypes
5
12
  *
@@ -17,34 +24,34 @@ export declare type ListTypes = 'fuzzylist' | 'blocklist' | 'allowlist';
17
24
  * @property version - Version number of this configuration
18
25
  * @property whitelist - List of approved origins
19
26
  */
20
- export interface EthPhishingResponse {
27
+ export declare type EthPhishingResponse = {
21
28
  blacklist: string[];
22
29
  fuzzylist: string[];
23
30
  tolerance: number;
24
31
  version: number;
25
32
  whitelist: string[];
26
- }
33
+ };
27
34
  /**
28
35
  * @type PhishingStalelist
29
36
  *
30
- * Interface defining expected type of the stalelist.json file.
37
+ * type defining expected type of the stalelist.json file.
31
38
  * @property eth_phishing_detect_config - Stale list sourced from eth-phishing-detect's config.json.
32
39
  * @property phishfort_hotlist - Stale list sourced from phishfort's hotlist.json. Only includes blocklist. Deduplicated entries from eth_phishing_detect_config.
33
40
  * @property tolerance - Fuzzy match tolerance level
34
41
  * @property lastUpdated - Timestamp of last update.
35
42
  * @property version - Stalelist data structure iteration.
36
43
  */
37
- export interface PhishingStalelist {
44
+ export declare type PhishingStalelist = {
38
45
  eth_phishing_detect_config: Record<ListTypes, string[]>;
39
46
  phishfort_hotlist: Record<ListTypes, string[]>;
40
47
  tolerance: number;
41
48
  version: number;
42
49
  lastUpdated: number;
43
- }
50
+ };
44
51
  /**
45
52
  * @type PhishingListState
46
53
  *
47
- * Interface defining the persisted list state. This is the persisted state that is updated frequently with `this.maybeUpdateState()`.
54
+ * type defining the persisted list state. This is the persisted state that is updated frequently with `this.maybeUpdateState()`.
48
55
  * @property allowlist - List of approved origins (legacy naming "whitelist")
49
56
  * @property blocklist - List of unapproved origins (legacy naming "blacklist")
50
57
  * @property fuzzylist - List of fuzzy-matched unapproved origins
@@ -53,7 +60,7 @@ export interface PhishingStalelist {
53
60
  * @property version - Version of the phishing list state.
54
61
  * @property name - Name of the list. Used for attribution.
55
62
  */
56
- export interface PhishingListState {
63
+ export declare type PhishingListState = {
57
64
  allowlist: string[];
58
65
  blocklist: string[];
59
66
  fuzzylist: string[];
@@ -61,42 +68,42 @@ export interface PhishingListState {
61
68
  version: number;
62
69
  lastUpdated: number;
63
70
  name: ListNames;
64
- }
71
+ };
65
72
  /**
66
73
  * @type EthPhishingDetectResult
67
74
  *
68
- * Interface that describes the result of the `test` method.
75
+ * type that describes the result of the `test` method.
69
76
  * @property name - Name of the config on which a match was found.
70
77
  * @property version - Version of the config on which a match was found.
71
78
  * @property result - Whether a domain was detected as a phishing domain. True means an unsafe domain.
72
79
  * @property match - The matching fuzzylist origin when a fuzzylist match is found. Returned as undefined for non-fuzzy true results.
73
80
  * @property type - The field of the config on which a match was found.
74
81
  */
75
- export interface EthPhishingDetectResult {
82
+ export declare type EthPhishingDetectResult = {
76
83
  name?: string;
77
84
  version?: string;
78
85
  result: boolean;
79
86
  match?: string;
80
87
  type: 'all' | 'fuzzy' | 'blocklist' | 'allowlist';
81
- }
88
+ };
82
89
  /**
83
90
  * @type HotlistDiff
84
91
  *
85
- * Interface defining the expected type of the diffs in hotlist.json file.
92
+ * type defining the expected type of the diffs in hotlist.json file.
86
93
  * @property url - Url of the diff entry.
87
94
  * @property timestamp - Timestamp at which the diff was identified.
88
95
  * @property targetList - The list name where the diff was identified.
89
96
  * @property isRemoval - Was the diff identified a removal type.
90
97
  */
91
- export interface HotlistDiff {
98
+ export declare type HotlistDiff = {
92
99
  url: string;
93
100
  timestamp: number;
94
101
  targetList: `${ListKeys}.${ListTypes}`;
95
102
  isRemoval?: boolean;
96
- }
97
- export interface DataResultWrapper<T> {
103
+ };
104
+ export declare type DataResultWrapper<T> = {
98
105
  data: T;
99
- }
106
+ };
100
107
  /**
101
108
  * @type Hotlist
102
109
  *
@@ -107,37 +114,6 @@ export interface DataResultWrapper<T> {
107
114
  * @property isRemoval - Was the diff identified a removal type.
108
115
  */
109
116
  export declare type Hotlist = HotlistDiff[];
110
- /**
111
- * @type PhishingConfig
112
- *
113
- * Phishing controller configuration
114
- * @property stalelistRefreshInterval - Polling interval used to fetch stale list.
115
- * @property hotlistRefreshInterval - Polling interval used to fetch hotlist diff list.
116
- */
117
- export interface PhishingConfig extends BaseConfig {
118
- stalelistRefreshInterval: number;
119
- hotlistRefreshInterval: number;
120
- }
121
- /**
122
- * @type PhishingState
123
- *
124
- * Phishing controller state
125
- * @property phishing - eth-phishing-detect configuration
126
- * @property whitelist - array of temporarily-approved origins
127
- */
128
- export interface PhishingState extends BaseState {
129
- phishingLists: PhishingListState[];
130
- whitelist: string[];
131
- hotlistLastFetched: number;
132
- stalelistLastFetched: number;
133
- }
134
- export declare const PHISHING_CONFIG_BASE_URL = "https://phishing-detection.metafi.codefi.network";
135
- export declare const METAMASK_STALELIST_FILE = "/v1/stalelist";
136
- export declare const METAMASK_HOTLIST_DIFF_FILE = "/v1/diffsSince";
137
- export declare const HOTLIST_REFRESH_INTERVAL: number;
138
- export declare const STALELIST_REFRESH_INTERVAL: number;
139
- export declare const METAMASK_STALELIST_URL: string;
140
- export declare const METAMASK_HOTLIST_DIFF_URL: string;
141
117
  /**
142
118
  * Enum containing upstream data provider source list keys.
143
119
  * These are the keys denoting lists consumed by the upstream data provider.
@@ -161,23 +137,58 @@ export declare const phishingListKeyNameMap: {
161
137
  eth_phishing_detect_config: ListNames;
162
138
  phishfort_hotlist: ListNames;
163
139
  };
140
+ declare const controllerName = "PhishingController";
141
+ /**
142
+ * @type PhishingControllerState
143
+ *
144
+ * Phishing controller state
145
+ * @property phishing - eth-phishing-detect configuration
146
+ * @property whitelist - array of temporarily-approved origins
147
+ */
148
+ export declare type PhishingControllerState = {
149
+ phishingLists: PhishingListState[];
150
+ whitelist: string[];
151
+ hotlistLastFetched: number;
152
+ stalelistLastFetched: number;
153
+ };
154
+ /**
155
+ * @type PhishingControllerOptions
156
+ *
157
+ * Phishing controller options
158
+ * @property stalelistRefreshInterval - Polling interval used to fetch stale list.
159
+ * @property hotlistRefreshInterval - Polling interval used to fetch hotlist diff list.
160
+ */
161
+ export declare type PhishingControllerOptions = {
162
+ stalelistRefreshInterval?: number;
163
+ hotlistRefreshInterval?: number;
164
+ messenger: PhishingControllerMessenger;
165
+ state?: Partial<PhishingControllerState>;
166
+ };
167
+ export declare type MaybeUpdateState = {
168
+ type: `${typeof controllerName}:maybeUpdateState`;
169
+ handler: PhishingController['maybeUpdateState'];
170
+ };
171
+ export declare type TestOrigin = {
172
+ type: `${typeof controllerName}:testOrigin`;
173
+ handler: PhishingController['test'];
174
+ };
175
+ export declare type PhishingControllerActions = MaybeUpdateState | TestOrigin;
176
+ export declare type PhishingControllerMessenger = RestrictedControllerMessenger<typeof controllerName, PhishingControllerActions, never, never, never>;
164
177
  /**
165
178
  * Controller that manages community-maintained lists of approved and unapproved website origins.
166
179
  */
167
- export declare class PhishingController extends BaseController<PhishingConfig, PhishingState> {
180
+ export declare class PhishingController extends BaseController<typeof controllerName, PhishingControllerState, PhishingControllerMessenger> {
168
181
  #private;
169
- private detector;
170
- /**
171
- * Name of this controller used during composition
172
- */
173
- name: string;
174
182
  /**
175
- * Creates a PhishingController instance.
183
+ * Construct a Phishing Controller.
176
184
  *
177
185
  * @param config - Initial options used to configure this controller.
178
- * @param state - Initial state to set on this controller.
186
+ * @param config.stalelistRefreshInterval - Polling interval used to fetch stale list.
187
+ * @param config.hotlistRefreshInterval - Polling interval used to fetch hotlist diff list.
188
+ * @param config.messenger - The controller restricted messenger.
189
+ * @param config.state - Initial state to set on this controller.
179
190
  */
180
- constructor(config?: Partial<PhishingConfig>, state?: Partial<PhishingState>);
191
+ constructor({ stalelistRefreshInterval, hotlistRefreshInterval, messenger, state, }: PhishingControllerOptions);
181
192
  /**
182
193
  * Updates this.detector with an instance of PhishingDetector using the current state.
183
194
  */
@@ -250,7 +261,6 @@ export declare class PhishingController extends BaseController<PhishingConfig, P
250
261
  * the in-progress update has finished.
251
262
  */
252
263
  updateStalelist(): Promise<void>;
253
- private queryConfig;
254
264
  }
255
265
  export default PhishingController;
256
266
  //# sourceMappingURL=PhishingController.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PhishingController.d.ts","sourceRoot":"","sources":["../src/PhishingController.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PhishingController.d.ts","sourceRoot":"","sources":["../src/PhishingController.ts"],"names":[],"mappings":"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"}
@@ -8,17 +8,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
12
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
13
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
14
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
15
- };
16
11
  var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
17
12
  if (kind === "m") throw new TypeError("Private method is not writable");
18
13
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
19
14
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
20
15
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
21
16
  };
17
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
18
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
19
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
20
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
21
+ };
22
22
  var __rest = (this && this.__rest) || function (s, e) {
23
23
  var t = {};
24
24
  for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
@@ -33,7 +33,7 @@ var __rest = (this && this.__rest) || function (s, e) {
33
33
  var __importDefault = (this && this.__importDefault) || function (mod) {
34
34
  return (mod && mod.__esModule) ? mod : { "default": mod };
35
35
  };
36
- var _PhishingController_instances, _PhishingController_inProgressHotlistUpdate, _PhishingController_inProgressStalelistUpdate, _PhishingController_updateStalelist, _PhishingController_updateHotlist;
36
+ var _PhishingController_instances, _PhishingController_detector, _PhishingController_stalelistRefreshInterval, _PhishingController_hotlistRefreshInterval, _PhishingController_inProgressHotlistUpdate, _PhishingController_inProgressStalelistUpdate, _PhishingController_registerMessageHandlers, _PhishingController_updateStalelist, _PhishingController_updateHotlist, _PhishingController_queryConfig;
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
38
  exports.PhishingController = exports.phishingListKeyNameMap = exports.ListNames = exports.ListKeys = exports.METAMASK_HOTLIST_DIFF_URL = exports.METAMASK_STALELIST_URL = exports.STALELIST_REFRESH_INTERVAL = exports.HOTLIST_REFRESH_INTERVAL = exports.METAMASK_HOTLIST_DIFF_FILE = exports.METAMASK_STALELIST_FILE = exports.PHISHING_CONFIG_BASE_URL = void 0;
39
39
  const base_controller_1 = require("@metamask/base-controller");
@@ -81,43 +81,61 @@ exports.phishingListKeyNameMap = {
81
81
  [ListKeys.EthPhishingDetectConfig]: ListNames.MetaMask,
82
82
  [ListKeys.PhishfortHotlist]: ListNames.Phishfort,
83
83
  };
84
+ const controllerName = 'PhishingController';
85
+ const metadata = {
86
+ phishingLists: { persist: true, anonymous: false },
87
+ whitelist: { persist: true, anonymous: false },
88
+ hotlistLastFetched: { persist: true, anonymous: false },
89
+ stalelistLastFetched: { persist: true, anonymous: false },
90
+ };
91
+ /**
92
+ * Get a default empty state for the controller.
93
+ * @returns The default empty state.
94
+ */
95
+ const getDefaultState = () => {
96
+ return {
97
+ phishingLists: [],
98
+ whitelist: [],
99
+ hotlistLastFetched: 0,
100
+ stalelistLastFetched: 0,
101
+ };
102
+ };
84
103
  /**
85
104
  * Controller that manages community-maintained lists of approved and unapproved website origins.
86
105
  */
87
- class PhishingController extends base_controller_1.BaseController {
106
+ class PhishingController extends base_controller_1.BaseControllerV2 {
88
107
  /**
89
- * Creates a PhishingController instance.
108
+ * Construct a Phishing Controller.
90
109
  *
91
110
  * @param config - Initial options used to configure this controller.
92
- * @param state - Initial state to set on this controller.
111
+ * @param config.stalelistRefreshInterval - Polling interval used to fetch stale list.
112
+ * @param config.hotlistRefreshInterval - Polling interval used to fetch hotlist diff list.
113
+ * @param config.messenger - The controller restricted messenger.
114
+ * @param config.state - Initial state to set on this controller.
93
115
  */
94
- constructor(config, state) {
95
- super(config, state);
116
+ constructor({ stalelistRefreshInterval = exports.STALELIST_REFRESH_INTERVAL, hotlistRefreshInterval = exports.HOTLIST_REFRESH_INTERVAL, messenger, state = {}, }) {
117
+ super({
118
+ name: controllerName,
119
+ metadata,
120
+ messenger,
121
+ state: Object.assign(Object.assign({}, getDefaultState()), state),
122
+ });
96
123
  _PhishingController_instances.add(this);
124
+ _PhishingController_detector.set(this, void 0);
125
+ _PhishingController_stalelistRefreshInterval.set(this, void 0);
126
+ _PhishingController_hotlistRefreshInterval.set(this, void 0);
97
127
  _PhishingController_inProgressHotlistUpdate.set(this, void 0);
98
128
  _PhishingController_inProgressStalelistUpdate.set(this, void 0);
99
- /**
100
- * Name of this controller used during composition
101
- */
102
- this.name = 'PhishingController';
103
- this.defaultConfig = {
104
- stalelistRefreshInterval: exports.STALELIST_REFRESH_INTERVAL,
105
- hotlistRefreshInterval: exports.HOTLIST_REFRESH_INTERVAL,
106
- };
107
- this.defaultState = {
108
- phishingLists: [],
109
- whitelist: [],
110
- hotlistLastFetched: 0,
111
- stalelistLastFetched: 0,
112
- };
113
- this.initialize();
129
+ __classPrivateFieldSet(this, _PhishingController_stalelistRefreshInterval, stalelistRefreshInterval, "f");
130
+ __classPrivateFieldSet(this, _PhishingController_hotlistRefreshInterval, hotlistRefreshInterval, "f");
131
+ __classPrivateFieldGet(this, _PhishingController_instances, "m", _PhishingController_registerMessageHandlers).call(this);
114
132
  this.updatePhishingDetector();
115
133
  }
116
134
  /**
117
135
  * Updates this.detector with an instance of PhishingDetector using the current state.
118
136
  */
119
137
  updatePhishingDetector() {
120
- this.detector = new detector_1.default(this.state.phishingLists);
138
+ __classPrivateFieldSet(this, _PhishingController_detector, new detector_1.default(this.state.phishingLists), "f");
121
139
  }
122
140
  /**
123
141
  * Set the interval at which the stale phishing list will be refetched.
@@ -127,7 +145,7 @@ class PhishingController extends base_controller_1.BaseController {
127
145
  * @param interval - the new interval, in ms.
128
146
  */
129
147
  setStalelistRefreshInterval(interval) {
130
- this.configure({ stalelistRefreshInterval: interval }, false, false);
148
+ __classPrivateFieldSet(this, _PhishingController_stalelistRefreshInterval, interval, "f");
131
149
  }
132
150
  /**
133
151
  * Set the interval at which the hot list will be refetched.
@@ -137,7 +155,7 @@ class PhishingController extends base_controller_1.BaseController {
137
155
  * @param interval - the new interval, in ms.
138
156
  */
139
157
  setHotlistRefreshInterval(interval) {
140
- this.configure({ hotlistRefreshInterval: interval }, false, false);
158
+ __classPrivateFieldSet(this, _PhishingController_hotlistRefreshInterval, interval, "f");
141
159
  }
142
160
  /**
143
161
  * Determine if an update to the stalelist configuration is needed.
@@ -146,7 +164,7 @@ class PhishingController extends base_controller_1.BaseController {
146
164
  */
147
165
  isStalelistOutOfDate() {
148
166
  return ((0, utils_1.fetchTimeNow)() - this.state.stalelistLastFetched >=
149
- this.config.stalelistRefreshInterval);
167
+ __classPrivateFieldGet(this, _PhishingController_stalelistRefreshInterval, "f"));
150
168
  }
151
169
  /**
152
170
  * Determine if an update to the hotlist configuration is needed.
@@ -155,7 +173,7 @@ class PhishingController extends base_controller_1.BaseController {
155
173
  */
156
174
  isHotlistOutOfDate() {
157
175
  return ((0, utils_1.fetchTimeNow)() - this.state.hotlistLastFetched >=
158
- this.config.hotlistRefreshInterval);
176
+ __classPrivateFieldGet(this, _PhishingController_hotlistRefreshInterval, "f"));
159
177
  }
160
178
  /**
161
179
  * Conditionally update the phishing configuration.
@@ -193,7 +211,7 @@ class PhishingController extends base_controller_1.BaseController {
193
211
  if (this.state.whitelist.includes(punycodeOrigin)) {
194
212
  return { result: false, type: 'all' }; // Same as whitelisted match returned by detector.check(...).
195
213
  }
196
- return this.detector.check(punycodeOrigin);
214
+ return __classPrivateFieldGet(this, _PhishingController_detector, "f").check(punycodeOrigin);
197
215
  }
198
216
  /**
199
217
  * Temporarily marks a given origin as approved.
@@ -206,7 +224,9 @@ class PhishingController extends base_controller_1.BaseController {
206
224
  if (whitelist.includes(punycodeOrigin)) {
207
225
  return;
208
226
  }
209
- this.update({ whitelist: [...whitelist, punycodeOrigin] });
227
+ this.update((draftState) => {
228
+ draftState.whitelist.push(punycodeOrigin);
229
+ });
210
230
  }
211
231
  /**
212
232
  * Update the hotlist.
@@ -250,43 +270,30 @@ class PhishingController extends base_controller_1.BaseController {
250
270
  }
251
271
  });
252
272
  }
253
- queryConfig(input) {
254
- return __awaiter(this, void 0, void 0, function* () {
255
- const response = yield (0, controller_utils_1.safelyExecute)(() => fetch(input, { cache: 'no-cache' }), true);
256
- switch (response === null || response === void 0 ? void 0 : response.status) {
257
- case 200: {
258
- return yield response.json();
259
- }
260
- default: {
261
- return null;
262
- }
263
- }
264
- });
265
- }
266
273
  }
267
274
  exports.PhishingController = PhishingController;
268
- _PhishingController_inProgressHotlistUpdate = new WeakMap(), _PhishingController_inProgressStalelistUpdate = new WeakMap(), _PhishingController_instances = new WeakSet(), _PhishingController_updateStalelist = function _PhishingController_updateStalelist() {
275
+ _PhishingController_detector = new WeakMap(), _PhishingController_stalelistRefreshInterval = new WeakMap(), _PhishingController_hotlistRefreshInterval = new WeakMap(), _PhishingController_inProgressHotlistUpdate = new WeakMap(), _PhishingController_inProgressStalelistUpdate = new WeakMap(), _PhishingController_instances = new WeakSet(), _PhishingController_registerMessageHandlers = function _PhishingController_registerMessageHandlers() {
276
+ this.messagingSystem.registerActionHandler(`${controllerName}:maybeUpdateState`, this.maybeUpdateState.bind(this));
277
+ this.messagingSystem.registerActionHandler(`${controllerName}:testOrigin`, this.test.bind(this));
278
+ }, _PhishingController_updateStalelist = function _PhishingController_updateStalelist() {
269
279
  return __awaiter(this, void 0, void 0, function* () {
270
- if (this.disabled) {
271
- return;
272
- }
273
280
  let stalelistResponse;
274
281
  let hotlistDiffsResponse;
275
282
  try {
276
- stalelistResponse = yield this.queryConfig(exports.METAMASK_STALELIST_URL).then((d) => d);
283
+ stalelistResponse = yield __classPrivateFieldGet(this, _PhishingController_instances, "m", _PhishingController_queryConfig).call(this, exports.METAMASK_STALELIST_URL).then((d) => d);
277
284
  // Fetching hotlist diffs relies on having a lastUpdated timestamp to do `GET /v1/diffsSince/:timestamp`,
278
285
  // so it doesn't make sense to call if there is not a timestamp to begin with.
279
286
  if ((stalelistResponse === null || stalelistResponse === void 0 ? void 0 : stalelistResponse.data) && stalelistResponse.data.lastUpdated > 0) {
280
- hotlistDiffsResponse = yield this.queryConfig(`${exports.METAMASK_HOTLIST_DIFF_URL}/${stalelistResponse.data.lastUpdated}`);
287
+ hotlistDiffsResponse = yield __classPrivateFieldGet(this, _PhishingController_instances, "m", _PhishingController_queryConfig).call(this, `${exports.METAMASK_HOTLIST_DIFF_URL}/${stalelistResponse.data.lastUpdated}`);
281
288
  }
282
289
  }
283
290
  finally {
284
291
  // Set `stalelistLastFetched` and `hotlistLastFetched` even for failed requests to prevent server
285
292
  // from being overwhelmed with traffic after a network disruption.
286
293
  const timeNow = (0, utils_1.fetchTimeNow)();
287
- this.update({
288
- stalelistLastFetched: timeNow,
289
- hotlistLastFetched: timeNow,
294
+ this.update((draftState) => {
295
+ draftState.stalelistLastFetched = timeNow;
296
+ draftState.hotlistLastFetched = timeNow;
290
297
  });
291
298
  }
292
299
  if (!stalelistResponse || !hotlistDiffsResponse) {
@@ -298,26 +305,23 @@ _PhishingController_inProgressHotlistUpdate = new WeakMap(), _PhishingController
298
305
  // Correctly shaping eth-phishing-detect state by applying hotlist diffs to the stalelist.
299
306
  const newPhishfortListState = (0, utils_1.applyDiffs)(phishfortListState, hotlistDiffsResponse.data, ListKeys.PhishfortHotlist);
300
307
  const newMetaMaskListState = (0, utils_1.applyDiffs)(metamaskListState, hotlistDiffsResponse.data, ListKeys.EthPhishingDetectConfig);
301
- this.update({
302
- phishingLists: [newMetaMaskListState, newPhishfortListState],
308
+ this.update((draftState) => {
309
+ draftState.phishingLists = [newMetaMaskListState, newPhishfortListState];
303
310
  });
304
311
  this.updatePhishingDetector();
305
312
  });
306
313
  }, _PhishingController_updateHotlist = function _PhishingController_updateHotlist() {
307
314
  return __awaiter(this, void 0, void 0, function* () {
308
- if (this.disabled) {
309
- return;
310
- }
311
315
  const lastDiffTimestamp = Math.max(...this.state.phishingLists.map(({ lastUpdated }) => lastUpdated));
312
316
  let hotlistResponse;
313
317
  try {
314
- hotlistResponse = yield this.queryConfig(`${exports.METAMASK_HOTLIST_DIFF_URL}/${lastDiffTimestamp}`);
318
+ hotlistResponse = yield __classPrivateFieldGet(this, _PhishingController_instances, "m", _PhishingController_queryConfig).call(this, `${exports.METAMASK_HOTLIST_DIFF_URL}/${lastDiffTimestamp}`);
315
319
  }
316
320
  finally {
317
321
  // Set `hotlistLastFetched` even for failed requests to prevent server from being overwhelmed with
318
322
  // traffic after a network disruption.
319
- this.update({
320
- hotlistLastFetched: (0, utils_1.fetchTimeNow)(),
323
+ this.update((draftState) => {
324
+ draftState.hotlistLastFetched = (0, utils_1.fetchTimeNow)();
321
325
  });
322
326
  }
323
327
  if (!(hotlistResponse === null || hotlistResponse === void 0 ? void 0 : hotlistResponse.data)) {
@@ -325,11 +329,23 @@ _PhishingController_inProgressHotlistUpdate = new WeakMap(), _PhishingController
325
329
  }
326
330
  const hotlist = hotlistResponse.data;
327
331
  const newPhishingLists = this.state.phishingLists.map((phishingList) => (0, utils_1.applyDiffs)(phishingList, hotlist, phishingListNameKeyMap[phishingList.name]));
328
- this.update({
329
- phishingLists: newPhishingLists,
332
+ this.update((draftState) => {
333
+ draftState.phishingLists = newPhishingLists;
330
334
  });
331
335
  this.updatePhishingDetector();
332
336
  });
337
+ }, _PhishingController_queryConfig = function _PhishingController_queryConfig(input) {
338
+ return __awaiter(this, void 0, void 0, function* () {
339
+ const response = yield (0, controller_utils_1.safelyExecute)(() => fetch(input, { cache: 'no-cache' }), true);
340
+ switch (response === null || response === void 0 ? void 0 : response.status) {
341
+ case 200: {
342
+ return yield response.json();
343
+ }
344
+ default: {
345
+ return null;
346
+ }
347
+ }
348
+ });
333
349
  };
334
350
  exports.default = PhishingController;
335
351
  //# sourceMappingURL=PhishingController.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PhishingController.js","sourceRoot":"","sources":["../src/PhishingController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BaseConfig, BaseState } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport { safelyExecute } from '@metamask/controller-utils';\nimport PhishingDetector from 'eth-phishing-detect/src/detector';\nimport { toASCII } from 'punycode/';\n\nimport { applyDiffs, fetchTimeNow } from './utils';\n\n/**\n * @type ListTypes\n *\n * Type outlining the types of lists provided by aggregating different source lists\n */\nexport type ListTypes = 'fuzzylist' | 'blocklist' | 'allowlist';\n\n/**\n * @type EthPhishingResponse\n *\n * Configuration response from the eth-phishing-detect package\n * consisting of approved and unapproved website origins\n * @property blacklist - List of unapproved origins\n * @property fuzzylist - List of fuzzy-matched unapproved origins\n * @property tolerance - Fuzzy match tolerance level\n * @property version - Version number of this configuration\n * @property whitelist - List of approved origins\n */\nexport interface EthPhishingResponse {\n blacklist: string[];\n fuzzylist: string[];\n tolerance: number;\n version: number;\n whitelist: string[];\n}\n\n/**\n * @type PhishingStalelist\n *\n * Interface defining expected type of the stalelist.json file.\n * @property eth_phishing_detect_config - Stale list sourced from eth-phishing-detect's config.json.\n * @property phishfort_hotlist - Stale list sourced from phishfort's hotlist.json. Only includes blocklist. Deduplicated entries from eth_phishing_detect_config.\n * @property tolerance - Fuzzy match tolerance level\n * @property lastUpdated - Timestamp of last update.\n * @property version - Stalelist data structure iteration.\n */\nexport interface PhishingStalelist {\n eth_phishing_detect_config: Record<ListTypes, string[]>;\n phishfort_hotlist: Record<ListTypes, string[]>;\n tolerance: number;\n version: number;\n lastUpdated: number;\n}\n\n/**\n * @type PhishingListState\n *\n * Interface defining the persisted list state. This is the persisted state that is updated frequently with `this.maybeUpdateState()`.\n * @property allowlist - List of approved origins (legacy naming \"whitelist\")\n * @property blocklist - List of unapproved origins (legacy naming \"blacklist\")\n * @property fuzzylist - List of fuzzy-matched unapproved origins\n * @property tolerance - Fuzzy match tolerance level\n * @property lastUpdated - Timestamp of last update.\n * @property version - Version of the phishing list state.\n * @property name - Name of the list. Used for attribution.\n */\nexport interface PhishingListState {\n allowlist: string[];\n blocklist: string[];\n fuzzylist: string[];\n tolerance: number;\n version: number;\n lastUpdated: number;\n name: ListNames;\n}\n\n/**\n * @type EthPhishingDetectResult\n *\n * Interface that describes the result of the `test` method.\n * @property name - Name of the config on which a match was found.\n * @property version - Version of the config on which a match was found.\n * @property result - Whether a domain was detected as a phishing domain. True means an unsafe domain.\n * @property match - The matching fuzzylist origin when a fuzzylist match is found. Returned as undefined for non-fuzzy true results.\n * @property type - The field of the config on which a match was found.\n */\nexport interface EthPhishingDetectResult {\n name?: string;\n version?: string;\n result: boolean;\n match?: string; // Returned as undefined for non-fuzzy true results.\n type: 'all' | 'fuzzy' | 'blocklist' | 'allowlist';\n}\n\n/**\n * @type HotlistDiff\n *\n * Interface defining the expected type of the diffs in hotlist.json file.\n * @property url - Url of the diff entry.\n * @property timestamp - Timestamp at which the diff was identified.\n * @property targetList - The list name where the diff was identified.\n * @property isRemoval - Was the diff identified a removal type.\n */\nexport interface HotlistDiff {\n url: string;\n timestamp: number;\n targetList: `${ListKeys}.${ListTypes}`;\n isRemoval?: boolean;\n}\n\nexport interface DataResultWrapper<T> {\n data: T;\n}\n\n/**\n * @type Hotlist\n *\n * Type defining expected hotlist.json file.\n * @property url - Url of the diff entry.\n * @property timestamp - Timestamp at which the diff was identified.\n * @property targetList - The list name where the diff was identified.\n * @property isRemoval - Was the diff identified a removal type.\n */\nexport type Hotlist = HotlistDiff[];\n\n/**\n * @type PhishingConfig\n *\n * Phishing controller configuration\n * @property stalelistRefreshInterval - Polling interval used to fetch stale list.\n * @property hotlistRefreshInterval - Polling interval used to fetch hotlist diff list.\n */\nexport interface PhishingConfig extends BaseConfig {\n stalelistRefreshInterval: number;\n hotlistRefreshInterval: number;\n}\n\n/**\n * @type PhishingState\n *\n * Phishing controller state\n * @property phishing - eth-phishing-detect configuration\n * @property whitelist - array of temporarily-approved origins\n */\nexport interface PhishingState extends BaseState {\n phishingLists: PhishingListState[];\n whitelist: string[];\n hotlistLastFetched: number;\n stalelistLastFetched: number;\n}\n\nexport const PHISHING_CONFIG_BASE_URL =\n 'https://phishing-detection.metafi.codefi.network';\n\nexport const METAMASK_STALELIST_FILE = '/v1/stalelist';\n\nexport const METAMASK_HOTLIST_DIFF_FILE = '/v1/diffsSince';\n\nexport const HOTLIST_REFRESH_INTERVAL = 30 * 60; // 30 mins in seconds\nexport const STALELIST_REFRESH_INTERVAL = 4 * 24 * 60 * 60; // 4 days in seconds\n\nexport const METAMASK_STALELIST_URL = `${PHISHING_CONFIG_BASE_URL}${METAMASK_STALELIST_FILE}`;\nexport const METAMASK_HOTLIST_DIFF_URL = `${PHISHING_CONFIG_BASE_URL}${METAMASK_HOTLIST_DIFF_FILE}`;\n\n/**\n * Enum containing upstream data provider source list keys.\n * These are the keys denoting lists consumed by the upstream data provider.\n */\nexport enum ListKeys {\n PhishfortHotlist = 'phishfort_hotlist',\n EthPhishingDetectConfig = 'eth_phishing_detect_config',\n}\n\n/**\n * Enum containing downstream client attribution names.\n */\nexport enum ListNames {\n MetaMask = 'MetaMask',\n Phishfort = 'Phishfort',\n}\n\n/**\n * Maps from downstream client attribution name\n * to list key sourced from upstream data provider.\n */\nconst phishingListNameKeyMap = {\n [ListNames.Phishfort]: ListKeys.PhishfortHotlist,\n [ListNames.MetaMask]: ListKeys.EthPhishingDetectConfig,\n};\n\n/**\n * Maps from list key sourced from upstream data\n * provider to downstream client attribution name.\n */\nexport const phishingListKeyNameMap = {\n [ListKeys.EthPhishingDetectConfig]: ListNames.MetaMask,\n [ListKeys.PhishfortHotlist]: ListNames.Phishfort,\n};\n\n/**\n * Controller that manages community-maintained lists of approved and unapproved website origins.\n */\nexport class PhishingController extends BaseController<\n PhishingConfig,\n PhishingState\n> {\n private detector: any;\n\n #inProgressHotlistUpdate: Promise<void> | undefined;\n\n #inProgressStalelistUpdate: Promise<void> | undefined;\n\n /**\n * Name of this controller used during composition\n */\n override name = 'PhishingController';\n\n /**\n * Creates a PhishingController instance.\n *\n * @param config - Initial options used to configure this controller.\n * @param state - Initial state to set on this controller.\n */\n constructor(\n config?: Partial<PhishingConfig>,\n state?: Partial<PhishingState>,\n ) {\n super(config, state);\n this.defaultConfig = {\n stalelistRefreshInterval: STALELIST_REFRESH_INTERVAL,\n hotlistRefreshInterval: HOTLIST_REFRESH_INTERVAL,\n };\n\n this.defaultState = {\n phishingLists: [],\n whitelist: [],\n hotlistLastFetched: 0,\n stalelistLastFetched: 0,\n };\n\n this.initialize();\n this.updatePhishingDetector();\n }\n\n /**\n * Updates this.detector with an instance of PhishingDetector using the current state.\n */\n updatePhishingDetector() {\n this.detector = new PhishingDetector(this.state.phishingLists);\n }\n\n /**\n * Set the interval at which the stale phishing list will be refetched.\n * Fetching will only occur on the next call to test/bypass.\n * For immediate update to the phishing list, call {@link updateStalelist} directly.\n *\n * @param interval - the new interval, in ms.\n */\n setStalelistRefreshInterval(interval: number) {\n this.configure({ stalelistRefreshInterval: interval }, false, false);\n }\n\n /**\n * Set the interval at which the hot list will be refetched.\n * Fetching will only occur on the next call to test/bypass.\n * For immediate update to the phishing list, call {@link updateHotlist} directly.\n *\n * @param interval - the new interval, in ms.\n */\n setHotlistRefreshInterval(interval: number) {\n this.configure({ hotlistRefreshInterval: interval }, false, false);\n }\n\n /**\n * Determine if an update to the stalelist configuration is needed.\n *\n * @returns Whether an update is needed\n */\n isStalelistOutOfDate() {\n return (\n fetchTimeNow() - this.state.stalelistLastFetched >=\n this.config.stalelistRefreshInterval\n );\n }\n\n /**\n * Determine if an update to the hotlist configuration is needed.\n *\n * @returns Whether an update is needed\n */\n isHotlistOutOfDate() {\n return (\n fetchTimeNow() - this.state.hotlistLastFetched >=\n this.config.hotlistRefreshInterval\n );\n }\n\n /**\n * Conditionally update the phishing configuration.\n *\n * If the stalelist configuration is out of date, this function will call `updateStalelist`\n * to update the configuration. This will automatically grab the hotlist,\n * so it isn't necessary to continue on to download the hotlist.\n *\n */\n async maybeUpdateState() {\n const staleListOutOfDate = this.isStalelistOutOfDate();\n if (staleListOutOfDate) {\n await this.updateStalelist();\n return;\n }\n const hotlistOutOfDate = this.isHotlistOutOfDate();\n if (hotlistOutOfDate) {\n await this.updateHotlist();\n }\n }\n\n /**\n * Determines if a given origin is unapproved.\n *\n * It is strongly recommended that you call {@link maybeUpdateState} before calling this,\n * to check whether the phishing configuration is up-to-date. It will be updated if necessary\n * by calling {@link updateStalelist} or {@link updateHotlist}.\n *\n * @param origin - Domain origin of a website.\n * @returns Whether the origin is an unapproved origin.\n */\n test(origin: string): EthPhishingDetectResult {\n const punycodeOrigin = toASCII(origin);\n if (this.state.whitelist.includes(punycodeOrigin)) {\n return { result: false, type: 'all' }; // Same as whitelisted match returned by detector.check(...).\n }\n return this.detector.check(punycodeOrigin);\n }\n\n /**\n * Temporarily marks a given origin as approved.\n *\n * @param origin - The origin to mark as approved.\n */\n bypass(origin: string) {\n const punycodeOrigin = toASCII(origin);\n const { whitelist } = this.state;\n if (whitelist.includes(punycodeOrigin)) {\n return;\n }\n this.update({ whitelist: [...whitelist, punycodeOrigin] });\n }\n\n /**\n * Update the hotlist.\n *\n * If an update is in progress, no additional update will be made. Instead this will wait until\n * the in-progress update has finished.\n */\n async updateHotlist() {\n if (this.#inProgressHotlistUpdate) {\n await this.#inProgressHotlistUpdate;\n return;\n }\n\n try {\n this.#inProgressHotlistUpdate = this.#updateHotlist();\n await this.#inProgressHotlistUpdate;\n } finally {\n this.#inProgressHotlistUpdate = undefined;\n }\n }\n\n /**\n * Update the stalelist.\n *\n * If an update is in progress, no additional update will be made. Instead this will wait until\n * the in-progress update has finished.\n */\n async updateStalelist() {\n if (this.#inProgressStalelistUpdate) {\n await this.#inProgressStalelistUpdate;\n return;\n }\n\n try {\n this.#inProgressStalelistUpdate = this.#updateStalelist();\n await this.#inProgressStalelistUpdate;\n } finally {\n this.#inProgressStalelistUpdate = undefined;\n }\n }\n\n /**\n * Update the stalelist configuration.\n *\n * This should only be called from the `updateStalelist` function, which is a wrapper around\n * this function that prevents redundant configuration updates.\n */\n async #updateStalelist() {\n if (this.disabled) {\n return;\n }\n\n let stalelistResponse;\n let hotlistDiffsResponse;\n try {\n stalelistResponse = await this.queryConfig<\n DataResultWrapper<PhishingStalelist>\n >(METAMASK_STALELIST_URL).then((d) => d);\n\n // Fetching hotlist diffs relies on having a lastUpdated timestamp to do `GET /v1/diffsSince/:timestamp`,\n // so it doesn't make sense to call if there is not a timestamp to begin with.\n if (stalelistResponse?.data && stalelistResponse.data.lastUpdated > 0) {\n hotlistDiffsResponse = await this.queryConfig<\n DataResultWrapper<Hotlist>\n >(`${METAMASK_HOTLIST_DIFF_URL}/${stalelistResponse.data.lastUpdated}`);\n }\n } finally {\n // Set `stalelistLastFetched` and `hotlistLastFetched` even for failed requests to prevent server\n // from being overwhelmed with traffic after a network disruption.\n const timeNow = fetchTimeNow();\n this.update({\n stalelistLastFetched: timeNow,\n hotlistLastFetched: timeNow,\n });\n }\n\n if (!stalelistResponse || !hotlistDiffsResponse) {\n return;\n }\n\n const { phishfort_hotlist, eth_phishing_detect_config, ...partialState } =\n stalelistResponse.data;\n\n const phishfortListState: PhishingListState = {\n ...phishfort_hotlist,\n ...partialState,\n fuzzylist: [], // Phishfort hotlist doesn't contain a fuzzylist\n allowlist: [], // Phishfort hotlist doesn't contain an allowlist\n name: phishingListKeyNameMap.phishfort_hotlist,\n };\n const metamaskListState: PhishingListState = {\n ...eth_phishing_detect_config,\n ...partialState,\n name: phishingListKeyNameMap.eth_phishing_detect_config,\n };\n // Correctly shaping eth-phishing-detect state by applying hotlist diffs to the stalelist.\n const newPhishfortListState: PhishingListState = applyDiffs(\n phishfortListState,\n hotlistDiffsResponse.data,\n ListKeys.PhishfortHotlist,\n );\n const newMetaMaskListState: PhishingListState = applyDiffs(\n metamaskListState,\n hotlistDiffsResponse.data,\n ListKeys.EthPhishingDetectConfig,\n );\n\n this.update({\n phishingLists: [newMetaMaskListState, newPhishfortListState],\n });\n this.updatePhishingDetector();\n }\n\n /**\n * Update the stalelist configuration.\n *\n * This should only be called from the `updateStalelist` function, which is a wrapper around\n * this function that prevents redundant configuration updates.\n */\n async #updateHotlist() {\n if (this.disabled) {\n return;\n }\n const lastDiffTimestamp = Math.max(\n ...this.state.phishingLists.map(({ lastUpdated }) => lastUpdated),\n );\n let hotlistResponse: DataResultWrapper<Hotlist> | null;\n\n try {\n hotlistResponse = await this.queryConfig<DataResultWrapper<Hotlist>>(\n `${METAMASK_HOTLIST_DIFF_URL}/${lastDiffTimestamp}`,\n );\n } finally {\n // Set `hotlistLastFetched` even for failed requests to prevent server from being overwhelmed with\n // traffic after a network disruption.\n this.update({\n hotlistLastFetched: fetchTimeNow(),\n });\n }\n\n if (!hotlistResponse?.data) {\n return;\n }\n const hotlist = hotlistResponse.data;\n const newPhishingLists = this.state.phishingLists.map((phishingList) =>\n applyDiffs(\n phishingList,\n hotlist,\n phishingListNameKeyMap[phishingList.name],\n ),\n );\n\n this.update({\n phishingLists: newPhishingLists,\n });\n this.updatePhishingDetector();\n }\n\n private async queryConfig<ResponseType>(\n input: RequestInfo,\n ): Promise<ResponseType | null> {\n const response = await safelyExecute(\n () => fetch(input, { cache: 'no-cache' }),\n true,\n );\n\n switch (response?.status) {\n case 200: {\n return await response.json();\n }\n\n default: {\n return null;\n }\n }\n }\n}\n\nexport default PhishingController;\n"]}
1
+ {"version":3,"file":"PhishingController.js","sourceRoot":"","sources":["../src/PhishingController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseControllerV2 as BaseController } from '@metamask/base-controller';\nimport { safelyExecute } from '@metamask/controller-utils';\nimport PhishingDetector from 'eth-phishing-detect/src/detector';\nimport { toASCII } from 'punycode/';\n\nimport { applyDiffs, fetchTimeNow } from './utils';\n\nexport const PHISHING_CONFIG_BASE_URL =\n 'https://phishing-detection.metafi.codefi.network';\n\nexport const METAMASK_STALELIST_FILE = '/v1/stalelist';\n\nexport const METAMASK_HOTLIST_DIFF_FILE = '/v1/diffsSince';\n\nexport const HOTLIST_REFRESH_INTERVAL = 30 * 60; // 30 mins in seconds\nexport const STALELIST_REFRESH_INTERVAL = 4 * 24 * 60 * 60; // 4 days in seconds\n\nexport const METAMASK_STALELIST_URL = `${PHISHING_CONFIG_BASE_URL}${METAMASK_STALELIST_FILE}`;\nexport const METAMASK_HOTLIST_DIFF_URL = `${PHISHING_CONFIG_BASE_URL}${METAMASK_HOTLIST_DIFF_FILE}`;\n\n/**\n * @type ListTypes\n *\n * Type outlining the types of lists provided by aggregating different source lists\n */\nexport type ListTypes = 'fuzzylist' | 'blocklist' | 'allowlist';\n\n/**\n * @type EthPhishingResponse\n *\n * Configuration response from the eth-phishing-detect package\n * consisting of approved and unapproved website origins\n * @property blacklist - List of unapproved origins\n * @property fuzzylist - List of fuzzy-matched unapproved origins\n * @property tolerance - Fuzzy match tolerance level\n * @property version - Version number of this configuration\n * @property whitelist - List of approved origins\n */\nexport type EthPhishingResponse = {\n blacklist: string[];\n fuzzylist: string[];\n tolerance: number;\n version: number;\n whitelist: string[];\n};\n\n/**\n * @type PhishingStalelist\n *\n * type defining expected type of the stalelist.json file.\n * @property eth_phishing_detect_config - Stale list sourced from eth-phishing-detect's config.json.\n * @property phishfort_hotlist - Stale list sourced from phishfort's hotlist.json. Only includes blocklist. Deduplicated entries from eth_phishing_detect_config.\n * @property tolerance - Fuzzy match tolerance level\n * @property lastUpdated - Timestamp of last update.\n * @property version - Stalelist data structure iteration.\n */\nexport type PhishingStalelist = {\n eth_phishing_detect_config: Record<ListTypes, string[]>;\n phishfort_hotlist: Record<ListTypes, string[]>;\n tolerance: number;\n version: number;\n lastUpdated: number;\n};\n\n/**\n * @type PhishingListState\n *\n * type defining the persisted list state. This is the persisted state that is updated frequently with `this.maybeUpdateState()`.\n * @property allowlist - List of approved origins (legacy naming \"whitelist\")\n * @property blocklist - List of unapproved origins (legacy naming \"blacklist\")\n * @property fuzzylist - List of fuzzy-matched unapproved origins\n * @property tolerance - Fuzzy match tolerance level\n * @property lastUpdated - Timestamp of last update.\n * @property version - Version of the phishing list state.\n * @property name - Name of the list. Used for attribution.\n */\nexport type PhishingListState = {\n allowlist: string[];\n blocklist: string[];\n fuzzylist: string[];\n tolerance: number;\n version: number;\n lastUpdated: number;\n name: ListNames;\n};\n\n/**\n * @type EthPhishingDetectResult\n *\n * type that describes the result of the `test` method.\n * @property name - Name of the config on which a match was found.\n * @property version - Version of the config on which a match was found.\n * @property result - Whether a domain was detected as a phishing domain. True means an unsafe domain.\n * @property match - The matching fuzzylist origin when a fuzzylist match is found. Returned as undefined for non-fuzzy true results.\n * @property type - The field of the config on which a match was found.\n */\nexport type EthPhishingDetectResult = {\n name?: string;\n version?: string;\n result: boolean;\n match?: string; // Returned as undefined for non-fuzzy true results.\n type: 'all' | 'fuzzy' | 'blocklist' | 'allowlist';\n};\n\n/**\n * @type HotlistDiff\n *\n * type defining the expected type of the diffs in hotlist.json file.\n * @property url - Url of the diff entry.\n * @property timestamp - Timestamp at which the diff was identified.\n * @property targetList - The list name where the diff was identified.\n * @property isRemoval - Was the diff identified a removal type.\n */\nexport type HotlistDiff = {\n url: string;\n timestamp: number;\n targetList: `${ListKeys}.${ListTypes}`;\n isRemoval?: boolean;\n};\n\nexport type DataResultWrapper<T> = {\n data: T;\n};\n\n/**\n * @type Hotlist\n *\n * Type defining expected hotlist.json file.\n * @property url - Url of the diff entry.\n * @property timestamp - Timestamp at which the diff was identified.\n * @property targetList - The list name where the diff was identified.\n * @property isRemoval - Was the diff identified a removal type.\n */\nexport type Hotlist = HotlistDiff[];\n\n/**\n * Enum containing upstream data provider source list keys.\n * These are the keys denoting lists consumed by the upstream data provider.\n */\nexport enum ListKeys {\n PhishfortHotlist = 'phishfort_hotlist',\n EthPhishingDetectConfig = 'eth_phishing_detect_config',\n}\n\n/**\n * Enum containing downstream client attribution names.\n */\nexport enum ListNames {\n MetaMask = 'MetaMask',\n Phishfort = 'Phishfort',\n}\n\n/**\n * Maps from downstream client attribution name\n * to list key sourced from upstream data provider.\n */\nconst phishingListNameKeyMap = {\n [ListNames.Phishfort]: ListKeys.PhishfortHotlist,\n [ListNames.MetaMask]: ListKeys.EthPhishingDetectConfig,\n};\n\n/**\n * Maps from list key sourced from upstream data\n * provider to downstream client attribution name.\n */\nexport const phishingListKeyNameMap = {\n [ListKeys.EthPhishingDetectConfig]: ListNames.MetaMask,\n [ListKeys.PhishfortHotlist]: ListNames.Phishfort,\n};\n\nconst controllerName = 'PhishingController';\n\nconst metadata = {\n phishingLists: { persist: true, anonymous: false },\n whitelist: { persist: true, anonymous: false },\n hotlistLastFetched: { persist: true, anonymous: false },\n stalelistLastFetched: { persist: true, anonymous: false },\n};\n\n/**\n * Get a default empty state for the controller.\n * @returns The default empty state.\n */\nconst getDefaultState = (): PhishingControllerState => {\n return {\n phishingLists: [],\n whitelist: [],\n hotlistLastFetched: 0,\n stalelistLastFetched: 0,\n };\n};\n\n/**\n * @type PhishingControllerState\n *\n * Phishing controller state\n * @property phishing - eth-phishing-detect configuration\n * @property whitelist - array of temporarily-approved origins\n */\nexport type PhishingControllerState = {\n phishingLists: PhishingListState[];\n whitelist: string[];\n hotlistLastFetched: number;\n stalelistLastFetched: number;\n};\n\n/**\n * @type PhishingControllerOptions\n *\n * Phishing controller options\n * @property stalelistRefreshInterval - Polling interval used to fetch stale list.\n * @property hotlistRefreshInterval - Polling interval used to fetch hotlist diff list.\n */\nexport type PhishingControllerOptions = {\n stalelistRefreshInterval?: number;\n hotlistRefreshInterval?: number;\n messenger: PhishingControllerMessenger;\n state?: Partial<PhishingControllerState>;\n};\n\nexport type MaybeUpdateState = {\n type: `${typeof controllerName}:maybeUpdateState`;\n handler: PhishingController['maybeUpdateState'];\n};\n\nexport type TestOrigin = {\n type: `${typeof controllerName}:testOrigin`;\n handler: PhishingController['test'];\n};\n\nexport type PhishingControllerActions = MaybeUpdateState | TestOrigin;\n\nexport type PhishingControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n PhishingControllerActions,\n never,\n never,\n never\n>;\n\n/**\n * Controller that manages community-maintained lists of approved and unapproved website origins.\n */\nexport class PhishingController extends BaseController<\n typeof controllerName,\n PhishingControllerState,\n PhishingControllerMessenger\n> {\n #detector: any;\n\n #stalelistRefreshInterval: number;\n\n #hotlistRefreshInterval: number;\n\n #inProgressHotlistUpdate?: Promise<void>;\n\n #inProgressStalelistUpdate?: Promise<void>;\n\n /**\n * Construct a Phishing Controller.\n *\n * @param config - Initial options used to configure this controller.\n * @param config.stalelistRefreshInterval - Polling interval used to fetch stale list.\n * @param config.hotlistRefreshInterval - Polling interval used to fetch hotlist diff list.\n * @param config.messenger - The controller restricted messenger.\n * @param config.state - Initial state to set on this controller.\n */\n constructor({\n stalelistRefreshInterval = STALELIST_REFRESH_INTERVAL,\n hotlistRefreshInterval = HOTLIST_REFRESH_INTERVAL,\n messenger,\n state = {},\n }: PhishingControllerOptions) {\n super({\n name: controllerName,\n metadata,\n messenger,\n state: {\n ...getDefaultState(),\n ...state,\n },\n });\n\n this.#stalelistRefreshInterval = stalelistRefreshInterval;\n this.#hotlistRefreshInterval = hotlistRefreshInterval;\n this.#registerMessageHandlers();\n\n this.updatePhishingDetector();\n }\n\n /**\n * Constructor helper for registering this controller's messaging system\n * actions.\n */\n #registerMessageHandlers(): void {\n this.messagingSystem.registerActionHandler(\n `${controllerName}:maybeUpdateState` as const,\n this.maybeUpdateState.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:testOrigin` as const,\n this.test.bind(this),\n );\n }\n\n /**\n * Updates this.detector with an instance of PhishingDetector using the current state.\n */\n updatePhishingDetector() {\n this.#detector = new PhishingDetector(this.state.phishingLists);\n }\n\n /**\n * Set the interval at which the stale phishing list will be refetched.\n * Fetching will only occur on the next call to test/bypass.\n * For immediate update to the phishing list, call {@link updateStalelist} directly.\n *\n * @param interval - the new interval, in ms.\n */\n setStalelistRefreshInterval(interval: number) {\n this.#stalelistRefreshInterval = interval;\n }\n\n /**\n * Set the interval at which the hot list will be refetched.\n * Fetching will only occur on the next call to test/bypass.\n * For immediate update to the phishing list, call {@link updateHotlist} directly.\n *\n * @param interval - the new interval, in ms.\n */\n setHotlistRefreshInterval(interval: number) {\n this.#hotlistRefreshInterval = interval;\n }\n\n /**\n * Determine if an update to the stalelist configuration is needed.\n *\n * @returns Whether an update is needed\n */\n isStalelistOutOfDate() {\n return (\n fetchTimeNow() - this.state.stalelistLastFetched >=\n this.#stalelistRefreshInterval\n );\n }\n\n /**\n * Determine if an update to the hotlist configuration is needed.\n *\n * @returns Whether an update is needed\n */\n isHotlistOutOfDate() {\n return (\n fetchTimeNow() - this.state.hotlistLastFetched >=\n this.#hotlistRefreshInterval\n );\n }\n\n /**\n * Conditionally update the phishing configuration.\n *\n * If the stalelist configuration is out of date, this function will call `updateStalelist`\n * to update the configuration. This will automatically grab the hotlist,\n * so it isn't necessary to continue on to download the hotlist.\n *\n */\n async maybeUpdateState() {\n const staleListOutOfDate = this.isStalelistOutOfDate();\n if (staleListOutOfDate) {\n await this.updateStalelist();\n return;\n }\n const hotlistOutOfDate = this.isHotlistOutOfDate();\n if (hotlistOutOfDate) {\n await this.updateHotlist();\n }\n }\n\n /**\n * Determines if a given origin is unapproved.\n *\n * It is strongly recommended that you call {@link maybeUpdateState} before calling this,\n * to check whether the phishing configuration is up-to-date. It will be updated if necessary\n * by calling {@link updateStalelist} or {@link updateHotlist}.\n *\n * @param origin - Domain origin of a website.\n * @returns Whether the origin is an unapproved origin.\n */\n test(origin: string): EthPhishingDetectResult {\n const punycodeOrigin = toASCII(origin);\n if (this.state.whitelist.includes(punycodeOrigin)) {\n return { result: false, type: 'all' }; // Same as whitelisted match returned by detector.check(...).\n }\n return this.#detector.check(punycodeOrigin);\n }\n\n /**\n * Temporarily marks a given origin as approved.\n *\n * @param origin - The origin to mark as approved.\n */\n bypass(origin: string) {\n const punycodeOrigin = toASCII(origin);\n const { whitelist } = this.state;\n if (whitelist.includes(punycodeOrigin)) {\n return;\n }\n this.update((draftState) => {\n draftState.whitelist.push(punycodeOrigin);\n });\n }\n\n /**\n * Update the hotlist.\n *\n * If an update is in progress, no additional update will be made. Instead this will wait until\n * the in-progress update has finished.\n */\n async updateHotlist() {\n if (this.#inProgressHotlistUpdate) {\n await this.#inProgressHotlistUpdate;\n return;\n }\n\n try {\n this.#inProgressHotlistUpdate = this.#updateHotlist();\n await this.#inProgressHotlistUpdate;\n } finally {\n this.#inProgressHotlistUpdate = undefined;\n }\n }\n\n /**\n * Update the stalelist.\n *\n * If an update is in progress, no additional update will be made. Instead this will wait until\n * the in-progress update has finished.\n */\n async updateStalelist() {\n if (this.#inProgressStalelistUpdate) {\n await this.#inProgressStalelistUpdate;\n return;\n }\n\n try {\n this.#inProgressStalelistUpdate = this.#updateStalelist();\n await this.#inProgressStalelistUpdate;\n } finally {\n this.#inProgressStalelistUpdate = undefined;\n }\n }\n\n /**\n * Update the stalelist configuration.\n *\n * This should only be called from the `updateStalelist` function, which is a wrapper around\n * this function that prevents redundant configuration updates.\n */\n async #updateStalelist() {\n let stalelistResponse;\n let hotlistDiffsResponse;\n try {\n stalelistResponse = await this.#queryConfig<\n DataResultWrapper<PhishingStalelist>\n >(METAMASK_STALELIST_URL).then((d) => d);\n\n // Fetching hotlist diffs relies on having a lastUpdated timestamp to do `GET /v1/diffsSince/:timestamp`,\n // so it doesn't make sense to call if there is not a timestamp to begin with.\n if (stalelistResponse?.data && stalelistResponse.data.lastUpdated > 0) {\n hotlistDiffsResponse = await this.#queryConfig<\n DataResultWrapper<Hotlist>\n >(`${METAMASK_HOTLIST_DIFF_URL}/${stalelistResponse.data.lastUpdated}`);\n }\n } finally {\n // Set `stalelistLastFetched` and `hotlistLastFetched` even for failed requests to prevent server\n // from being overwhelmed with traffic after a network disruption.\n const timeNow = fetchTimeNow();\n this.update((draftState) => {\n draftState.stalelistLastFetched = timeNow;\n draftState.hotlistLastFetched = timeNow;\n });\n }\n\n if (!stalelistResponse || !hotlistDiffsResponse) {\n return;\n }\n\n const { phishfort_hotlist, eth_phishing_detect_config, ...partialState } =\n stalelistResponse.data;\n\n const phishfortListState: PhishingListState = {\n ...phishfort_hotlist,\n ...partialState,\n fuzzylist: [], // Phishfort hotlist doesn't contain a fuzzylist\n allowlist: [], // Phishfort hotlist doesn't contain an allowlist\n name: phishingListKeyNameMap.phishfort_hotlist,\n };\n const metamaskListState: PhishingListState = {\n ...eth_phishing_detect_config,\n ...partialState,\n name: phishingListKeyNameMap.eth_phishing_detect_config,\n };\n // Correctly shaping eth-phishing-detect state by applying hotlist diffs to the stalelist.\n const newPhishfortListState: PhishingListState = applyDiffs(\n phishfortListState,\n hotlistDiffsResponse.data,\n ListKeys.PhishfortHotlist,\n );\n const newMetaMaskListState: PhishingListState = applyDiffs(\n metamaskListState,\n hotlistDiffsResponse.data,\n ListKeys.EthPhishingDetectConfig,\n );\n\n this.update((draftState) => {\n draftState.phishingLists = [newMetaMaskListState, newPhishfortListState];\n });\n this.updatePhishingDetector();\n }\n\n /**\n * Update the stalelist configuration.\n *\n * This should only be called from the `updateStalelist` function, which is a wrapper around\n * this function that prevents redundant configuration updates.\n */\n async #updateHotlist() {\n const lastDiffTimestamp = Math.max(\n ...this.state.phishingLists.map(({ lastUpdated }) => lastUpdated),\n );\n let hotlistResponse: DataResultWrapper<Hotlist> | null;\n\n try {\n hotlistResponse = await this.#queryConfig<DataResultWrapper<Hotlist>>(\n `${METAMASK_HOTLIST_DIFF_URL}/${lastDiffTimestamp}`,\n );\n } finally {\n // Set `hotlistLastFetched` even for failed requests to prevent server from being overwhelmed with\n // traffic after a network disruption.\n this.update((draftState) => {\n draftState.hotlistLastFetched = fetchTimeNow();\n });\n }\n\n if (!hotlistResponse?.data) {\n return;\n }\n const hotlist = hotlistResponse.data;\n const newPhishingLists = this.state.phishingLists.map((phishingList) =>\n applyDiffs(\n phishingList,\n hotlist,\n phishingListNameKeyMap[phishingList.name],\n ),\n );\n\n this.update((draftState) => {\n draftState.phishingLists = newPhishingLists;\n });\n this.updatePhishingDetector();\n }\n\n async #queryConfig<ResponseType>(\n input: RequestInfo,\n ): Promise<ResponseType | null> {\n const response = await safelyExecute(\n () => fetch(input, { cache: 'no-cache' }),\n true,\n );\n\n switch (response?.status) {\n case 200: {\n return await response.json();\n }\n\n default: {\n return null;\n }\n }\n }\n}\n\nexport default PhishingController;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/phishing-controller",
3
- "version": "6.0.1-preview.f71e42f",
3
+ "version": "7.0.0-preview.26b130a",
4
4
  "description": "Maintains a periodically updated list of approved and unapproved website origins",
5
5
  "keywords": [
6
6
  "MetaMask",
@@ -25,11 +25,12 @@
25
25
  "changelog:validate": "../../scripts/validate-changelog.sh @metamask/phishing-controller",
26
26
  "publish:preview": "yarn npm publish --tag preview",
27
27
  "test": "jest",
28
+ "test:clean": "jest --clearCache",
28
29
  "test:watch": "jest --watch"
29
30
  },
30
31
  "dependencies": {
31
- "@metamask/base-controller": "^3.2.1",
32
- "@metamask/controller-utils": "^4.3.2",
32
+ "@metamask/base-controller": "^3.2.3",
33
+ "@metamask/controller-utils": "^5.0.2",
33
34
  "@types/punycode": "^2.1.0",
34
35
  "eth-phishing-detect": "^1.2.0",
35
36
  "punycode": "^2.1.1"
@@ -42,9 +43,9 @@
42
43
  "nock": "^13.3.1",
43
44
  "sinon": "^9.2.4",
44
45
  "ts-jest": "^27.1.4",
45
- "typedoc": "^0.22.15",
46
- "typedoc-plugin-missing-exports": "^0.22.6",
47
- "typescript": "~4.6.3"
46
+ "typedoc": "^0.24.8",
47
+ "typedoc-plugin-missing-exports": "^2.0.0",
48
+ "typescript": "~4.8.4"
48
49
  },
49
50
  "engines": {
50
51
  "node": ">=16.0.0"