@metamask-previews/phishing-controller 12.0.2-preview-367cb1da → 12.0.2-preview-cf09c0a

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.
Files changed (81) hide show
  1. package/dist/PhishingController.cjs +453 -0
  2. package/dist/PhishingController.cjs.map +1 -0
  3. package/dist/{types/PhishingController.d.ts → PhishingController.d.cts} +4 -4
  4. package/dist/PhishingController.d.cts.map +1 -0
  5. package/dist/PhishingController.d.mts +304 -0
  6. package/dist/PhishingController.d.mts.map +1 -0
  7. package/dist/PhishingController.mjs +448 -37
  8. package/dist/PhishingController.mjs.map +1 -1
  9. package/dist/PhishingDetector.cjs +233 -0
  10. package/dist/PhishingDetector.cjs.map +1 -0
  11. package/dist/{types/PhishingDetector.d.ts → PhishingDetector.d.cts} +2 -2
  12. package/dist/PhishingDetector.d.cts.map +1 -0
  13. package/dist/PhishingDetector.d.mts +64 -0
  14. package/dist/PhishingDetector.d.mts.map +1 -0
  15. package/dist/PhishingDetector.mjs +227 -7
  16. package/dist/PhishingDetector.mjs.map +1 -1
  17. package/dist/index.cjs +23 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.cts +5 -0
  20. package/dist/index.d.cts.map +1 -0
  21. package/dist/index.d.mts +5 -0
  22. package/dist/index.d.mts.map +1 -0
  23. package/dist/index.mjs +3 -41
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/tests/utils.cjs +24 -0
  26. package/dist/tests/utils.cjs.map +1 -0
  27. package/dist/{types/tests/utils.d.ts → tests/utils.d.cts} +1 -1
  28. package/dist/tests/utils.d.cts.map +1 -0
  29. package/dist/tests/utils.d.mts +11 -0
  30. package/dist/tests/utils.d.mts.map +1 -0
  31. package/dist/tests/utils.mjs +18 -14
  32. package/dist/tests/utils.mjs.map +1 -1
  33. package/dist/types.cjs +40 -0
  34. package/dist/types.cjs.map +1 -0
  35. package/dist/{types/types.d.ts → types.d.cts} +1 -1
  36. package/dist/types.d.cts.map +1 -0
  37. package/dist/types.d.mts +50 -0
  38. package/dist/types.d.mts.map +1 -0
  39. package/dist/types.mjs +36 -7
  40. package/dist/types.mjs.map +1 -1
  41. package/dist/utils.cjs +242 -0
  42. package/dist/utils.cjs.map +1 -0
  43. package/dist/{types/utils.d.ts → utils.d.cts} +4 -4
  44. package/dist/utils.d.cts.map +1 -0
  45. package/dist/utils.d.mts +119 -0
  46. package/dist/utils.d.mts.map +1 -0
  47. package/dist/utils.mjs +224 -31
  48. package/dist/utils.mjs.map +1 -1
  49. package/package.json +12 -7
  50. package/dist/PhishingController.js +0 -39
  51. package/dist/PhishingController.js.map +0 -1
  52. package/dist/PhishingDetector.js +0 -9
  53. package/dist/PhishingDetector.js.map +0 -1
  54. package/dist/chunk-5NDIXQG5.js +0 -16
  55. package/dist/chunk-5NDIXQG5.js.map +0 -1
  56. package/dist/chunk-G2RL74NS.mjs +0 -16
  57. package/dist/chunk-G2RL74NS.mjs.map +0 -1
  58. package/dist/chunk-I3U4U3MW.mjs +0 -812
  59. package/dist/chunk-I3U4U3MW.mjs.map +0 -1
  60. package/dist/chunk-XUI43LEZ.mjs +0 -30
  61. package/dist/chunk-XUI43LEZ.mjs.map +0 -1
  62. package/dist/chunk-Z4BLTVTB.js +0 -30
  63. package/dist/chunk-Z4BLTVTB.js.map +0 -1
  64. package/dist/chunk-ZAOBCAQT.js +0 -812
  65. package/dist/chunk-ZAOBCAQT.js.map +0 -1
  66. package/dist/index.js +0 -42
  67. package/dist/index.js.map +0 -1
  68. package/dist/tests/utils.js +0 -16
  69. package/dist/tests/utils.js.map +0 -1
  70. package/dist/tsconfig.build.tsbuildinfo +0 -1
  71. package/dist/types/PhishingController.d.ts.map +0 -1
  72. package/dist/types/PhishingDetector.d.ts.map +0 -1
  73. package/dist/types/index.d.ts +0 -5
  74. package/dist/types/index.d.ts.map +0 -1
  75. package/dist/types/tests/utils.d.ts.map +0 -1
  76. package/dist/types/types.d.ts.map +0 -1
  77. package/dist/types/utils.d.ts.map +0 -1
  78. package/dist/types.js +0 -8
  79. package/dist/types.js.map +0 -1
  80. package/dist/utils.js +0 -33
  81. package/dist/utils.js.map +0 -1
