@metamask-previews/phishing-controller 11.0.0-preview-b5937ee4 → 11.0.0-preview-3b4ca958
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/dist/PhishingController.js +11 -2
- package/dist/PhishingController.mjs +10 -1
- package/dist/PhishingDetector.js +3 -2
- package/dist/PhishingDetector.mjs +2 -1
- package/dist/chunk-5NDIXQG5.js +16 -0
- package/dist/chunk-5NDIXQG5.js.map +1 -0
- package/dist/{chunk-VF3KVWS3.mjs → chunk-A3KBBZ6O.mjs} +229 -42
- package/dist/chunk-A3KBBZ6O.mjs.map +1 -0
- package/dist/chunk-G2RL74NS.mjs +16 -0
- package/dist/chunk-G2RL74NS.mjs.map +1 -0
- package/dist/{chunk-FACB7CYR.js → chunk-QLJTY3E3.js} +230 -43
- package/dist/chunk-QLJTY3E3.js.map +1 -0
- package/dist/index.js +11 -2
- package/dist/index.mjs +10 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/PhishingController.d.ts +66 -23
- package/dist/types/PhishingController.d.ts.map +1 -1
- package/dist/types/PhishingDetector.d.ts +14 -37
- package/dist/types/PhishingDetector.d.ts.map +1 -1
- package/dist/types/types.d.ts +50 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/utils.d.ts +22 -2
- package/dist/types/utils.d.ts.map +1 -1
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/types.mjs +8 -0
- package/dist/types.mjs.map +1 -0
- package/dist/utils.js +7 -2
- package/dist/utils.mjs +6 -1
- package/package.json +6 -3
- package/dist/chunk-FACB7CYR.js.map +0 -1
- package/dist/chunk-VF3KVWS3.mjs.map +0 -1
|
@@ -11,7 +11,12 @@
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
var _chunkQLJTY3E3js = require('./chunk-QLJTY3E3.js');
|
|
19
|
+
require('./chunk-5NDIXQG5.js');
|
|
15
20
|
require('./chunk-Z4BLTVTB.js');
|
|
16
21
|
|
|
17
22
|
|
|
@@ -26,5 +31,9 @@ require('./chunk-Z4BLTVTB.js');
|
|
|
26
31
|
|
|
27
32
|
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
exports.C2_DOMAIN_BLOCKLIST_ENDPOINT = _chunkQLJTY3E3js.C2_DOMAIN_BLOCKLIST_ENDPOINT; exports.C2_DOMAIN_BLOCKLIST_REFRESH_INTERVAL = _chunkQLJTY3E3js.C2_DOMAIN_BLOCKLIST_REFRESH_INTERVAL; exports.C2_DOMAIN_BLOCKLIST_URL = _chunkQLJTY3E3js.C2_DOMAIN_BLOCKLIST_URL; exports.CLIENT_SIDE_DETECION_BASE_URL = _chunkQLJTY3E3js.CLIENT_SIDE_DETECION_BASE_URL; exports.HOTLIST_REFRESH_INTERVAL = _chunkQLJTY3E3js.HOTLIST_REFRESH_INTERVAL; exports.ListKeys = _chunkQLJTY3E3js.ListKeys; exports.ListNames = _chunkQLJTY3E3js.ListNames; exports.METAMASK_HOTLIST_DIFF_FILE = _chunkQLJTY3E3js.METAMASK_HOTLIST_DIFF_FILE; exports.METAMASK_HOTLIST_DIFF_URL = _chunkQLJTY3E3js.METAMASK_HOTLIST_DIFF_URL; exports.METAMASK_STALELIST_FILE = _chunkQLJTY3E3js.METAMASK_STALELIST_FILE; exports.METAMASK_STALELIST_URL = _chunkQLJTY3E3js.METAMASK_STALELIST_URL; exports.PHISHING_CONFIG_BASE_URL = _chunkQLJTY3E3js.PHISHING_CONFIG_BASE_URL; exports.PhishingController = _chunkQLJTY3E3js.PhishingController; exports.STALELIST_REFRESH_INTERVAL = _chunkQLJTY3E3js.STALELIST_REFRESH_INTERVAL; exports.default = _chunkQLJTY3E3js.PhishingController_default; exports.phishingListKeyNameMap = _chunkQLJTY3E3js.phishingListKeyNameMap;
|
|
30
39
|
//# sourceMappingURL=PhishingController.js.map
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
+
C2_DOMAIN_BLOCKLIST_ENDPOINT,
|
|
3
|
+
C2_DOMAIN_BLOCKLIST_REFRESH_INTERVAL,
|
|
4
|
+
C2_DOMAIN_BLOCKLIST_URL,
|
|
5
|
+
CLIENT_SIDE_DETECION_BASE_URL,
|
|
2
6
|
HOTLIST_REFRESH_INTERVAL,
|
|
3
7
|
ListKeys,
|
|
4
8
|
ListNames,
|
|
@@ -11,9 +15,14 @@ import {
|
|
|
11
15
|
PhishingController_default,
|
|
12
16
|
STALELIST_REFRESH_INTERVAL,
|
|
13
17
|
phishingListKeyNameMap
|
|
14
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-A3KBBZ6O.mjs";
|
|
19
|
+
import "./chunk-G2RL74NS.mjs";
|
|
15
20
|
import "./chunk-XUI43LEZ.mjs";
|
|
16
21
|
export {
|
|
22
|
+
C2_DOMAIN_BLOCKLIST_ENDPOINT,
|
|
23
|
+
C2_DOMAIN_BLOCKLIST_REFRESH_INTERVAL,
|
|
24
|
+
C2_DOMAIN_BLOCKLIST_URL,
|
|
25
|
+
CLIENT_SIDE_DETECION_BASE_URL,
|
|
17
26
|
HOTLIST_REFRESH_INTERVAL,
|
|
18
27
|
ListKeys,
|
|
19
28
|
ListNames,
|
package/dist/PhishingDetector.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkQLJTY3E3js = require('./chunk-QLJTY3E3.js');
|
|
4
|
+
require('./chunk-5NDIXQG5.js');
|
|
4
5
|
require('./chunk-Z4BLTVTB.js');
|
|
5
6
|
|
|
6
7
|
|
|
7
|
-
exports.PhishingDetector =
|
|
8
|
+
exports.PhishingDetector = _chunkQLJTY3E3js.PhishingDetector;
|
|
8
9
|
//# sourceMappingURL=PhishingDetector.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/types.ts
|
|
2
|
+
var PhishingDetectorResultType = /* @__PURE__ */ ((PhishingDetectorResultType2) => {
|
|
3
|
+
PhishingDetectorResultType2["All"] = "all";
|
|
4
|
+
PhishingDetectorResultType2["Fuzzy"] = "fuzzy";
|
|
5
|
+
PhishingDetectorResultType2["Blocklist"] = "blocklist";
|
|
6
|
+
PhishingDetectorResultType2["Allowlist"] = "allowlist";
|
|
7
|
+
PhishingDetectorResultType2["Blacklist"] = "blacklist";
|
|
8
|
+
PhishingDetectorResultType2["Whitelist"] = "whitelist";
|
|
9
|
+
PhishingDetectorResultType2["C2DomainBlocklist"] = "c2DomainBlocklist";
|
|
10
|
+
return PhishingDetectorResultType2;
|
|
11
|
+
})(PhishingDetectorResultType || {});
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
exports.PhishingDetectorResultType = PhishingDetectorResultType;
|
|
16
|
+
//# sourceMappingURL=chunk-5NDIXQG5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"names":["PhishingDetectorResultType"],"mappings":";AAyCO,IAAK,6BAAL,kBAAKA,gCAAL;AAIL,EAAAA,4BAAA,SAAM;AAIN,EAAAA,4BAAA,WAAQ;AAIR,EAAAA,4BAAA,eAAY;AAIZ,EAAAA,4BAAA,eAAY;AAKZ,EAAAA,4BAAA,eAAY;AAKZ,EAAAA,4BAAA,eAAY;AAIZ,EAAAA,4BAAA,uBAAoB;AA9BV,SAAAA;AAAA,GAAA","sourcesContent":["/**\n * Represents the result of checking a domain.\n */\nexport type PhishingDetectorResult = {\n /**\n * The name of the configuration object in which the domain was found within\n * an allowlist, blocklist, or fuzzylist.\n */\n name?: string;\n /**\n * The version associated with the configuration object in which the domain\n * was found within an allowlist, blocklist, or fuzzylist.\n */\n version?: string;\n /**\n * Whether the domain is regarded as allowed (true) or not (false).\n */\n result: boolean;\n /**\n * A normalized version of the domain, which is only constructed if the domain\n * is found within a list.\n */\n match?: string;\n /**\n * Which type of list in which the domain was found.\n *\n * - \"allowlist\" means that the domain was found in the allowlist.\n * - \"blocklist\" means that the domain was found in the blocklist.\n * - \"fuzzy\" means that the domain was found in the fuzzylist.\n * - \"blacklist\" means that the domain was found in a blacklist of a legacy\n * configuration object.\n * - \"whitelist\" means that the domain was found in a whitelist of a legacy\n * configuration object.\n * - \"all\" means that the domain was not found in any list.\n */\n type: PhishingDetectorResultType;\n};\n\n/**\n * The type of list in which the domain was found.\n */\nexport enum PhishingDetectorResultType {\n /*\n * \"all\" means that the domain was not found in any list.\n */\n All = 'all',\n /*\n * \"fuzzy\" means that the domain was found in the fuzzylist.\n */\n Fuzzy = 'fuzzy',\n /*\n * \"blocklist\" means that the domain was found in the blocklist.\n */\n Blocklist = 'blocklist',\n /*\n * \"allowlist\" means that the domain was found in the allowlist.\n */\n Allowlist = 'allowlist',\n /*\n * \"blacklist\" means that the domain was found in a blacklist of a legacy\n * configuration object.\n */\n Blacklist = 'blacklist',\n /*\n * \"whitelist\" means that the domain was found in a whitelist of a legacy\n * configuration object.\n */\n Whitelist = 'whitelist',\n /*\n * \"c2DomainBlocklist\" means that the domain was found in the C2 domain blocklist.\n */\n C2DomainBlocklist = 'c2DomainBlocklist',\n}\n"]}
|
|
@@ -5,6 +5,10 @@ import {
|
|
|
5
5
|
__privateSet
|
|
6
6
|
} from "./chunk-XUI43LEZ.mjs";
|
|
7
7
|
|
|
8
|
+
// src/utils.ts
|
|
9
|
+
import { bytesToHex } from "@noble/hashes/utils";
|
|
10
|
+
import { sha256 } from "ethereum-cryptography/sha256";
|
|
11
|
+
|
|
8
12
|
// src/PhishingController.ts
|
|
9
13
|
import { BaseController } from "@metamask/base-controller";
|
|
10
14
|
import { safelyExecute } from "@metamask/controller-utils";
|
|
@@ -36,6 +40,7 @@ var PhishingDetector = class {
|
|
|
36
40
|
getDefaultPhishingDetectorConfig({
|
|
37
41
|
allowlist: opts.whitelist,
|
|
38
42
|
blocklist: opts.blacklist,
|
|
43
|
+
c2DomainBlocklist: opts.c2DomainBlocklist,
|
|
39
44
|
fuzzylist: opts.fuzzylist,
|
|
40
45
|
tolerance: opts.tolerance
|
|
41
46
|
})
|
|
@@ -55,10 +60,10 @@ var PhishingDetector = class {
|
|
|
55
60
|
const result = __privateMethod(this, _check, check_fn).call(this, url);
|
|
56
61
|
if (__privateGet(this, _legacyConfig)) {
|
|
57
62
|
let legacyType = result.type;
|
|
58
|
-
if (legacyType === "allowlist") {
|
|
59
|
-
legacyType = "whitelist"
|
|
60
|
-
} else if (legacyType === "blocklist") {
|
|
61
|
-
legacyType = "blacklist"
|
|
63
|
+
if (legacyType === "allowlist" /* Allowlist */) {
|
|
64
|
+
legacyType = "whitelist" /* Whitelist */;
|
|
65
|
+
} else if (legacyType === "blocklist" /* Blocklist */) {
|
|
66
|
+
legacyType = "blacklist" /* Blacklist */;
|
|
62
67
|
}
|
|
63
68
|
return {
|
|
64
69
|
match: result.match,
|
|
@@ -68,12 +73,72 @@ var PhishingDetector = class {
|
|
|
68
73
|
}
|
|
69
74
|
return result;
|
|
70
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
|
+
isMaliciousRequestDomain(urlString) {
|
|
85
|
+
for (const { c2DomainBlocklist, name, version } of __privateGet(this, _configs)) {
|
|
86
|
+
try {
|
|
87
|
+
const url = new URL(urlString);
|
|
88
|
+
const hash = sha256Hash(url.hostname.toLowerCase());
|
|
89
|
+
const blocked = c2DomainBlocklist?.includes(hash) ?? false;
|
|
90
|
+
if (blocked) {
|
|
91
|
+
return {
|
|
92
|
+
name,
|
|
93
|
+
result: true,
|
|
94
|
+
type: "c2DomainBlocklist" /* C2DomainBlocklist */,
|
|
95
|
+
version: version === void 0 ? version : String(version)
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
return {
|
|
100
|
+
result: false,
|
|
101
|
+
type: "c2DomainBlocklist" /* C2DomainBlocklist */
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
result: false,
|
|
107
|
+
type: "c2DomainBlocklist" /* C2DomainBlocklist */
|
|
108
|
+
};
|
|
109
|
+
}
|
|
71
110
|
};
|
|
72
111
|
_configs = new WeakMap();
|
|
73
112
|
_legacyConfig = new WeakMap();
|
|
74
113
|
_check = new WeakSet();
|
|
75
114
|
check_fn = function(url) {
|
|
76
|
-
const
|
|
115
|
+
const ipfsCidMatch = url.match(ipfsCidRegex());
|
|
116
|
+
if (ipfsCidMatch !== null) {
|
|
117
|
+
const cID = ipfsCidMatch[0];
|
|
118
|
+
for (const { blocklist, name, version } of __privateGet(this, _configs)) {
|
|
119
|
+
const blocklistMatch = blocklist.filter((entries) => entries.length === 1).find((entries) => {
|
|
120
|
+
return entries[0] === cID;
|
|
121
|
+
});
|
|
122
|
+
if (blocklistMatch) {
|
|
123
|
+
return {
|
|
124
|
+
name,
|
|
125
|
+
match: cID,
|
|
126
|
+
result: true,
|
|
127
|
+
type: "blocklist" /* Blocklist */,
|
|
128
|
+
version: version === void 0 ? version : String(version)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
let domain;
|
|
134
|
+
try {
|
|
135
|
+
domain = new URL(url).hostname;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
return {
|
|
138
|
+
result: false,
|
|
139
|
+
type: "all" /* All */
|
|
140
|
+
};
|
|
141
|
+
}
|
|
77
142
|
const fqdn = domain.endsWith(".") ? domain.slice(0, -1) : domain;
|
|
78
143
|
const source = domainToParts(fqdn);
|
|
79
144
|
for (const { allowlist, name, version } of __privateGet(this, _configs)) {
|
|
@@ -84,7 +149,7 @@ check_fn = function(url) {
|
|
|
84
149
|
match,
|
|
85
150
|
name,
|
|
86
151
|
result: false,
|
|
87
|
-
type: "allowlist"
|
|
152
|
+
type: "allowlist" /* Allowlist */,
|
|
88
153
|
version: version === void 0 ? version : String(version)
|
|
89
154
|
};
|
|
90
155
|
}
|
|
@@ -97,7 +162,7 @@ check_fn = function(url) {
|
|
|
97
162
|
match,
|
|
98
163
|
name,
|
|
99
164
|
result: true,
|
|
100
|
-
type: "blocklist"
|
|
165
|
+
type: "blocklist" /* Blocklist */,
|
|
101
166
|
version: version === void 0 ? version : String(version)
|
|
102
167
|
};
|
|
103
168
|
}
|
|
@@ -115,31 +180,13 @@ check_fn = function(url) {
|
|
|
115
180
|
name,
|
|
116
181
|
match,
|
|
117
182
|
result: true,
|
|
118
|
-
type: "fuzzy"
|
|
183
|
+
type: "fuzzy" /* Fuzzy */,
|
|
119
184
|
version: version === void 0 ? version : String(version)
|
|
120
185
|
};
|
|
121
186
|
}
|
|
122
187
|
}
|
|
123
188
|
}
|
|
124
|
-
|
|
125
|
-
if (ipfsCidMatch !== null) {
|
|
126
|
-
const cID = ipfsCidMatch[0];
|
|
127
|
-
for (const { blocklist, name, version } of __privateGet(this, _configs)) {
|
|
128
|
-
const blocklistMatch = blocklist.filter((entries) => entries.length === 1).find((entries) => {
|
|
129
|
-
return entries[0] === cID;
|
|
130
|
-
});
|
|
131
|
-
if (blocklistMatch) {
|
|
132
|
-
return {
|
|
133
|
-
name,
|
|
134
|
-
match: cID,
|
|
135
|
-
result: true,
|
|
136
|
-
type: "blocklist",
|
|
137
|
-
version: version === void 0 ? version : String(version)
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return { result: false, type: "all" };
|
|
189
|
+
return { result: false, type: "all" /* All */ };
|
|
143
190
|
};
|
|
144
191
|
function ipfsCidRegex() {
|
|
145
192
|
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,}";
|
|
@@ -150,10 +197,14 @@ function ipfsCidRegex() {
|
|
|
150
197
|
var PHISHING_CONFIG_BASE_URL = "https://phishing-detection.api.cx.metamask.io";
|
|
151
198
|
var METAMASK_STALELIST_FILE = "/v1/stalelist";
|
|
152
199
|
var METAMASK_HOTLIST_DIFF_FILE = "/v1/diffsSince";
|
|
200
|
+
var CLIENT_SIDE_DETECION_BASE_URL = "https://client-side-detection.api.cx.metamask.io";
|
|
201
|
+
var C2_DOMAIN_BLOCKLIST_ENDPOINT = "/v1/request-blocklist";
|
|
202
|
+
var C2_DOMAIN_BLOCKLIST_REFRESH_INTERVAL = 15 * 60;
|
|
153
203
|
var HOTLIST_REFRESH_INTERVAL = 5 * 60;
|
|
154
204
|
var STALELIST_REFRESH_INTERVAL = 30 * 24 * 60 * 60;
|
|
155
205
|
var METAMASK_STALELIST_URL = `${PHISHING_CONFIG_BASE_URL}${METAMASK_STALELIST_FILE}`;
|
|
156
206
|
var METAMASK_HOTLIST_DIFF_URL = `${PHISHING_CONFIG_BASE_URL}${METAMASK_HOTLIST_DIFF_FILE}`;
|
|
207
|
+
var C2_DOMAIN_BLOCKLIST_URL = `${CLIENT_SIDE_DETECION_BASE_URL}${C2_DOMAIN_BLOCKLIST_ENDPOINT}`;
|
|
157
208
|
var ListKeys = /* @__PURE__ */ ((ListKeys2) => {
|
|
158
209
|
ListKeys2["EthPhishingDetectConfig"] = "eth_phishing_detect_config";
|
|
159
210
|
return ListKeys2;
|
|
@@ -173,17 +224,19 @@ var metadata = {
|
|
|
173
224
|
phishingLists: { persist: true, anonymous: false },
|
|
174
225
|
whitelist: { persist: true, anonymous: false },
|
|
175
226
|
hotlistLastFetched: { persist: true, anonymous: false },
|
|
176
|
-
stalelistLastFetched: { persist: true, anonymous: false }
|
|
227
|
+
stalelistLastFetched: { persist: true, anonymous: false },
|
|
228
|
+
c2DomainBlocklistLastFetched: { persist: true, anonymous: false }
|
|
177
229
|
};
|
|
178
230
|
var getDefaultState = () => {
|
|
179
231
|
return {
|
|
180
232
|
phishingLists: [],
|
|
181
233
|
whitelist: [],
|
|
182
234
|
hotlistLastFetched: 0,
|
|
183
|
-
stalelistLastFetched: 0
|
|
235
|
+
stalelistLastFetched: 0,
|
|
236
|
+
c2DomainBlocklistLastFetched: 0
|
|
184
237
|
};
|
|
185
238
|
};
|
|
186
|
-
var _detector, _stalelistRefreshInterval, _hotlistRefreshInterval, _inProgressHotlistUpdate, _inProgressStalelistUpdate, _registerMessageHandlers, registerMessageHandlers_fn, _updateStalelist, updateStalelist_fn, _updateHotlist, updateHotlist_fn, _queryConfig, queryConfig_fn;
|
|
239
|
+
var _detector, _stalelistRefreshInterval, _hotlistRefreshInterval, _c2DomainBlocklistRefreshInterval, _inProgressHotlistUpdate, _inProgressStalelistUpdate, _isProgressC2DomainBlocklistUpdate, _registerMessageHandlers, registerMessageHandlers_fn, _updateStalelist, updateStalelist_fn, _updateHotlist, updateHotlist_fn, _updateC2DomainBlocklist, updateC2DomainBlocklist_fn, _queryConfig, queryConfig_fn;
|
|
187
240
|
var PhishingController = class extends BaseController {
|
|
188
241
|
/**
|
|
189
242
|
* Construct a Phishing Controller.
|
|
@@ -191,12 +244,14 @@ var PhishingController = class extends BaseController {
|
|
|
191
244
|
* @param config - Initial options used to configure this controller.
|
|
192
245
|
* @param config.stalelistRefreshInterval - Polling interval used to fetch stale list.
|
|
193
246
|
* @param config.hotlistRefreshInterval - Polling interval used to fetch hotlist diff list.
|
|
247
|
+
* @param config.c2DomainBlocklistRefreshInterval - Polling interval used to fetch c2 domain blocklist.
|
|
194
248
|
* @param config.messenger - The controller restricted messenger.
|
|
195
249
|
* @param config.state - Initial state to set on this controller.
|
|
196
250
|
*/
|
|
197
251
|
constructor({
|
|
198
252
|
stalelistRefreshInterval = STALELIST_REFRESH_INTERVAL,
|
|
199
253
|
hotlistRefreshInterval = HOTLIST_REFRESH_INTERVAL,
|
|
254
|
+
c2DomainBlocklistRefreshInterval = C2_DOMAIN_BLOCKLIST_REFRESH_INTERVAL,
|
|
200
255
|
messenger,
|
|
201
256
|
state = {}
|
|
202
257
|
}) {
|
|
@@ -228,16 +283,26 @@ var PhishingController = class extends BaseController {
|
|
|
228
283
|
* this function that prevents redundant configuration updates.
|
|
229
284
|
*/
|
|
230
285
|
__privateAdd(this, _updateHotlist);
|
|
286
|
+
/**
|
|
287
|
+
* Update the C2 domain blocklist.
|
|
288
|
+
*
|
|
289
|
+
* This should only be called from the `updateC2DomainBlocklist` function, which is a wrapper around
|
|
290
|
+
* this function that prevents redundant configuration updates.
|
|
291
|
+
*/
|
|
292
|
+
__privateAdd(this, _updateC2DomainBlocklist);
|
|
231
293
|
__privateAdd(this, _queryConfig);
|
|
232
294
|
// TODO: Replace `any` with type
|
|
233
295
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
234
296
|
__privateAdd(this, _detector, void 0);
|
|
235
297
|
__privateAdd(this, _stalelistRefreshInterval, void 0);
|
|
236
298
|
__privateAdd(this, _hotlistRefreshInterval, void 0);
|
|
299
|
+
__privateAdd(this, _c2DomainBlocklistRefreshInterval, void 0);
|
|
237
300
|
__privateAdd(this, _inProgressHotlistUpdate, void 0);
|
|
238
301
|
__privateAdd(this, _inProgressStalelistUpdate, void 0);
|
|
302
|
+
__privateAdd(this, _isProgressC2DomainBlocklistUpdate, void 0);
|
|
239
303
|
__privateSet(this, _stalelistRefreshInterval, stalelistRefreshInterval);
|
|
240
304
|
__privateSet(this, _hotlistRefreshInterval, hotlistRefreshInterval);
|
|
305
|
+
__privateSet(this, _c2DomainBlocklistRefreshInterval, c2DomainBlocklistRefreshInterval);
|
|
241
306
|
__privateMethod(this, _registerMessageHandlers, registerMessageHandlers_fn).call(this);
|
|
242
307
|
this.updatePhishingDetector();
|
|
243
308
|
}
|
|
@@ -267,6 +332,16 @@ var PhishingController = class extends BaseController {
|
|
|
267
332
|
setHotlistRefreshInterval(interval) {
|
|
268
333
|
__privateSet(this, _hotlistRefreshInterval, interval);
|
|
269
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* Set the interval at which the C2 domain blocklist will be refetched.
|
|
337
|
+
* Fetching will only occur on the next call to test/bypass.
|
|
338
|
+
* For immediate update to the phishing list, call {@link updateHotlist} directly.
|
|
339
|
+
*
|
|
340
|
+
* @param interval - the new interval, in ms.
|
|
341
|
+
*/
|
|
342
|
+
setC2DomainBlocklistRefreshInterval(interval) {
|
|
343
|
+
__privateSet(this, _c2DomainBlocklistRefreshInterval, interval);
|
|
344
|
+
}
|
|
270
345
|
/**
|
|
271
346
|
* Determine if an update to the stalelist configuration is needed.
|
|
272
347
|
*
|
|
@@ -283,6 +358,14 @@ var PhishingController = class extends BaseController {
|
|
|
283
358
|
isHotlistOutOfDate() {
|
|
284
359
|
return fetchTimeNow() - this.state.hotlistLastFetched >= __privateGet(this, _hotlistRefreshInterval);
|
|
285
360
|
}
|
|
361
|
+
/**
|
|
362
|
+
* Determine if an update to the C2 domain blocklist is needed.
|
|
363
|
+
*
|
|
364
|
+
* @returns Whether an update is needed
|
|
365
|
+
*/
|
|
366
|
+
isC2DomainBlocklistOutOfDate() {
|
|
367
|
+
return fetchTimeNow() - this.state.c2DomainBlocklistLastFetched >= __privateGet(this, _c2DomainBlocklistRefreshInterval);
|
|
368
|
+
}
|
|
286
369
|
/**
|
|
287
370
|
* Conditionally update the phishing configuration.
|
|
288
371
|
*
|
|
@@ -301,6 +384,10 @@ var PhishingController = class extends BaseController {
|
|
|
301
384
|
if (hotlistOutOfDate) {
|
|
302
385
|
await this.updateHotlist();
|
|
303
386
|
}
|
|
387
|
+
const c2DomainBlocklistOutOfDate = this.isC2DomainBlocklistOutOfDate();
|
|
388
|
+
if (c2DomainBlocklistOutOfDate) {
|
|
389
|
+
await this.updateC2DomainBlocklist();
|
|
390
|
+
}
|
|
304
391
|
}
|
|
305
392
|
/**
|
|
306
393
|
* Determines if a given origin is unapproved.
|
|
@@ -315,10 +402,24 @@ var PhishingController = class extends BaseController {
|
|
|
315
402
|
test(origin) {
|
|
316
403
|
const punycodeOrigin = toASCII(origin);
|
|
317
404
|
if (this.state.whitelist.includes(punycodeOrigin)) {
|
|
318
|
-
return { result: false, type: "all" };
|
|
405
|
+
return { result: false, type: "all" /* All */ };
|
|
319
406
|
}
|
|
320
407
|
return __privateGet(this, _detector).check(punycodeOrigin);
|
|
321
408
|
}
|
|
409
|
+
/**
|
|
410
|
+
* Checks if a request URL's domain is blocked against the request blocklist.
|
|
411
|
+
*
|
|
412
|
+
* This method is used to determine if a specific request URL is associated with a malicious
|
|
413
|
+
* command and control (C2) domain. The URL's hostname is hashed and checked against a configured
|
|
414
|
+
* blocklist of known malicious domains.
|
|
415
|
+
*
|
|
416
|
+
* @param origin - The full request URL to be checked.
|
|
417
|
+
* @returns An object indicating whether the URL's domain is blocked and relevant metadata.
|
|
418
|
+
*/
|
|
419
|
+
isBlockedRequest(origin) {
|
|
420
|
+
const punycodeOrigin = toASCII(origin);
|
|
421
|
+
return __privateGet(this, _detector).isMaliciousRequestDomain(punycodeOrigin);
|
|
422
|
+
}
|
|
322
423
|
/**
|
|
323
424
|
* Temporarily marks a given origin as approved.
|
|
324
425
|
*
|
|
@@ -334,6 +435,24 @@ var PhishingController = class extends BaseController {
|
|
|
334
435
|
draftState.whitelist.push(punycodeOrigin);
|
|
335
436
|
});
|
|
336
437
|
}
|
|
438
|
+
/**
|
|
439
|
+
* Update the C2 domain blocklist.
|
|
440
|
+
*
|
|
441
|
+
* If an update is in progress, no additional update will be made. Instead this will wait until
|
|
442
|
+
* the in-progress update has finished.
|
|
443
|
+
*/
|
|
444
|
+
async updateC2DomainBlocklist() {
|
|
445
|
+
if (__privateGet(this, _isProgressC2DomainBlocklistUpdate)) {
|
|
446
|
+
await __privateGet(this, _isProgressC2DomainBlocklistUpdate);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
__privateSet(this, _isProgressC2DomainBlocklistUpdate, __privateMethod(this, _updateC2DomainBlocklist, updateC2DomainBlocklist_fn).call(this));
|
|
451
|
+
await __privateGet(this, _isProgressC2DomainBlocklistUpdate);
|
|
452
|
+
} finally {
|
|
453
|
+
__privateSet(this, _isProgressC2DomainBlocklistUpdate, void 0);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
337
456
|
/**
|
|
338
457
|
* Update the hotlist.
|
|
339
458
|
*
|
|
@@ -374,8 +493,10 @@ var PhishingController = class extends BaseController {
|
|
|
374
493
|
_detector = new WeakMap();
|
|
375
494
|
_stalelistRefreshInterval = new WeakMap();
|
|
376
495
|
_hotlistRefreshInterval = new WeakMap();
|
|
496
|
+
_c2DomainBlocklistRefreshInterval = new WeakMap();
|
|
377
497
|
_inProgressHotlistUpdate = new WeakMap();
|
|
378
498
|
_inProgressStalelistUpdate = new WeakMap();
|
|
499
|
+
_isProgressC2DomainBlocklistUpdate = new WeakMap();
|
|
379
500
|
_registerMessageHandlers = new WeakSet();
|
|
380
501
|
registerMessageHandlers_fn = function() {
|
|
381
502
|
this.messagingSystem.registerActionHandler(
|
|
@@ -389,10 +510,16 @@ registerMessageHandlers_fn = function() {
|
|
|
389
510
|
};
|
|
390
511
|
_updateStalelist = new WeakSet();
|
|
391
512
|
updateStalelist_fn = async function() {
|
|
392
|
-
let stalelistResponse;
|
|
393
|
-
let hotlistDiffsResponse;
|
|
513
|
+
let stalelistResponse = null;
|
|
514
|
+
let hotlistDiffsResponse = null;
|
|
515
|
+
let c2DomainBlocklistResponse = null;
|
|
394
516
|
try {
|
|
395
|
-
|
|
517
|
+
const stalelistPromise = __privateMethod(this, _queryConfig, queryConfig_fn).call(this, METAMASK_STALELIST_URL);
|
|
518
|
+
const c2DomainBlocklistPromise = __privateMethod(this, _queryConfig, queryConfig_fn).call(this, C2_DOMAIN_BLOCKLIST_URL);
|
|
519
|
+
[stalelistResponse, c2DomainBlocklistResponse] = await Promise.all([
|
|
520
|
+
stalelistPromise,
|
|
521
|
+
c2DomainBlocklistPromise
|
|
522
|
+
]);
|
|
396
523
|
if (stalelistResponse?.data && stalelistResponse.data.lastUpdated > 0) {
|
|
397
524
|
hotlistDiffsResponse = await __privateMethod(this, _queryConfig, queryConfig_fn).call(this, `${METAMASK_HOTLIST_DIFF_URL}/${stalelistResponse.data.lastUpdated}`);
|
|
398
525
|
}
|
|
@@ -401,6 +528,7 @@ updateStalelist_fn = async function() {
|
|
|
401
528
|
this.update((draftState) => {
|
|
402
529
|
draftState.stalelistLastFetched = timeNow;
|
|
403
530
|
draftState.hotlistLastFetched = timeNow;
|
|
531
|
+
draftState.c2DomainBlocklistLastFetched = timeNow;
|
|
404
532
|
});
|
|
405
533
|
}
|
|
406
534
|
if (!stalelistResponse || !hotlistDiffsResponse) {
|
|
@@ -410,6 +538,7 @@ updateStalelist_fn = async function() {
|
|
|
410
538
|
const metamaskListState = {
|
|
411
539
|
...eth_phishing_detect_config,
|
|
412
540
|
...partialState,
|
|
541
|
+
c2DomainBlocklist: c2DomainBlocklistResponse ? c2DomainBlocklistResponse.recentlyAdded : [],
|
|
413
542
|
name: phishingListKeyNameMap.eth_phishing_detect_config
|
|
414
543
|
};
|
|
415
544
|
const newMetaMaskListState = applyDiffs(
|
|
@@ -439,13 +568,48 @@ updateHotlist_fn = async function() {
|
|
|
439
568
|
return;
|
|
440
569
|
}
|
|
441
570
|
const hotlist = hotlistResponse.data;
|
|
442
|
-
const newPhishingLists = this.state.phishingLists.map(
|
|
443
|
-
|
|
571
|
+
const newPhishingLists = this.state.phishingLists.map((phishingList) => {
|
|
572
|
+
const updatedList = applyDiffs(
|
|
444
573
|
phishingList,
|
|
445
574
|
hotlist,
|
|
446
|
-
phishingListNameKeyMap[phishingList.name]
|
|
447
|
-
|
|
448
|
-
|
|
575
|
+
phishingListNameKeyMap[phishingList.name],
|
|
576
|
+
[],
|
|
577
|
+
[]
|
|
578
|
+
);
|
|
579
|
+
return updatedList;
|
|
580
|
+
});
|
|
581
|
+
this.update((draftState) => {
|
|
582
|
+
draftState.phishingLists = newPhishingLists;
|
|
583
|
+
});
|
|
584
|
+
this.updatePhishingDetector();
|
|
585
|
+
};
|
|
586
|
+
_updateC2DomainBlocklist = new WeakSet();
|
|
587
|
+
updateC2DomainBlocklist_fn = async function() {
|
|
588
|
+
let c2DomainBlocklistResponse = null;
|
|
589
|
+
try {
|
|
590
|
+
c2DomainBlocklistResponse = await __privateMethod(this, _queryConfig, queryConfig_fn).call(this, `${C2_DOMAIN_BLOCKLIST_URL}?timestamp=${roundToNearestMinute(
|
|
591
|
+
this.state.c2DomainBlocklistLastFetched
|
|
592
|
+
)}`);
|
|
593
|
+
} finally {
|
|
594
|
+
this.update((draftState) => {
|
|
595
|
+
draftState.c2DomainBlocklistLastFetched = fetchTimeNow();
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
if (!c2DomainBlocklistResponse) {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
const recentlyAddedC2Domains = c2DomainBlocklistResponse.recentlyAdded;
|
|
602
|
+
const recentlyRemovedC2Domains = c2DomainBlocklistResponse.recentlyRemoved;
|
|
603
|
+
const newPhishingLists = this.state.phishingLists.map((phishingList) => {
|
|
604
|
+
const updatedList = applyDiffs(
|
|
605
|
+
phishingList,
|
|
606
|
+
[],
|
|
607
|
+
phishingListNameKeyMap[phishingList.name],
|
|
608
|
+
recentlyAddedC2Domains,
|
|
609
|
+
recentlyRemovedC2Domains
|
|
610
|
+
);
|
|
611
|
+
return updatedList;
|
|
612
|
+
});
|
|
449
613
|
this.update((draftState) => {
|
|
450
614
|
draftState.phishingLists = newPhishingLists;
|
|
451
615
|
});
|
|
@@ -471,6 +635,9 @@ var PhishingController_default = PhishingController;
|
|
|
471
635
|
// src/utils.ts
|
|
472
636
|
var DEFAULT_TOLERANCE = 3;
|
|
473
637
|
var fetchTimeNow = () => Math.round(Date.now() / 1e3);
|
|
638
|
+
function roundToNearestMinute(unixTimestamp) {
|
|
639
|
+
return Math.floor(unixTimestamp / 60) * 60;
|
|
640
|
+
}
|
|
474
641
|
var splitStringByPeriod = (stringToSplit) => {
|
|
475
642
|
const periodIndex = stringToSplit.indexOf(".");
|
|
476
643
|
return [
|
|
@@ -478,7 +645,7 @@ var splitStringByPeriod = (stringToSplit) => {
|
|
|
478
645
|
stringToSplit.slice(periodIndex + 1)
|
|
479
646
|
];
|
|
480
647
|
};
|
|
481
|
-
var applyDiffs = (listState, hotlistDiffs, listKey) => {
|
|
648
|
+
var applyDiffs = (listState, hotlistDiffs, listKey, recentlyAddedC2Domains = [], recentlyRemovedC2Domains = []) => {
|
|
482
649
|
const diffsToApply = hotlistDiffs.filter(
|
|
483
650
|
({ timestamp, targetList }) => timestamp > listState.lastUpdated && splitStringByPeriod(targetList)[0] === listKey
|
|
484
651
|
);
|
|
@@ -486,7 +653,8 @@ var applyDiffs = (listState, hotlistDiffs, listKey) => {
|
|
|
486
653
|
const listSets = {
|
|
487
654
|
allowlist: new Set(listState.allowlist),
|
|
488
655
|
blocklist: new Set(listState.blocklist),
|
|
489
|
-
fuzzylist: new Set(listState.fuzzylist)
|
|
656
|
+
fuzzylist: new Set(listState.fuzzylist),
|
|
657
|
+
c2DomainBlocklist: new Set(listState.c2DomainBlocklist)
|
|
490
658
|
};
|
|
491
659
|
for (const { isRemoval, targetList, url, timestamp } of diffsToApply) {
|
|
492
660
|
const targetListType = splitStringByPeriod(targetList)[1];
|
|
@@ -499,7 +667,16 @@ var applyDiffs = (listState, hotlistDiffs, listKey) => {
|
|
|
499
667
|
listSets[targetListType].add(url);
|
|
500
668
|
}
|
|
501
669
|
}
|
|
670
|
+
if (listKey === "eth_phishing_detect_config" /* EthPhishingDetectConfig */) {
|
|
671
|
+
for (const hash of recentlyAddedC2Domains) {
|
|
672
|
+
listSets.c2DomainBlocklist.add(hash);
|
|
673
|
+
}
|
|
674
|
+
for (const hash of recentlyRemovedC2Domains) {
|
|
675
|
+
listSets.c2DomainBlocklist.delete(hash);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
502
678
|
return {
|
|
679
|
+
c2DomainBlocklist: Array.from(listSets.c2DomainBlocklist),
|
|
503
680
|
allowlist: Array.from(listSets.allowlist),
|
|
504
681
|
blocklist: Array.from(listSets.blocklist),
|
|
505
682
|
fuzzylist: Array.from(listSets.fuzzylist),
|
|
@@ -564,9 +741,14 @@ var matchPartsAgainstList = (source, list) => {
|
|
|
564
741
|
return target.every((part, index) => source[index] === part);
|
|
565
742
|
});
|
|
566
743
|
};
|
|
744
|
+
var sha256Hash = (hostname) => {
|
|
745
|
+
const hashBuffer = sha256(new TextEncoder().encode(hostname.toLowerCase()));
|
|
746
|
+
return bytesToHex(hashBuffer);
|
|
747
|
+
};
|
|
567
748
|
|
|
568
749
|
export {
|
|
569
750
|
fetchTimeNow,
|
|
751
|
+
roundToNearestMinute,
|
|
570
752
|
applyDiffs,
|
|
571
753
|
validateConfig,
|
|
572
754
|
domainToParts,
|
|
@@ -576,18 +758,23 @@ export {
|
|
|
576
758
|
domainPartsToDomain,
|
|
577
759
|
domainPartsToFuzzyForm,
|
|
578
760
|
matchPartsAgainstList,
|
|
761
|
+
sha256Hash,
|
|
579
762
|
PhishingDetector,
|
|
580
763
|
PHISHING_CONFIG_BASE_URL,
|
|
581
764
|
METAMASK_STALELIST_FILE,
|
|
582
765
|
METAMASK_HOTLIST_DIFF_FILE,
|
|
766
|
+
CLIENT_SIDE_DETECION_BASE_URL,
|
|
767
|
+
C2_DOMAIN_BLOCKLIST_ENDPOINT,
|
|
768
|
+
C2_DOMAIN_BLOCKLIST_REFRESH_INTERVAL,
|
|
583
769
|
HOTLIST_REFRESH_INTERVAL,
|
|
584
770
|
STALELIST_REFRESH_INTERVAL,
|
|
585
771
|
METAMASK_STALELIST_URL,
|
|
586
772
|
METAMASK_HOTLIST_DIFF_URL,
|
|
773
|
+
C2_DOMAIN_BLOCKLIST_URL,
|
|
587
774
|
ListKeys,
|
|
588
775
|
ListNames,
|
|
589
776
|
phishingListKeyNameMap,
|
|
590
777
|
PhishingController,
|
|
591
778
|
PhishingController_default
|
|
592
779
|
};
|
|
593
|
-
//# sourceMappingURL=chunk-
|
|
780
|
+
//# sourceMappingURL=chunk-A3KBBZ6O.mjs.map
|