@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
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ 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");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ 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");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _PhishingDetector_instances, _PhishingDetector_configs, _PhishingDetector_legacyConfig, _PhishingDetector_check;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.PhishingDetector = void 0;
16
+ const fastest_levenshtein_1 = require("fastest-levenshtein");
17
+ const types_1 = require("./types.cjs");
18
+ const utils_1 = require("./utils.cjs");
19
+ class PhishingDetector {
20
+ /**
21
+ * Construct a phishing detector, which can check whether origins are known
22
+ * to be malicious or similar to common phishing targets.
23
+ *
24
+ * A list of configurations is accepted. Each origin checked is processed
25
+ * using each configuration in sequence, so the order defines which
26
+ * configurations take precedence.
27
+ *
28
+ * @param opts - Phishing detection options
29
+ */
30
+ constructor(opts) {
31
+ _PhishingDetector_instances.add(this);
32
+ _PhishingDetector_configs.set(this, void 0);
33
+ _PhishingDetector_legacyConfig.set(this, void 0);
34
+ // recommended configuration
35
+ if (Array.isArray(opts)) {
36
+ __classPrivateFieldSet(this, _PhishingDetector_configs, (0, utils_1.processConfigs)(opts), "f");
37
+ __classPrivateFieldSet(this, _PhishingDetector_legacyConfig, false, "f");
38
+ // legacy configuration
39
+ }
40
+ else {
41
+ __classPrivateFieldSet(this, _PhishingDetector_configs, [
42
+ (0, utils_1.getDefaultPhishingDetectorConfig)({
43
+ allowlist: opts.whitelist,
44
+ blocklist: opts.blacklist,
45
+ c2DomainBlocklist: opts.c2DomainBlocklist,
46
+ fuzzylist: opts.fuzzylist,
47
+ tolerance: opts.tolerance,
48
+ }),
49
+ ], "f");
50
+ __classPrivateFieldSet(this, _PhishingDetector_legacyConfig, true, "f");
51
+ }
52
+ }
53
+ /**
54
+ * Check if a url is known to be malicious or similar to a common phishing
55
+ * target. This will check the hostname and IPFS CID that is sometimes
56
+ * located in the path.
57
+ *
58
+ * @param url - The url to check.
59
+ * @returns The result of the check.
60
+ */
61
+ check(url) {
62
+ const result = __classPrivateFieldGet(this, _PhishingDetector_instances, "m", _PhishingDetector_check).call(this, url);
63
+ if (__classPrivateFieldGet(this, _PhishingDetector_legacyConfig, "f")) {
64
+ let legacyType = result.type;
65
+ if (legacyType === types_1.PhishingDetectorResultType.Allowlist) {
66
+ legacyType = types_1.PhishingDetectorResultType.Whitelist;
67
+ }
68
+ else if (legacyType === types_1.PhishingDetectorResultType.Blocklist) {
69
+ legacyType = types_1.PhishingDetectorResultType.Blacklist;
70
+ }
71
+ return {
72
+ match: result.match,
73
+ result: result.result,
74
+ type: legacyType,
75
+ };
76
+ }
77
+ return result;
78
+ }
79
+ /**
80
+ * Checks if a URL is blocked against the hashed request blocklist.
81
+ * This is done by hashing the URL's hostname and checking it against the hashed request blocklist.
82
+ *
83
+ *
84
+ * @param urlString - The URL to check.
85
+ * @returns An object indicating if the URL is blocked and relevant metadata.
86
+ */
87
+ isMaliciousC2Domain(urlString) {
88
+ let hostname;
89
+ try {
90
+ hostname = new URL(urlString).hostname;
91
+ }
92
+ catch (error) {
93
+ return {
94
+ result: false,
95
+ type: types_1.PhishingDetectorResultType.C2DomainBlocklist,
96
+ };
97
+ }
98
+ const fqdn = hostname.endsWith('.') ? hostname.slice(0, -1) : hostname;
99
+ const source = (0, utils_1.domainToParts)(fqdn);
100
+ for (const { allowlist, name, version } of __classPrivateFieldGet(this, _PhishingDetector_configs, "f")) {
101
+ // if source matches allowlist hostname (or subdomain thereof), PASS
102
+ const allowlistMatch = (0, utils_1.matchPartsAgainstList)(source, allowlist);
103
+ if (allowlistMatch) {
104
+ const match = (0, utils_1.domainPartsToDomain)(allowlistMatch);
105
+ return {
106
+ match,
107
+ name,
108
+ result: false,
109
+ type: types_1.PhishingDetectorResultType.Allowlist,
110
+ version: version === undefined ? version : String(version),
111
+ };
112
+ }
113
+ }
114
+ for (const { c2DomainBlocklist, name, version } of __classPrivateFieldGet(this, _PhishingDetector_configs, "f")) {
115
+ const hash = (0, utils_1.sha256Hash)(hostname.toLowerCase());
116
+ const blocked = c2DomainBlocklist?.includes(hash) ?? false;
117
+ if (blocked) {
118
+ return {
119
+ name,
120
+ result: true,
121
+ type: types_1.PhishingDetectorResultType.C2DomainBlocklist,
122
+ version: version === undefined ? version : String(version),
123
+ };
124
+ }
125
+ }
126
+ // did not match, PASS
127
+ return {
128
+ result: false,
129
+ type: types_1.PhishingDetectorResultType.C2DomainBlocklist,
130
+ };
131
+ }
132
+ }
133
+ exports.PhishingDetector = PhishingDetector;
134
+ _PhishingDetector_configs = new WeakMap(), _PhishingDetector_legacyConfig = new WeakMap(), _PhishingDetector_instances = new WeakSet(), _PhishingDetector_check = function _PhishingDetector_check(url) {
135
+ const ipfsCidMatch = url.match(ipfsCidRegex());
136
+ // Check for IPFS CID related blocklist entries
137
+ if (ipfsCidMatch !== null) {
138
+ // there is a cID string somewhere
139
+ // Determine if any of the entries are ipfs cids
140
+ // Depending on the gateway, the CID is in the path OR a subdomain, so we do a regex match on it all
141
+ const cID = ipfsCidMatch[0];
142
+ for (const { blocklist, name, version } of __classPrivateFieldGet(this, _PhishingDetector_configs, "f")) {
143
+ const blocklistMatch = blocklist
144
+ .filter((entries) => entries.length === 1)
145
+ .find((entries) => {
146
+ return entries[0] === cID;
147
+ });
148
+ if (blocklistMatch) {
149
+ return {
150
+ name,
151
+ match: cID,
152
+ result: true,
153
+ type: types_1.PhishingDetectorResultType.Blocklist,
154
+ version: version === undefined ? version : String(version),
155
+ };
156
+ }
157
+ }
158
+ }
159
+ let domain;
160
+ try {
161
+ domain = new URL(url).hostname;
162
+ }
163
+ catch (error) {
164
+ return {
165
+ result: false,
166
+ type: types_1.PhishingDetectorResultType.All,
167
+ };
168
+ }
169
+ const fqdn = domain.endsWith('.') ? domain.slice(0, -1) : domain;
170
+ const source = (0, utils_1.domainToParts)(fqdn);
171
+ for (const { allowlist, name, version } of __classPrivateFieldGet(this, _PhishingDetector_configs, "f")) {
172
+ // if source matches allowlist hostname (or subdomain thereof), PASS
173
+ const allowlistMatch = (0, utils_1.matchPartsAgainstList)(source, allowlist);
174
+ if (allowlistMatch) {
175
+ const match = (0, utils_1.domainPartsToDomain)(allowlistMatch);
176
+ return {
177
+ match,
178
+ name,
179
+ result: false,
180
+ type: types_1.PhishingDetectorResultType.Allowlist,
181
+ version: version === undefined ? version : String(version),
182
+ };
183
+ }
184
+ }
185
+ for (const { blocklist, fuzzylist, name, tolerance, version } of __classPrivateFieldGet(this, _PhishingDetector_configs, "f")) {
186
+ // if source matches blocklist hostname (or subdomain thereof), FAIL
187
+ const blocklistMatch = (0, utils_1.matchPartsAgainstList)(source, blocklist);
188
+ if (blocklistMatch) {
189
+ const match = (0, utils_1.domainPartsToDomain)(blocklistMatch);
190
+ return {
191
+ match,
192
+ name,
193
+ result: true,
194
+ type: types_1.PhishingDetectorResultType.Blocklist,
195
+ version: version === undefined ? version : String(version),
196
+ };
197
+ }
198
+ if (tolerance > 0) {
199
+ // check if near-match of whitelist domain, FAIL
200
+ let fuzzyForm = (0, utils_1.domainPartsToFuzzyForm)(source);
201
+ // strip www
202
+ fuzzyForm = fuzzyForm.replace(/^www\./u, '');
203
+ // check against fuzzylist
204
+ const levenshteinMatched = fuzzylist.find((targetParts) => {
205
+ const fuzzyTarget = (0, utils_1.domainPartsToFuzzyForm)(targetParts);
206
+ const dist = (0, fastest_levenshtein_1.distance)(fuzzyForm, fuzzyTarget);
207
+ return dist <= tolerance;
208
+ });
209
+ if (levenshteinMatched) {
210
+ const match = (0, utils_1.domainPartsToDomain)(levenshteinMatched);
211
+ return {
212
+ name,
213
+ match,
214
+ result: true,
215
+ type: types_1.PhishingDetectorResultType.Fuzzy,
216
+ version: version === undefined ? version : String(version),
217
+ };
218
+ }
219
+ }
220
+ }
221
+ // matched nothing, PASS
222
+ return { result: false, type: types_1.PhishingDetectorResultType.All };
223
+ };
224
+ /**
225
+ * Runs a regex match to determine if a string is a IPFS CID
226
+ * @returns Regex string for IPFS CID
227
+ */
228
+ function ipfsCidRegex() {
229
+ // regex from https://stackoverflow.com/a/67176726
230
+ 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,}';
231
+ return new RegExp(reg, 'u');
232
+ }
233
+ //# sourceMappingURL=PhishingDetector.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PhishingDetector.cjs","sourceRoot":"","sources":["../src/PhishingDetector.ts"],"names":[],"mappings":"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","sourcesContent":["import { distance } from 'fastest-levenshtein';\n\nimport {\n PhishingDetectorResultType,\n type PhishingDetectorResult,\n} from './types';\nimport {\n domainPartsToDomain,\n domainPartsToFuzzyForm,\n domainToParts,\n getDefaultPhishingDetectorConfig,\n matchPartsAgainstList,\n processConfigs,\n sha256Hash,\n} from './utils';\n\nexport type LegacyPhishingDetectorList = {\n whitelist?: string[];\n blacklist?: string[];\n c2DomainBlocklist?: string[];\n} & FuzzyTolerance;\n\nexport type PhishingDetectorList = {\n allowlist?: string[];\n blocklist?: string[];\n c2DomainBlocklist?: string[];\n name?: string;\n version?: string | number;\n tolerance?: number;\n} & FuzzyTolerance;\n\nexport type FuzzyTolerance =\n | {\n tolerance?: number;\n fuzzylist: string[];\n }\n | {\n tolerance?: never;\n fuzzylist?: never;\n };\n\nexport type PhishingDetectorOptions =\n | LegacyPhishingDetectorList\n | PhishingDetectorList[];\n\nexport type PhishingDetectorConfiguration = {\n name?: string;\n version?: number | string;\n allowlist: string[][];\n blocklist: string[][];\n c2DomainBlocklist?: string[];\n fuzzylist: string[][];\n tolerance: number;\n};\n\nexport class PhishingDetector {\n #configs: PhishingDetectorConfiguration[];\n\n #legacyConfig: boolean;\n\n /**\n * Construct a phishing detector, which can check whether origins are known\n * to be malicious or similar to common phishing targets.\n *\n * A list of configurations is accepted. Each origin checked is processed\n * using each configuration in sequence, so the order defines which\n * configurations take precedence.\n *\n * @param opts - Phishing detection options\n */\n constructor(opts: PhishingDetectorOptions) {\n // recommended configuration\n if (Array.isArray(opts)) {\n this.#configs = processConfigs(opts);\n this.#legacyConfig = false;\n // legacy configuration\n } else {\n this.#configs = [\n getDefaultPhishingDetectorConfig({\n allowlist: opts.whitelist,\n blocklist: opts.blacklist,\n c2DomainBlocklist: opts.c2DomainBlocklist,\n fuzzylist: opts.fuzzylist,\n tolerance: opts.tolerance,\n }),\n ];\n this.#legacyConfig = true;\n }\n }\n\n /**\n * Check if a url is known to be malicious or similar to a common phishing\n * target. This will check the hostname and IPFS CID that is sometimes\n * located in the path.\n *\n * @param url - The url to check.\n * @returns The result of the check.\n */\n check(url: string): PhishingDetectorResult {\n const result = this.#check(url);\n\n if (this.#legacyConfig) {\n let legacyType = result.type;\n if (legacyType === PhishingDetectorResultType.Allowlist) {\n legacyType = PhishingDetectorResultType.Whitelist;\n } else if (legacyType === PhishingDetectorResultType.Blocklist) {\n legacyType = PhishingDetectorResultType.Blacklist;\n }\n return {\n match: result.match,\n result: result.result,\n type: legacyType,\n };\n }\n return result;\n }\n\n #check(url: string): PhishingDetectorResult {\n const ipfsCidMatch = url.match(ipfsCidRegex());\n\n // Check for IPFS CID related blocklist entries\n if (ipfsCidMatch !== null) {\n // there is a cID string somewhere\n // Determine if any of the entries are ipfs cids\n // Depending on the gateway, the CID is in the path OR a subdomain, so we do a regex match on it all\n const cID = ipfsCidMatch[0];\n for (const { blocklist, name, version } of this.#configs) {\n const blocklistMatch = blocklist\n .filter((entries) => entries.length === 1)\n .find((entries) => {\n return entries[0] === cID;\n });\n if (blocklistMatch) {\n return {\n name,\n match: cID,\n result: true,\n type: PhishingDetectorResultType.Blocklist,\n version: version === undefined ? version : String(version),\n };\n }\n }\n }\n\n let domain;\n try {\n domain = new URL(url).hostname;\n } catch (error) {\n return {\n result: false,\n type: PhishingDetectorResultType.All,\n };\n }\n\n const fqdn = domain.endsWith('.') ? domain.slice(0, -1) : domain;\n\n const source = domainToParts(fqdn);\n\n for (const { allowlist, name, version } of this.#configs) {\n // if source matches allowlist hostname (or subdomain thereof), PASS\n const allowlistMatch = matchPartsAgainstList(source, allowlist);\n if (allowlistMatch) {\n const match = domainPartsToDomain(allowlistMatch);\n return {\n match,\n name,\n result: false,\n type: PhishingDetectorResultType.Allowlist,\n version: version === undefined ? version : String(version),\n };\n }\n }\n\n for (const { blocklist, fuzzylist, name, tolerance, version } of this\n .#configs) {\n // if source matches blocklist hostname (or subdomain thereof), FAIL\n const blocklistMatch = matchPartsAgainstList(source, blocklist);\n if (blocklistMatch) {\n const match = domainPartsToDomain(blocklistMatch);\n return {\n match,\n name,\n result: true,\n type: PhishingDetectorResultType.Blocklist,\n version: version === undefined ? version : String(version),\n };\n }\n\n if (tolerance > 0) {\n // check if near-match of whitelist domain, FAIL\n let fuzzyForm = domainPartsToFuzzyForm(source);\n // strip www\n fuzzyForm = fuzzyForm.replace(/^www\\./u, '');\n // check against fuzzylist\n const levenshteinMatched = fuzzylist.find((targetParts) => {\n const fuzzyTarget = domainPartsToFuzzyForm(targetParts);\n const dist = distance(fuzzyForm, fuzzyTarget);\n return dist <= tolerance;\n });\n if (levenshteinMatched) {\n const match = domainPartsToDomain(levenshteinMatched);\n return {\n name,\n match,\n result: true,\n type: PhishingDetectorResultType.Fuzzy,\n version: version === undefined ? version : String(version),\n };\n }\n }\n }\n\n // matched nothing, PASS\n return { result: false, type: PhishingDetectorResultType.All };\n }\n\n /**\n * Checks if a URL is blocked against the hashed request blocklist.\n * This is done by hashing the URL's hostname and checking it against the hashed request blocklist.\n *\n *\n * @param urlString - The URL to check.\n * @returns An object indicating if the URL is blocked and relevant metadata.\n */\n isMaliciousC2Domain(urlString: string): PhishingDetectorResult {\n let hostname;\n try {\n hostname = new URL(urlString).hostname;\n } catch (error) {\n return {\n result: false,\n type: PhishingDetectorResultType.C2DomainBlocklist,\n };\n }\n\n const fqdn = hostname.endsWith('.') ? hostname.slice(0, -1) : hostname;\n\n const source = domainToParts(fqdn);\n\n for (const { allowlist, name, version } of this.#configs) {\n // if source matches allowlist hostname (or subdomain thereof), PASS\n const allowlistMatch = matchPartsAgainstList(source, allowlist);\n if (allowlistMatch) {\n const match = domainPartsToDomain(allowlistMatch);\n return {\n match,\n name,\n result: false,\n type: PhishingDetectorResultType.Allowlist,\n version: version === undefined ? version : String(version),\n };\n }\n }\n\n for (const { c2DomainBlocklist, name, version } of this.#configs) {\n const hash = sha256Hash(hostname.toLowerCase());\n const blocked = c2DomainBlocklist?.includes(hash) ?? false;\n\n if (blocked) {\n return {\n name,\n result: true,\n type: PhishingDetectorResultType.C2DomainBlocklist,\n version: version === undefined ? version : String(version),\n };\n }\n }\n // did not match, PASS\n return {\n result: false,\n type: PhishingDetectorResultType.C2DomainBlocklist,\n };\n }\n}\n\n/**\n * Runs a regex match to determine if a string is a IPFS CID\n * @returns Regex string for IPFS CID\n */\nfunction ipfsCidRegex() {\n // regex from https://stackoverflow.com/a/67176726\n const reg =\n '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,}';\n return new RegExp(reg, 'u');\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { type PhishingDetectorResult } from './types';
1
+ import { type PhishingDetectorResult } from "./types.cjs";
2
2
  export type LegacyPhishingDetectorList = {
3
3
  whitelist?: string[];
4
4
  blacklist?: string[];
@@ -61,4 +61,4 @@ export declare class PhishingDetector {
61
61
  */
62
62
  isMaliciousC2Domain(urlString: string): PhishingDetectorResult;
63
63
  }
64
- //# sourceMappingURL=PhishingDetector.d.ts.map
64
+ //# sourceMappingURL=PhishingDetector.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PhishingDetector.d.cts","sourceRoot":"","sources":["../src/PhishingDetector.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,sBAAsB,EAC5B,oBAAgB;AAWjB,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B,GAAG,cAAc,CAAC;AAEnB,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,cAAc,CAAC;AAEnB,MAAM,MAAM,cAAc,GACtB;IACE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,GACD;IACE,SAAS,CAAC,EAAE,KAAK,CAAC;IAClB,SAAS,CAAC,EAAE,KAAK,CAAC;CACnB,CAAC;AAEN,MAAM,MAAM,uBAAuB,GAC/B,0BAA0B,GAC1B,oBAAoB,EAAE,CAAC;AAE3B,MAAM,MAAM,6BAA6B,GAAG;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,qBAAa,gBAAgB;;IAK3B;;;;;;;;;OASG;gBACS,IAAI,EAAE,uBAAuB;IAoBzC;;;;;;;OAOG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,sBAAsB;IAsH1C;;;;;;;OAOG;IACH,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,sBAAsB;CAiD/D"}
@@ -0,0 +1,64 @@
1
+ import { type PhishingDetectorResult } from "./types.mjs";
2
+ export type LegacyPhishingDetectorList = {
3
+ whitelist?: string[];
4
+ blacklist?: string[];
5
+ c2DomainBlocklist?: string[];
6
+ } & FuzzyTolerance;
7
+ export type PhishingDetectorList = {
8
+ allowlist?: string[];
9
+ blocklist?: string[];
10
+ c2DomainBlocklist?: string[];
11
+ name?: string;
12
+ version?: string | number;
13
+ tolerance?: number;
14
+ } & FuzzyTolerance;
15
+ export type FuzzyTolerance = {
16
+ tolerance?: number;
17
+ fuzzylist: string[];
18
+ } | {
19
+ tolerance?: never;
20
+ fuzzylist?: never;
21
+ };
22
+ export type PhishingDetectorOptions = LegacyPhishingDetectorList | PhishingDetectorList[];
23
+ export type PhishingDetectorConfiguration = {
24
+ name?: string;
25
+ version?: number | string;
26
+ allowlist: string[][];
27
+ blocklist: string[][];
28
+ c2DomainBlocklist?: string[];
29
+ fuzzylist: string[][];
30
+ tolerance: number;
31
+ };
32
+ export declare class PhishingDetector {
33
+ #private;
34
+ /**
35
+ * Construct a phishing detector, which can check whether origins are known
36
+ * to be malicious or similar to common phishing targets.
37
+ *
38
+ * A list of configurations is accepted. Each origin checked is processed
39
+ * using each configuration in sequence, so the order defines which
40
+ * configurations take precedence.
41
+ *
42
+ * @param opts - Phishing detection options
43
+ */
44
+ constructor(opts: PhishingDetectorOptions);
45
+ /**
46
+ * Check if a url is known to be malicious or similar to a common phishing
47
+ * target. This will check the hostname and IPFS CID that is sometimes
48
+ * located in the path.
49
+ *
50
+ * @param url - The url to check.
51
+ * @returns The result of the check.
52
+ */
53
+ check(url: string): PhishingDetectorResult;
54
+ /**
55
+ * Checks if a URL is blocked against the hashed request blocklist.
56
+ * This is done by hashing the URL's hostname and checking it against the hashed request blocklist.
57
+ *
58
+ *
59
+ * @param urlString - The URL to check.
60
+ * @returns An object indicating if the URL is blocked and relevant metadata.
61
+ */
62
+ isMaliciousC2Domain(urlString: string): PhishingDetectorResult;
63
+ }
64
+ //# sourceMappingURL=PhishingDetector.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PhishingDetector.d.mts","sourceRoot":"","sources":["../src/PhishingDetector.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,sBAAsB,EAC5B,oBAAgB;AAWjB,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B,GAAG,cAAc,CAAC;AAEnB,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,cAAc,CAAC;AAEnB,MAAM,MAAM,cAAc,GACtB;IACE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,GACD;IACE,SAAS,CAAC,EAAE,KAAK,CAAC;IAClB,SAAS,CAAC,EAAE,KAAK,CAAC;CACnB,CAAC;AAEN,MAAM,MAAM,uBAAuB,GAC/B,0BAA0B,GAC1B,oBAAoB,EAAE,CAAC;AAE3B,MAAM,MAAM,6BAA6B,GAAG;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,qBAAa,gBAAgB;;IAK3B;;;;;;;;;OASG;gBACS,IAAI,EAAE,uBAAuB;IAoBzC;;;;;;;OAOG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,sBAAsB;IAsH1C;;;;;;;OAOG;IACH,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,sBAAsB;CAiD/D"}
@@ -1,9 +1,229 @@
1
- import {
2
- PhishingDetector
3
- } from "./chunk-I3U4U3MW.mjs";
4
- import "./chunk-G2RL74NS.mjs";
5
- import "./chunk-XUI43LEZ.mjs";
6
- export {
7
- PhishingDetector
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ 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");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
8
6
  };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ 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");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _PhishingDetector_instances, _PhishingDetector_configs, _PhishingDetector_legacyConfig, _PhishingDetector_check;
13
+ import { distance } from "fastest-levenshtein";
14
+ import { PhishingDetectorResultType } from "./types.mjs";
15
+ import { domainPartsToDomain, domainPartsToFuzzyForm, domainToParts, getDefaultPhishingDetectorConfig, matchPartsAgainstList, processConfigs, sha256Hash } from "./utils.mjs";
16
+ export class PhishingDetector {
17
+ /**
18
+ * Construct a phishing detector, which can check whether origins are known
19
+ * to be malicious or similar to common phishing targets.
20
+ *
21
+ * A list of configurations is accepted. Each origin checked is processed
22
+ * using each configuration in sequence, so the order defines which
23
+ * configurations take precedence.
24
+ *
25
+ * @param opts - Phishing detection options
26
+ */
27
+ constructor(opts) {
28
+ _PhishingDetector_instances.add(this);
29
+ _PhishingDetector_configs.set(this, void 0);
30
+ _PhishingDetector_legacyConfig.set(this, void 0);
31
+ // recommended configuration
32
+ if (Array.isArray(opts)) {
33
+ __classPrivateFieldSet(this, _PhishingDetector_configs, processConfigs(opts), "f");
34
+ __classPrivateFieldSet(this, _PhishingDetector_legacyConfig, false, "f");
35
+ // legacy configuration
36
+ }
37
+ else {
38
+ __classPrivateFieldSet(this, _PhishingDetector_configs, [
39
+ getDefaultPhishingDetectorConfig({
40
+ allowlist: opts.whitelist,
41
+ blocklist: opts.blacklist,
42
+ c2DomainBlocklist: opts.c2DomainBlocklist,
43
+ fuzzylist: opts.fuzzylist,
44
+ tolerance: opts.tolerance,
45
+ }),
46
+ ], "f");
47
+ __classPrivateFieldSet(this, _PhishingDetector_legacyConfig, true, "f");
48
+ }
49
+ }
50
+ /**
51
+ * Check if a url is known to be malicious or similar to a common phishing
52
+ * target. This will check the hostname and IPFS CID that is sometimes
53
+ * located in the path.
54
+ *
55
+ * @param url - The url to check.
56
+ * @returns The result of the check.
57
+ */
58
+ check(url) {
59
+ const result = __classPrivateFieldGet(this, _PhishingDetector_instances, "m", _PhishingDetector_check).call(this, url);
60
+ if (__classPrivateFieldGet(this, _PhishingDetector_legacyConfig, "f")) {
61
+ let legacyType = result.type;
62
+ if (legacyType === PhishingDetectorResultType.Allowlist) {
63
+ legacyType = PhishingDetectorResultType.Whitelist;
64
+ }
65
+ else if (legacyType === PhishingDetectorResultType.Blocklist) {
66
+ legacyType = PhishingDetectorResultType.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
+ }
89
+ catch (error) {
90
+ return {
91
+ result: false,
92
+ type: PhishingDetectorResultType.C2DomainBlocklist,
93
+ };
94
+ }
95
+ const fqdn = hostname.endsWith('.') ? hostname.slice(0, -1) : hostname;
96
+ const source = domainToParts(fqdn);
97
+ for (const { allowlist, name, version } of __classPrivateFieldGet(this, _PhishingDetector_configs, "f")) {
98
+ // if source matches allowlist hostname (or subdomain thereof), PASS
99
+ const allowlistMatch = matchPartsAgainstList(source, allowlist);
100
+ if (allowlistMatch) {
101
+ const match = domainPartsToDomain(allowlistMatch);
102
+ return {
103
+ match,
104
+ name,
105
+ result: false,
106
+ type: PhishingDetectorResultType.Allowlist,
107
+ version: version === undefined ? version : String(version),
108
+ };
109
+ }
110
+ }
111
+ for (const { c2DomainBlocklist, name, version } of __classPrivateFieldGet(this, _PhishingDetector_configs, "f")) {
112
+ const hash = sha256Hash(hostname.toLowerCase());
113
+ const blocked = c2DomainBlocklist?.includes(hash) ?? false;
114
+ if (blocked) {
115
+ return {
116
+ name,
117
+ result: true,
118
+ type: PhishingDetectorResultType.C2DomainBlocklist,
119
+ version: version === undefined ? version : String(version),
120
+ };
121
+ }
122
+ }
123
+ // did not match, PASS
124
+ return {
125
+ result: false,
126
+ type: PhishingDetectorResultType.C2DomainBlocklist,
127
+ };
128
+ }
129
+ }
130
+ _PhishingDetector_configs = new WeakMap(), _PhishingDetector_legacyConfig = new WeakMap(), _PhishingDetector_instances = new WeakSet(), _PhishingDetector_check = function _PhishingDetector_check(url) {
131
+ const ipfsCidMatch = url.match(ipfsCidRegex());
132
+ // Check for IPFS CID related blocklist entries
133
+ if (ipfsCidMatch !== null) {
134
+ // there is a cID string somewhere
135
+ // Determine if any of the entries are ipfs cids
136
+ // Depending on the gateway, the CID is in the path OR a subdomain, so we do a regex match on it all
137
+ const cID = ipfsCidMatch[0];
138
+ for (const { blocklist, name, version } of __classPrivateFieldGet(this, _PhishingDetector_configs, "f")) {
139
+ const blocklistMatch = blocklist
140
+ .filter((entries) => entries.length === 1)
141
+ .find((entries) => {
142
+ return entries[0] === cID;
143
+ });
144
+ if (blocklistMatch) {
145
+ return {
146
+ name,
147
+ match: cID,
148
+ result: true,
149
+ type: PhishingDetectorResultType.Blocklist,
150
+ version: version === undefined ? version : String(version),
151
+ };
152
+ }
153
+ }
154
+ }
155
+ let domain;
156
+ try {
157
+ domain = new URL(url).hostname;
158
+ }
159
+ catch (error) {
160
+ return {
161
+ result: false,
162
+ type: PhishingDetectorResultType.All,
163
+ };
164
+ }
165
+ const fqdn = domain.endsWith('.') ? domain.slice(0, -1) : domain;
166
+ const source = domainToParts(fqdn);
167
+ for (const { allowlist, name, version } of __classPrivateFieldGet(this, _PhishingDetector_configs, "f")) {
168
+ // if source matches allowlist hostname (or subdomain thereof), PASS
169
+ const allowlistMatch = matchPartsAgainstList(source, allowlist);
170
+ if (allowlistMatch) {
171
+ const match = domainPartsToDomain(allowlistMatch);
172
+ return {
173
+ match,
174
+ name,
175
+ result: false,
176
+ type: PhishingDetectorResultType.Allowlist,
177
+ version: version === undefined ? version : String(version),
178
+ };
179
+ }
180
+ }
181
+ for (const { blocklist, fuzzylist, name, tolerance, version } of __classPrivateFieldGet(this, _PhishingDetector_configs, "f")) {
182
+ // if source matches blocklist hostname (or subdomain thereof), FAIL
183
+ const blocklistMatch = matchPartsAgainstList(source, blocklist);
184
+ if (blocklistMatch) {
185
+ const match = domainPartsToDomain(blocklistMatch);
186
+ return {
187
+ match,
188
+ name,
189
+ result: true,
190
+ type: PhishingDetectorResultType.Blocklist,
191
+ version: version === undefined ? version : String(version),
192
+ };
193
+ }
194
+ if (tolerance > 0) {
195
+ // check if near-match of whitelist domain, FAIL
196
+ let fuzzyForm = domainPartsToFuzzyForm(source);
197
+ // strip www
198
+ fuzzyForm = fuzzyForm.replace(/^www\./u, '');
199
+ // check against fuzzylist
200
+ const levenshteinMatched = fuzzylist.find((targetParts) => {
201
+ const fuzzyTarget = domainPartsToFuzzyForm(targetParts);
202
+ const dist = distance(fuzzyForm, fuzzyTarget);
203
+ return dist <= tolerance;
204
+ });
205
+ if (levenshteinMatched) {
206
+ const match = domainPartsToDomain(levenshteinMatched);
207
+ return {
208
+ name,
209
+ match,
210
+ result: true,
211
+ type: PhishingDetectorResultType.Fuzzy,
212
+ version: version === undefined ? version : String(version),
213
+ };
214
+ }
215
+ }
216
+ }
217
+ // matched nothing, PASS
218
+ return { result: false, type: PhishingDetectorResultType.All };
219
+ };
220
+ /**
221
+ * Runs a regex match to determine if a string is a IPFS CID
222
+ * @returns Regex string for IPFS CID
223
+ */
224
+ function ipfsCidRegex() {
225
+ // regex from https://stackoverflow.com/a/67176726
226
+ 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,}';
227
+ return new RegExp(reg, 'u');
228
+ }
9
229
  //# sourceMappingURL=PhishingDetector.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"file":"PhishingDetector.mjs","sourceRoot":"","sources":["../src/PhishingDetector.ts"],"names":[],"mappings":"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","sourcesContent":["import { distance } from 'fastest-levenshtein';\n\nimport {\n PhishingDetectorResultType,\n type PhishingDetectorResult,\n} from './types';\nimport {\n domainPartsToDomain,\n domainPartsToFuzzyForm,\n domainToParts,\n getDefaultPhishingDetectorConfig,\n matchPartsAgainstList,\n processConfigs,\n sha256Hash,\n} from './utils';\n\nexport type LegacyPhishingDetectorList = {\n whitelist?: string[];\n blacklist?: string[];\n c2DomainBlocklist?: string[];\n} & FuzzyTolerance;\n\nexport type PhishingDetectorList = {\n allowlist?: string[];\n blocklist?: string[];\n c2DomainBlocklist?: string[];\n name?: string;\n version?: string | number;\n tolerance?: number;\n} & FuzzyTolerance;\n\nexport type FuzzyTolerance =\n | {\n tolerance?: number;\n fuzzylist: string[];\n }\n | {\n tolerance?: never;\n fuzzylist?: never;\n };\n\nexport type PhishingDetectorOptions =\n | LegacyPhishingDetectorList\n | PhishingDetectorList[];\n\nexport type PhishingDetectorConfiguration = {\n name?: string;\n version?: number | string;\n allowlist: string[][];\n blocklist: string[][];\n c2DomainBlocklist?: string[];\n fuzzylist: string[][];\n tolerance: number;\n};\n\nexport class PhishingDetector {\n #configs: PhishingDetectorConfiguration[];\n\n #legacyConfig: boolean;\n\n /**\n * Construct a phishing detector, which can check whether origins are known\n * to be malicious or similar to common phishing targets.\n *\n * A list of configurations is accepted. Each origin checked is processed\n * using each configuration in sequence, so the order defines which\n * configurations take precedence.\n *\n * @param opts - Phishing detection options\n */\n constructor(opts: PhishingDetectorOptions) {\n // recommended configuration\n if (Array.isArray(opts)) {\n this.#configs = processConfigs(opts);\n this.#legacyConfig = false;\n // legacy configuration\n } else {\n this.#configs = [\n getDefaultPhishingDetectorConfig({\n allowlist: opts.whitelist,\n blocklist: opts.blacklist,\n c2DomainBlocklist: opts.c2DomainBlocklist,\n fuzzylist: opts.fuzzylist,\n tolerance: opts.tolerance,\n }),\n ];\n this.#legacyConfig = true;\n }\n }\n\n /**\n * Check if a url is known to be malicious or similar to a common phishing\n * target. This will check the hostname and IPFS CID that is sometimes\n * located in the path.\n *\n * @param url - The url to check.\n * @returns The result of the check.\n */\n check(url: string): PhishingDetectorResult {\n const result = this.#check(url);\n\n if (this.#legacyConfig) {\n let legacyType = result.type;\n if (legacyType === PhishingDetectorResultType.Allowlist) {\n legacyType = PhishingDetectorResultType.Whitelist;\n } else if (legacyType === PhishingDetectorResultType.Blocklist) {\n legacyType = PhishingDetectorResultType.Blacklist;\n }\n return {\n match: result.match,\n result: result.result,\n type: legacyType,\n };\n }\n return result;\n }\n\n #check(url: string): PhishingDetectorResult {\n const ipfsCidMatch = url.match(ipfsCidRegex());\n\n // Check for IPFS CID related blocklist entries\n if (ipfsCidMatch !== null) {\n // there is a cID string somewhere\n // Determine if any of the entries are ipfs cids\n // Depending on the gateway, the CID is in the path OR a subdomain, so we do a regex match on it all\n const cID = ipfsCidMatch[0];\n for (const { blocklist, name, version } of this.#configs) {\n const blocklistMatch = blocklist\n .filter((entries) => entries.length === 1)\n .find((entries) => {\n return entries[0] === cID;\n });\n if (blocklistMatch) {\n return {\n name,\n match: cID,\n result: true,\n type: PhishingDetectorResultType.Blocklist,\n version: version === undefined ? version : String(version),\n };\n }\n }\n }\n\n let domain;\n try {\n domain = new URL(url).hostname;\n } catch (error) {\n return {\n result: false,\n type: PhishingDetectorResultType.All,\n };\n }\n\n const fqdn = domain.endsWith('.') ? domain.slice(0, -1) : domain;\n\n const source = domainToParts(fqdn);\n\n for (const { allowlist, name, version } of this.#configs) {\n // if source matches allowlist hostname (or subdomain thereof), PASS\n const allowlistMatch = matchPartsAgainstList(source, allowlist);\n if (allowlistMatch) {\n const match = domainPartsToDomain(allowlistMatch);\n return {\n match,\n name,\n result: false,\n type: PhishingDetectorResultType.Allowlist,\n version: version === undefined ? version : String(version),\n };\n }\n }\n\n for (const { blocklist, fuzzylist, name, tolerance, version } of this\n .#configs) {\n // if source matches blocklist hostname (or subdomain thereof), FAIL\n const blocklistMatch = matchPartsAgainstList(source, blocklist);\n if (blocklistMatch) {\n const match = domainPartsToDomain(blocklistMatch);\n return {\n match,\n name,\n result: true,\n type: PhishingDetectorResultType.Blocklist,\n version: version === undefined ? version : String(version),\n };\n }\n\n if (tolerance > 0) {\n // check if near-match of whitelist domain, FAIL\n let fuzzyForm = domainPartsToFuzzyForm(source);\n // strip www\n fuzzyForm = fuzzyForm.replace(/^www\\./u, '');\n // check against fuzzylist\n const levenshteinMatched = fuzzylist.find((targetParts) => {\n const fuzzyTarget = domainPartsToFuzzyForm(targetParts);\n const dist = distance(fuzzyForm, fuzzyTarget);\n return dist <= tolerance;\n });\n if (levenshteinMatched) {\n const match = domainPartsToDomain(levenshteinMatched);\n return {\n name,\n match,\n result: true,\n type: PhishingDetectorResultType.Fuzzy,\n version: version === undefined ? version : String(version),\n };\n }\n }\n }\n\n // matched nothing, PASS\n return { result: false, type: PhishingDetectorResultType.All };\n }\n\n /**\n * Checks if a URL is blocked against the hashed request blocklist.\n * This is done by hashing the URL's hostname and checking it against the hashed request blocklist.\n *\n *\n * @param urlString - The URL to check.\n * @returns An object indicating if the URL is blocked and relevant metadata.\n */\n isMaliciousC2Domain(urlString: string): PhishingDetectorResult {\n let hostname;\n try {\n hostname = new URL(urlString).hostname;\n } catch (error) {\n return {\n result: false,\n type: PhishingDetectorResultType.C2DomainBlocklist,\n };\n }\n\n const fqdn = hostname.endsWith('.') ? hostname.slice(0, -1) : hostname;\n\n const source = domainToParts(fqdn);\n\n for (const { allowlist, name, version } of this.#configs) {\n // if source matches allowlist hostname (or subdomain thereof), PASS\n const allowlistMatch = matchPartsAgainstList(source, allowlist);\n if (allowlistMatch) {\n const match = domainPartsToDomain(allowlistMatch);\n return {\n match,\n name,\n result: false,\n type: PhishingDetectorResultType.Allowlist,\n version: version === undefined ? version : String(version),\n };\n }\n }\n\n for (const { c2DomainBlocklist, name, version } of this.#configs) {\n const hash = sha256Hash(hostname.toLowerCase());\n const blocked = c2DomainBlocklist?.includes(hash) ?? false;\n\n if (blocked) {\n return {\n name,\n result: true,\n type: PhishingDetectorResultType.C2DomainBlocklist,\n version: version === undefined ? version : String(version),\n };\n }\n }\n // did not match, PASS\n return {\n result: false,\n type: PhishingDetectorResultType.C2DomainBlocklist,\n };\n }\n}\n\n/**\n * Runs a regex match to determine if a string is a IPFS CID\n * @returns Regex string for IPFS CID\n */\nfunction ipfsCidRegex() {\n // regex from https://stackoverflow.com/a/67176726\n const reg =\n '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,}';\n return new RegExp(reg, 'u');\n}\n"]}
package/dist/index.cjs ADDED
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.PhishingDetectorResultType = exports.PhishingDetector = void 0;
18
+ __exportStar(require("./PhishingController.cjs"), exports);
19
+ var PhishingDetector_1 = require("./PhishingDetector.cjs");
20
+ Object.defineProperty(exports, "PhishingDetector", { enumerable: true, get: function () { return PhishingDetector_1.PhishingDetector; } });
21
+ var types_1 = require("./types.cjs");
22
+ Object.defineProperty(exports, "PhishingDetectorResultType", { enumerable: true, get: function () { return types_1.PhishingDetectorResultType; } });
23
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,2DAAqC;AAQrC,2DAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AACzB,qCAAqD;AAA5C,mHAAA,0BAA0B,OAAA","sourcesContent":["export * from './PhishingController';\nexport type {\n LegacyPhishingDetectorList,\n PhishingDetectorList,\n FuzzyTolerance,\n PhishingDetectorOptions,\n PhishingDetectorConfiguration,\n} from './PhishingDetector';\nexport { PhishingDetector } from './PhishingDetector';\nexport { PhishingDetectorResultType } from './types';\n"]}