@@ -1,812 +0,0 @@
1
- import {
2
- __privateAdd,
3
- __privateGet,
4
- __privateMethod,
5
- __privateSet
6
- } from "./chunk-XUI43LEZ.mjs";
7
-
8
- // src/utils.ts
9
- import { bytesToHex } from "@noble/hashes/utils";
10
- import { sha256 } from "ethereum-cryptography/sha256";
11
-
12
- // src/PhishingController.ts
13
- import { BaseController } from "@metamask/base-controller";
14
- import { safelyExecute } from "@metamask/controller-utils";
15
- import { toASCII } from "punycode/";
16
-
17
- // src/PhishingDetector.ts
18
- import { distance } from "fastest-levenshtein";
19
- var _configs, _legacyConfig, _check, check_fn;
20
- var PhishingDetector = class {
21
- /**
22
- * Construct a phishing detector, which can check whether origins are known
23
- * to be malicious or similar to common phishing targets.
24
- *
25
- * A list of configurations is accepted. Each origin checked is processed
26
- * using each configuration in sequence, so the order defines which
27
- * configurations take precedence.
28
- *
29
- * @param opts - Phishing detection options
30
- */
31
- constructor(opts) {
32
- __privateAdd(this, _check);
33
- __privateAdd(this, _configs, void 0);
34
- __privateAdd(this, _legacyConfig, void 0);
35
- if (Array.isArray(opts)) {
36
- __privateSet(this, _configs, processConfigs(opts));
37
- __privateSet(this, _legacyConfig, false);
38
- } else {
39
- __privateSet(this, _configs, [
40
- getDefaultPhishingDetectorConfig({
41
- allowlist: opts.whitelist,
42
- blocklist: opts.blacklist,
43
- c2DomainBlocklist: opts.c2DomainBlocklist,
44
- fuzzylist: opts.fuzzylist,
45
- tolerance: opts.tolerance
46
- })
47
- ]);
48
- __privateSet(this, _legacyConfig, true);
49
- }
50
- }
51
- /**
52
- * Check if a url is known to be malicious or similar to a common phishing
53
- * target. This will check the hostname and IPFS CID that is sometimes
54
- * located in the path.
55
- *
56
- * @param url - The url to check.
57
- * @returns The result of the check.
58
- */
59
- check(url) {
60
- const result = __privateMethod(this, _check, check_fn).call(this, url);
61
- if (__privateGet(this, _legacyConfig)) {
62
- let legacyType = result.type;
63
- if (legacyType === "allowlist" /* Allowlist */) {
64
- legacyType = "whitelist" /* Whitelist */;
65
- } else if (legacyType === "blocklist" /* Blocklist */) {
66
- legacyType = "blacklist" /* Blacklist */;
67
- }
68
- return {
69
- match: result.match,
70
- result: result.result,
71
- type: legacyType
72
- };
73
- }
74
- return result;
75
- }
76
- /**
77
- * Checks if a URL is blocked against the hashed request blocklist.
78
- * This is done by hashing the URL's hostname and checking it against the hashed request blocklist.
79
- *
80
- *
81
- * @param urlString - The URL to check.
82
- * @returns An object indicating if the URL is blocked and relevant metadata.
83
- */
84
- isMaliciousC2Domain(urlString) {
85
- let hostname;
86
- try {
87
- hostname = new URL(urlString).hostname;
88
- } catch (error) {
89
- return {
90
- result: false,
91
- type: "c2DomainBlocklist" /* C2DomainBlocklist */
92
- };
93
- }
94
- const fqdn = hostname.endsWith(".") ? hostname.slice(0, -1) : hostname;
95
- const source = domainToParts(fqdn);
96
- for (const { allowlist, name, version } of __privateGet(this, _configs)) {
97
- const allowlistMatch = matchPartsAgainstList(source, allowlist);
98
- if (allowlistMatch) {
99
- const match = domainPartsToDomain(allowlistMatch);
100
- return {
101
- match,
102
- name,
103
- result: false,
104
- type: "allowlist" /* Allowlist */,
105
- version: version === void 0 ? version : String(version)
106
- };
107
- }
108
- }
109
- for (const { c2DomainBlocklist, name, version } of __privateGet(this, _configs)) {
110
- const hash = sha256Hash(hostname.toLowerCase());
111
- const blocked = c2DomainBlocklist?.includes(hash) ?? false;
112
- if (blocked) {
113
- return {
114
- name,
115
- result: true,
116
- type: "c2DomainBlocklist" /* C2DomainBlocklist */,
117
- version: version === void 0 ? version : String(version)
118
- };
119
- }
120
- }
121
- return {
122
- result: false,
123
- type: "c2DomainBlocklist" /* C2DomainBlocklist */
124
- };
125
- }
126
- };
127
- _configs = new WeakMap();
128
- _legacyConfig = new WeakMap();
129
- _check = new WeakSet();
130
- check_fn = function(url) {
131
- const ipfsCidMatch = url.match(ipfsCidRegex());
132
- if (ipfsCidMatch !== null) {
133
- const cID = ipfsCidMatch[0];
134
- for (const { blocklist, name, version } of __privateGet(this, _configs)) {
135
- const blocklistMatch = blocklist.filter((entries) => entries.length === 1).find((entries) => {
136
- return entries[0] === cID;
137
- });
138
- if (blocklistMatch) {
139
- return {
140
- name,
141
- match: cID,
142
- result: true,
143
- type: "blocklist" /* Blocklist */,
144
- version: version === void 0 ? version : String(version)
145
- };
146
- }
147
- }
148
- }
149
- let domain;
150
- try {
151
- domain = new URL(url).hostname;
152
- } catch (error) {
153
- return {
154
- result: false,
155
- type: "all" /* All */
156
- };
157
- }
158
- const fqdn = domain.endsWith(".") ? domain.slice(0, -1) : domain;
159
- const source = domainToParts(fqdn);
160
- for (const { allowlist, name, version } of __privateGet(this, _configs)) {
161
- const allowlistMatch = matchPartsAgainstList(source, allowlist);
162
- if (allowlistMatch) {
163
- const match = domainPartsToDomain(allowlistMatch);
164
- return {
165
- match,
166
- name,
167
- result: false,
168
- type: "allowlist" /* Allowlist */,
169
- version: version === void 0 ? version : String(version)
170
- };
171
- }
172
- }
173
- for (const { blocklist, fuzzylist, name, tolerance, version } of __privateGet(this, _configs)) {
174
- const blocklistMatch = matchPartsAgainstList(source, blocklist);
175
- if (blocklistMatch) {
176
- const match = domainPartsToDomain(blocklistMatch);
177
- return {
178
- match,
179
- name,
180
- result: true,
181
- type: "blocklist" /* Blocklist */,
182
- version: version === void 0 ? version : String(version)
183
- };
184
- }
185
- if (tolerance > 0) {
186
- let fuzzyForm = domainPartsToFuzzyForm(source);
187
- fuzzyForm = fuzzyForm.replace(/^www\./u, "");
188
- const levenshteinMatched = fuzzylist.find((targetParts) => {
189
- const fuzzyTarget = domainPartsToFuzzyForm(targetParts);
190
- const dist = distance(fuzzyForm, fuzzyTarget);
191
- return dist <= tolerance;
192
- });
193
- if (levenshteinMatched) {
194
- const match = domainPartsToDomain(levenshteinMatched);
195
- return {
196
- name,
197
- match,
198
- result: true,
199
- type: "fuzzy" /* Fuzzy */,
200
- version: version === void 0 ? version : String(version)
201
- };
202
- }
203
- }
204
- }
205
- return { result: false, type: "all" /* All */ };
206
- };
207
- function ipfsCidRegex() {
208
- const reg = "Qm[1-9A-HJ-NP-Za-km-z]{44,}|b[A-Za-z2-7]{58,}|B[A-Z2-7]{58,}|z[1-9A-HJ-NP-Za-km-z]{48,}|F[0-9A-F]{50,}";
209
- return new RegExp(reg, "u");
210
- }
211
-
212
- // src/PhishingController.ts
213
- var PHISHING_CONFIG_BASE_URL = "https://phishing-detection.api.cx.metamask.io";
214
- var METAMASK_STALELIST_FILE = "/v1/stalelist";
215
- var METAMASK_HOTLIST_DIFF_FILE = "/v1/diffsSince";
216
- var CLIENT_SIDE_DETECION_BASE_URL = "https://client-side-detection.api.cx.metamask.io";
217
- var C2_DOMAIN_BLOCKLIST_ENDPOINT = "/v1/request-blocklist";
218
- var C2_DOMAIN_BLOCKLIST_REFRESH_INTERVAL = 15 * 60;
219
- var HOTLIST_REFRESH_INTERVAL = 5 * 60;
220
- var STALELIST_REFRESH_INTERVAL = 30 * 24 * 60 * 60;
221
- var METAMASK_STALELIST_URL = `${PHISHING_CONFIG_BASE_URL}${METAMASK_STALELIST_FILE}`;
222
- var METAMASK_HOTLIST_DIFF_URL = `${PHISHING_CONFIG_BASE_URL}${METAMASK_HOTLIST_DIFF_FILE}`;
223
- var C2_DOMAIN_BLOCKLIST_URL = `${CLIENT_SIDE_DETECION_BASE_URL}${C2_DOMAIN_BLOCKLIST_ENDPOINT}`;
224
- var ListKeys = /* @__PURE__ */ ((ListKeys2) => {
225
- ListKeys2["EthPhishingDetectConfig"] = "eth_phishing_detect_config";
226
- return ListKeys2;
227
- })(ListKeys || {});
228
- var ListNames = /* @__PURE__ */ ((ListNames2) => {
229
- ListNames2["MetaMask"] = "MetaMask";
230
- return ListNames2;
231
- })(ListNames || {});
232
- var phishingListNameKeyMap = {
233
- ["MetaMask" /* MetaMask */]: "eth_phishing_detect_config" /* EthPhishingDetectConfig */
234
- };
235
- var phishingListKeyNameMap = {
236
- ["eth_phishing_detect_config" /* EthPhishingDetectConfig */]: "MetaMask" /* MetaMask */
237
- };
238
- var controllerName = "PhishingController";
239
- var metadata = {
240
- phishingLists: { persist: true, anonymous: false },
241
- whitelist: { persist: true, anonymous: false },
242
- hotlistLastFetched: { persist: true, anonymous: false },
243
- stalelistLastFetched: { persist: true, anonymous: false },
244
- c2DomainBlocklistLastFetched: { persist: true, anonymous: false }
245
- };
246
- var getDefaultState = () => {
247
- return {
248
- phishingLists: [],
249
- whitelist: [],
250
- hotlistLastFetched: 0,
251
- stalelistLastFetched: 0,
252
- c2DomainBlocklistLastFetched: 0
253
- };
254
- };
255
- var _detector, _stalelistRefreshInterval, _hotlistRefreshInterval, _c2DomainBlocklistRefreshInterval, _inProgressHotlistUpdate, _inProgressStalelistUpdate, _isProgressC2DomainBlocklistUpdate, _registerMessageHandlers, registerMessageHandlers_fn, _updateStalelist, updateStalelist_fn, _updateHotlist, updateHotlist_fn, _updateC2DomainBlocklist, updateC2DomainBlocklist_fn, _queryConfig, queryConfig_fn;
256
- var PhishingController = class extends BaseController {
257
- /**
258
- * Construct a Phishing Controller.
259
- *
260
- * @param config - Initial options used to configure this controller.
261
- * @param config.stalelistRefreshInterval - Polling interval used to fetch stale list.
262
- * @param config.hotlistRefreshInterval - Polling interval used to fetch hotlist diff list.
263
- * @param config.c2DomainBlocklistRefreshInterval - Polling interval used to fetch c2 domain blocklist.
264
- * @param config.messenger - The controller restricted messenger.
265
- * @param config.state - Initial state to set on this controller.
266
- */
267
- constructor({
268
- stalelistRefreshInterval = STALELIST_REFRESH_INTERVAL,
269
- hotlistRefreshInterval = HOTLIST_REFRESH_INTERVAL,
270
- c2DomainBlocklistRefreshInterval = C2_DOMAIN_BLOCKLIST_REFRESH_INTERVAL,
271
- messenger,
272
- state = {}
273
- }) {
274
- super({
275
- name: controllerName,
276
- metadata,
277
- messenger,
278
- state: {
279
- ...getDefaultState(),
280
- ...state
281
- }
282
- });
283
- /**
284
- * Constructor helper for registering this controller's messaging system
285
- * actions.
286
- */
287
- __privateAdd(this, _registerMessageHandlers);
288
- /**
289
- * Update the stalelist configuration.
290
- *
291
- * This should only be called from the `updateStalelist` function, which is a wrapper around
292
- * this function that prevents redundant configuration updates.
293
- */
294
- __privateAdd(this, _updateStalelist);
295
- /**
296
- * Update the stalelist configuration.
297
- *
298
- * This should only be called from the `updateStalelist` function, which is a wrapper around
299
- * this function that prevents redundant configuration updates.
300
- */
301
- __privateAdd(this, _updateHotlist);
302
- /**
303
- * Update the C2 domain blocklist.
304
- *
305
- * This should only be called from the `updateC2DomainBlocklist` function, which is a wrapper around
306
- * this function that prevents redundant configuration updates.
307
- */
308
- __privateAdd(this, _updateC2DomainBlocklist);
309
- __privateAdd(this, _queryConfig);
310
- // TODO: Replace `any` with type
311
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
312
- __privateAdd(this, _detector, void 0);
313
- __privateAdd(this, _stalelistRefreshInterval, void 0);
314
- __privateAdd(this, _hotlistRefreshInterval, void 0);
315
- __privateAdd(this, _c2DomainBlocklistRefreshInterval, void 0);
316
- __privateAdd(this, _inProgressHotlistUpdate, void 0);
317
- __privateAdd(this, _inProgressStalelistUpdate, void 0);
318
- __privateAdd(this, _isProgressC2DomainBlocklistUpdate, void 0);
319
- __privateSet(this, _stalelistRefreshInterval, stalelistRefreshInterval);
320
- __privateSet(this, _hotlistRefreshInterval, hotlistRefreshInterval);
321
- __privateSet(this, _c2DomainBlocklistRefreshInterval, c2DomainBlocklistRefreshInterval);
322
- __privateMethod(this, _registerMessageHandlers, registerMessageHandlers_fn).call(this);
323
- this.updatePhishingDetector();
324
- }
325
- /**
326
- * Updates this.detector with an instance of PhishingDetector using the current state.
327
- */
328
- updatePhishingDetector() {
329
- __privateSet(this, _detector, new PhishingDetector(this.state.phishingLists));
330
- }
331
- /**
332
- * Set the interval at which the stale phishing list will be refetched.
333
- * Fetching will only occur on the next call to test/bypass.
334
- * For immediate update to the phishing list, call {@link updateStalelist} directly.
335
- *
336
- * @param interval - the new interval, in ms.
337
- */
338
- setStalelistRefreshInterval(interval) {
339
- __privateSet(this, _stalelistRefreshInterval, interval);
340
- }
341
- /**
342
- * Set the interval at which the hot list will be refetched.
343
- * Fetching will only occur on the next call to test/bypass.
344
- * For immediate update to the phishing list, call {@link updateHotlist} directly.
345
- *
346
- * @param interval - the new interval, in ms.
347
- */
348
- setHotlistRefreshInterval(interval) {
349
- __privateSet(this, _hotlistRefreshInterval, interval);
350
- }
351
- /**
352
- * Set the interval at which the C2 domain blocklist will be refetched.
353
- * Fetching will only occur on the next call to test/bypass.
354
- * For immediate update to the phishing list, call {@link updateHotlist} directly.
355
- *
356
- * @param interval - the new interval, in ms.
357
- */
358
- setC2DomainBlocklistRefreshInterval(interval) {
359
- __privateSet(this, _c2DomainBlocklistRefreshInterval, interval);
360
- }
361
- /**
362
- * Determine if an update to the stalelist configuration is needed.
363
- *
364
- * @returns Whether an update is needed
365
- */
366
- isStalelistOutOfDate() {
367
- return fetchTimeNow() - this.state.stalelistLastFetched >= __privateGet(this, _stalelistRefreshInterval);
368
- }
369
- /**
370
- * Determine if an update to the hotlist configuration is needed.
371
- *
372
- * @returns Whether an update is needed
373
- */
374
- isHotlistOutOfDate() {
375
- return fetchTimeNow() - this.state.hotlistLastFetched >= __privateGet(this, _hotlistRefreshInterval);
376
- }
377
- /**
378
- * Determine if an update to the C2 domain blocklist is needed.
379
- *
380
- * @returns Whether an update is needed
381
- */
382
- isC2DomainBlocklistOutOfDate() {
383
- return fetchTimeNow() - this.state.c2DomainBlocklistLastFetched >= __privateGet(this, _c2DomainBlocklistRefreshInterval);
384
- }
385
- /**
386
- * Conditionally update the phishing configuration.
387
- *
388
- * If the stalelist configuration is out of date, this function will call `updateStalelist`
389
- * to update the configuration. This will automatically grab the hotlist,
390
- * so it isn't necessary to continue on to download the hotlist and the c2 domain blocklist.
391
- *
392
- */
393
- async maybeUpdateState() {
394
- const staleListOutOfDate = this.isStalelistOutOfDate();
395
- if (staleListOutOfDate) {
396
- await this.updateStalelist();
397
- return;
398
- }
399
- const hotlistOutOfDate = this.isHotlistOutOfDate();
400
- if (hotlistOutOfDate) {
401
- await this.updateHotlist();
402
- }
403
- const c2DomainBlocklistOutOfDate = this.isC2DomainBlocklistOutOfDate();
404
- if (c2DomainBlocklistOutOfDate) {
405
- await this.updateC2DomainBlocklist();
406
- }
407
- }
408
- /**
409
- * Determines if a given origin is unapproved.
410
- *
411
- * It is strongly recommended that you call {@link maybeUpdateState} before calling this,
412
- * to check whether the phishing configuration is up-to-date. It will be updated if necessary
413
- * by calling {@link updateStalelist} or {@link updateHotlist}.
414
- *
415
- * @param origin - Domain origin of a website.
416
- * @returns Whether the origin is an unapproved origin.
417
- */
418
- test(origin) {
419
- const punycodeOrigin = toASCII(origin);
420
- const hostname = getHostnameFromUrl(punycodeOrigin);
421
- if (this.state.whitelist.includes(hostname || punycodeOrigin)) {
422
- return { result: false, type: "all" /* All */ };
423
- }
424
- return __privateGet(this, _detector).check(punycodeOrigin);
425
- }
426
- /**
427
- * Checks if a request URL's domain is blocked against the request blocklist.
428
- *
429
- * This method is used to determine if a specific request URL is associated with a malicious
430
- * command and control (C2) domain. The URL's hostname is hashed and checked against a configured
431
- * blocklist of known malicious domains.
432
- *
433
- * @param origin - The full request URL to be checked.
434
- * @returns An object indicating whether the URL's domain is blocked and relevant metadata.
435
- */
436
- isBlockedRequest(origin) {
437
- const punycodeOrigin = toASCII(origin);
438
- const hostname = getHostnameFromUrl(punycodeOrigin);
439
- if (this.state.whitelist.includes(hostname || punycodeOrigin)) {
440
- return { result: false, type: "all" /* All */ };
441
- }
442
- return __privateGet(this, _detector).isMaliciousC2Domain(punycodeOrigin);
443
- }
444
- /**
445
- * Temporarily marks a given origin as approved.
446
- *
447
- * @param origin - The origin to mark as approved.
448
- */
449
- bypass(origin) {
450
- const punycodeOrigin = toASCII(origin);
451
- const hostname = getHostnameFromUrl(punycodeOrigin);
452
- const { whitelist } = this.state;
453
- if (whitelist.includes(hostname || punycodeOrigin)) {
454
- return;
455
- }
456
- this.update((draftState) => {
457
- draftState.whitelist.push(hostname || punycodeOrigin);
458
- });
459
- }
460
- /**
461
- * Update the C2 domain blocklist.
462
- *
463
- * If an update is in progress, no additional update will be made. Instead this will wait until
464
- * the in-progress update has finished.
465
- */
466
- async updateC2DomainBlocklist() {
467
- if (__privateGet(this, _isProgressC2DomainBlocklistUpdate)) {
468
- await __privateGet(this, _isProgressC2DomainBlocklistUpdate);
469
- return;
470
- }
471
- try {
472
- __privateSet(this, _isProgressC2DomainBlocklistUpdate, __privateMethod(this, _updateC2DomainBlocklist, updateC2DomainBlocklist_fn).call(this));
473
- await __privateGet(this, _isProgressC2DomainBlocklistUpdate);
474
- } finally {
475
- __privateSet(this, _isProgressC2DomainBlocklistUpdate, void 0);
476
- }
477
- }
478
- /**
479
- * Update the hotlist.
480
- *
481
- * If an update is in progress, no additional update will be made. Instead this will wait until
482
- * the in-progress update has finished.
483
- */
484
- async updateHotlist() {
485
- if (__privateGet(this, _inProgressHotlistUpdate)) {
486
- await __privateGet(this, _inProgressHotlistUpdate);
487
- return;
488
- }
489
- try {
490
- __privateSet(this, _inProgressHotlistUpdate, __privateMethod(this, _updateHotlist, updateHotlist_fn).call(this));
491
- await __privateGet(this, _inProgressHotlistUpdate);
492
- } finally {
493
- __privateSet(this, _inProgressHotlistUpdate, void 0);
494
- }
495
- }
496
- /**
497
- * Update the stalelist.
498
- *
499
- * If an update is in progress, no additional update will be made. Instead this will wait until
500
- * the in-progress update has finished.
501
- */
502
- async updateStalelist() {
503
- if (__privateGet(this, _inProgressStalelistUpdate)) {
504
- await __privateGet(this, _inProgressStalelistUpdate);
505
- return;
506
- }
507
- try {
508
- __privateSet(this, _inProgressStalelistUpdate, __privateMethod(this, _updateStalelist, updateStalelist_fn).call(this));
509
- await __privateGet(this, _inProgressStalelistUpdate);
510
- } finally {
511
- __privateSet(this, _inProgressStalelistUpdate, void 0);
512
- }
513
- }
514
- };
515
- _detector = new WeakMap();
516
- _stalelistRefreshInterval = new WeakMap();
517
- _hotlistRefreshInterval = new WeakMap();
518
- _c2DomainBlocklistRefreshInterval = new WeakMap();
519
- _inProgressHotlistUpdate = new WeakMap();
520
- _inProgressStalelistUpdate = new WeakMap();
521
- _isProgressC2DomainBlocklistUpdate = new WeakMap();
522
- _registerMessageHandlers = new WeakSet();
523
- registerMessageHandlers_fn = function() {
524
- this.messagingSystem.registerActionHandler(
525
- `${controllerName}:maybeUpdateState`,
526
- this.maybeUpdateState.bind(this)
527
- );
528
- this.messagingSystem.registerActionHandler(
529
- `${controllerName}:testOrigin`,
530
- this.test.bind(this)
531
- );
532
- };
533
- _updateStalelist = new WeakSet();
534
- updateStalelist_fn = async function() {
535
- let stalelistResponse = null;
536
- let hotlistDiffsResponse = null;
537
- let c2DomainBlocklistResponse = null;
538
- try {
539
- const stalelistPromise = __privateMethod(this, _queryConfig, queryConfig_fn).call(this, METAMASK_STALELIST_URL);
540
- const c2DomainBlocklistPromise = __privateMethod(this, _queryConfig, queryConfig_fn).call(this, C2_DOMAIN_BLOCKLIST_URL);
541
- [stalelistResponse, c2DomainBlocklistResponse] = await Promise.all([
542
- stalelistPromise,
543
- c2DomainBlocklistPromise
544
- ]);
545
- if (stalelistResponse?.data && stalelistResponse.data.lastUpdated > 0) {
546
- hotlistDiffsResponse = await __privateMethod(this, _queryConfig, queryConfig_fn).call(this, `${METAMASK_HOTLIST_DIFF_URL}/${stalelistResponse.data.lastUpdated}`);
547
- }
548
- } finally {
549
- const timeNow = fetchTimeNow();
550
- this.update((draftState) => {
551
- draftState.stalelistLastFetched = timeNow;
552
- draftState.hotlistLastFetched = timeNow;
553
- draftState.c2DomainBlocklistLastFetched = timeNow;
554
- });
555
- }
556
- if (!stalelistResponse || !hotlistDiffsResponse) {
557
- return;
558
- }
559
- const { eth_phishing_detect_config, ...partialState } = stalelistResponse.data;
560
- const metamaskListState = {
561
- ...eth_phishing_detect_config,
562
- ...partialState,
563
- c2DomainBlocklist: c2DomainBlocklistResponse ? c2DomainBlocklistResponse.recentlyAdded : [],
564
- name: phishingListKeyNameMap.eth_phishing_detect_config
565
- };
566
- const newMetaMaskListState = applyDiffs(
567
- metamaskListState,
568
- hotlistDiffsResponse.data,
569
- "eth_phishing_detect_config" /* EthPhishingDetectConfig */
570
- );
571
- this.update((draftState) => {
572
- draftState.phishingLists = [newMetaMaskListState];
573
- });
574
- this.updatePhishingDetector();
575
- };
576
- _updateHotlist = new WeakSet();
577
- updateHotlist_fn = async function() {
578
- const lastDiffTimestamp = Math.max(
579
- ...this.state.phishingLists.map(({ lastUpdated }) => lastUpdated)
580
- );
581
- let hotlistResponse;
582
- try {
583
- hotlistResponse = await __privateMethod(this, _queryConfig, queryConfig_fn).call(this, `${METAMASK_HOTLIST_DIFF_URL}/${lastDiffTimestamp}`);
584
- } finally {
585
- this.update((draftState) => {
586
- draftState.hotlistLastFetched = fetchTimeNow();
587
- });
588
- }
589
- if (!hotlistResponse?.data) {
590
- return;
591
- }
592
- const hotlist = hotlistResponse.data;
593
- const newPhishingLists = this.state.phishingLists.map((phishingList) => {
594
- const updatedList = applyDiffs(
595
- phishingList,
596
- hotlist,
597
- phishingListNameKeyMap[phishingList.name],
598
- [],
599
- []
600
- );
601
- return updatedList;
602
- });
603
- this.update((draftState) => {
604
- draftState.phishingLists = newPhishingLists;
605
- });
606
- this.updatePhishingDetector();
607
- };
608
- _updateC2DomainBlocklist = new WeakSet();
609
- updateC2DomainBlocklist_fn = async function() {
610
- let c2DomainBlocklistResponse = null;
611
- try {
612
- c2DomainBlocklistResponse = await __privateMethod(this, _queryConfig, queryConfig_fn).call(this, `${C2_DOMAIN_BLOCKLIST_URL}?timestamp=${roundToNearestMinute(
613
- this.state.c2DomainBlocklistLastFetched
614
- )}`);
615
- } finally {
616
- this.update((draftState) => {
617
- draftState.c2DomainBlocklistLastFetched = fetchTimeNow();
618
- });
619
- }
620
- if (!c2DomainBlocklistResponse) {
621
- return;
622
- }
623
- const recentlyAddedC2Domains = c2DomainBlocklistResponse.recentlyAdded;
624
- const recentlyRemovedC2Domains = c2DomainBlocklistResponse.recentlyRemoved;
625
- const newPhishingLists = this.state.phishingLists.map((phishingList) => {
626
- const updatedList = applyDiffs(
627
- phishingList,
628
- [],
629
- phishingListNameKeyMap[phishingList.name],
630
- recentlyAddedC2Domains,
631
- recentlyRemovedC2Domains
632
- );
633
- return updatedList;
634
- });
635
- this.update((draftState) => {
636
- draftState.phishingLists = newPhishingLists;
637
- });
638
- this.updatePhishingDetector();
639
- };
640
- _queryConfig = new WeakSet();
641
- queryConfig_fn = async function(input) {
642
- const response = await safelyExecute(
643
- () => fetch(input, { cache: "no-cache" }),
644
- true
645
- );
646
- switch (response?.status) {
647
- case 200: {
648
- return await response.json();
649
- }
650
- default: {
651
- return null;
652
- }
653
- }
654
- };
655
- var PhishingController_default = PhishingController;
656
-
657
- // src/utils.ts
658
- var DEFAULT_TOLERANCE = 3;
659
- var fetchTimeNow = () => Math.round(Date.now() / 1e3);
660
- function roundToNearestMinute(unixTimestamp) {
661
- return Math.floor(unixTimestamp / 60) * 60;
662
- }
663
- var splitStringByPeriod = (stringToSplit) => {
664
- const periodIndex = stringToSplit.indexOf(".");
665
- return [
666
- stringToSplit.slice(0, periodIndex),
667
- stringToSplit.slice(periodIndex + 1)
668
- ];
669
- };
670
- var applyDiffs = (listState, hotlistDiffs, listKey, recentlyAddedC2Domains = [], recentlyRemovedC2Domains = []) => {
671
- const diffsToApply = hotlistDiffs.filter(
672
- ({ timestamp, targetList }) => timestamp > listState.lastUpdated && splitStringByPeriod(targetList)[0] === listKey
673
- );
674
- let latestDiffTimestamp = listState.lastUpdated;
675
- const listSets = {
676
- allowlist: new Set(listState.allowlist),
677
- blocklist: new Set(listState.blocklist),
678
- fuzzylist: new Set(listState.fuzzylist),
679
- c2DomainBlocklist: new Set(listState.c2DomainBlocklist)
680
- };
681
- for (const { isRemoval, targetList, url, timestamp } of diffsToApply) {
682
- const targetListType = splitStringByPeriod(targetList)[1];
683
- if (timestamp > latestDiffTimestamp) {
684
- latestDiffTimestamp = timestamp;
685
- }
686
- if (isRemoval) {
687
- listSets[targetListType].delete(url);
688
- } else {
689
- listSets[targetListType].add(url);
690
- }
691
- }
692
- if (listKey === "eth_phishing_detect_config" /* EthPhishingDetectConfig */) {
693
- for (const hash of recentlyAddedC2Domains) {
694
- listSets.c2DomainBlocklist.add(hash);
695
- }
696
- for (const hash of recentlyRemovedC2Domains) {
697
- listSets.c2DomainBlocklist.delete(hash);
698
- }
699
- }
700
- return {
701
- c2DomainBlocklist: Array.from(listSets.c2DomainBlocklist),
702
- allowlist: Array.from(listSets.allowlist),
703
- blocklist: Array.from(listSets.blocklist),
704
- fuzzylist: Array.from(listSets.fuzzylist),
705
- version: listState.version,
706
- name: phishingListKeyNameMap[listKey],
707
- tolerance: listState.tolerance,
708
- lastUpdated: latestDiffTimestamp
709
- };
710
- };
711
- function validateConfig(config) {
712
- if (config === null || typeof config !== "object") {
713
- throw new Error("Invalid config");
714
- }
715
- if ("tolerance" in config && !("fuzzylist" in config)) {
716
- throw new Error("Fuzzylist tolerance provided without fuzzylist");
717
- }
718
- if ("name" in config && (typeof config.name !== "string" || config.name === "")) {
719
- throw new Error("Invalid config parameter: 'name'");
720
- }
721
- if ("version" in config && (!["number", "string"].includes(typeof config.version) || config.version === "")) {
722
- throw new Error("Invalid config parameter: 'version'");
723
- }
724
- }
725
- var domainToParts = (domain) => {
726
- try {
727
- return domain.split(".").reverse();
728
- } catch (e) {
729
- throw new Error(JSON.stringify(domain));
730
- }
731
- };
732
- var processDomainList = (list) => {
733
- return list.map(domainToParts);
734
- };
735
- var getDefaultPhishingDetectorConfig = ({
736
- allowlist = [],
737
- blocklist = [],
738
- fuzzylist = [],
739
- tolerance = DEFAULT_TOLERANCE
740
- }) => ({
741
- allowlist: processDomainList(allowlist),
742
- blocklist: processDomainList(blocklist),
743
- fuzzylist: processDomainList(fuzzylist),
744
- tolerance
745
- });
746
- var processConfigs = (configs = []) => {
747
- return configs.map((config) => {
748
- validateConfig(config);
749
- return { ...config, ...getDefaultPhishingDetectorConfig(config) };
750
- });
751
- };
752
- var domainPartsToDomain = (domainParts) => {
753
- return domainParts.slice().reverse().join(".");
754
- };
755
- var domainPartsToFuzzyForm = (domainParts) => {
756
- return domainParts.slice(1).reverse().join(".");
757
- };
758
- var matchPartsAgainstList = (source, list) => {
759
- return list.find((target) => {
760
- if (target.length > source.length) {
761
- return false;
762
- }
763
- return target.every((part, index) => source[index] === part);
764
- });
765
- };
766
- var sha256Hash = (hostname) => {
767
- const hashBuffer = sha256(new TextEncoder().encode(hostname.toLowerCase()));
768
- return bytesToHex(hashBuffer);
769
- };
770
- var getHostnameFromUrl = (url) => {
771
- let hostname;
772
- try {
773
- hostname = new URL(url).hostname;
774
- } catch (error) {
775
- return null;
776
- }
777
- return hostname;
778
- };
779
-
780
- export {
781
- fetchTimeNow,
782
- roundToNearestMinute,
783
- applyDiffs,
784
- validateConfig,
785
- domainToParts,
786
- processDomainList,
787
- getDefaultPhishingDetectorConfig,
788
- processConfigs,
789
- domainPartsToDomain,
790
- domainPartsToFuzzyForm,
791
- matchPartsAgainstList,
792
- sha256Hash,
793
- getHostnameFromUrl,
794
- PhishingDetector,
795
- PHISHING_CONFIG_BASE_URL,
796
- METAMASK_STALELIST_FILE,
797
- METAMASK_HOTLIST_DIFF_FILE,
798
- CLIENT_SIDE_DETECION_BASE_URL,
799
- C2_DOMAIN_BLOCKLIST_ENDPOINT,
800
- C2_DOMAIN_BLOCKLIST_REFRESH_INTERVAL,
801
- HOTLIST_REFRESH_INTERVAL,
802
- STALELIST_REFRESH_INTERVAL,
803
- METAMASK_STALELIST_URL,
804
- METAMASK_HOTLIST_DIFF_URL,
805
- C2_DOMAIN_BLOCKLIST_URL,
806
- ListKeys,
807
- ListNames,
808
- phishingListKeyNameMap,
809
- PhishingController,
810
- PhishingController_default
811
- };
812
- //# sourceMappingURL=chunk-I3U4U3MW.mjs.map