@metamask-previews/phishing-controller 13.1.0-preview-5a701133 → 14.0.0-preview-c20b7569
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -1
- package/dist/CacheManager.cjs +177 -0
- package/dist/CacheManager.cjs.map +1 -0
- package/dist/CacheManager.d.cts +104 -0
- package/dist/CacheManager.d.cts.map +1 -0
- package/dist/CacheManager.d.mts +104 -0
- package/dist/CacheManager.d.mts.map +1 -0
- package/dist/CacheManager.mjs +173 -0
- package/dist/CacheManager.mjs.map +1 -0
- package/dist/PhishingController.cjs +186 -8
- package/dist/PhishingController.cjs.map +1 -1
- package/dist/PhishingController.d.cts +41 -6
- package/dist/PhishingController.d.cts.map +1 -1
- package/dist/PhishingController.d.mts +41 -6
- package/dist/PhishingController.d.mts.map +1 -1
- package/dist/PhishingController.mjs +186 -8
- package/dist/PhishingController.mjs.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/tests/utils.cjs +79 -1
- package/dist/tests/utils.cjs.map +1 -1
- package/dist/tests/utils.d.cts +59 -0
- package/dist/tests/utils.d.cts.map +1 -1
- package/dist/tests/utils.d.mts +59 -0
- package/dist/tests/utils.d.mts.map +1 -1
- package/dist/tests/utils.mjs +75 -0
- package/dist/tests/utils.mjs.map +1 -1
- package/dist/types.cjs +35 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +68 -0
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +68 -0
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +34 -0
- package/dist/types.mjs.map +1 -1
- package/dist/utils.cjs +54 -1
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +55 -0
- package/dist/utils.d.cts.map +1 -1
- package/dist/utils.d.mts +55 -0
- package/dist/utils.d.mts.map +1 -1
- package/dist/utils.mjs +50 -0
- package/dist/utils.mjs.map +1 -1
- package/package.json +5 -1
- package/dist/UrlScanCache.cjs +0 -127
- package/dist/UrlScanCache.cjs.map +0 -1
- package/dist/UrlScanCache.d.cts +0 -67
- package/dist/UrlScanCache.d.cts.map +0 -1
- package/dist/UrlScanCache.d.mts +0 -67
- package/dist/UrlScanCache.d.mts.map +0 -1
- package/dist/UrlScanCache.mjs +0 -123
- package/dist/UrlScanCache.mjs.map +0 -1
package/dist/utils.d.cts
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import type { Hotlist, PhishingListState } from "./PhishingController.cjs";
|
2
2
|
import { ListKeys } from "./PhishingController.cjs";
|
3
3
|
import type { PhishingDetectorList, PhishingDetectorConfiguration } from "./PhishingDetector.cjs";
|
4
|
+
import { type TokenScanCacheData, type TokenScanResult } from "./types.cjs";
|
4
5
|
/**
|
5
6
|
* Fetches current epoch time in seconds.
|
6
7
|
*
|
@@ -137,4 +138,58 @@ export declare const getHostnameFromWebUrl: (url: string) => [string, boolean];
|
|
137
138
|
* // Returns: ['example.com', 'sub.example.com']
|
138
139
|
*/
|
139
140
|
export declare const generateParentDomains: (sourceParts: string[], limit?: number) => string[];
|
141
|
+
/**
|
142
|
+
* Builds a cache key for a token scan result.
|
143
|
+
*
|
144
|
+
* @param chainId - The chain ID.
|
145
|
+
* @param address - The token address.
|
146
|
+
* @returns The cache key.
|
147
|
+
*/
|
148
|
+
export declare const buildCacheKey: (chainId: string, address: string) => string;
|
149
|
+
/**
|
150
|
+
* Resolves the chain name from a chain ID.
|
151
|
+
*
|
152
|
+
* @param chainId - The chain ID.
|
153
|
+
* @param mapping - The mapping of chain IDs to chain names.
|
154
|
+
* @returns The chain name.
|
155
|
+
*/
|
156
|
+
export declare const resolveChainName: (chainId: string, mapping?: {
|
157
|
+
readonly '0x1': "ethereum";
|
158
|
+
readonly '0x89': "polygon";
|
159
|
+
readonly '0x38': "bsc";
|
160
|
+
readonly '0xa4b1': "arbitrum";
|
161
|
+
readonly '0xa86a': "avalanche";
|
162
|
+
readonly '0x2105': "base";
|
163
|
+
readonly '0xa': "optimism";
|
164
|
+
readonly '0x76adf1': "zora";
|
165
|
+
readonly '0xe708': "linea";
|
166
|
+
readonly '0x27bc86aa': "degen";
|
167
|
+
readonly '0x144': "zksync";
|
168
|
+
readonly '0x82750': "scroll";
|
169
|
+
readonly '0x13e31': "blast";
|
170
|
+
readonly '0x74c': "soneium";
|
171
|
+
readonly '0x79a': "soneium-minato";
|
172
|
+
readonly '0x14a34': "base-sepolia";
|
173
|
+
readonly '0xab5': "abstract";
|
174
|
+
readonly '0x849ea': "zero-network";
|
175
|
+
readonly '0x138de': "berachain";
|
176
|
+
readonly '0x82': "unichain";
|
177
|
+
readonly '0x7e4': "ronin";
|
178
|
+
readonly '0x127': "hedera";
|
179
|
+
}) => string | null;
|
180
|
+
/**
|
181
|
+
* Split tokens into cached results and tokens that need to be fetched.
|
182
|
+
*
|
183
|
+
* @param cache - Cache-like object with get method.
|
184
|
+
* @param cache.get - Method to retrieve cached data by key.
|
185
|
+
* @param chainId - The chain ID.
|
186
|
+
* @param tokens - Array of token addresses.
|
187
|
+
* @returns Object containing cached results and tokens to fetch.
|
188
|
+
*/
|
189
|
+
export declare const splitCacheHits: (cache: {
|
190
|
+
get: (key: string) => TokenScanCacheData | undefined;
|
191
|
+
}, chainId: string, tokens: string[]) => {
|
192
|
+
cachedResults: Record<string, TokenScanResult>;
|
193
|
+
tokensToFetch: string[];
|
194
|
+
};
|
140
195
|
//# sourceMappingURL=utils.d.cts.map
|
package/dist/utils.d.cts.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"utils.d.cts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,iCAA6B;AACvE,OAAO,EAAE,QAAQ,EAA0B,iCAA6B;AACxE,OAAO,KAAK,EACV,oBAAoB,EACpB,6BAA6B,EAC9B,+BAA2B;
|
1
|
+
{"version":3,"file":"utils.d.cts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,iCAA6B;AACvE,OAAO,EAAE,QAAQ,EAA0B,iCAA6B;AACxE,OAAO,KAAK,EACV,oBAAoB,EACpB,6BAA6B,EAC9B,+BAA2B;AAC5B,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACrB,oBAAgB;AAIjB;;;;GAIG;AACH,eAAO,MAAM,YAAY,QAAO,MAAuC,CAAC;AAExE;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAElE;AAkBD;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,cACV,iBAAiB,kCAEnB,QAAQ,2BACO,MAAM,EAAE,6BACN,MAAM,EAAE,KACjC,iBAoDF,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,MAAM,IAAI,iBAAiB,CAuBrC;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,WAAY,MAAM,aAM3C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,SAAU,MAAM,EAAE,eAE/C,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gCAAgC;;;;;;MAWzC,6BAKF,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,cAAc,aAChB,oBAAoB,EAAE,KAC9B,6BAA6B,EAe/B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,gBAAiB,MAAM,EAAE,WAExD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,gBAAiB,MAAM,EAAE,WAE3D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,WAAY,MAAM,EAAE,QAAQ,MAAM,EAAE,EAAE,yBASvE,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,UAAU,aAAc,MAAM,KAAG,MAG7C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,QAAS,MAAM,KAAG,MAAM,GAAG,IAYzD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,QAAS,MAAM,KAAG,CAAC,MAAM,EAAE,OAAO,CAUnE,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,qBAAqB,gBACnB,MAAM,EAAE,qBAEpB,MAAM,EA2BR,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,YAAa,MAAM,WAAW,MAAM,WAE7D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,YAClB,MAAM;;;;;;;;;;;;;;;;;;;;;;;MAEd,MAAM,GAAG,IAEX,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc;eACL,MAAM,KAAK,kBAAkB,GAAG,SAAS;YACpD,MAAM,UACP,MAAM,EAAE;mBAED,OAAO,MAAM,EAAE,eAAe,CAAC;mBAC/B,MAAM,EAAE;CAqBxB,CAAC"}
|
package/dist/utils.d.mts
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import type { Hotlist, PhishingListState } from "./PhishingController.mjs";
|
2
2
|
import { ListKeys } from "./PhishingController.mjs";
|
3
3
|
import type { PhishingDetectorList, PhishingDetectorConfiguration } from "./PhishingDetector.mjs";
|
4
|
+
import { type TokenScanCacheData, type TokenScanResult } from "./types.mjs";
|
4
5
|
/**
|
5
6
|
* Fetches current epoch time in seconds.
|
6
7
|
*
|
@@ -137,4 +138,58 @@ export declare const getHostnameFromWebUrl: (url: string) => [string, boolean];
|
|
137
138
|
* // Returns: ['example.com', 'sub.example.com']
|
138
139
|
*/
|
139
140
|
export declare const generateParentDomains: (sourceParts: string[], limit?: number) => string[];
|
141
|
+
/**
|
142
|
+
* Builds a cache key for a token scan result.
|
143
|
+
*
|
144
|
+
* @param chainId - The chain ID.
|
145
|
+
* @param address - The token address.
|
146
|
+
* @returns The cache key.
|
147
|
+
*/
|
148
|
+
export declare const buildCacheKey: (chainId: string, address: string) => string;
|
149
|
+
/**
|
150
|
+
* Resolves the chain name from a chain ID.
|
151
|
+
*
|
152
|
+
* @param chainId - The chain ID.
|
153
|
+
* @param mapping - The mapping of chain IDs to chain names.
|
154
|
+
* @returns The chain name.
|
155
|
+
*/
|
156
|
+
export declare const resolveChainName: (chainId: string, mapping?: {
|
157
|
+
readonly '0x1': "ethereum";
|
158
|
+
readonly '0x89': "polygon";
|
159
|
+
readonly '0x38': "bsc";
|
160
|
+
readonly '0xa4b1': "arbitrum";
|
161
|
+
readonly '0xa86a': "avalanche";
|
162
|
+
readonly '0x2105': "base";
|
163
|
+
readonly '0xa': "optimism";
|
164
|
+
readonly '0x76adf1': "zora";
|
165
|
+
readonly '0xe708': "linea";
|
166
|
+
readonly '0x27bc86aa': "degen";
|
167
|
+
readonly '0x144': "zksync";
|
168
|
+
readonly '0x82750': "scroll";
|
169
|
+
readonly '0x13e31': "blast";
|
170
|
+
readonly '0x74c': "soneium";
|
171
|
+
readonly '0x79a': "soneium-minato";
|
172
|
+
readonly '0x14a34': "base-sepolia";
|
173
|
+
readonly '0xab5': "abstract";
|
174
|
+
readonly '0x849ea': "zero-network";
|
175
|
+
readonly '0x138de': "berachain";
|
176
|
+
readonly '0x82': "unichain";
|
177
|
+
readonly '0x7e4': "ronin";
|
178
|
+
readonly '0x127': "hedera";
|
179
|
+
}) => string | null;
|
180
|
+
/**
|
181
|
+
* Split tokens into cached results and tokens that need to be fetched.
|
182
|
+
*
|
183
|
+
* @param cache - Cache-like object with get method.
|
184
|
+
* @param cache.get - Method to retrieve cached data by key.
|
185
|
+
* @param chainId - The chain ID.
|
186
|
+
* @param tokens - Array of token addresses.
|
187
|
+
* @returns Object containing cached results and tokens to fetch.
|
188
|
+
*/
|
189
|
+
export declare const splitCacheHits: (cache: {
|
190
|
+
get: (key: string) => TokenScanCacheData | undefined;
|
191
|
+
}, chainId: string, tokens: string[]) => {
|
192
|
+
cachedResults: Record<string, TokenScanResult>;
|
193
|
+
tokensToFetch: string[];
|
194
|
+
};
|
140
195
|
//# sourceMappingURL=utils.d.mts.map
|
package/dist/utils.d.mts.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"utils.d.mts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,iCAA6B;AACvE,OAAO,EAAE,QAAQ,EAA0B,iCAA6B;AACxE,OAAO,KAAK,EACV,oBAAoB,EACpB,6BAA6B,EAC9B,+BAA2B;
|
1
|
+
{"version":3,"file":"utils.d.mts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,iCAA6B;AACvE,OAAO,EAAE,QAAQ,EAA0B,iCAA6B;AACxE,OAAO,KAAK,EACV,oBAAoB,EACpB,6BAA6B,EAC9B,+BAA2B;AAC5B,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACrB,oBAAgB;AAIjB;;;;GAIG;AACH,eAAO,MAAM,YAAY,QAAO,MAAuC,CAAC;AAExE;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAElE;AAkBD;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,cACV,iBAAiB,kCAEnB,QAAQ,2BACO,MAAM,EAAE,6BACN,MAAM,EAAE,KACjC,iBAoDF,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,MAAM,IAAI,iBAAiB,CAuBrC;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,WAAY,MAAM,aAM3C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,SAAU,MAAM,EAAE,eAE/C,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gCAAgC;;;;;;MAWzC,6BAKF,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,cAAc,aAChB,oBAAoB,EAAE,KAC9B,6BAA6B,EAe/B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,gBAAiB,MAAM,EAAE,WAExD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,gBAAiB,MAAM,EAAE,WAE3D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,WAAY,MAAM,EAAE,QAAQ,MAAM,EAAE,EAAE,yBASvE,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,UAAU,aAAc,MAAM,KAAG,MAG7C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,QAAS,MAAM,KAAG,MAAM,GAAG,IAYzD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,QAAS,MAAM,KAAG,CAAC,MAAM,EAAE,OAAO,CAUnE,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,qBAAqB,gBACnB,MAAM,EAAE,qBAEpB,MAAM,EA2BR,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,YAAa,MAAM,WAAW,MAAM,WAE7D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,YAClB,MAAM;;;;;;;;;;;;;;;;;;;;;;;MAEd,MAAM,GAAG,IAEX,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc;eACL,MAAM,KAAK,kBAAkB,GAAG,SAAS;YACpD,MAAM,UACP,MAAM,EAAE;mBAED,OAAO,MAAM,EAAE,eAAe,CAAC;mBAC/B,MAAM,EAAE;CAqBxB,CAAC"}
|
package/dist/utils.mjs
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import { bytesToHex } from "@noble/hashes/utils";
|
2
2
|
import { sha256 } from "ethereum-cryptography/sha256";
|
3
3
|
import { ListKeys, phishingListKeyNameMap } from "./PhishingController.mjs";
|
4
|
+
import { DEFAULT_CHAIN_ID_TO_NAME } from "./types.mjs";
|
4
5
|
const DEFAULT_TOLERANCE = 3;
|
5
6
|
/**
|
6
7
|
* Fetches current epoch time in seconds.
|
@@ -295,4 +296,53 @@ export const generateParentDomains = (sourceParts, limit = 5) => {
|
|
295
296
|
}
|
296
297
|
return domains;
|
297
298
|
};
|
299
|
+
/**
|
300
|
+
* Builds a cache key for a token scan result.
|
301
|
+
*
|
302
|
+
* @param chainId - The chain ID.
|
303
|
+
* @param address - The token address.
|
304
|
+
* @returns The cache key.
|
305
|
+
*/
|
306
|
+
export const buildCacheKey = (chainId, address) => {
|
307
|
+
return `${chainId.toLowerCase()}:${address.toLowerCase()}`;
|
308
|
+
};
|
309
|
+
/**
|
310
|
+
* Resolves the chain name from a chain ID.
|
311
|
+
*
|
312
|
+
* @param chainId - The chain ID.
|
313
|
+
* @param mapping - The mapping of chain IDs to chain names.
|
314
|
+
* @returns The chain name.
|
315
|
+
*/
|
316
|
+
export const resolveChainName = (chainId, mapping = DEFAULT_CHAIN_ID_TO_NAME) => {
|
317
|
+
return mapping[chainId.toLowerCase()] ?? null;
|
318
|
+
};
|
319
|
+
/**
|
320
|
+
* Split tokens into cached results and tokens that need to be fetched.
|
321
|
+
*
|
322
|
+
* @param cache - Cache-like object with get method.
|
323
|
+
* @param cache.get - Method to retrieve cached data by key.
|
324
|
+
* @param chainId - The chain ID.
|
325
|
+
* @param tokens - Array of token addresses.
|
326
|
+
* @returns Object containing cached results and tokens to fetch.
|
327
|
+
*/
|
328
|
+
export const splitCacheHits = (cache, chainId, tokens) => {
|
329
|
+
const cachedResults = {};
|
330
|
+
const tokensToFetch = [];
|
331
|
+
for (const addr of tokens) {
|
332
|
+
const normalizedAddr = addr.toLowerCase();
|
333
|
+
const key = buildCacheKey(chainId, normalizedAddr);
|
334
|
+
const hit = cache.get(key);
|
335
|
+
if (hit) {
|
336
|
+
cachedResults[normalizedAddr] = {
|
337
|
+
result_type: hit.result_type,
|
338
|
+
chain: chainId,
|
339
|
+
address: normalizedAddr,
|
340
|
+
};
|
341
|
+
}
|
342
|
+
else {
|
343
|
+
tokensToFetch.push(normalizedAddr);
|
344
|
+
}
|
345
|
+
}
|
346
|
+
return { cachedResults, tokensToFetch };
|
347
|
+
};
|
298
348
|
//# sourceMappingURL=utils.mjs.map
|
package/dist/utils.mjs.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"utils.mjs","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { bytesToHex } from '@noble/hashes/utils';\nimport { sha256 } from 'ethereum-cryptography/sha256';\n\nimport type { Hotlist, PhishingListState } from './PhishingController';\nimport { ListKeys, phishingListKeyNameMap } from './PhishingController';\nimport type {\n PhishingDetectorList,\n PhishingDetectorConfiguration,\n} from './PhishingDetector';\n\nconst DEFAULT_TOLERANCE = 3;\n\n/**\n * Fetches current epoch time in seconds.\n *\n * @returns the Date.now() time in seconds instead of miliseconds. backend files rely on timestamps in seconds since epoch.\n */\nexport const fetchTimeNow = (): number => Math.round(Date.now() / 1000);\n\n/**\n * Rounds a Unix timestamp down to the nearest minute.\n *\n * @param unixTimestamp - The Unix timestamp to be rounded.\n * @returns The rounded Unix timestamp.\n */\nexport function roundToNearestMinute(unixTimestamp: number): number {\n return Math.floor(unixTimestamp / 60) * 60;\n}\n\n/**\n * Split a string into two pieces, using the first period as the delimiter.\n *\n * @param stringToSplit - The string to split.\n * @returns An array of length two containing the beginning and end of the string.\n */\nconst splitStringByPeriod = <Start extends string, End extends string>(\n stringToSplit: `${Start}.${End}`,\n): [Start, End] => {\n const periodIndex = stringToSplit.indexOf('.');\n return [\n stringToSplit.slice(0, periodIndex) as Start,\n stringToSplit.slice(periodIndex + 1) as End,\n ];\n};\n\n/**\n * Determines which diffs are applicable to the listState, then applies those diffs.\n *\n * @param listState - the stalelist or the existing liststate that diffs will be applied to.\n * @param hotlistDiffs - the diffs to apply to the listState if valid.\n * @param listKey - the key associated with the input/output phishing list state.\n * @param recentlyAddedC2Domains - list of hashed C2 domains to add to the local c2 domain blocklist\n * @param recentlyRemovedC2Domains - list of hashed C2 domains to remove from the local c2 domain blocklist\n * @returns the new list state\n */\nexport const applyDiffs = (\n listState: PhishingListState,\n hotlistDiffs: Hotlist,\n listKey: ListKeys,\n recentlyAddedC2Domains: string[] = [],\n recentlyRemovedC2Domains: string[] = [],\n): PhishingListState => {\n // filter to remove diffs that were added before the lastUpdate time.\n // filter to remove diffs that aren't applicable to the specified list (by listKey).\n const diffsToApply = hotlistDiffs.filter(\n ({ timestamp, targetList }) =>\n timestamp > listState.lastUpdated &&\n splitStringByPeriod(targetList)[0] === listKey,\n );\n\n // the reason behind using latestDiffTimestamp as the lastUpdated time\n // is so that we can benefit server-side from memoization due to end client's\n // `GET /v1/diffSince/:timestamp` requests lining up with\n // our periodic updates (which create diffs at specific timestamps).\n let latestDiffTimestamp = listState.lastUpdated;\n\n const listSets = {\n allowlist: new Set(listState.allowlist),\n blocklist: new Set(listState.blocklist),\n fuzzylist: new Set(listState.fuzzylist),\n c2DomainBlocklist: new Set(listState.c2DomainBlocklist),\n };\n for (const { isRemoval, targetList, url, timestamp } of diffsToApply) {\n const targetListType = splitStringByPeriod(targetList)[1];\n if (timestamp > latestDiffTimestamp) {\n latestDiffTimestamp = timestamp;\n }\n if (isRemoval) {\n listSets[targetListType].delete(url);\n } else {\n listSets[targetListType].add(url);\n }\n }\n\n if (listKey === ListKeys.EthPhishingDetectConfig) {\n for (const hash of recentlyAddedC2Domains) {\n listSets.c2DomainBlocklist.add(hash);\n }\n for (const hash of recentlyRemovedC2Domains) {\n listSets.c2DomainBlocklist.delete(hash);\n }\n }\n\n return {\n c2DomainBlocklist: Array.from(listSets.c2DomainBlocklist),\n allowlist: Array.from(listSets.allowlist),\n blocklist: Array.from(listSets.blocklist),\n fuzzylist: Array.from(listSets.fuzzylist),\n version: listState.version,\n name: phishingListKeyNameMap[listKey],\n tolerance: listState.tolerance,\n lastUpdated: latestDiffTimestamp,\n };\n};\n\n/**\n * Validates the configuration object for the phishing detector.\n *\n * @param config - the configuration object to validate.\n * @throws an error if the configuration is invalid.\n */\nexport function validateConfig(\n config: unknown,\n): asserts config is PhishingListState {\n if (config === null || typeof config !== 'object') {\n throw new Error('Invalid config');\n }\n\n if ('tolerance' in config && !('fuzzylist' in config)) {\n throw new Error('Fuzzylist tolerance provided without fuzzylist');\n }\n\n if (\n 'name' in config &&\n (typeof config.name !== 'string' || config.name === '')\n ) {\n throw new Error(\"Invalid config parameter: 'name'\");\n }\n\n if (\n 'version' in config &&\n (!['number', 'string'].includes(typeof config.version) ||\n config.version === '')\n ) {\n throw new Error(\"Invalid config parameter: 'version'\");\n }\n}\n\n/**\n * Converts a domain string to a list of domain parts.\n *\n * @param domain - the domain string to convert.\n * @returns the list of domain parts.\n */\nexport const domainToParts = (domain: string) => {\n try {\n return domain.split('.').reverse();\n } catch (e) {\n throw new Error(JSON.stringify(domain));\n }\n};\n\n/**\n * Converts a list of domain strings to a list of domain parts.\n *\n * @param list - the list of domain strings to convert.\n * @returns the list of domain parts.\n */\nexport const processDomainList = (list: string[]) => {\n return list.map(domainToParts);\n};\n\n/**\n * Gets the default phishing detector configuration.\n *\n * @param override - the optional override for the configuration.\n * @param override.allowlist - the optional allowlist to override.\n * @param override.blocklist - the optional blocklist to override.\n * @param override.c2DomainBlocklist - the optional c2DomainBlocklist to override.\n * @param override.fuzzylist - the optional fuzzylist to override.\n * @param override.tolerance - the optional tolerance to override.\n * @returns the default phishing detector configuration.\n */\nexport const getDefaultPhishingDetectorConfig = ({\n allowlist = [],\n blocklist = [],\n fuzzylist = [],\n tolerance = DEFAULT_TOLERANCE,\n}: {\n allowlist?: string[];\n blocklist?: string[];\n c2DomainBlocklist?: string[];\n fuzzylist?: string[];\n tolerance?: number;\n}): PhishingDetectorConfiguration => ({\n allowlist: processDomainList(allowlist),\n blocklist: processDomainList(blocklist),\n fuzzylist: processDomainList(fuzzylist),\n tolerance,\n});\n\n/**\n * Processes the configurations for the phishing detector, filtering out any invalid configs.\n *\n * @param configs - The configurations to process.\n * @returns An array of processed and valid configurations.\n */\nexport const processConfigs = (\n configs: PhishingDetectorList[] = [],\n): PhishingDetectorConfiguration[] => {\n return configs\n .filter((config) => {\n try {\n validateConfig(config);\n return true;\n } catch (error) {\n console.error(error);\n return false;\n }\n })\n .map((config) => ({\n ...config,\n ...getDefaultPhishingDetectorConfig(config),\n }));\n};\n\n/**\n * Converts a list of domain parts to a domain string.\n *\n * @param domainParts - the list of domain parts.\n * @returns the domain string.\n */\nexport const domainPartsToDomain = (domainParts: string[]) => {\n return domainParts.slice().reverse().join('.');\n};\n\n/**\n * Converts a list of domain parts to a fuzzy form.\n *\n * @param domainParts - the list of domain parts.\n * @returns the fuzzy form of the domain.\n */\nexport const domainPartsToFuzzyForm = (domainParts: string[]) => {\n return domainParts.slice(1).reverse().join('.');\n};\n\n/**\n * Matches the target parts, ignoring extra subdomains on source.\n *\n * @param source - the source domain parts.\n * @param list - the list of domain parts to match against.\n * @returns the parts for the first found matching entry.\n */\nexport const matchPartsAgainstList = (source: string[], list: string[][]) => {\n return list.find((target) => {\n // target domain has more parts than source, fail\n if (target.length > source.length) {\n return false;\n }\n // source matches target or (is deeper subdomain)\n return target.every((part, index) => source[index] === part);\n });\n};\n\n/**\n * Generate the SHA-256 hash of a hostname.\n *\n * @param hostname - The hostname to hash.\n * @returns The SHA-256 hash of the hostname.\n */\nexport const sha256Hash = (hostname: string): string => {\n const hashBuffer = sha256(new TextEncoder().encode(hostname.toLowerCase()));\n return bytesToHex(hashBuffer);\n};\n\n/**\n * Extracts the hostname from a URL.\n *\n * @param url - The URL to extract the hostname from.\n * @returns The hostname extracted from the URL, or null if the URL is invalid.\n */\nexport const getHostnameFromUrl = (url: string): string | null => {\n let hostname;\n try {\n hostname = new URL(url).hostname;\n // above will not throw if 'http://.' is passed. in fact, any string with a dot will pass.\n if (!hostname || hostname.split('.').join('') === '') {\n return null;\n }\n } catch {\n return null;\n }\n return hostname;\n};\n\n/**\n * getHostnameFromWebUrl returns the hostname from a web URL.\n * It returns the hostname and a boolean indicating if the hostname is valid.\n *\n * @param url - The web URL to extract the hostname from.\n * @returns A tuple containing the extracted hostname and a boolean indicating if the hostname is valid.\n * @example\n * getHostnameFromWebUrl('https://example.com') // Returns: ['example.com', true]\n * getHostnameFromWebUrl('example.com') // Returns: ['', false]\n * getHostnameFromWebUrl('https://') // Returns: ['', false]\n * getHostnameFromWebUrl('') // Returns: ['', false]\n */\nexport const getHostnameFromWebUrl = (url: string): [string, boolean] => {\n if (\n !url.toLowerCase().startsWith('http://') &&\n !url.toLowerCase().startsWith('https://')\n ) {\n return ['', false];\n }\n\n const hostname = getHostnameFromUrl(url);\n return [hostname || '', Boolean(hostname)];\n};\n\n/**\n * Generates all possible parent domains up to a specified limit.\n *\n * @param sourceParts - The list of domain parts in normal order (e.g., ['evil', 'domain', 'co', 'uk']).\n * @param limit - The maximum number of parent domains to generate (default is 5).\n * @returns An array of parent domains starting from the base TLD to the most specific subdomain.\n * @example\n * generateParentDomains(['evil', 'domain', 'co', 'uk'], 5)\n * // Returns: ['co.uk', 'domain.co.uk', 'evil.domain.co.uk']\n *\n * generateParentDomains(['uk'], 5)\n * // Returns: ['uk']\n *\n * generateParentDomains(['sub', 'example', 'com'], 5)\n * // Returns: ['example.com', 'sub.example.com']\n */\nexport const generateParentDomains = (\n sourceParts: string[],\n limit = 5,\n): string[] => {\n const domains: string[] = [];\n\n if (sourceParts.length === 0) {\n return domains;\n }\n\n if (sourceParts.length === 1) {\n // Single-segment hostname (e.g., 'uk')\n domains.push(sourceParts[0].toLowerCase());\n } else {\n // Start with the base domain or TLD (last two labels, e.g., 'co.uk' or 'example.com')\n const baseDomain = sourceParts.slice(-2).join('.');\n domains.push(baseDomain.toLowerCase());\n\n // Iteratively add one subdomain level at a time, up to the specified limit\n for (\n let i = sourceParts.length - 3;\n i >= 0 && domains.length < limit;\n i--\n ) {\n const domain = sourceParts.slice(i).join('.');\n domains.push(domain.toLowerCase());\n }\n }\n\n return domains;\n};\n"]}
|
1
|
+
{"version":3,"file":"utils.mjs","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { bytesToHex } from '@noble/hashes/utils';\nimport { sha256 } from 'ethereum-cryptography/sha256';\n\nimport type { Hotlist, PhishingListState } from './PhishingController';\nimport { ListKeys, phishingListKeyNameMap } from './PhishingController';\nimport type {\n PhishingDetectorList,\n PhishingDetectorConfiguration,\n} from './PhishingDetector';\nimport {\n DEFAULT_CHAIN_ID_TO_NAME,\n type TokenScanCacheData,\n type TokenScanResult,\n} from './types';\n\nconst DEFAULT_TOLERANCE = 3;\n\n/**\n * Fetches current epoch time in seconds.\n *\n * @returns the Date.now() time in seconds instead of miliseconds. backend files rely on timestamps in seconds since epoch.\n */\nexport const fetchTimeNow = (): number => Math.round(Date.now() / 1000);\n\n/**\n * Rounds a Unix timestamp down to the nearest minute.\n *\n * @param unixTimestamp - The Unix timestamp to be rounded.\n * @returns The rounded Unix timestamp.\n */\nexport function roundToNearestMinute(unixTimestamp: number): number {\n return Math.floor(unixTimestamp / 60) * 60;\n}\n\n/**\n * Split a string into two pieces, using the first period as the delimiter.\n *\n * @param stringToSplit - The string to split.\n * @returns An array of length two containing the beginning and end of the string.\n */\nconst splitStringByPeriod = <Start extends string, End extends string>(\n stringToSplit: `${Start}.${End}`,\n): [Start, End] => {\n const periodIndex = stringToSplit.indexOf('.');\n return [\n stringToSplit.slice(0, periodIndex) as Start,\n stringToSplit.slice(periodIndex + 1) as End,\n ];\n};\n\n/**\n * Determines which diffs are applicable to the listState, then applies those diffs.\n *\n * @param listState - the stalelist or the existing liststate that diffs will be applied to.\n * @param hotlistDiffs - the diffs to apply to the listState if valid.\n * @param listKey - the key associated with the input/output phishing list state.\n * @param recentlyAddedC2Domains - list of hashed C2 domains to add to the local c2 domain blocklist\n * @param recentlyRemovedC2Domains - list of hashed C2 domains to remove from the local c2 domain blocklist\n * @returns the new list state\n */\nexport const applyDiffs = (\n listState: PhishingListState,\n hotlistDiffs: Hotlist,\n listKey: ListKeys,\n recentlyAddedC2Domains: string[] = [],\n recentlyRemovedC2Domains: string[] = [],\n): PhishingListState => {\n // filter to remove diffs that were added before the lastUpdate time.\n // filter to remove diffs that aren't applicable to the specified list (by listKey).\n const diffsToApply = hotlistDiffs.filter(\n ({ timestamp, targetList }) =>\n timestamp > listState.lastUpdated &&\n splitStringByPeriod(targetList)[0] === listKey,\n );\n\n // the reason behind using latestDiffTimestamp as the lastUpdated time\n // is so that we can benefit server-side from memoization due to end client's\n // `GET /v1/diffSince/:timestamp` requests lining up with\n // our periodic updates (which create diffs at specific timestamps).\n let latestDiffTimestamp = listState.lastUpdated;\n\n const listSets = {\n allowlist: new Set(listState.allowlist),\n blocklist: new Set(listState.blocklist),\n fuzzylist: new Set(listState.fuzzylist),\n c2DomainBlocklist: new Set(listState.c2DomainBlocklist),\n };\n for (const { isRemoval, targetList, url, timestamp } of diffsToApply) {\n const targetListType = splitStringByPeriod(targetList)[1];\n if (timestamp > latestDiffTimestamp) {\n latestDiffTimestamp = timestamp;\n }\n if (isRemoval) {\n listSets[targetListType].delete(url);\n } else {\n listSets[targetListType].add(url);\n }\n }\n\n if (listKey === ListKeys.EthPhishingDetectConfig) {\n for (const hash of recentlyAddedC2Domains) {\n listSets.c2DomainBlocklist.add(hash);\n }\n for (const hash of recentlyRemovedC2Domains) {\n listSets.c2DomainBlocklist.delete(hash);\n }\n }\n\n return {\n c2DomainBlocklist: Array.from(listSets.c2DomainBlocklist),\n allowlist: Array.from(listSets.allowlist),\n blocklist: Array.from(listSets.blocklist),\n fuzzylist: Array.from(listSets.fuzzylist),\n version: listState.version,\n name: phishingListKeyNameMap[listKey],\n tolerance: listState.tolerance,\n lastUpdated: latestDiffTimestamp,\n };\n};\n\n/**\n * Validates the configuration object for the phishing detector.\n *\n * @param config - the configuration object to validate.\n * @throws an error if the configuration is invalid.\n */\nexport function validateConfig(\n config: unknown,\n): asserts config is PhishingListState {\n if (config === null || typeof config !== 'object') {\n throw new Error('Invalid config');\n }\n\n if ('tolerance' in config && !('fuzzylist' in config)) {\n throw new Error('Fuzzylist tolerance provided without fuzzylist');\n }\n\n if (\n 'name' in config &&\n (typeof config.name !== 'string' || config.name === '')\n ) {\n throw new Error(\"Invalid config parameter: 'name'\");\n }\n\n if (\n 'version' in config &&\n (!['number', 'string'].includes(typeof config.version) ||\n config.version === '')\n ) {\n throw new Error(\"Invalid config parameter: 'version'\");\n }\n}\n\n/**\n * Converts a domain string to a list of domain parts.\n *\n * @param domain - the domain string to convert.\n * @returns the list of domain parts.\n */\nexport const domainToParts = (domain: string) => {\n try {\n return domain.split('.').reverse();\n } catch (e) {\n throw new Error(JSON.stringify(domain));\n }\n};\n\n/**\n * Converts a list of domain strings to a list of domain parts.\n *\n * @param list - the list of domain strings to convert.\n * @returns the list of domain parts.\n */\nexport const processDomainList = (list: string[]) => {\n return list.map(domainToParts);\n};\n\n/**\n * Gets the default phishing detector configuration.\n *\n * @param override - the optional override for the configuration.\n * @param override.allowlist - the optional allowlist to override.\n * @param override.blocklist - the optional blocklist to override.\n * @param override.c2DomainBlocklist - the optional c2DomainBlocklist to override.\n * @param override.fuzzylist - the optional fuzzylist to override.\n * @param override.tolerance - the optional tolerance to override.\n * @returns the default phishing detector configuration.\n */\nexport const getDefaultPhishingDetectorConfig = ({\n allowlist = [],\n blocklist = [],\n fuzzylist = [],\n tolerance = DEFAULT_TOLERANCE,\n}: {\n allowlist?: string[];\n blocklist?: string[];\n c2DomainBlocklist?: string[];\n fuzzylist?: string[];\n tolerance?: number;\n}): PhishingDetectorConfiguration => ({\n allowlist: processDomainList(allowlist),\n blocklist: processDomainList(blocklist),\n fuzzylist: processDomainList(fuzzylist),\n tolerance,\n});\n\n/**\n * Processes the configurations for the phishing detector, filtering out any invalid configs.\n *\n * @param configs - The configurations to process.\n * @returns An array of processed and valid configurations.\n */\nexport const processConfigs = (\n configs: PhishingDetectorList[] = [],\n): PhishingDetectorConfiguration[] => {\n return configs\n .filter((config) => {\n try {\n validateConfig(config);\n return true;\n } catch (error) {\n console.error(error);\n return false;\n }\n })\n .map((config) => ({\n ...config,\n ...getDefaultPhishingDetectorConfig(config),\n }));\n};\n\n/**\n * Converts a list of domain parts to a domain string.\n *\n * @param domainParts - the list of domain parts.\n * @returns the domain string.\n */\nexport const domainPartsToDomain = (domainParts: string[]) => {\n return domainParts.slice().reverse().join('.');\n};\n\n/**\n * Converts a list of domain parts to a fuzzy form.\n *\n * @param domainParts - the list of domain parts.\n * @returns the fuzzy form of the domain.\n */\nexport const domainPartsToFuzzyForm = (domainParts: string[]) => {\n return domainParts.slice(1).reverse().join('.');\n};\n\n/**\n * Matches the target parts, ignoring extra subdomains on source.\n *\n * @param source - the source domain parts.\n * @param list - the list of domain parts to match against.\n * @returns the parts for the first found matching entry.\n */\nexport const matchPartsAgainstList = (source: string[], list: string[][]) => {\n return list.find((target) => {\n // target domain has more parts than source, fail\n if (target.length > source.length) {\n return false;\n }\n // source matches target or (is deeper subdomain)\n return target.every((part, index) => source[index] === part);\n });\n};\n\n/**\n * Generate the SHA-256 hash of a hostname.\n *\n * @param hostname - The hostname to hash.\n * @returns The SHA-256 hash of the hostname.\n */\nexport const sha256Hash = (hostname: string): string => {\n const hashBuffer = sha256(new TextEncoder().encode(hostname.toLowerCase()));\n return bytesToHex(hashBuffer);\n};\n\n/**\n * Extracts the hostname from a URL.\n *\n * @param url - The URL to extract the hostname from.\n * @returns The hostname extracted from the URL, or null if the URL is invalid.\n */\nexport const getHostnameFromUrl = (url: string): string | null => {\n let hostname;\n try {\n hostname = new URL(url).hostname;\n // above will not throw if 'http://.' is passed. in fact, any string with a dot will pass.\n if (!hostname || hostname.split('.').join('') === '') {\n return null;\n }\n } catch {\n return null;\n }\n return hostname;\n};\n\n/**\n * getHostnameFromWebUrl returns the hostname from a web URL.\n * It returns the hostname and a boolean indicating if the hostname is valid.\n *\n * @param url - The web URL to extract the hostname from.\n * @returns A tuple containing the extracted hostname and a boolean indicating if the hostname is valid.\n * @example\n * getHostnameFromWebUrl('https://example.com') // Returns: ['example.com', true]\n * getHostnameFromWebUrl('example.com') // Returns: ['', false]\n * getHostnameFromWebUrl('https://') // Returns: ['', false]\n * getHostnameFromWebUrl('') // Returns: ['', false]\n */\nexport const getHostnameFromWebUrl = (url: string): [string, boolean] => {\n if (\n !url.toLowerCase().startsWith('http://') &&\n !url.toLowerCase().startsWith('https://')\n ) {\n return ['', false];\n }\n\n const hostname = getHostnameFromUrl(url);\n return [hostname || '', Boolean(hostname)];\n};\n\n/**\n * Generates all possible parent domains up to a specified limit.\n *\n * @param sourceParts - The list of domain parts in normal order (e.g., ['evil', 'domain', 'co', 'uk']).\n * @param limit - The maximum number of parent domains to generate (default is 5).\n * @returns An array of parent domains starting from the base TLD to the most specific subdomain.\n * @example\n * generateParentDomains(['evil', 'domain', 'co', 'uk'], 5)\n * // Returns: ['co.uk', 'domain.co.uk', 'evil.domain.co.uk']\n *\n * generateParentDomains(['uk'], 5)\n * // Returns: ['uk']\n *\n * generateParentDomains(['sub', 'example', 'com'], 5)\n * // Returns: ['example.com', 'sub.example.com']\n */\nexport const generateParentDomains = (\n sourceParts: string[],\n limit = 5,\n): string[] => {\n const domains: string[] = [];\n\n if (sourceParts.length === 0) {\n return domains;\n }\n\n if (sourceParts.length === 1) {\n // Single-segment hostname (e.g., 'uk')\n domains.push(sourceParts[0].toLowerCase());\n } else {\n // Start with the base domain or TLD (last two labels, e.g., 'co.uk' or 'example.com')\n const baseDomain = sourceParts.slice(-2).join('.');\n domains.push(baseDomain.toLowerCase());\n\n // Iteratively add one subdomain level at a time, up to the specified limit\n for (\n let i = sourceParts.length - 3;\n i >= 0 && domains.length < limit;\n i--\n ) {\n const domain = sourceParts.slice(i).join('.');\n domains.push(domain.toLowerCase());\n }\n }\n\n return domains;\n};\n\n/**\n * Builds a cache key for a token scan result.\n *\n * @param chainId - The chain ID.\n * @param address - The token address.\n * @returns The cache key.\n */\nexport const buildCacheKey = (chainId: string, address: string) => {\n return `${chainId.toLowerCase()}:${address.toLowerCase()}`;\n};\n\n/**\n * Resolves the chain name from a chain ID.\n *\n * @param chainId - The chain ID.\n * @param mapping - The mapping of chain IDs to chain names.\n * @returns The chain name.\n */\nexport const resolveChainName = (\n chainId: string,\n mapping = DEFAULT_CHAIN_ID_TO_NAME,\n): string | null => {\n return mapping[chainId.toLowerCase() as keyof typeof mapping] ?? null;\n};\n\n/**\n * Split tokens into cached results and tokens that need to be fetched.\n *\n * @param cache - Cache-like object with get method.\n * @param cache.get - Method to retrieve cached data by key.\n * @param chainId - The chain ID.\n * @param tokens - Array of token addresses.\n * @returns Object containing cached results and tokens to fetch.\n */\nexport const splitCacheHits = (\n cache: { get: (key: string) => TokenScanCacheData | undefined },\n chainId: string,\n tokens: string[],\n): {\n cachedResults: Record<string, TokenScanResult>;\n tokensToFetch: string[];\n} => {\n const cachedResults: Record<string, TokenScanResult> = {};\n const tokensToFetch: string[] = [];\n\n for (const addr of tokens) {\n const normalizedAddr = addr.toLowerCase();\n const key = buildCacheKey(chainId, normalizedAddr);\n const hit = cache.get(key);\n if (hit) {\n cachedResults[normalizedAddr] = {\n result_type: hit.result_type,\n chain: chainId,\n address: normalizedAddr,\n };\n } else {\n tokensToFetch.push(normalizedAddr);\n }\n }\n\n return { cachedResults, tokensToFetch };\n};\n"]}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@metamask-previews/phishing-controller",
|
3
|
-
"version": "
|
3
|
+
"version": "14.0.0-preview-c20b7569",
|
4
4
|
"description": "Maintains a periodically updated list of approved and unapproved website origins",
|
5
5
|
"keywords": [
|
6
6
|
"MetaMask",
|
@@ -57,6 +57,7 @@
|
|
57
57
|
},
|
58
58
|
"devDependencies": {
|
59
59
|
"@metamask/auto-changelog": "^3.4.4",
|
60
|
+
"@metamask/transaction-controller": "^60.4.0",
|
60
61
|
"@types/jest": "^27.4.1",
|
61
62
|
"deepmerge": "^4.2.2",
|
62
63
|
"jest": "^27.5.1",
|
@@ -67,6 +68,9 @@
|
|
67
68
|
"typedoc-plugin-missing-exports": "^2.0.0",
|
68
69
|
"typescript": "~5.2.2"
|
69
70
|
},
|
71
|
+
"peerDependencies": {
|
72
|
+
"@metamask/transaction-controller": "^60.4.0"
|
73
|
+
},
|
70
74
|
"engines": {
|
71
75
|
"node": "^18.18 || >=20"
|
72
76
|
},
|
package/dist/UrlScanCache.cjs
DELETED
@@ -1,127 +0,0 @@
|
|
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 _UrlScanCache_instances, _UrlScanCache_cacheTTL, _UrlScanCache_maxCacheSize, _UrlScanCache_cache, _UrlScanCache_updateState, _UrlScanCache_persistCache, _UrlScanCache_evictEntries;
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
15
|
-
exports.UrlScanCache = exports.DEFAULT_URL_SCAN_CACHE_MAX_SIZE = exports.DEFAULT_URL_SCAN_CACHE_TTL = void 0;
|
16
|
-
const utils_1 = require("./utils.cjs");
|
17
|
-
/**
|
18
|
-
* Default values for URL scan cache
|
19
|
-
*/
|
20
|
-
exports.DEFAULT_URL_SCAN_CACHE_TTL = 300; // 5 minutes in seconds
|
21
|
-
exports.DEFAULT_URL_SCAN_CACHE_MAX_SIZE = 100;
|
22
|
-
/**
|
23
|
-
* UrlScanCache class
|
24
|
-
*
|
25
|
-
* Handles caching of URL scan results with TTL and size limits
|
26
|
-
*/
|
27
|
-
class UrlScanCache {
|
28
|
-
/**
|
29
|
-
* Constructor for UrlScanCache
|
30
|
-
*
|
31
|
-
* @param options - Cache configuration options
|
32
|
-
* @param options.cacheTTL - Time to live in seconds for cached entries
|
33
|
-
* @param options.maxCacheSize - Maximum number of entries in the cache
|
34
|
-
* @param options.initialCache - Initial cache state
|
35
|
-
* @param options.updateState - Function to update the state when cache changes
|
36
|
-
*/
|
37
|
-
constructor({ cacheTTL = exports.DEFAULT_URL_SCAN_CACHE_TTL, maxCacheSize = exports.DEFAULT_URL_SCAN_CACHE_MAX_SIZE, initialCache = {}, updateState, }) {
|
38
|
-
_UrlScanCache_instances.add(this);
|
39
|
-
_UrlScanCache_cacheTTL.set(this, void 0);
|
40
|
-
_UrlScanCache_maxCacheSize.set(this, void 0);
|
41
|
-
_UrlScanCache_cache.set(this, void 0);
|
42
|
-
_UrlScanCache_updateState.set(this, void 0);
|
43
|
-
__classPrivateFieldSet(this, _UrlScanCache_cacheTTL, cacheTTL, "f");
|
44
|
-
__classPrivateFieldSet(this, _UrlScanCache_maxCacheSize, maxCacheSize, "f");
|
45
|
-
__classPrivateFieldSet(this, _UrlScanCache_cache, new Map(Object.entries(initialCache)), "f");
|
46
|
-
__classPrivateFieldSet(this, _UrlScanCache_updateState, updateState, "f");
|
47
|
-
__classPrivateFieldGet(this, _UrlScanCache_instances, "m", _UrlScanCache_evictEntries).call(this);
|
48
|
-
}
|
49
|
-
/**
|
50
|
-
* Set the time-to-live for cached entries
|
51
|
-
*
|
52
|
-
* @param ttl - The TTL in seconds
|
53
|
-
*/
|
54
|
-
setTTL(ttl) {
|
55
|
-
__classPrivateFieldSet(this, _UrlScanCache_cacheTTL, ttl, "f");
|
56
|
-
}
|
57
|
-
/**
|
58
|
-
* Set the maximum cache size
|
59
|
-
*
|
60
|
-
* @param maxSize - The maximum cache size
|
61
|
-
*/
|
62
|
-
setMaxSize(maxSize) {
|
63
|
-
__classPrivateFieldSet(this, _UrlScanCache_maxCacheSize, maxSize, "f");
|
64
|
-
__classPrivateFieldGet(this, _UrlScanCache_instances, "m", _UrlScanCache_evictEntries).call(this);
|
65
|
-
}
|
66
|
-
/**
|
67
|
-
* Clear the cache
|
68
|
-
*/
|
69
|
-
clear() {
|
70
|
-
__classPrivateFieldGet(this, _UrlScanCache_cache, "f").clear();
|
71
|
-
__classPrivateFieldGet(this, _UrlScanCache_instances, "m", _UrlScanCache_persistCache).call(this);
|
72
|
-
}
|
73
|
-
/**
|
74
|
-
* Get a cached result if it exists and is not expired
|
75
|
-
*
|
76
|
-
* @param hostname - The hostname to check
|
77
|
-
* @returns The cached scan result or undefined if not found or expired
|
78
|
-
*/
|
79
|
-
get(hostname) {
|
80
|
-
const cacheEntry = __classPrivateFieldGet(this, _UrlScanCache_cache, "f").get(hostname);
|
81
|
-
if (!cacheEntry) {
|
82
|
-
return undefined;
|
83
|
-
}
|
84
|
-
// Check if the entry is expired
|
85
|
-
const now = (0, utils_1.fetchTimeNow)();
|
86
|
-
if (now - cacheEntry.timestamp > __classPrivateFieldGet(this, _UrlScanCache_cacheTTL, "f")) {
|
87
|
-
// Entry expired, remove it from cache
|
88
|
-
__classPrivateFieldGet(this, _UrlScanCache_cache, "f").delete(hostname);
|
89
|
-
__classPrivateFieldGet(this, _UrlScanCache_instances, "m", _UrlScanCache_persistCache).call(this);
|
90
|
-
return undefined;
|
91
|
-
}
|
92
|
-
return cacheEntry.result;
|
93
|
-
}
|
94
|
-
/**
|
95
|
-
* Add an entry to the cache, evicting oldest entries if necessary
|
96
|
-
*
|
97
|
-
* @param hostname - The hostname to cache
|
98
|
-
* @param result - The scan result to cache
|
99
|
-
*/
|
100
|
-
add(hostname, result) {
|
101
|
-
__classPrivateFieldGet(this, _UrlScanCache_cache, "f").set(hostname, {
|
102
|
-
result,
|
103
|
-
timestamp: (0, utils_1.fetchTimeNow)(),
|
104
|
-
});
|
105
|
-
__classPrivateFieldGet(this, _UrlScanCache_instances, "m", _UrlScanCache_evictEntries).call(this);
|
106
|
-
__classPrivateFieldGet(this, _UrlScanCache_instances, "m", _UrlScanCache_persistCache).call(this);
|
107
|
-
}
|
108
|
-
}
|
109
|
-
exports.UrlScanCache = UrlScanCache;
|
110
|
-
_UrlScanCache_cacheTTL = new WeakMap(), _UrlScanCache_maxCacheSize = new WeakMap(), _UrlScanCache_cache = new WeakMap(), _UrlScanCache_updateState = new WeakMap(), _UrlScanCache_instances = new WeakSet(), _UrlScanCache_persistCache = function _UrlScanCache_persistCache() {
|
111
|
-
__classPrivateFieldGet(this, _UrlScanCache_updateState, "f").call(this, Object.fromEntries(__classPrivateFieldGet(this, _UrlScanCache_cache, "f")));
|
112
|
-
}, _UrlScanCache_evictEntries = function _UrlScanCache_evictEntries() {
|
113
|
-
if (__classPrivateFieldGet(this, _UrlScanCache_cache, "f").size <= __classPrivateFieldGet(this, _UrlScanCache_maxCacheSize, "f")) {
|
114
|
-
return;
|
115
|
-
}
|
116
|
-
const entriesToRemove = __classPrivateFieldGet(this, _UrlScanCache_cache, "f").size - __classPrivateFieldGet(this, _UrlScanCache_maxCacheSize, "f");
|
117
|
-
let count = 0;
|
118
|
-
// Delete the oldest entries
|
119
|
-
for (const key of __classPrivateFieldGet(this, _UrlScanCache_cache, "f").keys()) {
|
120
|
-
if (count >= entriesToRemove) {
|
121
|
-
break;
|
122
|
-
}
|
123
|
-
__classPrivateFieldGet(this, _UrlScanCache_cache, "f").delete(key);
|
124
|
-
count += 1;
|
125
|
-
}
|
126
|
-
};
|
127
|
-
//# sourceMappingURL=UrlScanCache.cjs.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"UrlScanCache.cjs","sourceRoot":"","sources":["../src/UrlScanCache.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PhishingDetectionScanResult } from './types';\nimport { fetchTimeNow } from './utils';\n\n/**\n * Cache entry for URL scan results\n */\nexport type UrlScanCacheEntry = {\n result: PhishingDetectionScanResult;\n timestamp: number;\n};\n\n/**\n * Default values for URL scan cache\n */\nexport const DEFAULT_URL_SCAN_CACHE_TTL = 300; // 5 minutes in seconds\nexport const DEFAULT_URL_SCAN_CACHE_MAX_SIZE = 100;\n\n/**\n * UrlScanCache class\n *\n * Handles caching of URL scan results with TTL and size limits\n */\nexport class UrlScanCache {\n #cacheTTL: number;\n\n #maxCacheSize: number;\n\n readonly #cache: Map<string, UrlScanCacheEntry>;\n\n readonly #updateState: (cache: Record<string, UrlScanCacheEntry>) => void;\n\n /**\n * Constructor for UrlScanCache\n *\n * @param options - Cache configuration options\n * @param options.cacheTTL - Time to live in seconds for cached entries\n * @param options.maxCacheSize - Maximum number of entries in the cache\n * @param options.initialCache - Initial cache state\n * @param options.updateState - Function to update the state when cache changes\n */\n constructor({\n cacheTTL = DEFAULT_URL_SCAN_CACHE_TTL,\n maxCacheSize = DEFAULT_URL_SCAN_CACHE_MAX_SIZE,\n initialCache = {},\n updateState,\n }: {\n cacheTTL?: number;\n maxCacheSize?: number;\n initialCache?: Record<string, UrlScanCacheEntry>;\n updateState: (cache: Record<string, UrlScanCacheEntry>) => void;\n }) {\n this.#cacheTTL = cacheTTL;\n this.#maxCacheSize = maxCacheSize;\n this.#cache = new Map(Object.entries(initialCache));\n this.#updateState = updateState;\n this.#evictEntries();\n }\n\n /**\n * Set the time-to-live for cached entries\n *\n * @param ttl - The TTL in seconds\n */\n setTTL(ttl: number): void {\n this.#cacheTTL = ttl;\n }\n\n /**\n * Set the maximum cache size\n *\n * @param maxSize - The maximum cache size\n */\n setMaxSize(maxSize: number): void {\n this.#maxCacheSize = maxSize;\n this.#evictEntries();\n }\n\n /**\n * Clear the cache\n */\n clear(): void {\n this.#cache.clear();\n this.#persistCache();\n }\n\n /**\n * Get a cached result if it exists and is not expired\n *\n * @param hostname - The hostname to check\n * @returns The cached scan result or undefined if not found or expired\n */\n get(hostname: string): PhishingDetectionScanResult | undefined {\n const cacheEntry = this.#cache.get(hostname);\n if (!cacheEntry) {\n return undefined;\n }\n\n // Check if the entry is expired\n const now = fetchTimeNow();\n if (now - cacheEntry.timestamp > this.#cacheTTL) {\n // Entry expired, remove it from cache\n this.#cache.delete(hostname);\n this.#persistCache();\n return undefined;\n }\n\n return cacheEntry.result;\n }\n\n /**\n * Add an entry to the cache, evicting oldest entries if necessary\n *\n * @param hostname - The hostname to cache\n * @param result - The scan result to cache\n */\n add(hostname: string, result: PhishingDetectionScanResult): void {\n this.#cache.set(hostname, {\n result,\n timestamp: fetchTimeNow(),\n });\n\n this.#evictEntries();\n\n this.#persistCache();\n }\n\n /**\n * Persist the current cache state\n */\n #persistCache(): void {\n this.#updateState(Object.fromEntries(this.#cache));\n }\n\n /**\n * Evict oldest entries if cache exceeds max size\n */\n #evictEntries(): void {\n if (this.#cache.size <= this.#maxCacheSize) {\n return;\n }\n\n const entriesToRemove = this.#cache.size - this.#maxCacheSize;\n let count = 0;\n // Delete the oldest entries\n for (const key of this.#cache.keys()) {\n if (count >= entriesToRemove) {\n break;\n }\n this.#cache.delete(key);\n count += 1;\n }\n }\n}\n"]}
|
package/dist/UrlScanCache.d.cts
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
import type { PhishingDetectionScanResult } from "./types.cjs";
|
2
|
-
/**
|
3
|
-
* Cache entry for URL scan results
|
4
|
-
*/
|
5
|
-
export type UrlScanCacheEntry = {
|
6
|
-
result: PhishingDetectionScanResult;
|
7
|
-
timestamp: number;
|
8
|
-
};
|
9
|
-
/**
|
10
|
-
* Default values for URL scan cache
|
11
|
-
*/
|
12
|
-
export declare const DEFAULT_URL_SCAN_CACHE_TTL = 300;
|
13
|
-
export declare const DEFAULT_URL_SCAN_CACHE_MAX_SIZE = 100;
|
14
|
-
/**
|
15
|
-
* UrlScanCache class
|
16
|
-
*
|
17
|
-
* Handles caching of URL scan results with TTL and size limits
|
18
|
-
*/
|
19
|
-
export declare class UrlScanCache {
|
20
|
-
#private;
|
21
|
-
/**
|
22
|
-
* Constructor for UrlScanCache
|
23
|
-
*
|
24
|
-
* @param options - Cache configuration options
|
25
|
-
* @param options.cacheTTL - Time to live in seconds for cached entries
|
26
|
-
* @param options.maxCacheSize - Maximum number of entries in the cache
|
27
|
-
* @param options.initialCache - Initial cache state
|
28
|
-
* @param options.updateState - Function to update the state when cache changes
|
29
|
-
*/
|
30
|
-
constructor({ cacheTTL, maxCacheSize, initialCache, updateState, }: {
|
31
|
-
cacheTTL?: number;
|
32
|
-
maxCacheSize?: number;
|
33
|
-
initialCache?: Record<string, UrlScanCacheEntry>;
|
34
|
-
updateState: (cache: Record<string, UrlScanCacheEntry>) => void;
|
35
|
-
});
|
36
|
-
/**
|
37
|
-
* Set the time-to-live for cached entries
|
38
|
-
*
|
39
|
-
* @param ttl - The TTL in seconds
|
40
|
-
*/
|
41
|
-
setTTL(ttl: number): void;
|
42
|
-
/**
|
43
|
-
* Set the maximum cache size
|
44
|
-
*
|
45
|
-
* @param maxSize - The maximum cache size
|
46
|
-
*/
|
47
|
-
setMaxSize(maxSize: number): void;
|
48
|
-
/**
|
49
|
-
* Clear the cache
|
50
|
-
*/
|
51
|
-
clear(): void;
|
52
|
-
/**
|
53
|
-
* Get a cached result if it exists and is not expired
|
54
|
-
*
|
55
|
-
* @param hostname - The hostname to check
|
56
|
-
* @returns The cached scan result or undefined if not found or expired
|
57
|
-
*/
|
58
|
-
get(hostname: string): PhishingDetectionScanResult | undefined;
|
59
|
-
/**
|
60
|
-
* Add an entry to the cache, evicting oldest entries if necessary
|
61
|
-
*
|
62
|
-
* @param hostname - The hostname to cache
|
63
|
-
* @param result - The scan result to cache
|
64
|
-
*/
|
65
|
-
add(hostname: string, result: PhishingDetectionScanResult): void;
|
66
|
-
}
|
67
|
-
//# sourceMappingURL=UrlScanCache.d.cts.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"UrlScanCache.d.cts","sourceRoot":"","sources":["../src/UrlScanCache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,oBAAgB;AAG3D;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,2BAA2B,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD;;;;GAIG;AACH,qBAAa,YAAY;;IASvB;;;;;;;;OAQG;gBACS,EACV,QAAqC,EACrC,YAA8C,EAC9C,YAAiB,EACjB,WAAW,GACZ,EAAE;QACD,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;QACjD,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,KAAK,IAAI,CAAC;KACjE;IAQD;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB;;;;OAIG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAKjC;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;;;;OAKG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,2BAA2B,GAAG,SAAS;IAkB9D;;;;;OAKG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,2BAA2B,GAAG,IAAI;CAqCjE"}
|
package/dist/UrlScanCache.d.mts
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
import type { PhishingDetectionScanResult } from "./types.mjs";
|
2
|
-
/**
|
3
|
-
* Cache entry for URL scan results
|
4
|
-
*/
|
5
|
-
export type UrlScanCacheEntry = {
|
6
|
-
result: PhishingDetectionScanResult;
|
7
|
-
timestamp: number;
|
8
|
-
};
|
9
|
-
/**
|
10
|
-
* Default values for URL scan cache
|
11
|
-
*/
|
12
|
-
export declare const DEFAULT_URL_SCAN_CACHE_TTL = 300;
|
13
|
-
export declare const DEFAULT_URL_SCAN_CACHE_MAX_SIZE = 100;
|
14
|
-
/**
|
15
|
-
* UrlScanCache class
|
16
|
-
*
|
17
|
-
* Handles caching of URL scan results with TTL and size limits
|
18
|
-
*/
|
19
|
-
export declare class UrlScanCache {
|
20
|
-
#private;
|
21
|
-
/**
|
22
|
-
* Constructor for UrlScanCache
|
23
|
-
*
|
24
|
-
* @param options - Cache configuration options
|
25
|
-
* @param options.cacheTTL - Time to live in seconds for cached entries
|
26
|
-
* @param options.maxCacheSize - Maximum number of entries in the cache
|
27
|
-
* @param options.initialCache - Initial cache state
|
28
|
-
* @param options.updateState - Function to update the state when cache changes
|
29
|
-
*/
|
30
|
-
constructor({ cacheTTL, maxCacheSize, initialCache, updateState, }: {
|
31
|
-
cacheTTL?: number;
|
32
|
-
maxCacheSize?: number;
|
33
|
-
initialCache?: Record<string, UrlScanCacheEntry>;
|
34
|
-
updateState: (cache: Record<string, UrlScanCacheEntry>) => void;
|
35
|
-
});
|
36
|
-
/**
|
37
|
-
* Set the time-to-live for cached entries
|
38
|
-
*
|
39
|
-
* @param ttl - The TTL in seconds
|
40
|
-
*/
|
41
|
-
setTTL(ttl: number): void;
|
42
|
-
/**
|
43
|
-
* Set the maximum cache size
|
44
|
-
*
|
45
|
-
* @param maxSize - The maximum cache size
|
46
|
-
*/
|
47
|
-
setMaxSize(maxSize: number): void;
|
48
|
-
/**
|
49
|
-
* Clear the cache
|
50
|
-
*/
|
51
|
-
clear(): void;
|
52
|
-
/**
|
53
|
-
* Get a cached result if it exists and is not expired
|
54
|
-
*
|
55
|
-
* @param hostname - The hostname to check
|
56
|
-
* @returns The cached scan result or undefined if not found or expired
|
57
|
-
*/
|
58
|
-
get(hostname: string): PhishingDetectionScanResult | undefined;
|
59
|
-
/**
|
60
|
-
* Add an entry to the cache, evicting oldest entries if necessary
|
61
|
-
*
|
62
|
-
* @param hostname - The hostname to cache
|
63
|
-
* @param result - The scan result to cache
|
64
|
-
*/
|
65
|
-
add(hostname: string, result: PhishingDetectionScanResult): void;
|
66
|
-
}
|
67
|
-
//# sourceMappingURL=UrlScanCache.d.mts.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"UrlScanCache.d.mts","sourceRoot":"","sources":["../src/UrlScanCache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,oBAAgB;AAG3D;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,2BAA2B,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD;;;;GAIG;AACH,qBAAa,YAAY;;IASvB;;;;;;;;OAQG;gBACS,EACV,QAAqC,EACrC,YAA8C,EAC9C,YAAiB,EACjB,WAAW,GACZ,EAAE;QACD,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;QACjD,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,KAAK,IAAI,CAAC;KACjE;IAQD;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB;;;;OAIG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAKjC;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;;;;OAKG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,2BAA2B,GAAG,SAAS;IAkB9D;;;;;OAKG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,2BAA2B,GAAG,IAAI;CAqCjE"}
|