@chainpatrol/sdk 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,11 +1,19 @@
1
1
  declare const ContinueAtOwnRisk = "CHAINPATROL_CONTINUE_AT_OWN_RISK";
2
+ declare const IgnorelistUpdated = "CHAINPATROL_IGNORELIST_UPDATED";
3
+ declare const CloseCurrentTab = "CHAINPATROL_CLOSE_CURRENT_TAB";
2
4
  declare const Events: {
3
5
  readonly ContinueAtOwnRisk: "CHAINPATROL_CONTINUE_AT_OWN_RISK";
6
+ readonly IgnorelistUpdated: "CHAINPATROL_IGNORELIST_UPDATED";
7
+ readonly CloseCurrentTab: "CHAINPATROL_CLOSE_CURRENT_TAB";
4
8
  };
5
9
  type EventData = {
6
10
  [ContinueAtOwnRisk]: {
7
11
  domain: string;
8
12
  };
13
+ [IgnorelistUpdated]: {
14
+ domain: string;
15
+ };
16
+ [CloseCurrentTab]: {};
9
17
  };
10
18
 
11
19
  type Handle = {
package/dist/index.js CHANGED
@@ -46,8 +46,12 @@ var __async = (__this, __arguments, generator) => {
46
46
 
47
47
  // src/events.ts
48
48
  var ContinueAtOwnRisk = "CHAINPATROL_CONTINUE_AT_OWN_RISK";
49
+ var IgnorelistUpdated = "CHAINPATROL_IGNORELIST_UPDATED";
50
+ var CloseCurrentTab = "CHAINPATROL_CLOSE_CURRENT_TAB";
49
51
  var Events = {
50
- ContinueAtOwnRisk
52
+ ContinueAtOwnRisk,
53
+ IgnorelistUpdated,
54
+ CloseCurrentTab
51
55
  };
52
56
 
53
57
  // src/logger.ts
@@ -93,24 +97,65 @@ var Logger = class _Logger {
93
97
  };
94
98
 
95
99
  // src/relay.ts
100
+ function getExtensionRuntime() {
101
+ var _a, _b;
102
+ if ((_a = globalThis.browser) == null ? void 0 : _a.runtime) {
103
+ return globalThis.browser.runtime;
104
+ }
105
+ if ((_b = globalThis.chrome) == null ? void 0 : _b.runtime) {
106
+ return globalThis.chrome.runtime;
107
+ }
108
+ throw new Error("No extension runtime found");
109
+ }
96
110
  function isExtensionHost() {
97
111
  var _a, _b;
98
112
  return !!((_a = globalThis.browser) == null ? void 0 : _a.runtime) || !!((_b = globalThis.chrome) == null ? void 0 : _b.runtime);
99
113
  }
114
+ function isContentScript() {
115
+ return isExtensionHost() && isBrowserHost();
116
+ }
117
+ function isBackgroundScript() {
118
+ return isExtensionHost() && !isBrowserHost();
119
+ }
100
120
  function isBrowserHost() {
101
121
  return !!globalThis.window;
102
122
  }
103
- function getExtensionHandle() {
123
+ function getBackgroundScriptHandle() {
124
+ const runtime = getExtensionRuntime();
104
125
  return {
105
126
  addListener: (callback) => {
106
- globalThis.chrome.runtime.onMessage.addListener(callback);
127
+ runtime.onMessage.addListener(callback);
107
128
  },
108
129
  removeListener: (callback) => {
109
- globalThis.chrome.runtime.onMessage.removeListener(callback);
130
+ runtime.onMessage.removeListener(callback);
110
131
  },
111
- postMessage: (message) => {
112
- globalThis.chrome.runtime.sendMessage(message);
113
- }
132
+ postMessage: (message) => __async(this, null, function* () {
133
+ const [tab] = yield globalThis.chrome.tabs.query({
134
+ active: true,
135
+ lastFocusedWindow: true
136
+ });
137
+ if (!tab.id || tab.id === globalThis.chrome.tabs.TAB_ID_NONE) {
138
+ console.error("No active tab found");
139
+ return;
140
+ }
141
+ globalThis.chrome.tabs.sendMessage(tab.id, message);
142
+ })
143
+ };
144
+ }
145
+ function getContentScriptHandle() {
146
+ const runtime = getExtensionRuntime();
147
+ return {
148
+ addListener: (callback) => {
149
+ runtime.onMessage.addListener(callback);
150
+ },
151
+ removeListener: (callback) => {
152
+ runtime.onMessage.removeListener(callback);
153
+ },
154
+ postMessage: (message) => __async(this, null, function* () {
155
+ runtime.sendMessage(message).catch((error) => {
156
+ console.error("Failed to send message", { message, error });
157
+ });
158
+ })
114
159
  };
115
160
  }
116
161
  function getBrowserHandle() {
@@ -200,11 +245,14 @@ var _Relay = class _Relay {
200
245
  _Relay.handles = [];
201
246
  _Relay.logger = new Logger({ component: "Relay" });
202
247
  (() => {
203
- if (isExtensionHost()) {
204
- _Relay.logger.info("Detected extension host");
205
- _Relay.handles.push(getExtensionHandle());
206
- }
207
- if (isBrowserHost()) {
248
+ if (isBackgroundScript()) {
249
+ _Relay.logger.info("Detected background script");
250
+ _Relay.handles.push(getBackgroundScriptHandle());
251
+ } else if (isContentScript()) {
252
+ _Relay.logger.info("Detected content script");
253
+ _Relay.handles.push(getContentScriptHandle());
254
+ _Relay.handles.push(getBrowserHandle());
255
+ } else if (isBrowserHost()) {
208
256
  _Relay.logger.info("Detected browser host");
209
257
  _Relay.handles.push(getBrowserHandle());
210
258
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/events.ts","../src/logger.ts","../src/relay.ts","../src/detector.ts","../../../node_modules/normalize-url/index.js","../src/client.ts","../src/storage/index.ts","../src/storage/define-storage.ts","../src/storage/extension.ts","../src/storage/browser.ts","../src/storage/memory.ts"],"names":["LogLevel","list"],"mappings":"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","sourcesContent":["// const ContinueAtOwnRisk = Symbol(\"CHAINPATROL_CONTINUE_AT_OWN_RISK\");\nconst ContinueAtOwnRisk = \"CHAINPATROL_CONTINUE_AT_OWN_RISK\";\n\nexport const Events = {\n ContinueAtOwnRisk,\n} as const;\n\nexport type EventData = {\n [ContinueAtOwnRisk]: {\n domain: string;\n };\n};\n","export enum LogLevel {\n DEBUG,\n INFO,\n WARN,\n ERROR,\n NONE,\n}\n\nexport class Logger {\n private meta: Record<string, unknown> = {};\n private minLevel: LogLevel;\n\n constructor(\n meta?: Record<string, unknown>,\n minLevel: LogLevel = LogLevel.NONE\n ) {\n if (meta) {\n this.meta = meta;\n }\n this.minLevel = minLevel;\n }\n\n public with(fields: Record<string, unknown>) {\n return new Logger({ ...this.meta, ...fields }, this.minLevel);\n }\n\n public debug(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.DEBUG, message, fields);\n }\n\n public info(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.INFO, message, fields);\n }\n\n public warn(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.WARN, message, fields);\n }\n\n public error(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.ERROR, message, fields);\n }\n\n private log(\n level: LogLevel,\n message: string,\n fields?: Record<string, unknown>\n ) {\n if (level < this.minLevel) {\n return; // Skip logging if log level is lower than minimum level\n }\n\n const logObj = { message, data: { ...fields }, ...this.meta };\n const logString = JSON.stringify(logObj, null, 2);\n console.log(`[${LogLevel[level].toUpperCase()}] ${logString}`);\n }\n}\n","import { EventData } from \"./events\";\nimport { Logger } from \"./logger\";\n\ntype Message<EventType extends keyof EventData> = {\n type: EventType;\n data: EventData[EventType];\n _meta: {\n id: string;\n origin: string;\n };\n};\n\nfunction isExtensionHost() {\n return (\n !!(globalThis as any).browser?.runtime ||\n !!(globalThis as any).chrome?.runtime\n );\n}\n\nfunction isBrowserHost() {\n return !!(globalThis as any).window;\n}\n\ntype Handle = {\n addListener: (callback: (message: any) => void) => void;\n removeListener: (callback: (message: any) => void) => void;\n postMessage: (message: any) => void;\n};\n\nfunction getExtensionHandle(): Handle {\n return {\n addListener: (callback: (message: any) => void) => {\n globalThis.chrome.runtime.onMessage.addListener(callback);\n },\n removeListener: (callback: (message: any) => void) => {\n globalThis.chrome.runtime.onMessage.removeListener(callback);\n },\n postMessage: (message: any) => {\n globalThis.chrome.runtime.sendMessage(message);\n },\n };\n}\n\nfunction getBrowserHandle(): Handle {\n return {\n addListener: (callback: (message: any) => void) => {\n globalThis.window.addEventListener(\"message\", (event) => {\n if (event.source !== globalThis.window) {\n return;\n }\n callback(event.data);\n });\n },\n removeListener: (callback: (message: any) => void) => {\n globalThis.window.removeEventListener(\"message\", callback);\n },\n postMessage: (message: any) => {\n globalThis.window.postMessage(message, \"*\");\n },\n };\n}\n\n/**\n * Relay contains methods for implementing an event relay. It is used to\n * forward events between Javascript contexts:\n *\n * `<window>` <-> `<content script>` <-> `<background script>`\n *\n * Call `Relay.send` to send an event from one context to another. Call\n * `Relay.on` to listen for events in the current context. Call `Relay.run`\n * to start listening for events in the current context and forward them\n * to all other contexts.\n */\nexport class Relay {\n protected static handles: Handle[] = [];\n private static readonly logger = new Logger({ component: \"Relay\" });\n\n static {\n if (isExtensionHost()) {\n Relay.logger.info(\"Detected extension host\");\n Relay.handles.push(getExtensionHandle());\n }\n if (isBrowserHost()) {\n Relay.logger.info(\"Detected browser host\");\n Relay.handles.push(getBrowserHandle());\n }\n }\n\n public static send<EventType extends keyof EventData>(\n type: EventType,\n data: EventData[EventType]\n ) {\n const message = {\n type,\n data,\n _meta: {\n id: Math.random().toString(36).substring(2, 9),\n origin: globalThis.location.origin,\n },\n } satisfies Message<EventType>;\n\n Relay.logger.debug(\"Sending message\", message);\n\n for (const handle of Relay.handles) {\n handle.postMessage(message);\n }\n }\n\n public static run(events: (keyof EventData)[]) {\n const listeners = new Set<(message: any) => void>();\n\n for (const handle of Relay.handles) {\n const listener = Relay.handleMessage.bind(null, handle, events);\n listeners.add(listener);\n handle.addListener(listener);\n }\n\n Relay.logger.debug(\"Started relay\", { events });\n\n return () => {\n for (const handle of Relay.handles) {\n for (const listener of listeners) {\n handle.removeListener(listener);\n }\n }\n\n Relay.logger.debug(\"Stopped relay\", { events });\n };\n }\n\n private static handleMessage(\n sourceHandle: Handle,\n events: (keyof EventData)[],\n message: any\n ) {\n if (!events.includes(message.type)) {\n Relay.logger.debug(\"Ignoring message\", { message });\n return;\n }\n\n const destinationHandles = Relay.handles.filter(\n (handle) => handle !== sourceHandle\n );\n\n for (const destinationHandle of destinationHandles) {\n destinationHandle.postMessage(message);\n }\n }\n\n public static on<EventType extends keyof EventData>(\n targetEvent: EventType,\n callback: (data: EventData[EventType]) => void\n ) {\n const listeners = new Set<(message: any) => void>();\n for (const handle of Relay.handles) {\n const listener = (message: any) => {\n if (message.type !== targetEvent) {\n Relay.logger.debug(\"Ignoring message\", { message });\n return;\n }\n callback(message.data);\n };\n listeners.add(listener);\n handle.addListener(listener);\n }\n\n return () => {\n for (const handle of Relay.handles) {\n for (const listener of listeners) {\n handle.removeListener(listener);\n }\n }\n };\n }\n}\n","import { parse as parseDomain } from \"tldts\";\nimport normalizeUrl from \"normalize-url\";\nimport { z } from \"zod\";\n\nimport { AssetStatus, ChainPatrolClient } from \"./client\";\nimport { Memory } from \"./storage\";\nimport type { Storage } from \"./storage/types\";\nimport { Logger } from \"./logger\";\n\ntype Prettify<T> = T extends infer U ? (U extends string ? U : never) : never;\n\nexport type DetectorAssetStatus = Prettify<AssetStatus | \"IGNORED\">;\n\ntype Mode = \"cloud\" | \"local\";\n\n// Branded type for domain strings\ntype Domain = string & { __domain: never };\n\nexport type URLResult =\n | {\n ok: true;\n status: DetectorAssetStatus;\n url: string;\n redirectUrl?: string;\n }\n | {\n ok: false;\n url: string;\n error: string;\n };\n\nexport class DomainParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"DomainParseError\";\n }\n}\n\nexport class ThreatDetector {\n static StorageKeys = {\n ALLOWLIST: \"chainpatrol.allowed\",\n BLOCKLIST: \"chainpatrol.blocked\",\n IGNORELIST: \"chainpatrol.ignored\",\n };\n\n private static readonly CHAINPATROL_WARNING_URL =\n \"https://app.chainpatrol.io/warning\";\n private static readonly Schema = z.object({\n version: z.literal(1),\n data: z.object({\n domains: z.array(z.string()),\n }),\n });\n\n private mode: Mode;\n private client?: ChainPatrolClient;\n private storage: Storage;\n private redirectUrl?: string | ((url: string) => string);\n private readonly logger = new Logger({ component: \"ThreatDetector\" });\n\n constructor({\n mode,\n storage,\n redirectUrl,\n }: {\n mode: \"local\";\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n });\n\n constructor({\n mode,\n apiKey,\n storage,\n redirectUrl,\n }: {\n mode: \"cloud\";\n apiKey: string;\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n });\n\n constructor({\n mode,\n apiKey,\n proxyUrl,\n storage,\n redirectUrl,\n }: {\n mode: \"cloud\";\n apiKey: string;\n proxyUrl?: string;\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n });\n\n constructor({\n mode = \"cloud\",\n apiKey = \"\",\n storage = Memory(),\n proxyUrl,\n redirectUrl,\n }: {\n mode?: Mode;\n apiKey?: string;\n proxyUrl?: string;\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n }) {\n this.mode = mode;\n this.storage = storage;\n this.redirectUrl = redirectUrl ?? ThreatDetector.CHAINPATROL_WARNING_URL;\n if (mode === \"cloud\") {\n this.client = new ChainPatrolClient({\n apiKey: apiKey,\n baseUrl: proxyUrl,\n });\n }\n }\n\n public async url(url: string): Promise<URLResult> {\n this.logger.debug(\"Checking URL\", { url });\n\n let domains: Domain[];\n try {\n domains = this.generateDomains(url);\n } catch (e) {\n this.logger.error(\"Unable to parse domain\", { url, error: e });\n return {\n ok: false,\n url,\n error:\n e instanceof DomainParseError ? e.message : \"Unable to parse domain\",\n };\n }\n\n this.logger.debug(\"Generated domains\", { domains });\n\n let results = (\n await Promise.all(domains.map((domain) => this.urlHelper(domain, url)))\n ).filter(\n <T extends URLResult>(r: T): r is T extends { ok: true } ? T : never =>\n r.ok\n );\n\n if (results.length === 0) {\n return {\n ok: false,\n url,\n error: \"URL does not have a valid domain\",\n };\n }\n\n this.logger.debug(\"Results for domains\", { results });\n\n if (results.some((r) => r.status === \"IGNORED\")) {\n return {\n ok: true,\n status: \"IGNORED\",\n url,\n };\n }\n\n for (const result of results) {\n if (result.ok && result.status !== \"UNKNOWN\") {\n return result;\n }\n }\n\n return results[0];\n }\n\n private generateDomains(_url: string): Domain[] {\n const domain = this.parseDomainOrThrow(_url);\n\n const domains = [domain];\n\n const parsedDomain = parseDomain(domain);\n if (!parsedDomain.subdomain) {\n return domains;\n }\n\n const subdomainParts = parsedDomain.subdomain?.split(\".\") ?? [];\n\n for (let i = 0; i < subdomainParts.length; i++) {\n const subdomain = subdomainParts.slice(i).join(\".\");\n domains.unshift(`${subdomain}.${domain}` as Domain);\n }\n\n return domains;\n }\n\n private async urlHelper(domain: Domain, url: string): Promise<URLResult> {\n let status = await this.getStatusFromCache(domain);\n\n if (this.mode === \"cloud\" && this.client && status === \"UNKNOWN\") {\n try {\n const res = await this.client.asset.check({\n type: \"URL\",\n content: domain,\n });\n\n // Update cache storage\n this.logger.debug(\"Updating cache\", { domain, status: res.status });\n if (res.status === \"ALLOWED\") {\n this.addDomainToCache(domain, ThreatDetector.StorageKeys.ALLOWLIST);\n } else if (res.status === \"BLOCKED\") {\n this.addDomainToCache(domain, ThreatDetector.StorageKeys.BLOCKLIST);\n }\n\n status = res.status;\n } catch (e) {\n return {\n ok: false,\n url: domain,\n error: \"Unable to check URL\",\n };\n }\n }\n\n let redirectUrl: string | undefined;\n\n if (status === \"BLOCKED\") {\n if (typeof this.redirectUrl === \"function\") {\n redirectUrl = this.redirectUrl(url);\n } else if (typeof this.redirectUrl === \"string\") {\n const newUrl = new URL(this.redirectUrl);\n newUrl.searchParams.set(\"originUrl\", url);\n redirectUrl = newUrl.toString();\n }\n }\n\n return {\n ok: true,\n status,\n url: domain,\n redirectUrl,\n };\n }\n\n public async allow(\n url: string\n ): Promise<\n { ok: true; url: string } | { ok: false; url: string; error: string }\n > {\n try {\n const domain = this.parseDomainOrThrow(url);\n\n this.logger.debug(\"Allowing URL\", { url, domain });\n\n await this.invalidateDomainInCaches(domain);\n await this.addDomainToCache(domain, ThreatDetector.StorageKeys.ALLOWLIST);\n\n return {\n ok: true,\n url: domain,\n };\n } catch (e) {\n this.logger.error(\"Unable to allow URL\", { url, error: e });\n return {\n ok: false,\n url,\n error: \"Unable to allow URL\",\n };\n }\n }\n\n public async block(\n url: string\n ): Promise<\n { ok: true; url: string } | { ok: false; url: string; error: string }\n > {\n try {\n const domain = this.parseDomainOrThrow(url);\n\n this.logger.debug(\"Blocking URL\", { url, domain });\n\n await this.invalidateDomainInCaches(domain);\n await this.addDomainToCache(domain, ThreatDetector.StorageKeys.BLOCKLIST);\n\n return {\n ok: true,\n url: domain,\n };\n } catch (e) {\n this.logger.error(\"Unable to block URL\", { url, error: e });\n return {\n ok: false,\n url,\n error: \"Unable to block URL\",\n };\n }\n }\n\n public async ignore(\n url: string\n ): Promise<\n { ok: true; url: string } | { ok: false; url: string; error: string }\n > {\n try {\n const domain = this.parseDomainOrThrow(url);\n\n this.logger.debug(\"Ignoring URL\", { url, domain });\n\n await this.addDomainToCache(\n domain,\n ThreatDetector.StorageKeys.IGNORELIST\n );\n\n return {\n ok: true,\n url: domain,\n };\n } catch (e) {\n this.logger.error(\"Unable to ignore URL\", { url, error: e });\n return {\n ok: false,\n url,\n error: \"Unable to ignore URL\",\n };\n }\n }\n\n private parseDomainOrThrow(url: string): Domain {\n this.logger.debug(\"Parsing domain\", { url });\n try {\n const normalizedUrl = normalizeUrl(url, {\n stripWWW: false,\n removeTrailingSlash: false,\n });\n\n this.logger.debug(\"Normalized URL\", { from: url, to: normalizedUrl });\n\n const parsedURL = new URL(normalizedUrl);\n\n this.logger.debug(\"Extract domain from URL\", { url: parsedURL.hostname });\n\n const parsedSubdomainResult = parseDomain(parsedURL.hostname);\n\n this.logger.debug(\"Parsed domain\", { parsedSubdomainResult });\n\n if (parsedSubdomainResult.hostname === \"localhost\") {\n throw new DomainParseError(\"ThreatDetector does not support localhost\");\n }\n\n if (parsedSubdomainResult.isIp) {\n throw new DomainParseError(\n \"ThreatDetector does not support IP addresses\"\n );\n }\n\n if (!parsedSubdomainResult.domain) {\n throw new DomainParseError(\"Unable to parse domain\");\n }\n\n const domain = parsedSubdomainResult.domain as Domain;\n\n return domain;\n } catch (e) {\n if (e instanceof DomainParseError) {\n throw e;\n } else {\n this.logger.error(\"Unable to parse domain\", { url, error: e });\n throw new DomainParseError(\"Unable to parse domain\");\n }\n }\n }\n\n private async invalidateDomainInCaches(domain: Domain): Promise<void> {\n await this.invalidateDomainInCache(\n domain,\n ThreatDetector.StorageKeys.ALLOWLIST\n );\n await this.invalidateDomainInCache(\n domain,\n ThreatDetector.StorageKeys.BLOCKLIST\n );\n }\n\n private async invalidateDomainInCache(\n domain: Domain,\n key: string\n ): Promise<void> {\n const data = await this.storage.get(key);\n\n if (!data) {\n return;\n }\n\n const list = await this.getListFromStorage(key);\n\n if (!list.data.domains.includes(domain)) {\n return;\n }\n\n list.data.domains = list.data.domains.filter((u) => u !== domain);\n\n await this.setListInStorage(key, list);\n }\n\n private async addDomainToCache(domain: Domain, key: string): Promise<void> {\n const list = await this.getListFromStorage(key);\n\n if (list.data.domains.includes(domain)) {\n return;\n }\n\n list.data.domains.push(domain);\n\n await this.setListInStorage(key, list);\n }\n\n private async isDomainInList(domain: Domain, key: string): Promise<boolean> {\n const list = await this.getListFromStorage(key);\n return list.data.domains.includes(domain);\n }\n\n private async getListFromStorage(\n key: string\n ): Promise<z.infer<typeof ThreatDetector.Schema>> {\n const data = await this.storage.get(key);\n\n if (!data) {\n const list: z.infer<typeof ThreatDetector.Schema> = {\n version: 1,\n data: {\n domains: [],\n },\n };\n await this.setListInStorage(key, list);\n return list;\n }\n\n const list = ThreatDetector.Schema.parse(JSON.parse(data));\n\n return list;\n }\n\n private async setListInStorage(\n key: string,\n list: z.infer<typeof ThreatDetector.Schema>\n ): Promise<void> {\n await this.storage.set(key, JSON.stringify(list));\n }\n\n private async getStatusFromCache(\n domain: Domain\n ): Promise<DetectorAssetStatus> {\n if (\n await this.isDomainInList(domain, ThreatDetector.StorageKeys.IGNORELIST)\n ) {\n return \"IGNORED\";\n } else if (\n await this.isDomainInList(domain, ThreatDetector.StorageKeys.BLOCKLIST)\n ) {\n return \"BLOCKED\";\n } else if (\n await this.isDomainInList(domain, ThreatDetector.StorageKeys.ALLOWLIST)\n ) {\n return \"ALLOWED\";\n } else {\n return \"UNKNOWN\";\n }\n }\n}\n","// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs\nconst DATA_URL_DEFAULT_MIME_TYPE = 'text/plain';\nconst DATA_URL_DEFAULT_CHARSET = 'us-ascii';\n\nconst testParameter = (name, filters) => filters.some(filter => filter instanceof RegExp ? filter.test(name) : filter === name);\n\nconst supportedProtocols = new Set([\n\t'https:',\n\t'http:',\n\t'file:',\n]);\n\nconst hasCustomProtocol = urlString => {\n\ttry {\n\t\tconst {protocol} = new URL(urlString);\n\t\treturn protocol.endsWith(':') && !supportedProtocols.has(protocol);\n\t} catch {\n\t\treturn false;\n\t}\n};\n\nconst normalizeDataURL = (urlString, {stripHash}) => {\n\tconst match = /^data:(?<type>[^,]*?),(?<data>[^#]*?)(?:#(?<hash>.*))?$/.exec(urlString);\n\n\tif (!match) {\n\t\tthrow new Error(`Invalid URL: ${urlString}`);\n\t}\n\n\tlet {type, data, hash} = match.groups;\n\tconst mediaType = type.split(';');\n\thash = stripHash ? '' : hash;\n\n\tlet isBase64 = false;\n\tif (mediaType[mediaType.length - 1] === 'base64') {\n\t\tmediaType.pop();\n\t\tisBase64 = true;\n\t}\n\n\t// Lowercase MIME type\n\tconst mimeType = mediaType.shift()?.toLowerCase() ?? '';\n\tconst attributes = mediaType\n\t\t.map(attribute => {\n\t\t\tlet [key, value = ''] = attribute.split('=').map(string => string.trim());\n\n\t\t\t// Lowercase `charset`\n\t\t\tif (key === 'charset') {\n\t\t\t\tvalue = value.toLowerCase();\n\n\t\t\t\tif (value === DATA_URL_DEFAULT_CHARSET) {\n\t\t\t\t\treturn '';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn `${key}${value ? `=${value}` : ''}`;\n\t\t})\n\t\t.filter(Boolean);\n\n\tconst normalizedMediaType = [\n\t\t...attributes,\n\t];\n\n\tif (isBase64) {\n\t\tnormalizedMediaType.push('base64');\n\t}\n\n\tif (normalizedMediaType.length > 0 || (mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE)) {\n\t\tnormalizedMediaType.unshift(mimeType);\n\t}\n\n\treturn `data:${normalizedMediaType.join(';')},${isBase64 ? data.trim() : data}${hash ? `#${hash}` : ''}`;\n};\n\nexport default function normalizeUrl(urlString, options) {\n\toptions = {\n\t\tdefaultProtocol: 'http',\n\t\tnormalizeProtocol: true,\n\t\tforceHttp: false,\n\t\tforceHttps: false,\n\t\tstripAuthentication: true,\n\t\tstripHash: false,\n\t\tstripTextFragment: true,\n\t\tstripWWW: true,\n\t\tremoveQueryParameters: [/^utm_\\w+/i],\n\t\tremoveTrailingSlash: true,\n\t\tremoveSingleSlash: true,\n\t\tremoveDirectoryIndex: false,\n\t\tremoveExplicitPort: false,\n\t\tsortQueryParameters: true,\n\t\t...options,\n\t};\n\n\t// Legacy: Append `:` to the protocol if missing.\n\tif (typeof options.defaultProtocol === 'string' && !options.defaultProtocol.endsWith(':')) {\n\t\toptions.defaultProtocol = `${options.defaultProtocol}:`;\n\t}\n\n\turlString = urlString.trim();\n\n\t// Data URL\n\tif (/^data:/i.test(urlString)) {\n\t\treturn normalizeDataURL(urlString, options);\n\t}\n\n\tif (hasCustomProtocol(urlString)) {\n\t\treturn urlString;\n\t}\n\n\tconst hasRelativeProtocol = urlString.startsWith('//');\n\tconst isRelativeUrl = !hasRelativeProtocol && /^\\.*\\//.test(urlString);\n\n\t// Prepend protocol\n\tif (!isRelativeUrl) {\n\t\turlString = urlString.replace(/^(?!(?:\\w+:)?\\/\\/)|^\\/\\//, options.defaultProtocol);\n\t}\n\n\tconst urlObject = new URL(urlString);\n\n\tif (options.forceHttp && options.forceHttps) {\n\t\tthrow new Error('The `forceHttp` and `forceHttps` options cannot be used together');\n\t}\n\n\tif (options.forceHttp && urlObject.protocol === 'https:') {\n\t\turlObject.protocol = 'http:';\n\t}\n\n\tif (options.forceHttps && urlObject.protocol === 'http:') {\n\t\turlObject.protocol = 'https:';\n\t}\n\n\t// Remove auth\n\tif (options.stripAuthentication) {\n\t\turlObject.username = '';\n\t\turlObject.password = '';\n\t}\n\n\t// Remove hash\n\tif (options.stripHash) {\n\t\turlObject.hash = '';\n\t} else if (options.stripTextFragment) {\n\t\turlObject.hash = urlObject.hash.replace(/#?:~:text.*?$/i, '');\n\t}\n\n\t// Remove duplicate slashes if not preceded by a protocol\n\t// NOTE: This could be implemented using a single negative lookbehind\n\t// regex, but we avoid that to maintain compatibility with older js engines\n\t// which do not have support for that feature.\n\tif (urlObject.pathname) {\n\t\t// TODO: Replace everything below with `urlObject.pathname = urlObject.pathname.replace(/(?<!\\b[a-z][a-z\\d+\\-.]{1,50}:)\\/{2,}/g, '/');` when Safari supports negative lookbehind.\n\n\t\t// Split the string by occurrences of this protocol regex, and perform\n\t\t// duplicate-slash replacement on the strings between those occurrences\n\t\t// (if any).\n\t\tconst protocolRegex = /\\b[a-z][a-z\\d+\\-.]{1,50}:\\/\\//g;\n\n\t\tlet lastIndex = 0;\n\t\tlet result = '';\n\t\tfor (;;) {\n\t\t\tconst match = protocolRegex.exec(urlObject.pathname);\n\t\t\tif (!match) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst protocol = match[0];\n\t\t\tconst protocolAtIndex = match.index;\n\t\t\tconst intermediate = urlObject.pathname.slice(lastIndex, protocolAtIndex);\n\n\t\t\tresult += intermediate.replace(/\\/{2,}/g, '/');\n\t\t\tresult += protocol;\n\t\t\tlastIndex = protocolAtIndex + protocol.length;\n\t\t}\n\n\t\tconst remnant = urlObject.pathname.slice(lastIndex, urlObject.pathname.length);\n\t\tresult += remnant.replace(/\\/{2,}/g, '/');\n\n\t\turlObject.pathname = result;\n\t}\n\n\t// Decode URI octets\n\tif (urlObject.pathname) {\n\t\ttry {\n\t\t\turlObject.pathname = decodeURI(urlObject.pathname);\n\t\t} catch {}\n\t}\n\n\t// Remove directory index\n\tif (options.removeDirectoryIndex === true) {\n\t\toptions.removeDirectoryIndex = [/^index\\.[a-z]+$/];\n\t}\n\n\tif (Array.isArray(options.removeDirectoryIndex) && options.removeDirectoryIndex.length > 0) {\n\t\tlet pathComponents = urlObject.pathname.split('/');\n\t\tconst lastComponent = pathComponents[pathComponents.length - 1];\n\n\t\tif (testParameter(lastComponent, options.removeDirectoryIndex)) {\n\t\t\tpathComponents = pathComponents.slice(0, -1);\n\t\t\turlObject.pathname = pathComponents.slice(1).join('/') + '/';\n\t\t}\n\t}\n\n\tif (urlObject.hostname) {\n\t\t// Remove trailing dot\n\t\turlObject.hostname = urlObject.hostname.replace(/\\.$/, '');\n\n\t\t// Remove `www.`\n\t\tif (options.stripWWW && /^www\\.(?!www\\.)[a-z\\-\\d]{1,63}\\.[a-z.\\-\\d]{2,63}$/.test(urlObject.hostname)) {\n\t\t\t// Each label should be max 63 at length (min: 1).\n\t\t\t// Source: https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names\n\t\t\t// Each TLD should be up to 63 characters long (min: 2).\n\t\t\t// It is technically possible to have a single character TLD, but none currently exist.\n\t\t\turlObject.hostname = urlObject.hostname.replace(/^www\\./, '');\n\t\t}\n\t}\n\n\t// Remove query unwanted parameters\n\tif (Array.isArray(options.removeQueryParameters)) {\n\t\t// eslint-disable-next-line unicorn/no-useless-spread -- We are intentionally spreading to get a copy.\n\t\tfor (const key of [...urlObject.searchParams.keys()]) {\n\t\t\tif (testParameter(key, options.removeQueryParameters)) {\n\t\t\t\turlObject.searchParams.delete(key);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!Array.isArray(options.keepQueryParameters) && options.removeQueryParameters === true) {\n\t\turlObject.search = '';\n\t}\n\n\t// Keep wanted query parameters\n\tif (Array.isArray(options.keepQueryParameters) && options.keepQueryParameters.length > 0) {\n\t\t// eslint-disable-next-line unicorn/no-useless-spread -- We are intentionally spreading to get a copy.\n\t\tfor (const key of [...urlObject.searchParams.keys()]) {\n\t\t\tif (!testParameter(key, options.keepQueryParameters)) {\n\t\t\t\turlObject.searchParams.delete(key);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sort query parameters\n\tif (options.sortQueryParameters) {\n\t\turlObject.searchParams.sort();\n\n\t\t// Calling `.sort()` encodes the search parameters, so we need to decode them again.\n\t\ttry {\n\t\t\turlObject.search = decodeURIComponent(urlObject.search);\n\t\t} catch {}\n\t}\n\n\tif (options.removeTrailingSlash) {\n\t\turlObject.pathname = urlObject.pathname.replace(/\\/$/, '');\n\t}\n\n\t// Remove an explicit port number, excluding a default port number, if applicable\n\tif (options.removeExplicitPort && urlObject.port) {\n\t\turlObject.port = '';\n\t}\n\n\tconst oldUrlString = urlString;\n\n\t// Take advantage of many of the Node `url` normalizations\n\turlString = urlObject.toString();\n\n\tif (!options.removeSingleSlash && urlObject.pathname === '/' && !oldUrlString.endsWith('/') && urlObject.hash === '') {\n\t\turlString = urlString.replace(/\\/$/, '');\n\t}\n\n\t// Remove ending `/` unless removeSingleSlash is false\n\tif ((options.removeTrailingSlash || urlObject.pathname === '/') && urlObject.hash === '' && options.removeSingleSlash) {\n\t\turlString = urlString.replace(/\\/$/, '');\n\t}\n\n\t// Restore relative protocol, if applicable\n\tif (hasRelativeProtocol && !options.normalizeProtocol) {\n\t\turlString = urlString.replace(/^http:\\/\\//, '//');\n\t}\n\n\t// Remove http/https\n\tif (options.stripProtocol) {\n\t\turlString = urlString.replace(/^(?:https?:)?\\/\\//, '');\n\t}\n\n\treturn urlString;\n}\n","import { Logger } from \"./logger\";\n\nexport type ChainPatrolClientOptions = {\n apiKey: string;\n baseUrl?: string;\n};\n\ntype ApiRequest = {\n path: string[];\n method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n body?: unknown;\n};\n\nexport type AssetType = \"URL\" | \"PAGE\" | \"ADDRESS\";\nexport type AssetStatus = \"ALLOWED\" | \"BLOCKED\" | \"UNKNOWN\";\n\nfunction trimTrailingSlashes(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\nexport class ChainPatrolClient {\n public readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly logger = new Logger({ component: \"ChainPatrolClient\" });\n\n constructor(options: ChainPatrolClientOptions) {\n this.baseUrl = options.baseUrl ?? \"https://app.chainpatrol.io/api/\";\n if (!options.apiKey) {\n throw new Error(\"ChainPatrol API key is required\");\n }\n this.apiKey = options.apiKey;\n }\n\n private async fetch<TResult = unknown>(req: ApiRequest): Promise<TResult> {\n const url = `${trimTrailingSlashes(this.baseUrl)}/${req.path.join(\"/\")}`;\n this.logger.debug(\"fetch\", { url, req });\n const res = await fetch(url, {\n method: req.method,\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Api-Key\": this.apiKey,\n },\n body: JSON.stringify(req.body),\n });\n if (!res.ok) {\n throw new Error(await res.text());\n }\n return res.json();\n }\n\n public get asset() {\n return {\n check: async (req: {\n type: AssetType;\n content: string;\n }): Promise<{\n /**\n * \"ALLOWED\" - the asset is on the allowlist\n * \"BLOCKED\" - the asset is on the blocklist\n * \"UNKNOWN\" - the asset's status is not known\n */\n status: AssetStatus;\n /**\n * If the asset is allowed or blocked, this will be the reason why.\n * ChainPatrol aggregates data from multiple sources, so this will\n * tell you which source blocked or allowed the asset.\n *\n * ex. 'eth-phishing-detect' - the asset is on MetaMask's blocklist\n * 'reported' - the asset is on ChainPatrol's blocklist because\n * it was reported by a user\n */\n reason?: string;\n }> => {\n return await this.fetch<{\n status: AssetStatus;\n reason?: string;\n }>({\n path: [\"v2\", \"asset\", \"check\"],\n method: \"POST\",\n body: req,\n });\n },\n\n list: async (req: {\n /**\n * Asset type\n */\n type: AssetType;\n /**\n * Status of the assets to retrieve\n */\n status: AssetStatus;\n /**\n * The start date to list assets from. This should be in the format `YYYY-MM-DD` and is inclusive.\n */\n startDate?: string;\n /**\n * The end date to list assets from. This should be in the format `YYYY-MM-DD` and is inclusive.\n */\n endDate?: string;\n }): Promise<\n {\n content: string;\n type: AssetType;\n status: AssetStatus;\n }[]\n > => {\n return await this.fetch<\n {\n content: string;\n type: AssetType;\n status: AssetStatus;\n }[]\n >({\n path: [\"v2\", \"asset\", \"list\"],\n method: \"GET\",\n body: req,\n });\n },\n };\n }\n}\n","export * from \"./extension\";\nexport * from \"./browser\";\nexport * from \"./memory\";\nexport { defineStorage } from \"./define-storage\";\nexport * from \"./types\";\n","import { ThreatDetector } from \"../detector\";\nimport { Storage } from \"./types\";\n\nexport interface Context {\n keys: string[];\n}\n\nexport function defineStorage<T extends Storage>(\n config: (ctx: Context) => T\n): () => T {\n return () =>\n config({\n keys: Object.values(ThreatDetector.StorageKeys),\n });\n}\n","import { defineStorage } from \"./define-storage\";\n\nexport const Extension = defineStorage(({ keys }) => {\n return {\n get: async (key: string) => {\n const result = await chrome.storage.local.get(key);\n return result[key];\n },\n set: async (key: string, value: string) => {\n await chrome.storage.local.set({ [key]: value });\n },\n delete: async (key: string) => {\n await chrome.storage.local.remove(key);\n },\n size: async () => {\n const usageBytes = await chrome.storage.local.getBytesInUse(keys);\n return usageBytes;\n },\n };\n});\n","import { defineStorage } from \"./define-storage\";\n\nfunction isStorageAvailable(type: \"localStorage\" | \"sessionStorage\") {\n let storage;\n try {\n storage = window[type];\n const x = \"__storage_test__\";\n storage.setItem(x, x);\n storage.removeItem(x);\n return true;\n } catch (e) {\n return (\n e instanceof DOMException &&\n // everything except Firefox\n (e.code === 22 ||\n // Firefox\n e.code === 1014 ||\n // test name field too, because code might not be present\n // everything except Firefox\n e.name === \"QuotaExceededError\" ||\n // Firefox\n e.name === \"NS_ERROR_DOM_QUOTA_REACHED\") &&\n // acknowledge QuotaExceededError only if there's something already stored\n storage &&\n storage.length !== 0\n );\n }\n}\n\nexport const Browser = defineStorage(({ keys }) => {\n if (!isStorageAvailable(\"localStorage\")) {\n throw new Error(\"localStorage is not available\");\n }\n\n return {\n get: async (key: string) => {\n return localStorage.getItem(key);\n },\n set: async (key: string, value: string) => {\n localStorage.setItem(key, value);\n },\n delete: async (key: string) => {\n localStorage.removeItem(key);\n },\n size: async () => {\n let total = 0;\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key && keys.includes(key)) {\n total += localStorage.getItem(key)?.length ?? 0;\n }\n }\n return total;\n },\n };\n});\n","import { defineStorage } from \"./define-storage\";\n\nexport const Memory = defineStorage(() => {\n const storage = new Map<string, string>();\n return {\n get: async (key: string) => {\n return storage.get(key) || null;\n },\n set: async (key: string, value: string) => {\n storage.set(key, value);\n },\n delete: async (key: string) => {\n storage.delete(key);\n },\n size: async () => {\n let total = 0;\n for (const value of storage.values()) {\n total += value.length;\n }\n return total;\n },\n };\n});\n"]}
1
+ {"version":3,"sources":["../src/events.ts","../src/logger.ts","../src/relay.ts","../src/detector.ts","../../../node_modules/normalize-url/index.js","../src/client.ts","../src/storage/index.ts","../src/storage/define-storage.ts","../src/storage/extension.ts","../src/storage/browser.ts","../src/storage/memory.ts"],"names":["LogLevel","list"],"mappings":"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","sourcesContent":["const ContinueAtOwnRisk = \"CHAINPATROL_CONTINUE_AT_OWN_RISK\";\nconst IgnorelistUpdated = \"CHAINPATROL_IGNORELIST_UPDATED\";\nconst CloseCurrentTab = \"CHAINPATROL_CLOSE_CURRENT_TAB\";\n\nexport const Events = {\n ContinueAtOwnRisk,\n IgnorelistUpdated,\n CloseCurrentTab,\n} as const;\n\nexport type EventData = {\n [ContinueAtOwnRisk]: {\n domain: string;\n };\n [IgnorelistUpdated]: {\n domain: string;\n };\n [CloseCurrentTab]: {};\n};\n","export enum LogLevel {\n DEBUG,\n INFO,\n WARN,\n ERROR,\n NONE,\n}\n\nexport class Logger {\n private meta: Record<string, unknown> = {};\n private minLevel: LogLevel;\n\n constructor(\n meta?: Record<string, unknown>,\n minLevel: LogLevel = LogLevel.NONE\n ) {\n if (meta) {\n this.meta = meta;\n }\n this.minLevel = minLevel;\n }\n\n public with(fields: Record<string, unknown>) {\n return new Logger({ ...this.meta, ...fields }, this.minLevel);\n }\n\n public debug(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.DEBUG, message, fields);\n }\n\n public info(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.INFO, message, fields);\n }\n\n public warn(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.WARN, message, fields);\n }\n\n public error(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.ERROR, message, fields);\n }\n\n private log(\n level: LogLevel,\n message: string,\n fields?: Record<string, unknown>\n ) {\n if (level < this.minLevel) {\n return; // Skip logging if log level is lower than minimum level\n }\n\n const logObj = { message, data: { ...fields }, ...this.meta };\n const logString = JSON.stringify(logObj, null, 2);\n console.log(`[${LogLevel[level].toUpperCase()}] ${logString}`);\n }\n}\n","import { EventData } from \"./events\";\nimport { Logger } from \"./logger\";\n\ntype Message<EventType extends keyof EventData> = {\n type: EventType;\n data: EventData[EventType];\n _meta: {\n id: string;\n origin: string;\n };\n};\n\nfunction getExtensionRuntime(): typeof chrome.runtime {\n if ((globalThis as any).browser?.runtime) {\n return (globalThis as any).browser.runtime;\n }\n if (globalThis.chrome?.runtime) {\n return globalThis.chrome.runtime;\n }\n throw new Error(\"No extension runtime found\");\n}\n\nfunction isExtensionHost() {\n return (\n !!(globalThis as any).browser?.runtime ||\n !!(globalThis as any).chrome?.runtime\n );\n}\n\nfunction isContentScript() {\n return isExtensionHost() && isBrowserHost();\n}\n\nfunction isBackgroundScript() {\n return isExtensionHost() && !isBrowserHost();\n}\n\nfunction isBrowserHost() {\n return !!(globalThis as any).window;\n}\n\ntype Handle = {\n addListener: (callback: (message: any) => void) => void;\n removeListener: (callback: (message: any) => void) => void;\n postMessage: (message: any) => void;\n};\n\nfunction getBackgroundScriptHandle(): Handle {\n const runtime = getExtensionRuntime();\n return {\n addListener: (callback: (message: any) => void) => {\n runtime.onMessage.addListener(callback);\n },\n removeListener: (callback: (message: any) => void) => {\n runtime.onMessage.removeListener(callback);\n },\n postMessage: async (message: any) => {\n const [tab] = await globalThis.chrome.tabs.query({\n active: true,\n lastFocusedWindow: true,\n });\n if (!tab.id || tab.id === globalThis.chrome.tabs.TAB_ID_NONE) {\n console.error(\"No active tab found\");\n return;\n }\n globalThis.chrome.tabs.sendMessage(tab.id, message);\n },\n };\n}\n\nfunction getContentScriptHandle(): Handle {\n const runtime = getExtensionRuntime();\n return {\n addListener: (callback: (message: any) => void) => {\n runtime.onMessage.addListener(callback);\n },\n removeListener: (callback: (message: any) => void) => {\n runtime.onMessage.removeListener(callback);\n },\n postMessage: async (message: any) => {\n runtime.sendMessage(message).catch((error) => {\n console.error(\"Failed to send message\", { message, error });\n });\n },\n };\n}\n\nfunction getBrowserHandle(): Handle {\n return {\n addListener: (callback: (message: any) => void) => {\n globalThis.window.addEventListener(\"message\", (event) => {\n if (event.source !== globalThis.window) {\n return;\n }\n callback(event.data);\n });\n },\n removeListener: (callback: (message: any) => void) => {\n globalThis.window.removeEventListener(\"message\", callback);\n },\n postMessage: (message: any) => {\n globalThis.window.postMessage(message, \"*\");\n },\n };\n}\n\n/**\n * Relay contains methods for implementing an event relay. It is used to\n * forward events between Javascript contexts:\n *\n * `<window>` <-> `<content script>` <-> `<background script>`\n *\n * Call `Relay.send` to send an event from one context to another. Call\n * `Relay.on` to listen for events in the current context. Call `Relay.run`\n * to start listening for events in the current context and forward them\n * to all other contexts.\n */\nexport class Relay {\n protected static handles: Handle[] = [];\n private static readonly logger = new Logger({ component: \"Relay\" });\n\n static {\n if (isBackgroundScript()) {\n Relay.logger.info(\"Detected background script\");\n Relay.handles.push(getBackgroundScriptHandle());\n } else if (isContentScript()) {\n Relay.logger.info(\"Detected content script\");\n Relay.handles.push(getContentScriptHandle());\n Relay.handles.push(getBrowserHandle());\n } else if (isBrowserHost()) {\n Relay.logger.info(\"Detected browser host\");\n Relay.handles.push(getBrowserHandle());\n }\n }\n\n public static send<EventType extends keyof EventData>(\n type: EventType,\n data: EventData[EventType]\n ) {\n const message = {\n type,\n data,\n _meta: {\n id: Math.random().toString(36).substring(2, 9),\n origin: globalThis.location.origin,\n },\n } satisfies Message<EventType>;\n\n Relay.logger.debug(\"Sending message\", message);\n\n for (const handle of Relay.handles) {\n handle.postMessage(message);\n }\n }\n\n public static run(events: (keyof EventData)[]) {\n const listeners = new Set<(message: any) => void>();\n\n for (const handle of Relay.handles) {\n const listener = Relay.handleMessage.bind(null, handle, events);\n listeners.add(listener);\n handle.addListener(listener);\n }\n\n Relay.logger.debug(\"Started relay\", { events });\n\n return () => {\n for (const handle of Relay.handles) {\n for (const listener of listeners) {\n handle.removeListener(listener);\n }\n }\n\n Relay.logger.debug(\"Stopped relay\", { events });\n };\n }\n\n private static handleMessage(\n sourceHandle: Handle,\n events: (keyof EventData)[],\n message: any\n ) {\n if (!events.includes(message.type)) {\n Relay.logger.debug(\"Ignoring message\", { message });\n return;\n }\n\n const destinationHandles = Relay.handles.filter(\n (handle) => handle !== sourceHandle\n );\n\n for (const destinationHandle of destinationHandles) {\n destinationHandle.postMessage(message);\n }\n }\n\n public static on<EventType extends keyof EventData>(\n targetEvent: EventType,\n callback: (data: EventData[EventType]) => void\n ) {\n const listeners = new Set<(message: any) => void>();\n for (const handle of Relay.handles) {\n const listener = (message: any) => {\n if (message.type !== targetEvent) {\n Relay.logger.debug(\"Ignoring message\", { message });\n return;\n }\n callback(message.data);\n };\n listeners.add(listener);\n handle.addListener(listener);\n }\n\n return () => {\n for (const handle of Relay.handles) {\n for (const listener of listeners) {\n handle.removeListener(listener);\n }\n }\n };\n }\n}\n","import { parse as parseDomain } from \"tldts\";\nimport normalizeUrl from \"normalize-url\";\nimport { z } from \"zod\";\n\nimport { AssetStatus, ChainPatrolClient } from \"./client\";\nimport { Memory } from \"./storage\";\nimport type { Storage } from \"./storage/types\";\nimport { Logger } from \"./logger\";\n\ntype Prettify<T> = T extends infer U ? (U extends string ? U : never) : never;\n\nexport type DetectorAssetStatus = Prettify<AssetStatus | \"IGNORED\">;\n\ntype Mode = \"cloud\" | \"local\";\n\n// Branded type for domain strings\ntype Domain = string & { __domain: never };\n\nexport type URLResult =\n | {\n ok: true;\n status: DetectorAssetStatus;\n url: string;\n redirectUrl?: string;\n }\n | {\n ok: false;\n url: string;\n error: string;\n };\n\nexport class DomainParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"DomainParseError\";\n }\n}\n\nexport class ThreatDetector {\n static StorageKeys = {\n ALLOWLIST: \"chainpatrol.allowed\",\n BLOCKLIST: \"chainpatrol.blocked\",\n IGNORELIST: \"chainpatrol.ignored\",\n };\n\n private static readonly CHAINPATROL_WARNING_URL =\n \"https://app.chainpatrol.io/warning\";\n private static readonly Schema = z.object({\n version: z.literal(1),\n data: z.object({\n domains: z.array(z.string()),\n }),\n });\n\n private mode: Mode;\n private client?: ChainPatrolClient;\n private storage: Storage;\n private redirectUrl?: string | ((url: string) => string);\n private readonly logger = new Logger({ component: \"ThreatDetector\" });\n\n constructor({\n mode,\n storage,\n redirectUrl,\n }: {\n mode: \"local\";\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n });\n\n constructor({\n mode,\n apiKey,\n storage,\n redirectUrl,\n }: {\n mode: \"cloud\";\n apiKey: string;\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n });\n\n constructor({\n mode,\n apiKey,\n proxyUrl,\n storage,\n redirectUrl,\n }: {\n mode: \"cloud\";\n apiKey: string;\n proxyUrl?: string;\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n });\n\n constructor({\n mode = \"cloud\",\n apiKey = \"\",\n storage = Memory(),\n proxyUrl,\n redirectUrl,\n }: {\n mode?: Mode;\n apiKey?: string;\n proxyUrl?: string;\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n }) {\n this.mode = mode;\n this.storage = storage;\n this.redirectUrl = redirectUrl ?? ThreatDetector.CHAINPATROL_WARNING_URL;\n if (mode === \"cloud\") {\n this.client = new ChainPatrolClient({\n apiKey: apiKey,\n baseUrl: proxyUrl,\n });\n }\n }\n\n public async url(url: string): Promise<URLResult> {\n this.logger.debug(\"Checking URL\", { url });\n\n let domains: Domain[];\n try {\n domains = this.generateDomains(url);\n } catch (e) {\n this.logger.error(\"Unable to parse domain\", { url, error: e });\n return {\n ok: false,\n url,\n error:\n e instanceof DomainParseError ? e.message : \"Unable to parse domain\",\n };\n }\n\n this.logger.debug(\"Generated domains\", { domains });\n\n let results = (\n await Promise.all(domains.map((domain) => this.urlHelper(domain, url)))\n ).filter(\n <T extends URLResult>(r: T): r is T extends { ok: true } ? T : never =>\n r.ok\n );\n\n if (results.length === 0) {\n return {\n ok: false,\n url,\n error: \"URL does not have a valid domain\",\n };\n }\n\n this.logger.debug(\"Results for domains\", { results });\n\n if (results.some((r) => r.status === \"IGNORED\")) {\n return {\n ok: true,\n status: \"IGNORED\",\n url,\n };\n }\n\n for (const result of results) {\n if (result.ok && result.status !== \"UNKNOWN\") {\n return result;\n }\n }\n\n return results[0];\n }\n\n private generateDomains(_url: string): Domain[] {\n const domain = this.parseDomainOrThrow(_url);\n\n const domains = [domain];\n\n const parsedDomain = parseDomain(domain);\n if (!parsedDomain.subdomain) {\n return domains;\n }\n\n const subdomainParts = parsedDomain.subdomain?.split(\".\") ?? [];\n\n for (let i = 0; i < subdomainParts.length; i++) {\n const subdomain = subdomainParts.slice(i).join(\".\");\n domains.unshift(`${subdomain}.${domain}` as Domain);\n }\n\n return domains;\n }\n\n private async urlHelper(domain: Domain, url: string): Promise<URLResult> {\n let status = await this.getStatusFromCache(domain);\n\n if (this.mode === \"cloud\" && this.client && status === \"UNKNOWN\") {\n try {\n const res = await this.client.asset.check({\n type: \"URL\",\n content: domain,\n });\n\n // Update cache storage\n this.logger.debug(\"Updating cache\", { domain, status: res.status });\n if (res.status === \"ALLOWED\") {\n this.addDomainToCache(domain, ThreatDetector.StorageKeys.ALLOWLIST);\n } else if (res.status === \"BLOCKED\") {\n this.addDomainToCache(domain, ThreatDetector.StorageKeys.BLOCKLIST);\n }\n\n status = res.status;\n } catch (e) {\n return {\n ok: false,\n url: domain,\n error: \"Unable to check URL\",\n };\n }\n }\n\n let redirectUrl: string | undefined;\n\n if (status === \"BLOCKED\") {\n if (typeof this.redirectUrl === \"function\") {\n redirectUrl = this.redirectUrl(url);\n } else if (typeof this.redirectUrl === \"string\") {\n const newUrl = new URL(this.redirectUrl);\n newUrl.searchParams.set(\"originUrl\", url);\n redirectUrl = newUrl.toString();\n }\n }\n\n return {\n ok: true,\n status,\n url: domain,\n redirectUrl,\n };\n }\n\n public async allow(\n url: string\n ): Promise<\n { ok: true; url: string } | { ok: false; url: string; error: string }\n > {\n try {\n const domain = this.parseDomainOrThrow(url);\n\n this.logger.debug(\"Allowing URL\", { url, domain });\n\n await this.invalidateDomainInCaches(domain);\n await this.addDomainToCache(domain, ThreatDetector.StorageKeys.ALLOWLIST);\n\n return {\n ok: true,\n url: domain,\n };\n } catch (e) {\n this.logger.error(\"Unable to allow URL\", { url, error: e });\n return {\n ok: false,\n url,\n error: \"Unable to allow URL\",\n };\n }\n }\n\n public async block(\n url: string\n ): Promise<\n { ok: true; url: string } | { ok: false; url: string; error: string }\n > {\n try {\n const domain = this.parseDomainOrThrow(url);\n\n this.logger.debug(\"Blocking URL\", { url, domain });\n\n await this.invalidateDomainInCaches(domain);\n await this.addDomainToCache(domain, ThreatDetector.StorageKeys.BLOCKLIST);\n\n return {\n ok: true,\n url: domain,\n };\n } catch (e) {\n this.logger.error(\"Unable to block URL\", { url, error: e });\n return {\n ok: false,\n url,\n error: \"Unable to block URL\",\n };\n }\n }\n\n public async ignore(\n url: string\n ): Promise<\n { ok: true; url: string } | { ok: false; url: string; error: string }\n > {\n try {\n const domain = this.parseDomainOrThrow(url);\n\n this.logger.debug(\"Ignoring URL\", { url, domain });\n\n await this.addDomainToCache(\n domain,\n ThreatDetector.StorageKeys.IGNORELIST\n );\n\n return {\n ok: true,\n url: domain,\n };\n } catch (e) {\n this.logger.error(\"Unable to ignore URL\", { url, error: e });\n return {\n ok: false,\n url,\n error: \"Unable to ignore URL\",\n };\n }\n }\n\n private parseDomainOrThrow(url: string): Domain {\n this.logger.debug(\"Parsing domain\", { url });\n try {\n const normalizedUrl = normalizeUrl(url, {\n stripWWW: false,\n removeTrailingSlash: false,\n });\n\n this.logger.debug(\"Normalized URL\", { from: url, to: normalizedUrl });\n\n const parsedURL = new URL(normalizedUrl);\n\n this.logger.debug(\"Extract domain from URL\", { url: parsedURL.hostname });\n\n const parsedSubdomainResult = parseDomain(parsedURL.hostname);\n\n this.logger.debug(\"Parsed domain\", { parsedSubdomainResult });\n\n if (parsedSubdomainResult.hostname === \"localhost\") {\n throw new DomainParseError(\"ThreatDetector does not support localhost\");\n }\n\n if (parsedSubdomainResult.isIp) {\n throw new DomainParseError(\n \"ThreatDetector does not support IP addresses\"\n );\n }\n\n if (!parsedSubdomainResult.domain) {\n throw new DomainParseError(\"Unable to parse domain\");\n }\n\n const domain = parsedSubdomainResult.domain as Domain;\n\n return domain;\n } catch (e) {\n if (e instanceof DomainParseError) {\n throw e;\n } else {\n this.logger.error(\"Unable to parse domain\", { url, error: e });\n throw new DomainParseError(\"Unable to parse domain\");\n }\n }\n }\n\n private async invalidateDomainInCaches(domain: Domain): Promise<void> {\n await this.invalidateDomainInCache(\n domain,\n ThreatDetector.StorageKeys.ALLOWLIST\n );\n await this.invalidateDomainInCache(\n domain,\n ThreatDetector.StorageKeys.BLOCKLIST\n );\n }\n\n private async invalidateDomainInCache(\n domain: Domain,\n key: string\n ): Promise<void> {\n const data = await this.storage.get(key);\n\n if (!data) {\n return;\n }\n\n const list = await this.getListFromStorage(key);\n\n if (!list.data.domains.includes(domain)) {\n return;\n }\n\n list.data.domains = list.data.domains.filter((u) => u !== domain);\n\n await this.setListInStorage(key, list);\n }\n\n private async addDomainToCache(domain: Domain, key: string): Promise<void> {\n const list = await this.getListFromStorage(key);\n\n if (list.data.domains.includes(domain)) {\n return;\n }\n\n list.data.domains.push(domain);\n\n await this.setListInStorage(key, list);\n }\n\n private async isDomainInList(domain: Domain, key: string): Promise<boolean> {\n const list = await this.getListFromStorage(key);\n return list.data.domains.includes(domain);\n }\n\n private async getListFromStorage(\n key: string\n ): Promise<z.infer<typeof ThreatDetector.Schema>> {\n const data = await this.storage.get(key);\n\n if (!data) {\n const list: z.infer<typeof ThreatDetector.Schema> = {\n version: 1,\n data: {\n domains: [],\n },\n };\n await this.setListInStorage(key, list);\n return list;\n }\n\n const list = ThreatDetector.Schema.parse(JSON.parse(data));\n\n return list;\n }\n\n private async setListInStorage(\n key: string,\n list: z.infer<typeof ThreatDetector.Schema>\n ): Promise<void> {\n await this.storage.set(key, JSON.stringify(list));\n }\n\n private async getStatusFromCache(\n domain: Domain\n ): Promise<DetectorAssetStatus> {\n if (\n await this.isDomainInList(domain, ThreatDetector.StorageKeys.IGNORELIST)\n ) {\n return \"IGNORED\";\n } else if (\n await this.isDomainInList(domain, ThreatDetector.StorageKeys.BLOCKLIST)\n ) {\n return \"BLOCKED\";\n } else if (\n await this.isDomainInList(domain, ThreatDetector.StorageKeys.ALLOWLIST)\n ) {\n return \"ALLOWED\";\n } else {\n return \"UNKNOWN\";\n }\n }\n}\n","// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs\nconst DATA_URL_DEFAULT_MIME_TYPE = 'text/plain';\nconst DATA_URL_DEFAULT_CHARSET = 'us-ascii';\n\nconst testParameter = (name, filters) => filters.some(filter => filter instanceof RegExp ? filter.test(name) : filter === name);\n\nconst supportedProtocols = new Set([\n\t'https:',\n\t'http:',\n\t'file:',\n]);\n\nconst hasCustomProtocol = urlString => {\n\ttry {\n\t\tconst {protocol} = new URL(urlString);\n\t\treturn protocol.endsWith(':') && !supportedProtocols.has(protocol);\n\t} catch {\n\t\treturn false;\n\t}\n};\n\nconst normalizeDataURL = (urlString, {stripHash}) => {\n\tconst match = /^data:(?<type>[^,]*?),(?<data>[^#]*?)(?:#(?<hash>.*))?$/.exec(urlString);\n\n\tif (!match) {\n\t\tthrow new Error(`Invalid URL: ${urlString}`);\n\t}\n\n\tlet {type, data, hash} = match.groups;\n\tconst mediaType = type.split(';');\n\thash = stripHash ? '' : hash;\n\n\tlet isBase64 = false;\n\tif (mediaType[mediaType.length - 1] === 'base64') {\n\t\tmediaType.pop();\n\t\tisBase64 = true;\n\t}\n\n\t// Lowercase MIME type\n\tconst mimeType = mediaType.shift()?.toLowerCase() ?? '';\n\tconst attributes = mediaType\n\t\t.map(attribute => {\n\t\t\tlet [key, value = ''] = attribute.split('=').map(string => string.trim());\n\n\t\t\t// Lowercase `charset`\n\t\t\tif (key === 'charset') {\n\t\t\t\tvalue = value.toLowerCase();\n\n\t\t\t\tif (value === DATA_URL_DEFAULT_CHARSET) {\n\t\t\t\t\treturn '';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn `${key}${value ? `=${value}` : ''}`;\n\t\t})\n\t\t.filter(Boolean);\n\n\tconst normalizedMediaType = [\n\t\t...attributes,\n\t];\n\n\tif (isBase64) {\n\t\tnormalizedMediaType.push('base64');\n\t}\n\n\tif (normalizedMediaType.length > 0 || (mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE)) {\n\t\tnormalizedMediaType.unshift(mimeType);\n\t}\n\n\treturn `data:${normalizedMediaType.join(';')},${isBase64 ? data.trim() : data}${hash ? `#${hash}` : ''}`;\n};\n\nexport default function normalizeUrl(urlString, options) {\n\toptions = {\n\t\tdefaultProtocol: 'http',\n\t\tnormalizeProtocol: true,\n\t\tforceHttp: false,\n\t\tforceHttps: false,\n\t\tstripAuthentication: true,\n\t\tstripHash: false,\n\t\tstripTextFragment: true,\n\t\tstripWWW: true,\n\t\tremoveQueryParameters: [/^utm_\\w+/i],\n\t\tremoveTrailingSlash: true,\n\t\tremoveSingleSlash: true,\n\t\tremoveDirectoryIndex: false,\n\t\tremoveExplicitPort: false,\n\t\tsortQueryParameters: true,\n\t\t...options,\n\t};\n\n\t// Legacy: Append `:` to the protocol if missing.\n\tif (typeof options.defaultProtocol === 'string' && !options.defaultProtocol.endsWith(':')) {\n\t\toptions.defaultProtocol = `${options.defaultProtocol}:`;\n\t}\n\n\turlString = urlString.trim();\n\n\t// Data URL\n\tif (/^data:/i.test(urlString)) {\n\t\treturn normalizeDataURL(urlString, options);\n\t}\n\n\tif (hasCustomProtocol(urlString)) {\n\t\treturn urlString;\n\t}\n\n\tconst hasRelativeProtocol = urlString.startsWith('//');\n\tconst isRelativeUrl = !hasRelativeProtocol && /^\\.*\\//.test(urlString);\n\n\t// Prepend protocol\n\tif (!isRelativeUrl) {\n\t\turlString = urlString.replace(/^(?!(?:\\w+:)?\\/\\/)|^\\/\\//, options.defaultProtocol);\n\t}\n\n\tconst urlObject = new URL(urlString);\n\n\tif (options.forceHttp && options.forceHttps) {\n\t\tthrow new Error('The `forceHttp` and `forceHttps` options cannot be used together');\n\t}\n\n\tif (options.forceHttp && urlObject.protocol === 'https:') {\n\t\turlObject.protocol = 'http:';\n\t}\n\n\tif (options.forceHttps && urlObject.protocol === 'http:') {\n\t\turlObject.protocol = 'https:';\n\t}\n\n\t// Remove auth\n\tif (options.stripAuthentication) {\n\t\turlObject.username = '';\n\t\turlObject.password = '';\n\t}\n\n\t// Remove hash\n\tif (options.stripHash) {\n\t\turlObject.hash = '';\n\t} else if (options.stripTextFragment) {\n\t\turlObject.hash = urlObject.hash.replace(/#?:~:text.*?$/i, '');\n\t}\n\n\t// Remove duplicate slashes if not preceded by a protocol\n\t// NOTE: This could be implemented using a single negative lookbehind\n\t// regex, but we avoid that to maintain compatibility with older js engines\n\t// which do not have support for that feature.\n\tif (urlObject.pathname) {\n\t\t// TODO: Replace everything below with `urlObject.pathname = urlObject.pathname.replace(/(?<!\\b[a-z][a-z\\d+\\-.]{1,50}:)\\/{2,}/g, '/');` when Safari supports negative lookbehind.\n\n\t\t// Split the string by occurrences of this protocol regex, and perform\n\t\t// duplicate-slash replacement on the strings between those occurrences\n\t\t// (if any).\n\t\tconst protocolRegex = /\\b[a-z][a-z\\d+\\-.]{1,50}:\\/\\//g;\n\n\t\tlet lastIndex = 0;\n\t\tlet result = '';\n\t\tfor (;;) {\n\t\t\tconst match = protocolRegex.exec(urlObject.pathname);\n\t\t\tif (!match) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst protocol = match[0];\n\t\t\tconst protocolAtIndex = match.index;\n\t\t\tconst intermediate = urlObject.pathname.slice(lastIndex, protocolAtIndex);\n\n\t\t\tresult += intermediate.replace(/\\/{2,}/g, '/');\n\t\t\tresult += protocol;\n\t\t\tlastIndex = protocolAtIndex + protocol.length;\n\t\t}\n\n\t\tconst remnant = urlObject.pathname.slice(lastIndex, urlObject.pathname.length);\n\t\tresult += remnant.replace(/\\/{2,}/g, '/');\n\n\t\turlObject.pathname = result;\n\t}\n\n\t// Decode URI octets\n\tif (urlObject.pathname) {\n\t\ttry {\n\t\t\turlObject.pathname = decodeURI(urlObject.pathname);\n\t\t} catch {}\n\t}\n\n\t// Remove directory index\n\tif (options.removeDirectoryIndex === true) {\n\t\toptions.removeDirectoryIndex = [/^index\\.[a-z]+$/];\n\t}\n\n\tif (Array.isArray(options.removeDirectoryIndex) && options.removeDirectoryIndex.length > 0) {\n\t\tlet pathComponents = urlObject.pathname.split('/');\n\t\tconst lastComponent = pathComponents[pathComponents.length - 1];\n\n\t\tif (testParameter(lastComponent, options.removeDirectoryIndex)) {\n\t\t\tpathComponents = pathComponents.slice(0, -1);\n\t\t\turlObject.pathname = pathComponents.slice(1).join('/') + '/';\n\t\t}\n\t}\n\n\tif (urlObject.hostname) {\n\t\t// Remove trailing dot\n\t\turlObject.hostname = urlObject.hostname.replace(/\\.$/, '');\n\n\t\t// Remove `www.`\n\t\tif (options.stripWWW && /^www\\.(?!www\\.)[a-z\\-\\d]{1,63}\\.[a-z.\\-\\d]{2,63}$/.test(urlObject.hostname)) {\n\t\t\t// Each label should be max 63 at length (min: 1).\n\t\t\t// Source: https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names\n\t\t\t// Each TLD should be up to 63 characters long (min: 2).\n\t\t\t// It is technically possible to have a single character TLD, but none currently exist.\n\t\t\turlObject.hostname = urlObject.hostname.replace(/^www\\./, '');\n\t\t}\n\t}\n\n\t// Remove query unwanted parameters\n\tif (Array.isArray(options.removeQueryParameters)) {\n\t\t// eslint-disable-next-line unicorn/no-useless-spread -- We are intentionally spreading to get a copy.\n\t\tfor (const key of [...urlObject.searchParams.keys()]) {\n\t\t\tif (testParameter(key, options.removeQueryParameters)) {\n\t\t\t\turlObject.searchParams.delete(key);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!Array.isArray(options.keepQueryParameters) && options.removeQueryParameters === true) {\n\t\turlObject.search = '';\n\t}\n\n\t// Keep wanted query parameters\n\tif (Array.isArray(options.keepQueryParameters) && options.keepQueryParameters.length > 0) {\n\t\t// eslint-disable-next-line unicorn/no-useless-spread -- We are intentionally spreading to get a copy.\n\t\tfor (const key of [...urlObject.searchParams.keys()]) {\n\t\t\tif (!testParameter(key, options.keepQueryParameters)) {\n\t\t\t\turlObject.searchParams.delete(key);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sort query parameters\n\tif (options.sortQueryParameters) {\n\t\turlObject.searchParams.sort();\n\n\t\t// Calling `.sort()` encodes the search parameters, so we need to decode them again.\n\t\ttry {\n\t\t\turlObject.search = decodeURIComponent(urlObject.search);\n\t\t} catch {}\n\t}\n\n\tif (options.removeTrailingSlash) {\n\t\turlObject.pathname = urlObject.pathname.replace(/\\/$/, '');\n\t}\n\n\t// Remove an explicit port number, excluding a default port number, if applicable\n\tif (options.removeExplicitPort && urlObject.port) {\n\t\turlObject.port = '';\n\t}\n\n\tconst oldUrlString = urlString;\n\n\t// Take advantage of many of the Node `url` normalizations\n\turlString = urlObject.toString();\n\n\tif (!options.removeSingleSlash && urlObject.pathname === '/' && !oldUrlString.endsWith('/') && urlObject.hash === '') {\n\t\turlString = urlString.replace(/\\/$/, '');\n\t}\n\n\t// Remove ending `/` unless removeSingleSlash is false\n\tif ((options.removeTrailingSlash || urlObject.pathname === '/') && urlObject.hash === '' && options.removeSingleSlash) {\n\t\turlString = urlString.replace(/\\/$/, '');\n\t}\n\n\t// Restore relative protocol, if applicable\n\tif (hasRelativeProtocol && !options.normalizeProtocol) {\n\t\turlString = urlString.replace(/^http:\\/\\//, '//');\n\t}\n\n\t// Remove http/https\n\tif (options.stripProtocol) {\n\t\turlString = urlString.replace(/^(?:https?:)?\\/\\//, '');\n\t}\n\n\treturn urlString;\n}\n","import { Logger } from \"./logger\";\n\nexport type ChainPatrolClientOptions = {\n apiKey: string;\n baseUrl?: string;\n};\n\ntype ApiRequest = {\n path: string[];\n method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n body?: unknown;\n};\n\nexport type AssetType = \"URL\" | \"PAGE\" | \"ADDRESS\";\nexport type AssetStatus = \"ALLOWED\" | \"BLOCKED\" | \"UNKNOWN\";\n\nfunction trimTrailingSlashes(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\nexport class ChainPatrolClient {\n public readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly logger = new Logger({ component: \"ChainPatrolClient\" });\n\n constructor(options: ChainPatrolClientOptions) {\n this.baseUrl = options.baseUrl ?? \"https://app.chainpatrol.io/api/\";\n if (!options.apiKey) {\n throw new Error(\"ChainPatrol API key is required\");\n }\n this.apiKey = options.apiKey;\n }\n\n private async fetch<TResult = unknown>(req: ApiRequest): Promise<TResult> {\n const url = `${trimTrailingSlashes(this.baseUrl)}/${req.path.join(\"/\")}`;\n this.logger.debug(\"fetch\", { url, req });\n const res = await fetch(url, {\n method: req.method,\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Api-Key\": this.apiKey,\n },\n body: JSON.stringify(req.body),\n });\n if (!res.ok) {\n throw new Error(await res.text());\n }\n return res.json();\n }\n\n public get asset() {\n return {\n check: async (req: {\n type: AssetType;\n content: string;\n }): Promise<{\n /**\n * \"ALLOWED\" - the asset is on the allowlist\n * \"BLOCKED\" - the asset is on the blocklist\n * \"UNKNOWN\" - the asset's status is not known\n */\n status: AssetStatus;\n /**\n * If the asset is allowed or blocked, this will be the reason why.\n * ChainPatrol aggregates data from multiple sources, so this will\n * tell you which source blocked or allowed the asset.\n *\n * ex. 'eth-phishing-detect' - the asset is on MetaMask's blocklist\n * 'reported' - the asset is on ChainPatrol's blocklist because\n * it was reported by a user\n */\n reason?: string;\n }> => {\n return await this.fetch<{\n status: AssetStatus;\n reason?: string;\n }>({\n path: [\"v2\", \"asset\", \"check\"],\n method: \"POST\",\n body: req,\n });\n },\n\n list: async (req: {\n /**\n * Asset type\n */\n type: AssetType;\n /**\n * Status of the assets to retrieve\n */\n status: AssetStatus;\n /**\n * The start date to list assets from. This should be in the format `YYYY-MM-DD` and is inclusive.\n */\n startDate?: string;\n /**\n * The end date to list assets from. This should be in the format `YYYY-MM-DD` and is inclusive.\n */\n endDate?: string;\n }): Promise<\n {\n content: string;\n type: AssetType;\n status: AssetStatus;\n }[]\n > => {\n return await this.fetch<\n {\n content: string;\n type: AssetType;\n status: AssetStatus;\n }[]\n >({\n path: [\"v2\", \"asset\", \"list\"],\n method: \"GET\",\n body: req,\n });\n },\n };\n }\n}\n","export * from \"./extension\";\nexport * from \"./browser\";\nexport * from \"./memory\";\nexport { defineStorage } from \"./define-storage\";\nexport * from \"./types\";\n","import { ThreatDetector } from \"../detector\";\nimport { Storage } from \"./types\";\n\nexport interface Context {\n keys: string[];\n}\n\nexport function defineStorage<T extends Storage>(\n config: (ctx: Context) => T\n): () => T {\n return () =>\n config({\n keys: Object.values(ThreatDetector.StorageKeys),\n });\n}\n","import { defineStorage } from \"./define-storage\";\n\nexport const Extension = defineStorage(({ keys }) => {\n return {\n get: async (key: string) => {\n const result = await chrome.storage.local.get(key);\n return result[key];\n },\n set: async (key: string, value: string) => {\n await chrome.storage.local.set({ [key]: value });\n },\n delete: async (key: string) => {\n await chrome.storage.local.remove(key);\n },\n size: async () => {\n const usageBytes = await chrome.storage.local.getBytesInUse(keys);\n return usageBytes;\n },\n };\n});\n","import { defineStorage } from \"./define-storage\";\n\nfunction isStorageAvailable(type: \"localStorage\" | \"sessionStorage\") {\n let storage;\n try {\n storage = window[type];\n const x = \"__storage_test__\";\n storage.setItem(x, x);\n storage.removeItem(x);\n return true;\n } catch (e) {\n return (\n e instanceof DOMException &&\n // everything except Firefox\n (e.code === 22 ||\n // Firefox\n e.code === 1014 ||\n // test name field too, because code might not be present\n // everything except Firefox\n e.name === \"QuotaExceededError\" ||\n // Firefox\n e.name === \"NS_ERROR_DOM_QUOTA_REACHED\") &&\n // acknowledge QuotaExceededError only if there's something already stored\n storage &&\n storage.length !== 0\n );\n }\n}\n\nexport const Browser = defineStorage(({ keys }) => {\n if (!isStorageAvailable(\"localStorage\")) {\n throw new Error(\"localStorage is not available\");\n }\n\n return {\n get: async (key: string) => {\n return localStorage.getItem(key);\n },\n set: async (key: string, value: string) => {\n localStorage.setItem(key, value);\n },\n delete: async (key: string) => {\n localStorage.removeItem(key);\n },\n size: async () => {\n let total = 0;\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key && keys.includes(key)) {\n total += localStorage.getItem(key)?.length ?? 0;\n }\n }\n return total;\n },\n };\n});\n","import { defineStorage } from \"./define-storage\";\n\nexport const Memory = defineStorage(() => {\n const storage = new Map<string, string>();\n return {\n get: async (key: string) => {\n return storage.get(key) || null;\n },\n set: async (key: string, value: string) => {\n storage.set(key, value);\n },\n delete: async (key: string) => {\n storage.delete(key);\n },\n size: async () => {\n let total = 0;\n for (const value of storage.values()) {\n total += value.length;\n }\n return total;\n },\n };\n});\n"]}
package/dist/index.mjs CHANGED
@@ -44,8 +44,12 @@ var __async = (__this, __arguments, generator) => {
44
44
 
45
45
  // src/events.ts
46
46
  var ContinueAtOwnRisk = "CHAINPATROL_CONTINUE_AT_OWN_RISK";
47
+ var IgnorelistUpdated = "CHAINPATROL_IGNORELIST_UPDATED";
48
+ var CloseCurrentTab = "CHAINPATROL_CLOSE_CURRENT_TAB";
47
49
  var Events = {
48
- ContinueAtOwnRisk
50
+ ContinueAtOwnRisk,
51
+ IgnorelistUpdated,
52
+ CloseCurrentTab
49
53
  };
50
54
 
51
55
  // src/logger.ts
@@ -91,24 +95,65 @@ var Logger = class _Logger {
91
95
  };
92
96
 
93
97
  // src/relay.ts
98
+ function getExtensionRuntime() {
99
+ var _a, _b;
100
+ if ((_a = globalThis.browser) == null ? void 0 : _a.runtime) {
101
+ return globalThis.browser.runtime;
102
+ }
103
+ if ((_b = globalThis.chrome) == null ? void 0 : _b.runtime) {
104
+ return globalThis.chrome.runtime;
105
+ }
106
+ throw new Error("No extension runtime found");
107
+ }
94
108
  function isExtensionHost() {
95
109
  var _a, _b;
96
110
  return !!((_a = globalThis.browser) == null ? void 0 : _a.runtime) || !!((_b = globalThis.chrome) == null ? void 0 : _b.runtime);
97
111
  }
112
+ function isContentScript() {
113
+ return isExtensionHost() && isBrowserHost();
114
+ }
115
+ function isBackgroundScript() {
116
+ return isExtensionHost() && !isBrowserHost();
117
+ }
98
118
  function isBrowserHost() {
99
119
  return !!globalThis.window;
100
120
  }
101
- function getExtensionHandle() {
121
+ function getBackgroundScriptHandle() {
122
+ const runtime = getExtensionRuntime();
102
123
  return {
103
124
  addListener: (callback) => {
104
- globalThis.chrome.runtime.onMessage.addListener(callback);
125
+ runtime.onMessage.addListener(callback);
105
126
  },
106
127
  removeListener: (callback) => {
107
- globalThis.chrome.runtime.onMessage.removeListener(callback);
128
+ runtime.onMessage.removeListener(callback);
108
129
  },
109
- postMessage: (message) => {
110
- globalThis.chrome.runtime.sendMessage(message);
111
- }
130
+ postMessage: (message) => __async(this, null, function* () {
131
+ const [tab] = yield globalThis.chrome.tabs.query({
132
+ active: true,
133
+ lastFocusedWindow: true
134
+ });
135
+ if (!tab.id || tab.id === globalThis.chrome.tabs.TAB_ID_NONE) {
136
+ console.error("No active tab found");
137
+ return;
138
+ }
139
+ globalThis.chrome.tabs.sendMessage(tab.id, message);
140
+ })
141
+ };
142
+ }
143
+ function getContentScriptHandle() {
144
+ const runtime = getExtensionRuntime();
145
+ return {
146
+ addListener: (callback) => {
147
+ runtime.onMessage.addListener(callback);
148
+ },
149
+ removeListener: (callback) => {
150
+ runtime.onMessage.removeListener(callback);
151
+ },
152
+ postMessage: (message) => __async(this, null, function* () {
153
+ runtime.sendMessage(message).catch((error) => {
154
+ console.error("Failed to send message", { message, error });
155
+ });
156
+ })
112
157
  };
113
158
  }
114
159
  function getBrowserHandle() {
@@ -198,11 +243,14 @@ var _Relay = class _Relay {
198
243
  _Relay.handles = [];
199
244
  _Relay.logger = new Logger({ component: "Relay" });
200
245
  (() => {
201
- if (isExtensionHost()) {
202
- _Relay.logger.info("Detected extension host");
203
- _Relay.handles.push(getExtensionHandle());
204
- }
205
- if (isBrowserHost()) {
246
+ if (isBackgroundScript()) {
247
+ _Relay.logger.info("Detected background script");
248
+ _Relay.handles.push(getBackgroundScriptHandle());
249
+ } else if (isContentScript()) {
250
+ _Relay.logger.info("Detected content script");
251
+ _Relay.handles.push(getContentScriptHandle());
252
+ _Relay.handles.push(getBrowserHandle());
253
+ } else if (isBrowserHost()) {
206
254
  _Relay.logger.info("Detected browser host");
207
255
  _Relay.handles.push(getBrowserHandle());
208
256
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/events.ts","../src/logger.ts","../src/relay.ts","../src/detector.ts","../../../node_modules/normalize-url/index.js","../src/client.ts","../src/storage/index.ts","../src/storage/define-storage.ts","../src/storage/extension.ts","../src/storage/browser.ts","../src/storage/memory.ts"],"names":["LogLevel","list"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,IAAM,oBAAoB;AAEnB,IAAM,SAAS;AAAA,EACpB;AACF;;;ACLO,IAAK,WAAL,kBAAKA,cAAL;AACL,EAAAA,oBAAA;AACA,EAAAA,oBAAA;AACA,EAAAA,oBAAA;AACA,EAAAA,oBAAA;AACA,EAAAA,oBAAA;AALU,SAAAA;AAAA,GAAA;AAQL,IAAM,SAAN,MAAM,QAAO;AAAA,EAIlB,YACE,MACA,WAAqB,cACrB;AANF,SAAQ,OAAgC,CAAC;AAOvC,QAAI,MAAM;AACR,WAAK,OAAO;AAAA,IACd;AACA,SAAK,WAAW;AAAA,EAClB;AAAA,EAEO,KAAK,QAAiC;AAC3C,WAAO,IAAI,QAAO,kCAAK,KAAK,OAAS,SAAU,KAAK,QAAQ;AAAA,EAC9D;AAAA,EAEO,MAAM,SAAiB,QAAkC;AAC9D,SAAK,IAAI,eAAgB,SAAS,MAAM;AAAA,EAC1C;AAAA,EAEO,KAAK,SAAiB,QAAkC;AAC7D,SAAK,IAAI,cAAe,SAAS,MAAM;AAAA,EACzC;AAAA,EAEO,KAAK,SAAiB,QAAkC;AAC7D,SAAK,IAAI,cAAe,SAAS,MAAM;AAAA,EACzC;AAAA,EAEO,MAAM,SAAiB,QAAkC;AAC9D,SAAK,IAAI,eAAgB,SAAS,MAAM;AAAA,EAC1C;AAAA,EAEQ,IACN,OACA,SACA,QACA;AACA,QAAI,QAAQ,KAAK,UAAU;AACzB;AAAA,IACF;AAEA,UAAM,SAAS,iBAAE,SAAS,MAAM,mBAAK,WAAa,KAAK;AACvD,UAAM,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC;AAChD,YAAQ,IAAI,IAAI,SAAS,KAAK,EAAE,YAAY,MAAM,WAAW;AAAA,EAC/D;AACF;;;AC3CA,SAAS,kBAAkB;AAZ3B;AAaE,SACE,CAAC,GAAE,gBAAmB,YAAnB,mBAA4B,YAC/B,CAAC,GAAE,gBAAmB,WAAnB,mBAA2B;AAElC;AAEA,SAAS,gBAAgB;AACvB,SAAO,CAAC,CAAE,WAAmB;AAC/B;AAQA,SAAS,qBAA6B;AACpC,SAAO;AAAA,IACL,aAAa,CAAC,aAAqC;AACjD,iBAAW,OAAO,QAAQ,UAAU,YAAY,QAAQ;AAAA,IAC1D;AAAA,IACA,gBAAgB,CAAC,aAAqC;AACpD,iBAAW,OAAO,QAAQ,UAAU,eAAe,QAAQ;AAAA,IAC7D;AAAA,IACA,aAAa,CAAC,YAAiB;AAC7B,iBAAW,OAAO,QAAQ,YAAY,OAAO;AAAA,IAC/C;AAAA,EACF;AACF;AAEA,SAAS,mBAA2B;AAClC,SAAO;AAAA,IACL,aAAa,CAAC,aAAqC;AACjD,iBAAW,OAAO,iBAAiB,WAAW,CAAC,UAAU;AACvD,YAAI,MAAM,WAAW,WAAW,QAAQ;AACtC;AAAA,QACF;AACA,iBAAS,MAAM,IAAI;AAAA,MACrB,CAAC;AAAA,IACH;AAAA,IACA,gBAAgB,CAAC,aAAqC;AACpD,iBAAW,OAAO,oBAAoB,WAAW,QAAQ;AAAA,IAC3D;AAAA,IACA,aAAa,CAAC,YAAiB;AAC7B,iBAAW,OAAO,YAAY,SAAS,GAAG;AAAA,IAC5C;AAAA,EACF;AACF;AAaO,IAAM,SAAN,MAAM,OAAM;AAAA,EAejB,OAAc,KACZ,MACA,MACA;AACA,UAAM,UAAU;AAAA,MACd;AAAA,MACA;AAAA,MACA,OAAO;AAAA,QACL,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC;AAAA,QAC7C,QAAQ,WAAW,SAAS;AAAA,MAC9B;AAAA,IACF;AAEA,WAAM,OAAO,MAAM,mBAAmB,OAAO;AAE7C,eAAW,UAAU,OAAM,SAAS;AAClC,aAAO,YAAY,OAAO;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,OAAc,IAAI,QAA6B;AAC7C,UAAM,YAAY,oBAAI,IAA4B;AAElD,eAAW,UAAU,OAAM,SAAS;AAClC,YAAM,WAAW,OAAM,cAAc,KAAK,MAAM,QAAQ,MAAM;AAC9D,gBAAU,IAAI,QAAQ;AACtB,aAAO,YAAY,QAAQ;AAAA,IAC7B;AAEA,WAAM,OAAO,MAAM,iBAAiB,EAAE,OAAO,CAAC;AAE9C,WAAO,MAAM;AACX,iBAAW,UAAU,OAAM,SAAS;AAClC,mBAAW,YAAY,WAAW;AAChC,iBAAO,eAAe,QAAQ;AAAA,QAChC;AAAA,MACF;AAEA,aAAM,OAAO,MAAM,iBAAiB,EAAE,OAAO,CAAC;AAAA,IAChD;AAAA,EACF;AAAA,EAEA,OAAe,cACb,cACA,QACA,SACA;AACA,QAAI,CAAC,OAAO,SAAS,QAAQ,IAAI,GAAG;AAClC,aAAM,OAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC;AAClD;AAAA,IACF;AAEA,UAAM,qBAAqB,OAAM,QAAQ;AAAA,MACvC,CAAC,WAAW,WAAW;AAAA,IACzB;AAEA,eAAW,qBAAqB,oBAAoB;AAClD,wBAAkB,YAAY,OAAO;AAAA,IACvC;AAAA,EACF;AAAA,EAEA,OAAc,GACZ,aACA,UACA;AACA,UAAM,YAAY,oBAAI,IAA4B;AAClD,eAAW,UAAU,OAAM,SAAS;AAClC,YAAM,WAAW,CAAC,YAAiB;AACjC,YAAI,QAAQ,SAAS,aAAa;AAChC,iBAAM,OAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC;AAClD;AAAA,QACF;AACA,iBAAS,QAAQ,IAAI;AAAA,MACvB;AACA,gBAAU,IAAI,QAAQ;AACtB,aAAO,YAAY,QAAQ;AAAA,IAC7B;AAEA,WAAO,MAAM;AACX,iBAAW,UAAU,OAAM,SAAS;AAClC,mBAAW,YAAY,WAAW;AAChC,iBAAO,eAAe,QAAQ;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AArGa,OACM,UAAoB,CAAC;AAD3B,OAEa,SAAS,IAAI,OAAO,EAAE,WAAW,QAAQ,CAAC;AAAA,CAElE,MAAO;AACL,MAAI,gBAAgB,GAAG;AACrB,WAAM,OAAO,KAAK,yBAAyB;AAC3C,WAAM,QAAQ,KAAK,mBAAmB,CAAC;AAAA,EACzC;AACA,MAAI,cAAc,GAAG;AACnB,WAAM,OAAO,KAAK,uBAAuB;AACzC,WAAM,QAAQ,KAAK,iBAAiB,CAAC;AAAA,EACvC;AACF;AAbK,IAAM,QAAN;;;ACzEP,SAAS,SAAS,mBAAmB;;;ACCrC,IAAM,6BAA6B;AACnC,IAAM,2BAA2B;AAEjC,IAAM,gBAAgB,CAAC,MAAM,YAAY,QAAQ,KAAK,YAAU,kBAAkB,SAAS,OAAO,KAAK,IAAI,IAAI,WAAW,IAAI;AAE9H,IAAM,qBAAqB,oBAAI,IAAI;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAED,IAAM,oBAAoB,eAAa;AACtC,MAAI;AACH,UAAM,EAAC,SAAQ,IAAI,IAAI,IAAI,SAAS;AACpC,WAAO,SAAS,SAAS,GAAG,KAAK,CAAC,mBAAmB,IAAI,QAAQ;AAAA,EAClE,SAAQ,GAAN;AACD,WAAO;AAAA,EACR;AACD;AAEA,IAAM,mBAAmB,CAAC,WAAW,EAAC,UAAS,MAAM;AArBrD;AAsBC,QAAM,QAAQ,WAAC,yDAAwD,EAAC,KAAK,SAAS;AAEtF,MAAI,CAAC,OAAO;AACX,UAAM,IAAI,MAAM,gBAAgB,WAAW;AAAA,EAC5C;AAEA,MAAI,EAAC,MAAM,MAAM,KAAI,IAAI,MAAM;AAC/B,QAAM,YAAY,KAAK,MAAM,GAAG;AAChC,SAAO,YAAY,KAAK;AAExB,MAAI,WAAW;AACf,MAAI,UAAU,UAAU,SAAS,CAAC,MAAM,UAAU;AACjD,cAAU,IAAI;AACd,eAAW;AAAA,EACZ;AAGA,QAAM,YAAW,qBAAU,MAAM,MAAhB,mBAAmB,kBAAnB,YAAoC;AACrD,QAAM,aAAa,UACjB,IAAI,eAAa;AACjB,QAAI,CAAC,KAAK,QAAQ,EAAE,IAAI,UAAU,MAAM,GAAG,EAAE,IAAI,YAAU,OAAO,KAAK,CAAC;AAGxE,QAAI,QAAQ,WAAW;AACtB,cAAQ,MAAM,YAAY;AAE1B,UAAI,UAAU,0BAA0B;AACvC,eAAO;AAAA,MACR;AAAA,IACD;AAEA,WAAO,GAAG,MAAM,QAAQ,IAAI,UAAU;AAAA,EACvC,CAAC,EACA,OAAO,OAAO;AAEhB,QAAM,sBAAsB;AAAA,IAC3B,GAAG;AAAA,EACJ;AAEA,MAAI,UAAU;AACb,wBAAoB,KAAK,QAAQ;AAAA,EAClC;AAEA,MAAI,oBAAoB,SAAS,KAAM,YAAY,aAAa,4BAA6B;AAC5F,wBAAoB,QAAQ,QAAQ;AAAA,EACrC;AAEA,SAAO,QAAQ,oBAAoB,KAAK,GAAG,KAAK,WAAW,KAAK,KAAK,IAAI,OAAO,OAAO,IAAI,SAAS;AACrG;AAEe,SAAR,aAA8B,WAAW,SAAS;AACxD,YAAU;AAAA,IACT,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,IACnB,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,qBAAqB;AAAA,IACrB,WAAW;AAAA,IACX,mBAAmB;AAAA,IACnB,UAAU;AAAA,IACV,uBAAuB,CAAC,WAAW;AAAA,IACnC,qBAAqB;AAAA,IACrB,mBAAmB;AAAA,IACnB,sBAAsB;AAAA,IACtB,oBAAoB;AAAA,IACpB,qBAAqB;AAAA,KAClB;AAIJ,MAAI,OAAO,QAAQ,oBAAoB,YAAY,CAAC,QAAQ,gBAAgB,SAAS,GAAG,GAAG;AAC1F,YAAQ,kBAAkB,GAAG,QAAQ;AAAA,EACtC;AAEA,cAAY,UAAU,KAAK;AAG3B,MAAI,UAAU,KAAK,SAAS,GAAG;AAC9B,WAAO,iBAAiB,WAAW,OAAO;AAAA,EAC3C;AAEA,MAAI,kBAAkB,SAAS,GAAG;AACjC,WAAO;AAAA,EACR;AAEA,QAAM,sBAAsB,UAAU,WAAW,IAAI;AACrD,QAAM,gBAAgB,CAAC,uBAAuB,SAAS,KAAK,SAAS;AAGrE,MAAI,CAAC,eAAe;AACnB,gBAAY,UAAU,QAAQ,4BAA4B,QAAQ,eAAe;AAAA,EAClF;AAEA,QAAM,YAAY,IAAI,IAAI,SAAS;AAEnC,MAAI,QAAQ,aAAa,QAAQ,YAAY;AAC5C,UAAM,IAAI,MAAM,kEAAkE;AAAA,EACnF;AAEA,MAAI,QAAQ,aAAa,UAAU,aAAa,UAAU;AACzD,cAAU,WAAW;AAAA,EACtB;AAEA,MAAI,QAAQ,cAAc,UAAU,aAAa,SAAS;AACzD,cAAU,WAAW;AAAA,EACtB;AAGA,MAAI,QAAQ,qBAAqB;AAChC,cAAU,WAAW;AACrB,cAAU,WAAW;AAAA,EACtB;AAGA,MAAI,QAAQ,WAAW;AACtB,cAAU,OAAO;AAAA,EAClB,WAAW,QAAQ,mBAAmB;AACrC,cAAU,OAAO,UAAU,KAAK,QAAQ,kBAAkB,EAAE;AAAA,EAC7D;AAMA,MAAI,UAAU,UAAU;AAMvB,UAAM,gBAAgB;AAEtB,QAAI,YAAY;AAChB,QAAI,SAAS;AACb,eAAS;AACR,YAAM,QAAQ,cAAc,KAAK,UAAU,QAAQ;AACnD,UAAI,CAAC,OAAO;AACX;AAAA,MACD;AAEA,YAAM,WAAW,MAAM,CAAC;AACxB,YAAM,kBAAkB,MAAM;AAC9B,YAAM,eAAe,UAAU,SAAS,MAAM,WAAW,eAAe;AAExE,gBAAU,aAAa,QAAQ,WAAW,GAAG;AAC7C,gBAAU;AACV,kBAAY,kBAAkB,SAAS;AAAA,IACxC;AAEA,UAAM,UAAU,UAAU,SAAS,MAAM,WAAW,UAAU,SAAS,MAAM;AAC7E,cAAU,QAAQ,QAAQ,WAAW,GAAG;AAExC,cAAU,WAAW;AAAA,EACtB;AAGA,MAAI,UAAU,UAAU;AACvB,QAAI;AACH,gBAAU,WAAW,UAAU,UAAU,QAAQ;AAAA,IAClD,SAAQ,GAAN;AAAA,IAAO;AAAA,EACV;AAGA,MAAI,QAAQ,yBAAyB,MAAM;AAC1C,YAAQ,uBAAuB,CAAC,iBAAiB;AAAA,EAClD;AAEA,MAAI,MAAM,QAAQ,QAAQ,oBAAoB,KAAK,QAAQ,qBAAqB,SAAS,GAAG;AAC3F,QAAI,iBAAiB,UAAU,SAAS,MAAM,GAAG;AACjD,UAAM,gBAAgB,eAAe,eAAe,SAAS,CAAC;AAE9D,QAAI,cAAc,eAAe,QAAQ,oBAAoB,GAAG;AAC/D,uBAAiB,eAAe,MAAM,GAAG,EAAE;AAC3C,gBAAU,WAAW,eAAe,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI;AAAA,IAC1D;AAAA,EACD;AAEA,MAAI,UAAU,UAAU;AAEvB,cAAU,WAAW,UAAU,SAAS,QAAQ,OAAO,EAAE;AAGzD,QAAI,QAAQ,YAAY,oDAAoD,KAAK,UAAU,QAAQ,GAAG;AAKrG,gBAAU,WAAW,UAAU,SAAS,QAAQ,UAAU,EAAE;AAAA,IAC7D;AAAA,EACD;AAGA,MAAI,MAAM,QAAQ,QAAQ,qBAAqB,GAAG;AAEjD,eAAW,OAAO,CAAC,GAAG,UAAU,aAAa,KAAK,CAAC,GAAG;AACrD,UAAI,cAAc,KAAK,QAAQ,qBAAqB,GAAG;AACtD,kBAAU,aAAa,OAAO,GAAG;AAAA,MAClC;AAAA,IACD;AAAA,EACD;AAEA,MAAI,CAAC,MAAM,QAAQ,QAAQ,mBAAmB,KAAK,QAAQ,0BAA0B,MAAM;AAC1F,cAAU,SAAS;AAAA,EACpB;AAGA,MAAI,MAAM,QAAQ,QAAQ,mBAAmB,KAAK,QAAQ,oBAAoB,SAAS,GAAG;AAEzF,eAAW,OAAO,CAAC,GAAG,UAAU,aAAa,KAAK,CAAC,GAAG;AACrD,UAAI,CAAC,cAAc,KAAK,QAAQ,mBAAmB,GAAG;AACrD,kBAAU,aAAa,OAAO,GAAG;AAAA,MAClC;AAAA,IACD;AAAA,EACD;AAGA,MAAI,QAAQ,qBAAqB;AAChC,cAAU,aAAa,KAAK;AAG5B,QAAI;AACH,gBAAU,SAAS,mBAAmB,UAAU,MAAM;AAAA,IACvD,SAAQ,GAAN;AAAA,IAAO;AAAA,EACV;AAEA,MAAI,QAAQ,qBAAqB;AAChC,cAAU,WAAW,UAAU,SAAS,QAAQ,OAAO,EAAE;AAAA,EAC1D;AAGA,MAAI,QAAQ,sBAAsB,UAAU,MAAM;AACjD,cAAU,OAAO;AAAA,EAClB;AAEA,QAAM,eAAe;AAGrB,cAAY,UAAU,SAAS;AAE/B,MAAI,CAAC,QAAQ,qBAAqB,UAAU,aAAa,OAAO,CAAC,aAAa,SAAS,GAAG,KAAK,UAAU,SAAS,IAAI;AACrH,gBAAY,UAAU,QAAQ,OAAO,EAAE;AAAA,EACxC;AAGA,OAAK,QAAQ,uBAAuB,UAAU,aAAa,QAAQ,UAAU,SAAS,MAAM,QAAQ,mBAAmB;AACtH,gBAAY,UAAU,QAAQ,OAAO,EAAE;AAAA,EACxC;AAGA,MAAI,uBAAuB,CAAC,QAAQ,mBAAmB;AACtD,gBAAY,UAAU,QAAQ,cAAc,IAAI;AAAA,EACjD;AAGA,MAAI,QAAQ,eAAe;AAC1B,gBAAY,UAAU,QAAQ,qBAAqB,EAAE;AAAA,EACtD;AAEA,SAAO;AACR;;;ADvRA,SAAS,SAAS;;;AEclB,SAAS,oBAAoB,KAAqB;AAChD,SAAO,IAAI,QAAQ,QAAQ,EAAE;AAC/B;AAEO,IAAM,oBAAN,MAAwB;AAAA,EAK7B,YAAY,SAAmC;AAF/C,SAAiB,SAAS,IAAI,OAAO,EAAE,WAAW,oBAAoB,CAAC;AAvBzE;AA0BI,SAAK,WAAU,aAAQ,YAAR,YAAmB;AAClC,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AACA,SAAK,SAAS,QAAQ;AAAA,EACxB;AAAA,EAEc,MAAyB,KAAmC;AAAA;AACxE,YAAM,MAAM,GAAG,oBAAoB,KAAK,OAAO,KAAK,IAAI,KAAK,KAAK,GAAG;AACrE,WAAK,OAAO,MAAM,SAAS,EAAE,KAAK,IAAI,CAAC;AACvC,YAAM,MAAM,MAAM,MAAM,KAAK;AAAA,QAC3B,QAAQ,IAAI;AAAA,QACZ,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,aAAa,KAAK;AAAA,QACpB;AAAA,QACA,MAAM,KAAK,UAAU,IAAI,IAAI;AAAA,MAC/B,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,MAAM,IAAI,KAAK,CAAC;AAAA,MAClC;AACA,aAAO,IAAI,KAAK;AAAA,IAClB;AAAA;AAAA,EAEA,IAAW,QAAQ;AACjB,WAAO;AAAA,MACL,OAAO,CAAO,QAoBR;AACJ,eAAO,MAAM,KAAK,MAGf;AAAA,UACD,MAAM,CAAC,MAAM,SAAS,OAAO;AAAA,UAC7B,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,MAEA,MAAM,CAAO,QAuBR;AACH,eAAO,MAAM,KAAK,MAMhB;AAAA,UACA,MAAM,CAAC,MAAM,SAAS,MAAM;AAAA,UAC5B,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;ACzHA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACOO,SAAS,cACd,QACS;AACT,SAAO,MACL,OAAO;AAAA,IACL,MAAM,OAAO,OAAO,eAAe,WAAW;AAAA,EAChD,CAAC;AACL;;;ACZO,IAAM,YAAY,cAAc,CAAC,EAAE,KAAK,MAAM;AACnD,SAAO;AAAA,IACL,KAAK,CAAO,QAAgB;AAC1B,YAAM,SAAS,MAAM,OAAO,QAAQ,MAAM,IAAI,GAAG;AACjD,aAAO,OAAO,GAAG;AAAA,IACnB;AAAA,IACA,KAAK,CAAO,KAAa,UAAkB;AACzC,YAAM,OAAO,QAAQ,MAAM,IAAI,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC;AAAA,IACjD;AAAA,IACA,QAAQ,CAAO,QAAgB;AAC7B,YAAM,OAAO,QAAQ,MAAM,OAAO,GAAG;AAAA,IACvC;AAAA,IACA,MAAM,MAAY;AAChB,YAAM,aAAa,MAAM,OAAO,QAAQ,MAAM,cAAc,IAAI;AAChE,aAAO;AAAA,IACT;AAAA,EACF;AACF,CAAC;;;ACjBD,SAAS,mBAAmB,MAAyC;AACnE,MAAI;AACJ,MAAI;AACF,cAAU,OAAO,IAAI;AACrB,UAAM,IAAI;AACV,YAAQ,QAAQ,GAAG,CAAC;AACpB,YAAQ,WAAW,CAAC;AACpB,WAAO;AAAA,EACT,SAAS,GAAP;AACA,WACE,aAAa;AAAA,KAEZ,EAAE,SAAS;AAAA,IAEV,EAAE,SAAS;AAAA;AAAA,IAGX,EAAE,SAAS;AAAA,IAEX,EAAE,SAAS;AAAA,IAEb,WACA,QAAQ,WAAW;AAAA,EAEvB;AACF;AAEO,IAAM,UAAU,cAAc,CAAC,EAAE,KAAK,MAAM;AACjD,MAAI,CAAC,mBAAmB,cAAc,GAAG;AACvC,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AAEA,SAAO;AAAA,IACL,KAAK,CAAO,QAAgB;AAC1B,aAAO,aAAa,QAAQ,GAAG;AAAA,IACjC;AAAA,IACA,KAAK,CAAO,KAAa,UAAkB;AACzC,mBAAa,QAAQ,KAAK,KAAK;AAAA,IACjC;AAAA,IACA,QAAQ,CAAO,QAAgB;AAC7B,mBAAa,WAAW,GAAG;AAAA,IAC7B;AAAA,IACA,MAAM,MAAY;AA5CtB;AA6CM,UAAI,QAAQ;AACZ,eAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;AAC5C,cAAM,MAAM,aAAa,IAAI,CAAC;AAC9B,YAAI,OAAO,KAAK,SAAS,GAAG,GAAG;AAC7B,oBAAS,wBAAa,QAAQ,GAAG,MAAxB,mBAA2B,WAA3B,YAAqC;AAAA,QAChD;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF,CAAC;;;ACrDM,IAAM,SAAS,cAAc,MAAM;AACxC,QAAM,UAAU,oBAAI,IAAoB;AACxC,SAAO;AAAA,IACL,KAAK,CAAO,QAAgB;AAC1B,aAAO,QAAQ,IAAI,GAAG,KAAK;AAAA,IAC7B;AAAA,IACA,KAAK,CAAO,KAAa,UAAkB;AACzC,cAAQ,IAAI,KAAK,KAAK;AAAA,IACxB;AAAA,IACA,QAAQ,CAAO,QAAgB;AAC7B,cAAQ,OAAO,GAAG;AAAA,IACpB;AAAA,IACA,MAAM,MAAY;AAChB,UAAI,QAAQ;AACZ,iBAAW,SAAS,QAAQ,OAAO,GAAG;AACpC,iBAAS,MAAM;AAAA,MACjB;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF,CAAC;;;APSM,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC1C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,kBAAN,MAAM,gBAAe;AAAA,EA0D1B,YAAY;AAAA,IACV,OAAO;AAAA,IACP,SAAS;AAAA,IACT,UAAU,OAAO;AAAA,IACjB;AAAA,IACA;AAAA,EACF,GAMG;AAlDH,SAAiB,SAAS,IAAI,OAAO,EAAE,WAAW,iBAAiB,CAAC;AAmDlE,SAAK,OAAO;AACZ,SAAK,UAAU;AACf,SAAK,cAAc,oCAAe,gBAAe;AACjD,QAAI,SAAS,SAAS;AACpB,WAAK,SAAS,IAAI,kBAAkB;AAAA,QAClC;AAAA,QACA,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEa,IAAI,KAAiC;AAAA;AAChD,WAAK,OAAO,MAAM,gBAAgB,EAAE,IAAI,CAAC;AAEzC,UAAI;AACJ,UAAI;AACF,kBAAU,KAAK,gBAAgB,GAAG;AAAA,MACpC,SAAS,GAAP;AACA,aAAK,OAAO,MAAM,0BAA0B,EAAE,KAAK,OAAO,EAAE,CAAC;AAC7D,eAAO;AAAA,UACL,IAAI;AAAA,UACJ;AAAA,UACA,OACE,aAAa,mBAAmB,EAAE,UAAU;AAAA,QAChD;AAAA,MACF;AAEA,WAAK,OAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC;AAElD,UAAI,WACF,MAAM,QAAQ,IAAI,QAAQ,IAAI,CAAC,WAAW,KAAK,UAAU,QAAQ,GAAG,CAAC,CAAC,GACtE;AAAA,QACA,CAAsB,MACpB,EAAE;AAAA,MACN;AAEA,UAAI,QAAQ,WAAW,GAAG;AACxB,eAAO;AAAA,UACL,IAAI;AAAA,UACJ;AAAA,UACA,OAAO;AAAA,QACT;AAAA,MACF;AAEA,WAAK,OAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC;AAEpD,UAAI,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,SAAS,GAAG;AAC/C,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,QAAQ;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,iBAAW,UAAU,SAAS;AAC5B,YAAI,OAAO,MAAM,OAAO,WAAW,WAAW;AAC5C,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,aAAO,QAAQ,CAAC;AAAA,IAClB;AAAA;AAAA,EAEQ,gBAAgB,MAAwB;AA5KlD;AA6KI,UAAM,SAAS,KAAK,mBAAmB,IAAI;AAE3C,UAAM,UAAU,CAAC,MAAM;AAEvB,UAAM,eAAe,YAAY,MAAM;AACvC,QAAI,CAAC,aAAa,WAAW;AAC3B,aAAO;AAAA,IACT;AAEA,UAAM,kBAAiB,wBAAa,cAAb,mBAAwB,MAAM,SAA9B,YAAsC,CAAC;AAE9D,aAAS,IAAI,GAAG,IAAI,eAAe,QAAQ,KAAK;AAC9C,YAAM,YAAY,eAAe,MAAM,CAAC,EAAE,KAAK,GAAG;AAClD,cAAQ,QAAQ,GAAG,aAAa,QAAkB;AAAA,IACpD;AAEA,WAAO;AAAA,EACT;AAAA,EAEc,UAAU,QAAgB,KAAiC;AAAA;AACvE,UAAI,SAAS,MAAM,KAAK,mBAAmB,MAAM;AAEjD,UAAI,KAAK,SAAS,WAAW,KAAK,UAAU,WAAW,WAAW;AAChE,YAAI;AACF,gBAAM,MAAM,MAAM,KAAK,OAAO,MAAM,MAAM;AAAA,YACxC,MAAM;AAAA,YACN,SAAS;AAAA,UACX,CAAC;AAGD,eAAK,OAAO,MAAM,kBAAkB,EAAE,QAAQ,QAAQ,IAAI,OAAO,CAAC;AAClE,cAAI,IAAI,WAAW,WAAW;AAC5B,iBAAK,iBAAiB,QAAQ,gBAAe,YAAY,SAAS;AAAA,UACpE,WAAW,IAAI,WAAW,WAAW;AACnC,iBAAK,iBAAiB,QAAQ,gBAAe,YAAY,SAAS;AAAA,UACpE;AAEA,mBAAS,IAAI;AAAA,QACf,SAAS,GAAP;AACA,iBAAO;AAAA,YACL,IAAI;AAAA,YACJ,KAAK;AAAA,YACL,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAEA,UAAI;AAEJ,UAAI,WAAW,WAAW;AACxB,YAAI,OAAO,KAAK,gBAAgB,YAAY;AAC1C,wBAAc,KAAK,YAAY,GAAG;AAAA,QACpC,WAAW,OAAO,KAAK,gBAAgB,UAAU;AAC/C,gBAAM,SAAS,IAAI,IAAI,KAAK,WAAW;AACvC,iBAAO,aAAa,IAAI,aAAa,GAAG;AACxC,wBAAc,OAAO,SAAS;AAAA,QAChC;AAAA,MACF;AAEA,aAAO;AAAA,QACL,IAAI;AAAA,QACJ;AAAA,QACA,KAAK;AAAA,QACL;AAAA,MACF;AAAA,IACF;AAAA;AAAA,EAEa,MACX,KAGA;AAAA;AACA,UAAI;AACF,cAAM,SAAS,KAAK,mBAAmB,GAAG;AAE1C,aAAK,OAAO,MAAM,gBAAgB,EAAE,KAAK,OAAO,CAAC;AAEjD,cAAM,KAAK,yBAAyB,MAAM;AAC1C,cAAM,KAAK,iBAAiB,QAAQ,gBAAe,YAAY,SAAS;AAExE,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,KAAK;AAAA,QACP;AAAA,MACF,SAAS,GAAP;AACA,aAAK,OAAO,MAAM,uBAAuB,EAAE,KAAK,OAAO,EAAE,CAAC;AAC1D,eAAO;AAAA,UACL,IAAI;AAAA,UACJ;AAAA,UACA,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA;AAAA,EAEa,MACX,KAGA;AAAA;AACA,UAAI;AACF,cAAM,SAAS,KAAK,mBAAmB,GAAG;AAE1C,aAAK,OAAO,MAAM,gBAAgB,EAAE,KAAK,OAAO,CAAC;AAEjD,cAAM,KAAK,yBAAyB,MAAM;AAC1C,cAAM,KAAK,iBAAiB,QAAQ,gBAAe,YAAY,SAAS;AAExE,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,KAAK;AAAA,QACP;AAAA,MACF,SAAS,GAAP;AACA,aAAK,OAAO,MAAM,uBAAuB,EAAE,KAAK,OAAO,EAAE,CAAC;AAC1D,eAAO;AAAA,UACL,IAAI;AAAA,UACJ;AAAA,UACA,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA;AAAA,EAEa,OACX,KAGA;AAAA;AACA,UAAI;AACF,cAAM,SAAS,KAAK,mBAAmB,GAAG;AAE1C,aAAK,OAAO,MAAM,gBAAgB,EAAE,KAAK,OAAO,CAAC;AAEjD,cAAM,KAAK;AAAA,UACT;AAAA,UACA,gBAAe,YAAY;AAAA,QAC7B;AAEA,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,KAAK;AAAA,QACP;AAAA,MACF,SAAS,GAAP;AACA,aAAK,OAAO,MAAM,wBAAwB,EAAE,KAAK,OAAO,EAAE,CAAC;AAC3D,eAAO;AAAA,UACL,IAAI;AAAA,UACJ;AAAA,UACA,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA;AAAA,EAEQ,mBAAmB,KAAqB;AAC9C,SAAK,OAAO,MAAM,kBAAkB,EAAE,IAAI,CAAC;AAC3C,QAAI;AACF,YAAM,gBAAgB,aAAa,KAAK;AAAA,QACtC,UAAU;AAAA,QACV,qBAAqB;AAAA,MACvB,CAAC;AAED,WAAK,OAAO,MAAM,kBAAkB,EAAE,MAAM,KAAK,IAAI,cAAc,CAAC;AAEpE,YAAM,YAAY,IAAI,IAAI,aAAa;AAEvC,WAAK,OAAO,MAAM,2BAA2B,EAAE,KAAK,UAAU,SAAS,CAAC;AAExE,YAAM,wBAAwB,YAAY,UAAU,QAAQ;AAE5D,WAAK,OAAO,MAAM,iBAAiB,EAAE,sBAAsB,CAAC;AAE5D,UAAI,sBAAsB,aAAa,aAAa;AAClD,cAAM,IAAI,iBAAiB,2CAA2C;AAAA,MACxE;AAEA,UAAI,sBAAsB,MAAM;AAC9B,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,UAAI,CAAC,sBAAsB,QAAQ;AACjC,cAAM,IAAI,iBAAiB,wBAAwB;AAAA,MACrD;AAEA,YAAM,SAAS,sBAAsB;AAErC,aAAO;AAAA,IACT,SAAS,GAAP;AACA,UAAI,aAAa,kBAAkB;AACjC,cAAM;AAAA,MACR,OAAO;AACL,aAAK,OAAO,MAAM,0BAA0B,EAAE,KAAK,OAAO,EAAE,CAAC;AAC7D,cAAM,IAAI,iBAAiB,wBAAwB;AAAA,MACrD;AAAA,IACF;AAAA,EACF;AAAA,EAEc,yBAAyB,QAA+B;AAAA;AACpE,YAAM,KAAK;AAAA,QACT;AAAA,QACA,gBAAe,YAAY;AAAA,MAC7B;AACA,YAAM,KAAK;AAAA,QACT;AAAA,QACA,gBAAe,YAAY;AAAA,MAC7B;AAAA,IACF;AAAA;AAAA,EAEc,wBACZ,QACA,KACe;AAAA;AACf,YAAM,OAAO,MAAM,KAAK,QAAQ,IAAI,GAAG;AAEvC,UAAI,CAAC,MAAM;AACT;AAAA,MACF;AAEA,YAAM,OAAO,MAAM,KAAK,mBAAmB,GAAG;AAE9C,UAAI,CAAC,KAAK,KAAK,QAAQ,SAAS,MAAM,GAAG;AACvC;AAAA,MACF;AAEA,WAAK,KAAK,UAAU,KAAK,KAAK,QAAQ,OAAO,CAAC,MAAM,MAAM,MAAM;AAEhE,YAAM,KAAK,iBAAiB,KAAK,IAAI;AAAA,IACvC;AAAA;AAAA,EAEc,iBAAiB,QAAgB,KAA4B;AAAA;AACzE,YAAM,OAAO,MAAM,KAAK,mBAAmB,GAAG;AAE9C,UAAI,KAAK,KAAK,QAAQ,SAAS,MAAM,GAAG;AACtC;AAAA,MACF;AAEA,WAAK,KAAK,QAAQ,KAAK,MAAM;AAE7B,YAAM,KAAK,iBAAiB,KAAK,IAAI;AAAA,IACvC;AAAA;AAAA,EAEc,eAAe,QAAgB,KAA+B;AAAA;AAC1E,YAAM,OAAO,MAAM,KAAK,mBAAmB,GAAG;AAC9C,aAAO,KAAK,KAAK,QAAQ,SAAS,MAAM;AAAA,IAC1C;AAAA;AAAA,EAEc,mBACZ,KACgD;AAAA;AAChD,YAAM,OAAO,MAAM,KAAK,QAAQ,IAAI,GAAG;AAEvC,UAAI,CAAC,MAAM;AACT,cAAMC,QAA8C;AAAA,UAClD,SAAS;AAAA,UACT,MAAM;AAAA,YACJ,SAAS,CAAC;AAAA,UACZ;AAAA,QACF;AACA,cAAM,KAAK,iBAAiB,KAAKA,KAAI;AACrC,eAAOA;AAAA,MACT;AAEA,YAAM,OAAO,gBAAe,OAAO,MAAM,KAAK,MAAM,IAAI,CAAC;AAEzD,aAAO;AAAA,IACT;AAAA;AAAA,EAEc,iBACZ,KACA,MACe;AAAA;AACf,YAAM,KAAK,QAAQ,IAAI,KAAK,KAAK,UAAU,IAAI,CAAC;AAAA,IAClD;AAAA;AAAA,EAEc,mBACZ,QAC8B;AAAA;AAC9B,UACE,MAAM,KAAK,eAAe,QAAQ,gBAAe,YAAY,UAAU,GACvE;AACA,eAAO;AAAA,MACT,WACE,MAAM,KAAK,eAAe,QAAQ,gBAAe,YAAY,SAAS,GACtE;AACA,eAAO;AAAA,MACT,WACE,MAAM,KAAK,eAAe,QAAQ,gBAAe,YAAY,SAAS,GACtE;AACA,eAAO;AAAA,MACT,OAAO;AACL,eAAO;AAAA,MACT;AAAA,IACF;AAAA;AACF;AA1aa,gBACJ,cAAc;AAAA,EACnB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,YAAY;AACd;AALW,gBAOa,0BACtB;AARS,gBASa,SAAS,EAAE,OAAO;AAAA,EACxC,SAAS,EAAE,QAAQ,CAAC;AAAA,EACpB,MAAM,EAAE,OAAO;AAAA,IACb,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EAC7B,CAAC;AACH,CAAC;AAdI,IAAM,iBAAN","sourcesContent":["// const ContinueAtOwnRisk = Symbol(\"CHAINPATROL_CONTINUE_AT_OWN_RISK\");\nconst ContinueAtOwnRisk = \"CHAINPATROL_CONTINUE_AT_OWN_RISK\";\n\nexport const Events = {\n ContinueAtOwnRisk,\n} as const;\n\nexport type EventData = {\n [ContinueAtOwnRisk]: {\n domain: string;\n };\n};\n","export enum LogLevel {\n DEBUG,\n INFO,\n WARN,\n ERROR,\n NONE,\n}\n\nexport class Logger {\n private meta: Record<string, unknown> = {};\n private minLevel: LogLevel;\n\n constructor(\n meta?: Record<string, unknown>,\n minLevel: LogLevel = LogLevel.NONE\n ) {\n if (meta) {\n this.meta = meta;\n }\n this.minLevel = minLevel;\n }\n\n public with(fields: Record<string, unknown>) {\n return new Logger({ ...this.meta, ...fields }, this.minLevel);\n }\n\n public debug(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.DEBUG, message, fields);\n }\n\n public info(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.INFO, message, fields);\n }\n\n public warn(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.WARN, message, fields);\n }\n\n public error(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.ERROR, message, fields);\n }\n\n private log(\n level: LogLevel,\n message: string,\n fields?: Record<string, unknown>\n ) {\n if (level < this.minLevel) {\n return; // Skip logging if log level is lower than minimum level\n }\n\n const logObj = { message, data: { ...fields }, ...this.meta };\n const logString = JSON.stringify(logObj, null, 2);\n console.log(`[${LogLevel[level].toUpperCase()}] ${logString}`);\n }\n}\n","import { EventData } from \"./events\";\nimport { Logger } from \"./logger\";\n\ntype Message<EventType extends keyof EventData> = {\n type: EventType;\n data: EventData[EventType];\n _meta: {\n id: string;\n origin: string;\n };\n};\n\nfunction isExtensionHost() {\n return (\n !!(globalThis as any).browser?.runtime ||\n !!(globalThis as any).chrome?.runtime\n );\n}\n\nfunction isBrowserHost() {\n return !!(globalThis as any).window;\n}\n\ntype Handle = {\n addListener: (callback: (message: any) => void) => void;\n removeListener: (callback: (message: any) => void) => void;\n postMessage: (message: any) => void;\n};\n\nfunction getExtensionHandle(): Handle {\n return {\n addListener: (callback: (message: any) => void) => {\n globalThis.chrome.runtime.onMessage.addListener(callback);\n },\n removeListener: (callback: (message: any) => void) => {\n globalThis.chrome.runtime.onMessage.removeListener(callback);\n },\n postMessage: (message: any) => {\n globalThis.chrome.runtime.sendMessage(message);\n },\n };\n}\n\nfunction getBrowserHandle(): Handle {\n return {\n addListener: (callback: (message: any) => void) => {\n globalThis.window.addEventListener(\"message\", (event) => {\n if (event.source !== globalThis.window) {\n return;\n }\n callback(event.data);\n });\n },\n removeListener: (callback: (message: any) => void) => {\n globalThis.window.removeEventListener(\"message\", callback);\n },\n postMessage: (message: any) => {\n globalThis.window.postMessage(message, \"*\");\n },\n };\n}\n\n/**\n * Relay contains methods for implementing an event relay. It is used to\n * forward events between Javascript contexts:\n *\n * `<window>` <-> `<content script>` <-> `<background script>`\n *\n * Call `Relay.send` to send an event from one context to another. Call\n * `Relay.on` to listen for events in the current context. Call `Relay.run`\n * to start listening for events in the current context and forward them\n * to all other contexts.\n */\nexport class Relay {\n protected static handles: Handle[] = [];\n private static readonly logger = new Logger({ component: \"Relay\" });\n\n static {\n if (isExtensionHost()) {\n Relay.logger.info(\"Detected extension host\");\n Relay.handles.push(getExtensionHandle());\n }\n if (isBrowserHost()) {\n Relay.logger.info(\"Detected browser host\");\n Relay.handles.push(getBrowserHandle());\n }\n }\n\n public static send<EventType extends keyof EventData>(\n type: EventType,\n data: EventData[EventType]\n ) {\n const message = {\n type,\n data,\n _meta: {\n id: Math.random().toString(36).substring(2, 9),\n origin: globalThis.location.origin,\n },\n } satisfies Message<EventType>;\n\n Relay.logger.debug(\"Sending message\", message);\n\n for (const handle of Relay.handles) {\n handle.postMessage(message);\n }\n }\n\n public static run(events: (keyof EventData)[]) {\n const listeners = new Set<(message: any) => void>();\n\n for (const handle of Relay.handles) {\n const listener = Relay.handleMessage.bind(null, handle, events);\n listeners.add(listener);\n handle.addListener(listener);\n }\n\n Relay.logger.debug(\"Started relay\", { events });\n\n return () => {\n for (const handle of Relay.handles) {\n for (const listener of listeners) {\n handle.removeListener(listener);\n }\n }\n\n Relay.logger.debug(\"Stopped relay\", { events });\n };\n }\n\n private static handleMessage(\n sourceHandle: Handle,\n events: (keyof EventData)[],\n message: any\n ) {\n if (!events.includes(message.type)) {\n Relay.logger.debug(\"Ignoring message\", { message });\n return;\n }\n\n const destinationHandles = Relay.handles.filter(\n (handle) => handle !== sourceHandle\n );\n\n for (const destinationHandle of destinationHandles) {\n destinationHandle.postMessage(message);\n }\n }\n\n public static on<EventType extends keyof EventData>(\n targetEvent: EventType,\n callback: (data: EventData[EventType]) => void\n ) {\n const listeners = new Set<(message: any) => void>();\n for (const handle of Relay.handles) {\n const listener = (message: any) => {\n if (message.type !== targetEvent) {\n Relay.logger.debug(\"Ignoring message\", { message });\n return;\n }\n callback(message.data);\n };\n listeners.add(listener);\n handle.addListener(listener);\n }\n\n return () => {\n for (const handle of Relay.handles) {\n for (const listener of listeners) {\n handle.removeListener(listener);\n }\n }\n };\n }\n}\n","import { parse as parseDomain } from \"tldts\";\nimport normalizeUrl from \"normalize-url\";\nimport { z } from \"zod\";\n\nimport { AssetStatus, ChainPatrolClient } from \"./client\";\nimport { Memory } from \"./storage\";\nimport type { Storage } from \"./storage/types\";\nimport { Logger } from \"./logger\";\n\ntype Prettify<T> = T extends infer U ? (U extends string ? U : never) : never;\n\nexport type DetectorAssetStatus = Prettify<AssetStatus | \"IGNORED\">;\n\ntype Mode = \"cloud\" | \"local\";\n\n// Branded type for domain strings\ntype Domain = string & { __domain: never };\n\nexport type URLResult =\n | {\n ok: true;\n status: DetectorAssetStatus;\n url: string;\n redirectUrl?: string;\n }\n | {\n ok: false;\n url: string;\n error: string;\n };\n\nexport class DomainParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"DomainParseError\";\n }\n}\n\nexport class ThreatDetector {\n static StorageKeys = {\n ALLOWLIST: \"chainpatrol.allowed\",\n BLOCKLIST: \"chainpatrol.blocked\",\n IGNORELIST: \"chainpatrol.ignored\",\n };\n\n private static readonly CHAINPATROL_WARNING_URL =\n \"https://app.chainpatrol.io/warning\";\n private static readonly Schema = z.object({\n version: z.literal(1),\n data: z.object({\n domains: z.array(z.string()),\n }),\n });\n\n private mode: Mode;\n private client?: ChainPatrolClient;\n private storage: Storage;\n private redirectUrl?: string | ((url: string) => string);\n private readonly logger = new Logger({ component: \"ThreatDetector\" });\n\n constructor({\n mode,\n storage,\n redirectUrl,\n }: {\n mode: \"local\";\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n });\n\n constructor({\n mode,\n apiKey,\n storage,\n redirectUrl,\n }: {\n mode: \"cloud\";\n apiKey: string;\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n });\n\n constructor({\n mode,\n apiKey,\n proxyUrl,\n storage,\n redirectUrl,\n }: {\n mode: \"cloud\";\n apiKey: string;\n proxyUrl?: string;\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n });\n\n constructor({\n mode = \"cloud\",\n apiKey = \"\",\n storage = Memory(),\n proxyUrl,\n redirectUrl,\n }: {\n mode?: Mode;\n apiKey?: string;\n proxyUrl?: string;\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n }) {\n this.mode = mode;\n this.storage = storage;\n this.redirectUrl = redirectUrl ?? ThreatDetector.CHAINPATROL_WARNING_URL;\n if (mode === \"cloud\") {\n this.client = new ChainPatrolClient({\n apiKey: apiKey,\n baseUrl: proxyUrl,\n });\n }\n }\n\n public async url(url: string): Promise<URLResult> {\n this.logger.debug(\"Checking URL\", { url });\n\n let domains: Domain[];\n try {\n domains = this.generateDomains(url);\n } catch (e) {\n this.logger.error(\"Unable to parse domain\", { url, error: e });\n return {\n ok: false,\n url,\n error:\n e instanceof DomainParseError ? e.message : \"Unable to parse domain\",\n };\n }\n\n this.logger.debug(\"Generated domains\", { domains });\n\n let results = (\n await Promise.all(domains.map((domain) => this.urlHelper(domain, url)))\n ).filter(\n <T extends URLResult>(r: T): r is T extends { ok: true } ? T : never =>\n r.ok\n );\n\n if (results.length === 0) {\n return {\n ok: false,\n url,\n error: \"URL does not have a valid domain\",\n };\n }\n\n this.logger.debug(\"Results for domains\", { results });\n\n if (results.some((r) => r.status === \"IGNORED\")) {\n return {\n ok: true,\n status: \"IGNORED\",\n url,\n };\n }\n\n for (const result of results) {\n if (result.ok && result.status !== \"UNKNOWN\") {\n return result;\n }\n }\n\n return results[0];\n }\n\n private generateDomains(_url: string): Domain[] {\n const domain = this.parseDomainOrThrow(_url);\n\n const domains = [domain];\n\n const parsedDomain = parseDomain(domain);\n if (!parsedDomain.subdomain) {\n return domains;\n }\n\n const subdomainParts = parsedDomain.subdomain?.split(\".\") ?? [];\n\n for (let i = 0; i < subdomainParts.length; i++) {\n const subdomain = subdomainParts.slice(i).join(\".\");\n domains.unshift(`${subdomain}.${domain}` as Domain);\n }\n\n return domains;\n }\n\n private async urlHelper(domain: Domain, url: string): Promise<URLResult> {\n let status = await this.getStatusFromCache(domain);\n\n if (this.mode === \"cloud\" && this.client && status === \"UNKNOWN\") {\n try {\n const res = await this.client.asset.check({\n type: \"URL\",\n content: domain,\n });\n\n // Update cache storage\n this.logger.debug(\"Updating cache\", { domain, status: res.status });\n if (res.status === \"ALLOWED\") {\n this.addDomainToCache(domain, ThreatDetector.StorageKeys.ALLOWLIST);\n } else if (res.status === \"BLOCKED\") {\n this.addDomainToCache(domain, ThreatDetector.StorageKeys.BLOCKLIST);\n }\n\n status = res.status;\n } catch (e) {\n return {\n ok: false,\n url: domain,\n error: \"Unable to check URL\",\n };\n }\n }\n\n let redirectUrl: string | undefined;\n\n if (status === \"BLOCKED\") {\n if (typeof this.redirectUrl === \"function\") {\n redirectUrl = this.redirectUrl(url);\n } else if (typeof this.redirectUrl === \"string\") {\n const newUrl = new URL(this.redirectUrl);\n newUrl.searchParams.set(\"originUrl\", url);\n redirectUrl = newUrl.toString();\n }\n }\n\n return {\n ok: true,\n status,\n url: domain,\n redirectUrl,\n };\n }\n\n public async allow(\n url: string\n ): Promise<\n { ok: true; url: string } | { ok: false; url: string; error: string }\n > {\n try {\n const domain = this.parseDomainOrThrow(url);\n\n this.logger.debug(\"Allowing URL\", { url, domain });\n\n await this.invalidateDomainInCaches(domain);\n await this.addDomainToCache(domain, ThreatDetector.StorageKeys.ALLOWLIST);\n\n return {\n ok: true,\n url: domain,\n };\n } catch (e) {\n this.logger.error(\"Unable to allow URL\", { url, error: e });\n return {\n ok: false,\n url,\n error: \"Unable to allow URL\",\n };\n }\n }\n\n public async block(\n url: string\n ): Promise<\n { ok: true; url: string } | { ok: false; url: string; error: string }\n > {\n try {\n const domain = this.parseDomainOrThrow(url);\n\n this.logger.debug(\"Blocking URL\", { url, domain });\n\n await this.invalidateDomainInCaches(domain);\n await this.addDomainToCache(domain, ThreatDetector.StorageKeys.BLOCKLIST);\n\n return {\n ok: true,\n url: domain,\n };\n } catch (e) {\n this.logger.error(\"Unable to block URL\", { url, error: e });\n return {\n ok: false,\n url,\n error: \"Unable to block URL\",\n };\n }\n }\n\n public async ignore(\n url: string\n ): Promise<\n { ok: true; url: string } | { ok: false; url: string; error: string }\n > {\n try {\n const domain = this.parseDomainOrThrow(url);\n\n this.logger.debug(\"Ignoring URL\", { url, domain });\n\n await this.addDomainToCache(\n domain,\n ThreatDetector.StorageKeys.IGNORELIST\n );\n\n return {\n ok: true,\n url: domain,\n };\n } catch (e) {\n this.logger.error(\"Unable to ignore URL\", { url, error: e });\n return {\n ok: false,\n url,\n error: \"Unable to ignore URL\",\n };\n }\n }\n\n private parseDomainOrThrow(url: string): Domain {\n this.logger.debug(\"Parsing domain\", { url });\n try {\n const normalizedUrl = normalizeUrl(url, {\n stripWWW: false,\n removeTrailingSlash: false,\n });\n\n this.logger.debug(\"Normalized URL\", { from: url, to: normalizedUrl });\n\n const parsedURL = new URL(normalizedUrl);\n\n this.logger.debug(\"Extract domain from URL\", { url: parsedURL.hostname });\n\n const parsedSubdomainResult = parseDomain(parsedURL.hostname);\n\n this.logger.debug(\"Parsed domain\", { parsedSubdomainResult });\n\n if (parsedSubdomainResult.hostname === \"localhost\") {\n throw new DomainParseError(\"ThreatDetector does not support localhost\");\n }\n\n if (parsedSubdomainResult.isIp) {\n throw new DomainParseError(\n \"ThreatDetector does not support IP addresses\"\n );\n }\n\n if (!parsedSubdomainResult.domain) {\n throw new DomainParseError(\"Unable to parse domain\");\n }\n\n const domain = parsedSubdomainResult.domain as Domain;\n\n return domain;\n } catch (e) {\n if (e instanceof DomainParseError) {\n throw e;\n } else {\n this.logger.error(\"Unable to parse domain\", { url, error: e });\n throw new DomainParseError(\"Unable to parse domain\");\n }\n }\n }\n\n private async invalidateDomainInCaches(domain: Domain): Promise<void> {\n await this.invalidateDomainInCache(\n domain,\n ThreatDetector.StorageKeys.ALLOWLIST\n );\n await this.invalidateDomainInCache(\n domain,\n ThreatDetector.StorageKeys.BLOCKLIST\n );\n }\n\n private async invalidateDomainInCache(\n domain: Domain,\n key: string\n ): Promise<void> {\n const data = await this.storage.get(key);\n\n if (!data) {\n return;\n }\n\n const list = await this.getListFromStorage(key);\n\n if (!list.data.domains.includes(domain)) {\n return;\n }\n\n list.data.domains = list.data.domains.filter((u) => u !== domain);\n\n await this.setListInStorage(key, list);\n }\n\n private async addDomainToCache(domain: Domain, key: string): Promise<void> {\n const list = await this.getListFromStorage(key);\n\n if (list.data.domains.includes(domain)) {\n return;\n }\n\n list.data.domains.push(domain);\n\n await this.setListInStorage(key, list);\n }\n\n private async isDomainInList(domain: Domain, key: string): Promise<boolean> {\n const list = await this.getListFromStorage(key);\n return list.data.domains.includes(domain);\n }\n\n private async getListFromStorage(\n key: string\n ): Promise<z.infer<typeof ThreatDetector.Schema>> {\n const data = await this.storage.get(key);\n\n if (!data) {\n const list: z.infer<typeof ThreatDetector.Schema> = {\n version: 1,\n data: {\n domains: [],\n },\n };\n await this.setListInStorage(key, list);\n return list;\n }\n\n const list = ThreatDetector.Schema.parse(JSON.parse(data));\n\n return list;\n }\n\n private async setListInStorage(\n key: string,\n list: z.infer<typeof ThreatDetector.Schema>\n ): Promise<void> {\n await this.storage.set(key, JSON.stringify(list));\n }\n\n private async getStatusFromCache(\n domain: Domain\n ): Promise<DetectorAssetStatus> {\n if (\n await this.isDomainInList(domain, ThreatDetector.StorageKeys.IGNORELIST)\n ) {\n return \"IGNORED\";\n } else if (\n await this.isDomainInList(domain, ThreatDetector.StorageKeys.BLOCKLIST)\n ) {\n return \"BLOCKED\";\n } else if (\n await this.isDomainInList(domain, ThreatDetector.StorageKeys.ALLOWLIST)\n ) {\n return \"ALLOWED\";\n } else {\n return \"UNKNOWN\";\n }\n }\n}\n","// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs\nconst DATA_URL_DEFAULT_MIME_TYPE = 'text/plain';\nconst DATA_URL_DEFAULT_CHARSET = 'us-ascii';\n\nconst testParameter = (name, filters) => filters.some(filter => filter instanceof RegExp ? filter.test(name) : filter === name);\n\nconst supportedProtocols = new Set([\n\t'https:',\n\t'http:',\n\t'file:',\n]);\n\nconst hasCustomProtocol = urlString => {\n\ttry {\n\t\tconst {protocol} = new URL(urlString);\n\t\treturn protocol.endsWith(':') && !supportedProtocols.has(protocol);\n\t} catch {\n\t\treturn false;\n\t}\n};\n\nconst normalizeDataURL = (urlString, {stripHash}) => {\n\tconst match = /^data:(?<type>[^,]*?),(?<data>[^#]*?)(?:#(?<hash>.*))?$/.exec(urlString);\n\n\tif (!match) {\n\t\tthrow new Error(`Invalid URL: ${urlString}`);\n\t}\n\n\tlet {type, data, hash} = match.groups;\n\tconst mediaType = type.split(';');\n\thash = stripHash ? '' : hash;\n\n\tlet isBase64 = false;\n\tif (mediaType[mediaType.length - 1] === 'base64') {\n\t\tmediaType.pop();\n\t\tisBase64 = true;\n\t}\n\n\t// Lowercase MIME type\n\tconst mimeType = mediaType.shift()?.toLowerCase() ?? '';\n\tconst attributes = mediaType\n\t\t.map(attribute => {\n\t\t\tlet [key, value = ''] = attribute.split('=').map(string => string.trim());\n\n\t\t\t// Lowercase `charset`\n\t\t\tif (key === 'charset') {\n\t\t\t\tvalue = value.toLowerCase();\n\n\t\t\t\tif (value === DATA_URL_DEFAULT_CHARSET) {\n\t\t\t\t\treturn '';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn `${key}${value ? `=${value}` : ''}`;\n\t\t})\n\t\t.filter(Boolean);\n\n\tconst normalizedMediaType = [\n\t\t...attributes,\n\t];\n\n\tif (isBase64) {\n\t\tnormalizedMediaType.push('base64');\n\t}\n\n\tif (normalizedMediaType.length > 0 || (mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE)) {\n\t\tnormalizedMediaType.unshift(mimeType);\n\t}\n\n\treturn `data:${normalizedMediaType.join(';')},${isBase64 ? data.trim() : data}${hash ? `#${hash}` : ''}`;\n};\n\nexport default function normalizeUrl(urlString, options) {\n\toptions = {\n\t\tdefaultProtocol: 'http',\n\t\tnormalizeProtocol: true,\n\t\tforceHttp: false,\n\t\tforceHttps: false,\n\t\tstripAuthentication: true,\n\t\tstripHash: false,\n\t\tstripTextFragment: true,\n\t\tstripWWW: true,\n\t\tremoveQueryParameters: [/^utm_\\w+/i],\n\t\tremoveTrailingSlash: true,\n\t\tremoveSingleSlash: true,\n\t\tremoveDirectoryIndex: false,\n\t\tremoveExplicitPort: false,\n\t\tsortQueryParameters: true,\n\t\t...options,\n\t};\n\n\t// Legacy: Append `:` to the protocol if missing.\n\tif (typeof options.defaultProtocol === 'string' && !options.defaultProtocol.endsWith(':')) {\n\t\toptions.defaultProtocol = `${options.defaultProtocol}:`;\n\t}\n\n\turlString = urlString.trim();\n\n\t// Data URL\n\tif (/^data:/i.test(urlString)) {\n\t\treturn normalizeDataURL(urlString, options);\n\t}\n\n\tif (hasCustomProtocol(urlString)) {\n\t\treturn urlString;\n\t}\n\n\tconst hasRelativeProtocol = urlString.startsWith('//');\n\tconst isRelativeUrl = !hasRelativeProtocol && /^\\.*\\//.test(urlString);\n\n\t// Prepend protocol\n\tif (!isRelativeUrl) {\n\t\turlString = urlString.replace(/^(?!(?:\\w+:)?\\/\\/)|^\\/\\//, options.defaultProtocol);\n\t}\n\n\tconst urlObject = new URL(urlString);\n\n\tif (options.forceHttp && options.forceHttps) {\n\t\tthrow new Error('The `forceHttp` and `forceHttps` options cannot be used together');\n\t}\n\n\tif (options.forceHttp && urlObject.protocol === 'https:') {\n\t\turlObject.protocol = 'http:';\n\t}\n\n\tif (options.forceHttps && urlObject.protocol === 'http:') {\n\t\turlObject.protocol = 'https:';\n\t}\n\n\t// Remove auth\n\tif (options.stripAuthentication) {\n\t\turlObject.username = '';\n\t\turlObject.password = '';\n\t}\n\n\t// Remove hash\n\tif (options.stripHash) {\n\t\turlObject.hash = '';\n\t} else if (options.stripTextFragment) {\n\t\turlObject.hash = urlObject.hash.replace(/#?:~:text.*?$/i, '');\n\t}\n\n\t// Remove duplicate slashes if not preceded by a protocol\n\t// NOTE: This could be implemented using a single negative lookbehind\n\t// regex, but we avoid that to maintain compatibility with older js engines\n\t// which do not have support for that feature.\n\tif (urlObject.pathname) {\n\t\t// TODO: Replace everything below with `urlObject.pathname = urlObject.pathname.replace(/(?<!\\b[a-z][a-z\\d+\\-.]{1,50}:)\\/{2,}/g, '/');` when Safari supports negative lookbehind.\n\n\t\t// Split the string by occurrences of this protocol regex, and perform\n\t\t// duplicate-slash replacement on the strings between those occurrences\n\t\t// (if any).\n\t\tconst protocolRegex = /\\b[a-z][a-z\\d+\\-.]{1,50}:\\/\\//g;\n\n\t\tlet lastIndex = 0;\n\t\tlet result = '';\n\t\tfor (;;) {\n\t\t\tconst match = protocolRegex.exec(urlObject.pathname);\n\t\t\tif (!match) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst protocol = match[0];\n\t\t\tconst protocolAtIndex = match.index;\n\t\t\tconst intermediate = urlObject.pathname.slice(lastIndex, protocolAtIndex);\n\n\t\t\tresult += intermediate.replace(/\\/{2,}/g, '/');\n\t\t\tresult += protocol;\n\t\t\tlastIndex = protocolAtIndex + protocol.length;\n\t\t}\n\n\t\tconst remnant = urlObject.pathname.slice(lastIndex, urlObject.pathname.length);\n\t\tresult += remnant.replace(/\\/{2,}/g, '/');\n\n\t\turlObject.pathname = result;\n\t}\n\n\t// Decode URI octets\n\tif (urlObject.pathname) {\n\t\ttry {\n\t\t\turlObject.pathname = decodeURI(urlObject.pathname);\n\t\t} catch {}\n\t}\n\n\t// Remove directory index\n\tif (options.removeDirectoryIndex === true) {\n\t\toptions.removeDirectoryIndex = [/^index\\.[a-z]+$/];\n\t}\n\n\tif (Array.isArray(options.removeDirectoryIndex) && options.removeDirectoryIndex.length > 0) {\n\t\tlet pathComponents = urlObject.pathname.split('/');\n\t\tconst lastComponent = pathComponents[pathComponents.length - 1];\n\n\t\tif (testParameter(lastComponent, options.removeDirectoryIndex)) {\n\t\t\tpathComponents = pathComponents.slice(0, -1);\n\t\t\turlObject.pathname = pathComponents.slice(1).join('/') + '/';\n\t\t}\n\t}\n\n\tif (urlObject.hostname) {\n\t\t// Remove trailing dot\n\t\turlObject.hostname = urlObject.hostname.replace(/\\.$/, '');\n\n\t\t// Remove `www.`\n\t\tif (options.stripWWW && /^www\\.(?!www\\.)[a-z\\-\\d]{1,63}\\.[a-z.\\-\\d]{2,63}$/.test(urlObject.hostname)) {\n\t\t\t// Each label should be max 63 at length (min: 1).\n\t\t\t// Source: https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names\n\t\t\t// Each TLD should be up to 63 characters long (min: 2).\n\t\t\t// It is technically possible to have a single character TLD, but none currently exist.\n\t\t\turlObject.hostname = urlObject.hostname.replace(/^www\\./, '');\n\t\t}\n\t}\n\n\t// Remove query unwanted parameters\n\tif (Array.isArray(options.removeQueryParameters)) {\n\t\t// eslint-disable-next-line unicorn/no-useless-spread -- We are intentionally spreading to get a copy.\n\t\tfor (const key of [...urlObject.searchParams.keys()]) {\n\t\t\tif (testParameter(key, options.removeQueryParameters)) {\n\t\t\t\turlObject.searchParams.delete(key);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!Array.isArray(options.keepQueryParameters) && options.removeQueryParameters === true) {\n\t\turlObject.search = '';\n\t}\n\n\t// Keep wanted query parameters\n\tif (Array.isArray(options.keepQueryParameters) && options.keepQueryParameters.length > 0) {\n\t\t// eslint-disable-next-line unicorn/no-useless-spread -- We are intentionally spreading to get a copy.\n\t\tfor (const key of [...urlObject.searchParams.keys()]) {\n\t\t\tif (!testParameter(key, options.keepQueryParameters)) {\n\t\t\t\turlObject.searchParams.delete(key);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sort query parameters\n\tif (options.sortQueryParameters) {\n\t\turlObject.searchParams.sort();\n\n\t\t// Calling `.sort()` encodes the search parameters, so we need to decode them again.\n\t\ttry {\n\t\t\turlObject.search = decodeURIComponent(urlObject.search);\n\t\t} catch {}\n\t}\n\n\tif (options.removeTrailingSlash) {\n\t\turlObject.pathname = urlObject.pathname.replace(/\\/$/, '');\n\t}\n\n\t// Remove an explicit port number, excluding a default port number, if applicable\n\tif (options.removeExplicitPort && urlObject.port) {\n\t\turlObject.port = '';\n\t}\n\n\tconst oldUrlString = urlString;\n\n\t// Take advantage of many of the Node `url` normalizations\n\turlString = urlObject.toString();\n\n\tif (!options.removeSingleSlash && urlObject.pathname === '/' && !oldUrlString.endsWith('/') && urlObject.hash === '') {\n\t\turlString = urlString.replace(/\\/$/, '');\n\t}\n\n\t// Remove ending `/` unless removeSingleSlash is false\n\tif ((options.removeTrailingSlash || urlObject.pathname === '/') && urlObject.hash === '' && options.removeSingleSlash) {\n\t\turlString = urlString.replace(/\\/$/, '');\n\t}\n\n\t// Restore relative protocol, if applicable\n\tif (hasRelativeProtocol && !options.normalizeProtocol) {\n\t\turlString = urlString.replace(/^http:\\/\\//, '//');\n\t}\n\n\t// Remove http/https\n\tif (options.stripProtocol) {\n\t\turlString = urlString.replace(/^(?:https?:)?\\/\\//, '');\n\t}\n\n\treturn urlString;\n}\n","import { Logger } from \"./logger\";\n\nexport type ChainPatrolClientOptions = {\n apiKey: string;\n baseUrl?: string;\n};\n\ntype ApiRequest = {\n path: string[];\n method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n body?: unknown;\n};\n\nexport type AssetType = \"URL\" | \"PAGE\" | \"ADDRESS\";\nexport type AssetStatus = \"ALLOWED\" | \"BLOCKED\" | \"UNKNOWN\";\n\nfunction trimTrailingSlashes(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\nexport class ChainPatrolClient {\n public readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly logger = new Logger({ component: \"ChainPatrolClient\" });\n\n constructor(options: ChainPatrolClientOptions) {\n this.baseUrl = options.baseUrl ?? \"https://app.chainpatrol.io/api/\";\n if (!options.apiKey) {\n throw new Error(\"ChainPatrol API key is required\");\n }\n this.apiKey = options.apiKey;\n }\n\n private async fetch<TResult = unknown>(req: ApiRequest): Promise<TResult> {\n const url = `${trimTrailingSlashes(this.baseUrl)}/${req.path.join(\"/\")}`;\n this.logger.debug(\"fetch\", { url, req });\n const res = await fetch(url, {\n method: req.method,\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Api-Key\": this.apiKey,\n },\n body: JSON.stringify(req.body),\n });\n if (!res.ok) {\n throw new Error(await res.text());\n }\n return res.json();\n }\n\n public get asset() {\n return {\n check: async (req: {\n type: AssetType;\n content: string;\n }): Promise<{\n /**\n * \"ALLOWED\" - the asset is on the allowlist\n * \"BLOCKED\" - the asset is on the blocklist\n * \"UNKNOWN\" - the asset's status is not known\n */\n status: AssetStatus;\n /**\n * If the asset is allowed or blocked, this will be the reason why.\n * ChainPatrol aggregates data from multiple sources, so this will\n * tell you which source blocked or allowed the asset.\n *\n * ex. 'eth-phishing-detect' - the asset is on MetaMask's blocklist\n * 'reported' - the asset is on ChainPatrol's blocklist because\n * it was reported by a user\n */\n reason?: string;\n }> => {\n return await this.fetch<{\n status: AssetStatus;\n reason?: string;\n }>({\n path: [\"v2\", \"asset\", \"check\"],\n method: \"POST\",\n body: req,\n });\n },\n\n list: async (req: {\n /**\n * Asset type\n */\n type: AssetType;\n /**\n * Status of the assets to retrieve\n */\n status: AssetStatus;\n /**\n * The start date to list assets from. This should be in the format `YYYY-MM-DD` and is inclusive.\n */\n startDate?: string;\n /**\n * The end date to list assets from. This should be in the format `YYYY-MM-DD` and is inclusive.\n */\n endDate?: string;\n }): Promise<\n {\n content: string;\n type: AssetType;\n status: AssetStatus;\n }[]\n > => {\n return await this.fetch<\n {\n content: string;\n type: AssetType;\n status: AssetStatus;\n }[]\n >({\n path: [\"v2\", \"asset\", \"list\"],\n method: \"GET\",\n body: req,\n });\n },\n };\n }\n}\n","export * from \"./extension\";\nexport * from \"./browser\";\nexport * from \"./memory\";\nexport { defineStorage } from \"./define-storage\";\nexport * from \"./types\";\n","import { ThreatDetector } from \"../detector\";\nimport { Storage } from \"./types\";\n\nexport interface Context {\n keys: string[];\n}\n\nexport function defineStorage<T extends Storage>(\n config: (ctx: Context) => T\n): () => T {\n return () =>\n config({\n keys: Object.values(ThreatDetector.StorageKeys),\n });\n}\n","import { defineStorage } from \"./define-storage\";\n\nexport const Extension = defineStorage(({ keys }) => {\n return {\n get: async (key: string) => {\n const result = await chrome.storage.local.get(key);\n return result[key];\n },\n set: async (key: string, value: string) => {\n await chrome.storage.local.set({ [key]: value });\n },\n delete: async (key: string) => {\n await chrome.storage.local.remove(key);\n },\n size: async () => {\n const usageBytes = await chrome.storage.local.getBytesInUse(keys);\n return usageBytes;\n },\n };\n});\n","import { defineStorage } from \"./define-storage\";\n\nfunction isStorageAvailable(type: \"localStorage\" | \"sessionStorage\") {\n let storage;\n try {\n storage = window[type];\n const x = \"__storage_test__\";\n storage.setItem(x, x);\n storage.removeItem(x);\n return true;\n } catch (e) {\n return (\n e instanceof DOMException &&\n // everything except Firefox\n (e.code === 22 ||\n // Firefox\n e.code === 1014 ||\n // test name field too, because code might not be present\n // everything except Firefox\n e.name === \"QuotaExceededError\" ||\n // Firefox\n e.name === \"NS_ERROR_DOM_QUOTA_REACHED\") &&\n // acknowledge QuotaExceededError only if there's something already stored\n storage &&\n storage.length !== 0\n );\n }\n}\n\nexport const Browser = defineStorage(({ keys }) => {\n if (!isStorageAvailable(\"localStorage\")) {\n throw new Error(\"localStorage is not available\");\n }\n\n return {\n get: async (key: string) => {\n return localStorage.getItem(key);\n },\n set: async (key: string, value: string) => {\n localStorage.setItem(key, value);\n },\n delete: async (key: string) => {\n localStorage.removeItem(key);\n },\n size: async () => {\n let total = 0;\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key && keys.includes(key)) {\n total += localStorage.getItem(key)?.length ?? 0;\n }\n }\n return total;\n },\n };\n});\n","import { defineStorage } from \"./define-storage\";\n\nexport const Memory = defineStorage(() => {\n const storage = new Map<string, string>();\n return {\n get: async (key: string) => {\n return storage.get(key) || null;\n },\n set: async (key: string, value: string) => {\n storage.set(key, value);\n },\n delete: async (key: string) => {\n storage.delete(key);\n },\n size: async () => {\n let total = 0;\n for (const value of storage.values()) {\n total += value.length;\n }\n return total;\n },\n };\n});\n"]}
1
+ {"version":3,"sources":["../src/events.ts","../src/logger.ts","../src/relay.ts","../src/detector.ts","../../../node_modules/normalize-url/index.js","../src/client.ts","../src/storage/index.ts","../src/storage/define-storage.ts","../src/storage/extension.ts","../src/storage/browser.ts","../src/storage/memory.ts"],"names":["LogLevel","list"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAM,oBAAoB;AAC1B,IAAM,oBAAoB;AAC1B,IAAM,kBAAkB;AAEjB,IAAM,SAAS;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AACF;;;ACRO,IAAK,WAAL,kBAAKA,cAAL;AACL,EAAAA,oBAAA;AACA,EAAAA,oBAAA;AACA,EAAAA,oBAAA;AACA,EAAAA,oBAAA;AACA,EAAAA,oBAAA;AALU,SAAAA;AAAA,GAAA;AAQL,IAAM,SAAN,MAAM,QAAO;AAAA,EAIlB,YACE,MACA,WAAqB,cACrB;AANF,SAAQ,OAAgC,CAAC;AAOvC,QAAI,MAAM;AACR,WAAK,OAAO;AAAA,IACd;AACA,SAAK,WAAW;AAAA,EAClB;AAAA,EAEO,KAAK,QAAiC;AAC3C,WAAO,IAAI,QAAO,kCAAK,KAAK,OAAS,SAAU,KAAK,QAAQ;AAAA,EAC9D;AAAA,EAEO,MAAM,SAAiB,QAAkC;AAC9D,SAAK,IAAI,eAAgB,SAAS,MAAM;AAAA,EAC1C;AAAA,EAEO,KAAK,SAAiB,QAAkC;AAC7D,SAAK,IAAI,cAAe,SAAS,MAAM;AAAA,EACzC;AAAA,EAEO,KAAK,SAAiB,QAAkC;AAC7D,SAAK,IAAI,cAAe,SAAS,MAAM;AAAA,EACzC;AAAA,EAEO,MAAM,SAAiB,QAAkC;AAC9D,SAAK,IAAI,eAAgB,SAAS,MAAM;AAAA,EAC1C;AAAA,EAEQ,IACN,OACA,SACA,QACA;AACA,QAAI,QAAQ,KAAK,UAAU;AACzB;AAAA,IACF;AAEA,UAAM,SAAS,iBAAE,SAAS,MAAM,mBAAK,WAAa,KAAK;AACvD,UAAM,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC;AAChD,YAAQ,IAAI,IAAI,SAAS,KAAK,EAAE,YAAY,MAAM,WAAW;AAAA,EAC/D;AACF;;;AC3CA,SAAS,sBAA6C;AAZtD;AAaE,OAAK,gBAAmB,YAAnB,mBAA4B,SAAS;AACxC,WAAQ,WAAmB,QAAQ;AAAA,EACrC;AACA,OAAI,gBAAW,WAAX,mBAAmB,SAAS;AAC9B,WAAO,WAAW,OAAO;AAAA,EAC3B;AACA,QAAM,IAAI,MAAM,4BAA4B;AAC9C;AAEA,SAAS,kBAAkB;AAtB3B;AAuBE,SACE,CAAC,GAAE,gBAAmB,YAAnB,mBAA4B,YAC/B,CAAC,GAAE,gBAAmB,WAAnB,mBAA2B;AAElC;AAEA,SAAS,kBAAkB;AACzB,SAAO,gBAAgB,KAAK,cAAc;AAC5C;AAEA,SAAS,qBAAqB;AAC5B,SAAO,gBAAgB,KAAK,CAAC,cAAc;AAC7C;AAEA,SAAS,gBAAgB;AACvB,SAAO,CAAC,CAAE,WAAmB;AAC/B;AAQA,SAAS,4BAAoC;AAC3C,QAAM,UAAU,oBAAoB;AACpC,SAAO;AAAA,IACL,aAAa,CAAC,aAAqC;AACjD,cAAQ,UAAU,YAAY,QAAQ;AAAA,IACxC;AAAA,IACA,gBAAgB,CAAC,aAAqC;AACpD,cAAQ,UAAU,eAAe,QAAQ;AAAA,IAC3C;AAAA,IACA,aAAa,CAAO,YAAiB;AACnC,YAAM,CAAC,GAAG,IAAI,MAAM,WAAW,OAAO,KAAK,MAAM;AAAA,QAC/C,QAAQ;AAAA,QACR,mBAAmB;AAAA,MACrB,CAAC;AACD,UAAI,CAAC,IAAI,MAAM,IAAI,OAAO,WAAW,OAAO,KAAK,aAAa;AAC5D,gBAAQ,MAAM,qBAAqB;AACnC;AAAA,MACF;AACA,iBAAW,OAAO,KAAK,YAAY,IAAI,IAAI,OAAO;AAAA,IACpD;AAAA,EACF;AACF;AAEA,SAAS,yBAAiC;AACxC,QAAM,UAAU,oBAAoB;AACpC,SAAO;AAAA,IACL,aAAa,CAAC,aAAqC;AACjD,cAAQ,UAAU,YAAY,QAAQ;AAAA,IACxC;AAAA,IACA,gBAAgB,CAAC,aAAqC;AACpD,cAAQ,UAAU,eAAe,QAAQ;AAAA,IAC3C;AAAA,IACA,aAAa,CAAO,YAAiB;AACnC,cAAQ,YAAY,OAAO,EAAE,MAAM,CAAC,UAAU;AAC5C,gBAAQ,MAAM,0BAA0B,EAAE,SAAS,MAAM,CAAC;AAAA,MAC5D,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,SAAS,mBAA2B;AAClC,SAAO;AAAA,IACL,aAAa,CAAC,aAAqC;AACjD,iBAAW,OAAO,iBAAiB,WAAW,CAAC,UAAU;AACvD,YAAI,MAAM,WAAW,WAAW,QAAQ;AACtC;AAAA,QACF;AACA,iBAAS,MAAM,IAAI;AAAA,MACrB,CAAC;AAAA,IACH;AAAA,IACA,gBAAgB,CAAC,aAAqC;AACpD,iBAAW,OAAO,oBAAoB,WAAW,QAAQ;AAAA,IAC3D;AAAA,IACA,aAAa,CAAC,YAAiB;AAC7B,iBAAW,OAAO,YAAY,SAAS,GAAG;AAAA,IAC5C;AAAA,EACF;AACF;AAaO,IAAM,SAAN,MAAM,OAAM;AAAA,EAkBjB,OAAc,KACZ,MACA,MACA;AACA,UAAM,UAAU;AAAA,MACd;AAAA,MACA;AAAA,MACA,OAAO;AAAA,QACL,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC;AAAA,QAC7C,QAAQ,WAAW,SAAS;AAAA,MAC9B;AAAA,IACF;AAEA,WAAM,OAAO,MAAM,mBAAmB,OAAO;AAE7C,eAAW,UAAU,OAAM,SAAS;AAClC,aAAO,YAAY,OAAO;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,OAAc,IAAI,QAA6B;AAC7C,UAAM,YAAY,oBAAI,IAA4B;AAElD,eAAW,UAAU,OAAM,SAAS;AAClC,YAAM,WAAW,OAAM,cAAc,KAAK,MAAM,QAAQ,MAAM;AAC9D,gBAAU,IAAI,QAAQ;AACtB,aAAO,YAAY,QAAQ;AAAA,IAC7B;AAEA,WAAM,OAAO,MAAM,iBAAiB,EAAE,OAAO,CAAC;AAE9C,WAAO,MAAM;AACX,iBAAW,UAAU,OAAM,SAAS;AAClC,mBAAW,YAAY,WAAW;AAChC,iBAAO,eAAe,QAAQ;AAAA,QAChC;AAAA,MACF;AAEA,aAAM,OAAO,MAAM,iBAAiB,EAAE,OAAO,CAAC;AAAA,IAChD;AAAA,EACF;AAAA,EAEA,OAAe,cACb,cACA,QACA,SACA;AACA,QAAI,CAAC,OAAO,SAAS,QAAQ,IAAI,GAAG;AAClC,aAAM,OAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC;AAClD;AAAA,IACF;AAEA,UAAM,qBAAqB,OAAM,QAAQ;AAAA,MACvC,CAAC,WAAW,WAAW;AAAA,IACzB;AAEA,eAAW,qBAAqB,oBAAoB;AAClD,wBAAkB,YAAY,OAAO;AAAA,IACvC;AAAA,EACF;AAAA,EAEA,OAAc,GACZ,aACA,UACA;AACA,UAAM,YAAY,oBAAI,IAA4B;AAClD,eAAW,UAAU,OAAM,SAAS;AAClC,YAAM,WAAW,CAAC,YAAiB;AACjC,YAAI,QAAQ,SAAS,aAAa;AAChC,iBAAM,OAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC;AAClD;AAAA,QACF;AACA,iBAAS,QAAQ,IAAI;AAAA,MACvB;AACA,gBAAU,IAAI,QAAQ;AACtB,aAAO,YAAY,QAAQ;AAAA,IAC7B;AAEA,WAAO,MAAM;AACX,iBAAW,UAAU,OAAM,SAAS;AAClC,mBAAW,YAAY,WAAW;AAChC,iBAAO,eAAe,QAAQ;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAxGa,OACM,UAAoB,CAAC;AAD3B,OAEa,SAAS,IAAI,OAAO,EAAE,WAAW,QAAQ,CAAC;AAAA,CAElE,MAAO;AACL,MAAI,mBAAmB,GAAG;AACxB,WAAM,OAAO,KAAK,4BAA4B;AAC9C,WAAM,QAAQ,KAAK,0BAA0B,CAAC;AAAA,EAChD,WAAW,gBAAgB,GAAG;AAC5B,WAAM,OAAO,KAAK,yBAAyB;AAC3C,WAAM,QAAQ,KAAK,uBAAuB,CAAC;AAC3C,WAAM,QAAQ,KAAK,iBAAiB,CAAC;AAAA,EACvC,WAAW,cAAc,GAAG;AAC1B,WAAM,OAAO,KAAK,uBAAuB;AACzC,WAAM,QAAQ,KAAK,iBAAiB,CAAC;AAAA,EACvC;AACF;AAhBK,IAAM,QAAN;;;ACrHP,SAAS,SAAS,mBAAmB;;;ACCrC,IAAM,6BAA6B;AACnC,IAAM,2BAA2B;AAEjC,IAAM,gBAAgB,CAAC,MAAM,YAAY,QAAQ,KAAK,YAAU,kBAAkB,SAAS,OAAO,KAAK,IAAI,IAAI,WAAW,IAAI;AAE9H,IAAM,qBAAqB,oBAAI,IAAI;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAED,IAAM,oBAAoB,eAAa;AACtC,MAAI;AACH,UAAM,EAAC,SAAQ,IAAI,IAAI,IAAI,SAAS;AACpC,WAAO,SAAS,SAAS,GAAG,KAAK,CAAC,mBAAmB,IAAI,QAAQ;AAAA,EAClE,SAAQ,GAAN;AACD,WAAO;AAAA,EACR;AACD;AAEA,IAAM,mBAAmB,CAAC,WAAW,EAAC,UAAS,MAAM;AArBrD;AAsBC,QAAM,QAAQ,WAAC,yDAAwD,EAAC,KAAK,SAAS;AAEtF,MAAI,CAAC,OAAO;AACX,UAAM,IAAI,MAAM,gBAAgB,WAAW;AAAA,EAC5C;AAEA,MAAI,EAAC,MAAM,MAAM,KAAI,IAAI,MAAM;AAC/B,QAAM,YAAY,KAAK,MAAM,GAAG;AAChC,SAAO,YAAY,KAAK;AAExB,MAAI,WAAW;AACf,MAAI,UAAU,UAAU,SAAS,CAAC,MAAM,UAAU;AACjD,cAAU,IAAI;AACd,eAAW;AAAA,EACZ;AAGA,QAAM,YAAW,qBAAU,MAAM,MAAhB,mBAAmB,kBAAnB,YAAoC;AACrD,QAAM,aAAa,UACjB,IAAI,eAAa;AACjB,QAAI,CAAC,KAAK,QAAQ,EAAE,IAAI,UAAU,MAAM,GAAG,EAAE,IAAI,YAAU,OAAO,KAAK,CAAC;AAGxE,QAAI,QAAQ,WAAW;AACtB,cAAQ,MAAM,YAAY;AAE1B,UAAI,UAAU,0BAA0B;AACvC,eAAO;AAAA,MACR;AAAA,IACD;AAEA,WAAO,GAAG,MAAM,QAAQ,IAAI,UAAU;AAAA,EACvC,CAAC,EACA,OAAO,OAAO;AAEhB,QAAM,sBAAsB;AAAA,IAC3B,GAAG;AAAA,EACJ;AAEA,MAAI,UAAU;AACb,wBAAoB,KAAK,QAAQ;AAAA,EAClC;AAEA,MAAI,oBAAoB,SAAS,KAAM,YAAY,aAAa,4BAA6B;AAC5F,wBAAoB,QAAQ,QAAQ;AAAA,EACrC;AAEA,SAAO,QAAQ,oBAAoB,KAAK,GAAG,KAAK,WAAW,KAAK,KAAK,IAAI,OAAO,OAAO,IAAI,SAAS;AACrG;AAEe,SAAR,aAA8B,WAAW,SAAS;AACxD,YAAU;AAAA,IACT,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,IACnB,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,qBAAqB;AAAA,IACrB,WAAW;AAAA,IACX,mBAAmB;AAAA,IACnB,UAAU;AAAA,IACV,uBAAuB,CAAC,WAAW;AAAA,IACnC,qBAAqB;AAAA,IACrB,mBAAmB;AAAA,IACnB,sBAAsB;AAAA,IACtB,oBAAoB;AAAA,IACpB,qBAAqB;AAAA,KAClB;AAIJ,MAAI,OAAO,QAAQ,oBAAoB,YAAY,CAAC,QAAQ,gBAAgB,SAAS,GAAG,GAAG;AAC1F,YAAQ,kBAAkB,GAAG,QAAQ;AAAA,EACtC;AAEA,cAAY,UAAU,KAAK;AAG3B,MAAI,UAAU,KAAK,SAAS,GAAG;AAC9B,WAAO,iBAAiB,WAAW,OAAO;AAAA,EAC3C;AAEA,MAAI,kBAAkB,SAAS,GAAG;AACjC,WAAO;AAAA,EACR;AAEA,QAAM,sBAAsB,UAAU,WAAW,IAAI;AACrD,QAAM,gBAAgB,CAAC,uBAAuB,SAAS,KAAK,SAAS;AAGrE,MAAI,CAAC,eAAe;AACnB,gBAAY,UAAU,QAAQ,4BAA4B,QAAQ,eAAe;AAAA,EAClF;AAEA,QAAM,YAAY,IAAI,IAAI,SAAS;AAEnC,MAAI,QAAQ,aAAa,QAAQ,YAAY;AAC5C,UAAM,IAAI,MAAM,kEAAkE;AAAA,EACnF;AAEA,MAAI,QAAQ,aAAa,UAAU,aAAa,UAAU;AACzD,cAAU,WAAW;AAAA,EACtB;AAEA,MAAI,QAAQ,cAAc,UAAU,aAAa,SAAS;AACzD,cAAU,WAAW;AAAA,EACtB;AAGA,MAAI,QAAQ,qBAAqB;AAChC,cAAU,WAAW;AACrB,cAAU,WAAW;AAAA,EACtB;AAGA,MAAI,QAAQ,WAAW;AACtB,cAAU,OAAO;AAAA,EAClB,WAAW,QAAQ,mBAAmB;AACrC,cAAU,OAAO,UAAU,KAAK,QAAQ,kBAAkB,EAAE;AAAA,EAC7D;AAMA,MAAI,UAAU,UAAU;AAMvB,UAAM,gBAAgB;AAEtB,QAAI,YAAY;AAChB,QAAI,SAAS;AACb,eAAS;AACR,YAAM,QAAQ,cAAc,KAAK,UAAU,QAAQ;AACnD,UAAI,CAAC,OAAO;AACX;AAAA,MACD;AAEA,YAAM,WAAW,MAAM,CAAC;AACxB,YAAM,kBAAkB,MAAM;AAC9B,YAAM,eAAe,UAAU,SAAS,MAAM,WAAW,eAAe;AAExE,gBAAU,aAAa,QAAQ,WAAW,GAAG;AAC7C,gBAAU;AACV,kBAAY,kBAAkB,SAAS;AAAA,IACxC;AAEA,UAAM,UAAU,UAAU,SAAS,MAAM,WAAW,UAAU,SAAS,MAAM;AAC7E,cAAU,QAAQ,QAAQ,WAAW,GAAG;AAExC,cAAU,WAAW;AAAA,EACtB;AAGA,MAAI,UAAU,UAAU;AACvB,QAAI;AACH,gBAAU,WAAW,UAAU,UAAU,QAAQ;AAAA,IAClD,SAAQ,GAAN;AAAA,IAAO;AAAA,EACV;AAGA,MAAI,QAAQ,yBAAyB,MAAM;AAC1C,YAAQ,uBAAuB,CAAC,iBAAiB;AAAA,EAClD;AAEA,MAAI,MAAM,QAAQ,QAAQ,oBAAoB,KAAK,QAAQ,qBAAqB,SAAS,GAAG;AAC3F,QAAI,iBAAiB,UAAU,SAAS,MAAM,GAAG;AACjD,UAAM,gBAAgB,eAAe,eAAe,SAAS,CAAC;AAE9D,QAAI,cAAc,eAAe,QAAQ,oBAAoB,GAAG;AAC/D,uBAAiB,eAAe,MAAM,GAAG,EAAE;AAC3C,gBAAU,WAAW,eAAe,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI;AAAA,IAC1D;AAAA,EACD;AAEA,MAAI,UAAU,UAAU;AAEvB,cAAU,WAAW,UAAU,SAAS,QAAQ,OAAO,EAAE;AAGzD,QAAI,QAAQ,YAAY,oDAAoD,KAAK,UAAU,QAAQ,GAAG;AAKrG,gBAAU,WAAW,UAAU,SAAS,QAAQ,UAAU,EAAE;AAAA,IAC7D;AAAA,EACD;AAGA,MAAI,MAAM,QAAQ,QAAQ,qBAAqB,GAAG;AAEjD,eAAW,OAAO,CAAC,GAAG,UAAU,aAAa,KAAK,CAAC,GAAG;AACrD,UAAI,cAAc,KAAK,QAAQ,qBAAqB,GAAG;AACtD,kBAAU,aAAa,OAAO,GAAG;AAAA,MAClC;AAAA,IACD;AAAA,EACD;AAEA,MAAI,CAAC,MAAM,QAAQ,QAAQ,mBAAmB,KAAK,QAAQ,0BAA0B,MAAM;AAC1F,cAAU,SAAS;AAAA,EACpB;AAGA,MAAI,MAAM,QAAQ,QAAQ,mBAAmB,KAAK,QAAQ,oBAAoB,SAAS,GAAG;AAEzF,eAAW,OAAO,CAAC,GAAG,UAAU,aAAa,KAAK,CAAC,GAAG;AACrD,UAAI,CAAC,cAAc,KAAK,QAAQ,mBAAmB,GAAG;AACrD,kBAAU,aAAa,OAAO,GAAG;AAAA,MAClC;AAAA,IACD;AAAA,EACD;AAGA,MAAI,QAAQ,qBAAqB;AAChC,cAAU,aAAa,KAAK;AAG5B,QAAI;AACH,gBAAU,SAAS,mBAAmB,UAAU,MAAM;AAAA,IACvD,SAAQ,GAAN;AAAA,IAAO;AAAA,EACV;AAEA,MAAI,QAAQ,qBAAqB;AAChC,cAAU,WAAW,UAAU,SAAS,QAAQ,OAAO,EAAE;AAAA,EAC1D;AAGA,MAAI,QAAQ,sBAAsB,UAAU,MAAM;AACjD,cAAU,OAAO;AAAA,EAClB;AAEA,QAAM,eAAe;AAGrB,cAAY,UAAU,SAAS;AAE/B,MAAI,CAAC,QAAQ,qBAAqB,UAAU,aAAa,OAAO,CAAC,aAAa,SAAS,GAAG,KAAK,UAAU,SAAS,IAAI;AACrH,gBAAY,UAAU,QAAQ,OAAO,EAAE;AAAA,EACxC;AAGA,OAAK,QAAQ,uBAAuB,UAAU,aAAa,QAAQ,UAAU,SAAS,MAAM,QAAQ,mBAAmB;AACtH,gBAAY,UAAU,QAAQ,OAAO,EAAE;AAAA,EACxC;AAGA,MAAI,uBAAuB,CAAC,QAAQ,mBAAmB;AACtD,gBAAY,UAAU,QAAQ,cAAc,IAAI;AAAA,EACjD;AAGA,MAAI,QAAQ,eAAe;AAC1B,gBAAY,UAAU,QAAQ,qBAAqB,EAAE;AAAA,EACtD;AAEA,SAAO;AACR;;;ADvRA,SAAS,SAAS;;;AEclB,SAAS,oBAAoB,KAAqB;AAChD,SAAO,IAAI,QAAQ,QAAQ,EAAE;AAC/B;AAEO,IAAM,oBAAN,MAAwB;AAAA,EAK7B,YAAY,SAAmC;AAF/C,SAAiB,SAAS,IAAI,OAAO,EAAE,WAAW,oBAAoB,CAAC;AAvBzE;AA0BI,SAAK,WAAU,aAAQ,YAAR,YAAmB;AAClC,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AACA,SAAK,SAAS,QAAQ;AAAA,EACxB;AAAA,EAEc,MAAyB,KAAmC;AAAA;AACxE,YAAM,MAAM,GAAG,oBAAoB,KAAK,OAAO,KAAK,IAAI,KAAK,KAAK,GAAG;AACrE,WAAK,OAAO,MAAM,SAAS,EAAE,KAAK,IAAI,CAAC;AACvC,YAAM,MAAM,MAAM,MAAM,KAAK;AAAA,QAC3B,QAAQ,IAAI;AAAA,QACZ,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,aAAa,KAAK;AAAA,QACpB;AAAA,QACA,MAAM,KAAK,UAAU,IAAI,IAAI;AAAA,MAC/B,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,MAAM,IAAI,KAAK,CAAC;AAAA,MAClC;AACA,aAAO,IAAI,KAAK;AAAA,IAClB;AAAA;AAAA,EAEA,IAAW,QAAQ;AACjB,WAAO;AAAA,MACL,OAAO,CAAO,QAoBR;AACJ,eAAO,MAAM,KAAK,MAGf;AAAA,UACD,MAAM,CAAC,MAAM,SAAS,OAAO;AAAA,UAC7B,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,MAEA,MAAM,CAAO,QAuBR;AACH,eAAO,MAAM,KAAK,MAMhB;AAAA,UACA,MAAM,CAAC,MAAM,SAAS,MAAM;AAAA,UAC5B,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;ACzHA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACOO,SAAS,cACd,QACS;AACT,SAAO,MACL,OAAO;AAAA,IACL,MAAM,OAAO,OAAO,eAAe,WAAW;AAAA,EAChD,CAAC;AACL;;;ACZO,IAAM,YAAY,cAAc,CAAC,EAAE,KAAK,MAAM;AACnD,SAAO;AAAA,IACL,KAAK,CAAO,QAAgB;AAC1B,YAAM,SAAS,MAAM,OAAO,QAAQ,MAAM,IAAI,GAAG;AACjD,aAAO,OAAO,GAAG;AAAA,IACnB;AAAA,IACA,KAAK,CAAO,KAAa,UAAkB;AACzC,YAAM,OAAO,QAAQ,MAAM,IAAI,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC;AAAA,IACjD;AAAA,IACA,QAAQ,CAAO,QAAgB;AAC7B,YAAM,OAAO,QAAQ,MAAM,OAAO,GAAG;AAAA,IACvC;AAAA,IACA,MAAM,MAAY;AAChB,YAAM,aAAa,MAAM,OAAO,QAAQ,MAAM,cAAc,IAAI;AAChE,aAAO;AAAA,IACT;AAAA,EACF;AACF,CAAC;;;ACjBD,SAAS,mBAAmB,MAAyC;AACnE,MAAI;AACJ,MAAI;AACF,cAAU,OAAO,IAAI;AACrB,UAAM,IAAI;AACV,YAAQ,QAAQ,GAAG,CAAC;AACpB,YAAQ,WAAW,CAAC;AACpB,WAAO;AAAA,EACT,SAAS,GAAP;AACA,WACE,aAAa;AAAA,KAEZ,EAAE,SAAS;AAAA,IAEV,EAAE,SAAS;AAAA;AAAA,IAGX,EAAE,SAAS;AAAA,IAEX,EAAE,SAAS;AAAA,IAEb,WACA,QAAQ,WAAW;AAAA,EAEvB;AACF;AAEO,IAAM,UAAU,cAAc,CAAC,EAAE,KAAK,MAAM;AACjD,MAAI,CAAC,mBAAmB,cAAc,GAAG;AACvC,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AAEA,SAAO;AAAA,IACL,KAAK,CAAO,QAAgB;AAC1B,aAAO,aAAa,QAAQ,GAAG;AAAA,IACjC;AAAA,IACA,KAAK,CAAO,KAAa,UAAkB;AACzC,mBAAa,QAAQ,KAAK,KAAK;AAAA,IACjC;AAAA,IACA,QAAQ,CAAO,QAAgB;AAC7B,mBAAa,WAAW,GAAG;AAAA,IAC7B;AAAA,IACA,MAAM,MAAY;AA5CtB;AA6CM,UAAI,QAAQ;AACZ,eAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;AAC5C,cAAM,MAAM,aAAa,IAAI,CAAC;AAC9B,YAAI,OAAO,KAAK,SAAS,GAAG,GAAG;AAC7B,oBAAS,wBAAa,QAAQ,GAAG,MAAxB,mBAA2B,WAA3B,YAAqC;AAAA,QAChD;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF,CAAC;;;ACrDM,IAAM,SAAS,cAAc,MAAM;AACxC,QAAM,UAAU,oBAAI,IAAoB;AACxC,SAAO;AAAA,IACL,KAAK,CAAO,QAAgB;AAC1B,aAAO,QAAQ,IAAI,GAAG,KAAK;AAAA,IAC7B;AAAA,IACA,KAAK,CAAO,KAAa,UAAkB;AACzC,cAAQ,IAAI,KAAK,KAAK;AAAA,IACxB;AAAA,IACA,QAAQ,CAAO,QAAgB;AAC7B,cAAQ,OAAO,GAAG;AAAA,IACpB;AAAA,IACA,MAAM,MAAY;AAChB,UAAI,QAAQ;AACZ,iBAAW,SAAS,QAAQ,OAAO,GAAG;AACpC,iBAAS,MAAM;AAAA,MACjB;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF,CAAC;;;APSM,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC1C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,kBAAN,MAAM,gBAAe;AAAA,EA0D1B,YAAY;AAAA,IACV,OAAO;AAAA,IACP,SAAS;AAAA,IACT,UAAU,OAAO;AAAA,IACjB;AAAA,IACA;AAAA,EACF,GAMG;AAlDH,SAAiB,SAAS,IAAI,OAAO,EAAE,WAAW,iBAAiB,CAAC;AAmDlE,SAAK,OAAO;AACZ,SAAK,UAAU;AACf,SAAK,cAAc,oCAAe,gBAAe;AACjD,QAAI,SAAS,SAAS;AACpB,WAAK,SAAS,IAAI,kBAAkB;AAAA,QAClC;AAAA,QACA,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEa,IAAI,KAAiC;AAAA;AAChD,WAAK,OAAO,MAAM,gBAAgB,EAAE,IAAI,CAAC;AAEzC,UAAI;AACJ,UAAI;AACF,kBAAU,KAAK,gBAAgB,GAAG;AAAA,MACpC,SAAS,GAAP;AACA,aAAK,OAAO,MAAM,0BAA0B,EAAE,KAAK,OAAO,EAAE,CAAC;AAC7D,eAAO;AAAA,UACL,IAAI;AAAA,UACJ;AAAA,UACA,OACE,aAAa,mBAAmB,EAAE,UAAU;AAAA,QAChD;AAAA,MACF;AAEA,WAAK,OAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC;AAElD,UAAI,WACF,MAAM,QAAQ,IAAI,QAAQ,IAAI,CAAC,WAAW,KAAK,UAAU,QAAQ,GAAG,CAAC,CAAC,GACtE;AAAA,QACA,CAAsB,MACpB,EAAE;AAAA,MACN;AAEA,UAAI,QAAQ,WAAW,GAAG;AACxB,eAAO;AAAA,UACL,IAAI;AAAA,UACJ;AAAA,UACA,OAAO;AAAA,QACT;AAAA,MACF;AAEA,WAAK,OAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC;AAEpD,UAAI,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,SAAS,GAAG;AAC/C,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,QAAQ;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,iBAAW,UAAU,SAAS;AAC5B,YAAI,OAAO,MAAM,OAAO,WAAW,WAAW;AAC5C,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,aAAO,QAAQ,CAAC;AAAA,IAClB;AAAA;AAAA,EAEQ,gBAAgB,MAAwB;AA5KlD;AA6KI,UAAM,SAAS,KAAK,mBAAmB,IAAI;AAE3C,UAAM,UAAU,CAAC,MAAM;AAEvB,UAAM,eAAe,YAAY,MAAM;AACvC,QAAI,CAAC,aAAa,WAAW;AAC3B,aAAO;AAAA,IACT;AAEA,UAAM,kBAAiB,wBAAa,cAAb,mBAAwB,MAAM,SAA9B,YAAsC,CAAC;AAE9D,aAAS,IAAI,GAAG,IAAI,eAAe,QAAQ,KAAK;AAC9C,YAAM,YAAY,eAAe,MAAM,CAAC,EAAE,KAAK,GAAG;AAClD,cAAQ,QAAQ,GAAG,aAAa,QAAkB;AAAA,IACpD;AAEA,WAAO;AAAA,EACT;AAAA,EAEc,UAAU,QAAgB,KAAiC;AAAA;AACvE,UAAI,SAAS,MAAM,KAAK,mBAAmB,MAAM;AAEjD,UAAI,KAAK,SAAS,WAAW,KAAK,UAAU,WAAW,WAAW;AAChE,YAAI;AACF,gBAAM,MAAM,MAAM,KAAK,OAAO,MAAM,MAAM;AAAA,YACxC,MAAM;AAAA,YACN,SAAS;AAAA,UACX,CAAC;AAGD,eAAK,OAAO,MAAM,kBAAkB,EAAE,QAAQ,QAAQ,IAAI,OAAO,CAAC;AAClE,cAAI,IAAI,WAAW,WAAW;AAC5B,iBAAK,iBAAiB,QAAQ,gBAAe,YAAY,SAAS;AAAA,UACpE,WAAW,IAAI,WAAW,WAAW;AACnC,iBAAK,iBAAiB,QAAQ,gBAAe,YAAY,SAAS;AAAA,UACpE;AAEA,mBAAS,IAAI;AAAA,QACf,SAAS,GAAP;AACA,iBAAO;AAAA,YACL,IAAI;AAAA,YACJ,KAAK;AAAA,YACL,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAEA,UAAI;AAEJ,UAAI,WAAW,WAAW;AACxB,YAAI,OAAO,KAAK,gBAAgB,YAAY;AAC1C,wBAAc,KAAK,YAAY,GAAG;AAAA,QACpC,WAAW,OAAO,KAAK,gBAAgB,UAAU;AAC/C,gBAAM,SAAS,IAAI,IAAI,KAAK,WAAW;AACvC,iBAAO,aAAa,IAAI,aAAa,GAAG;AACxC,wBAAc,OAAO,SAAS;AAAA,QAChC;AAAA,MACF;AAEA,aAAO;AAAA,QACL,IAAI;AAAA,QACJ;AAAA,QACA,KAAK;AAAA,QACL;AAAA,MACF;AAAA,IACF;AAAA;AAAA,EAEa,MACX,KAGA;AAAA;AACA,UAAI;AACF,cAAM,SAAS,KAAK,mBAAmB,GAAG;AAE1C,aAAK,OAAO,MAAM,gBAAgB,EAAE,KAAK,OAAO,CAAC;AAEjD,cAAM,KAAK,yBAAyB,MAAM;AAC1C,cAAM,KAAK,iBAAiB,QAAQ,gBAAe,YAAY,SAAS;AAExE,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,KAAK;AAAA,QACP;AAAA,MACF,SAAS,GAAP;AACA,aAAK,OAAO,MAAM,uBAAuB,EAAE,KAAK,OAAO,EAAE,CAAC;AAC1D,eAAO;AAAA,UACL,IAAI;AAAA,UACJ;AAAA,UACA,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA;AAAA,EAEa,MACX,KAGA;AAAA;AACA,UAAI;AACF,cAAM,SAAS,KAAK,mBAAmB,GAAG;AAE1C,aAAK,OAAO,MAAM,gBAAgB,EAAE,KAAK,OAAO,CAAC;AAEjD,cAAM,KAAK,yBAAyB,MAAM;AAC1C,cAAM,KAAK,iBAAiB,QAAQ,gBAAe,YAAY,SAAS;AAExE,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,KAAK;AAAA,QACP;AAAA,MACF,SAAS,GAAP;AACA,aAAK,OAAO,MAAM,uBAAuB,EAAE,KAAK,OAAO,EAAE,CAAC;AAC1D,eAAO;AAAA,UACL,IAAI;AAAA,UACJ;AAAA,UACA,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA;AAAA,EAEa,OACX,KAGA;AAAA;AACA,UAAI;AACF,cAAM,SAAS,KAAK,mBAAmB,GAAG;AAE1C,aAAK,OAAO,MAAM,gBAAgB,EAAE,KAAK,OAAO,CAAC;AAEjD,cAAM,KAAK;AAAA,UACT;AAAA,UACA,gBAAe,YAAY;AAAA,QAC7B;AAEA,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,KAAK;AAAA,QACP;AAAA,MACF,SAAS,GAAP;AACA,aAAK,OAAO,MAAM,wBAAwB,EAAE,KAAK,OAAO,EAAE,CAAC;AAC3D,eAAO;AAAA,UACL,IAAI;AAAA,UACJ;AAAA,UACA,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA;AAAA,EAEQ,mBAAmB,KAAqB;AAC9C,SAAK,OAAO,MAAM,kBAAkB,EAAE,IAAI,CAAC;AAC3C,QAAI;AACF,YAAM,gBAAgB,aAAa,KAAK;AAAA,QACtC,UAAU;AAAA,QACV,qBAAqB;AAAA,MACvB,CAAC;AAED,WAAK,OAAO,MAAM,kBAAkB,EAAE,MAAM,KAAK,IAAI,cAAc,CAAC;AAEpE,YAAM,YAAY,IAAI,IAAI,aAAa;AAEvC,WAAK,OAAO,MAAM,2BAA2B,EAAE,KAAK,UAAU,SAAS,CAAC;AAExE,YAAM,wBAAwB,YAAY,UAAU,QAAQ;AAE5D,WAAK,OAAO,MAAM,iBAAiB,EAAE,sBAAsB,CAAC;AAE5D,UAAI,sBAAsB,aAAa,aAAa;AAClD,cAAM,IAAI,iBAAiB,2CAA2C;AAAA,MACxE;AAEA,UAAI,sBAAsB,MAAM;AAC9B,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,UAAI,CAAC,sBAAsB,QAAQ;AACjC,cAAM,IAAI,iBAAiB,wBAAwB;AAAA,MACrD;AAEA,YAAM,SAAS,sBAAsB;AAErC,aAAO;AAAA,IACT,SAAS,GAAP;AACA,UAAI,aAAa,kBAAkB;AACjC,cAAM;AAAA,MACR,OAAO;AACL,aAAK,OAAO,MAAM,0BAA0B,EAAE,KAAK,OAAO,EAAE,CAAC;AAC7D,cAAM,IAAI,iBAAiB,wBAAwB;AAAA,MACrD;AAAA,IACF;AAAA,EACF;AAAA,EAEc,yBAAyB,QAA+B;AAAA;AACpE,YAAM,KAAK;AAAA,QACT;AAAA,QACA,gBAAe,YAAY;AAAA,MAC7B;AACA,YAAM,KAAK;AAAA,QACT;AAAA,QACA,gBAAe,YAAY;AAAA,MAC7B;AAAA,IACF;AAAA;AAAA,EAEc,wBACZ,QACA,KACe;AAAA;AACf,YAAM,OAAO,MAAM,KAAK,QAAQ,IAAI,GAAG;AAEvC,UAAI,CAAC,MAAM;AACT;AAAA,MACF;AAEA,YAAM,OAAO,MAAM,KAAK,mBAAmB,GAAG;AAE9C,UAAI,CAAC,KAAK,KAAK,QAAQ,SAAS,MAAM,GAAG;AACvC;AAAA,MACF;AAEA,WAAK,KAAK,UAAU,KAAK,KAAK,QAAQ,OAAO,CAAC,MAAM,MAAM,MAAM;AAEhE,YAAM,KAAK,iBAAiB,KAAK,IAAI;AAAA,IACvC;AAAA;AAAA,EAEc,iBAAiB,QAAgB,KAA4B;AAAA;AACzE,YAAM,OAAO,MAAM,KAAK,mBAAmB,GAAG;AAE9C,UAAI,KAAK,KAAK,QAAQ,SAAS,MAAM,GAAG;AACtC;AAAA,MACF;AAEA,WAAK,KAAK,QAAQ,KAAK,MAAM;AAE7B,YAAM,KAAK,iBAAiB,KAAK,IAAI;AAAA,IACvC;AAAA;AAAA,EAEc,eAAe,QAAgB,KAA+B;AAAA;AAC1E,YAAM,OAAO,MAAM,KAAK,mBAAmB,GAAG;AAC9C,aAAO,KAAK,KAAK,QAAQ,SAAS,MAAM;AAAA,IAC1C;AAAA;AAAA,EAEc,mBACZ,KACgD;AAAA;AAChD,YAAM,OAAO,MAAM,KAAK,QAAQ,IAAI,GAAG;AAEvC,UAAI,CAAC,MAAM;AACT,cAAMC,QAA8C;AAAA,UAClD,SAAS;AAAA,UACT,MAAM;AAAA,YACJ,SAAS,CAAC;AAAA,UACZ;AAAA,QACF;AACA,cAAM,KAAK,iBAAiB,KAAKA,KAAI;AACrC,eAAOA;AAAA,MACT;AAEA,YAAM,OAAO,gBAAe,OAAO,MAAM,KAAK,MAAM,IAAI,CAAC;AAEzD,aAAO;AAAA,IACT;AAAA;AAAA,EAEc,iBACZ,KACA,MACe;AAAA;AACf,YAAM,KAAK,QAAQ,IAAI,KAAK,KAAK,UAAU,IAAI,CAAC;AAAA,IAClD;AAAA;AAAA,EAEc,mBACZ,QAC8B;AAAA;AAC9B,UACE,MAAM,KAAK,eAAe,QAAQ,gBAAe,YAAY,UAAU,GACvE;AACA,eAAO;AAAA,MACT,WACE,MAAM,KAAK,eAAe,QAAQ,gBAAe,YAAY,SAAS,GACtE;AACA,eAAO;AAAA,MACT,WACE,MAAM,KAAK,eAAe,QAAQ,gBAAe,YAAY,SAAS,GACtE;AACA,eAAO;AAAA,MACT,OAAO;AACL,eAAO;AAAA,MACT;AAAA,IACF;AAAA;AACF;AA1aa,gBACJ,cAAc;AAAA,EACnB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,YAAY;AACd;AALW,gBAOa,0BACtB;AARS,gBASa,SAAS,EAAE,OAAO;AAAA,EACxC,SAAS,EAAE,QAAQ,CAAC;AAAA,EACpB,MAAM,EAAE,OAAO;AAAA,IACb,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EAC7B,CAAC;AACH,CAAC;AAdI,IAAM,iBAAN","sourcesContent":["const ContinueAtOwnRisk = \"CHAINPATROL_CONTINUE_AT_OWN_RISK\";\nconst IgnorelistUpdated = \"CHAINPATROL_IGNORELIST_UPDATED\";\nconst CloseCurrentTab = \"CHAINPATROL_CLOSE_CURRENT_TAB\";\n\nexport const Events = {\n ContinueAtOwnRisk,\n IgnorelistUpdated,\n CloseCurrentTab,\n} as const;\n\nexport type EventData = {\n [ContinueAtOwnRisk]: {\n domain: string;\n };\n [IgnorelistUpdated]: {\n domain: string;\n };\n [CloseCurrentTab]: {};\n};\n","export enum LogLevel {\n DEBUG,\n INFO,\n WARN,\n ERROR,\n NONE,\n}\n\nexport class Logger {\n private meta: Record<string, unknown> = {};\n private minLevel: LogLevel;\n\n constructor(\n meta?: Record<string, unknown>,\n minLevel: LogLevel = LogLevel.NONE\n ) {\n if (meta) {\n this.meta = meta;\n }\n this.minLevel = minLevel;\n }\n\n public with(fields: Record<string, unknown>) {\n return new Logger({ ...this.meta, ...fields }, this.minLevel);\n }\n\n public debug(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.DEBUG, message, fields);\n }\n\n public info(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.INFO, message, fields);\n }\n\n public warn(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.WARN, message, fields);\n }\n\n public error(message: string, fields?: Record<string, unknown>) {\n this.log(LogLevel.ERROR, message, fields);\n }\n\n private log(\n level: LogLevel,\n message: string,\n fields?: Record<string, unknown>\n ) {\n if (level < this.minLevel) {\n return; // Skip logging if log level is lower than minimum level\n }\n\n const logObj = { message, data: { ...fields }, ...this.meta };\n const logString = JSON.stringify(logObj, null, 2);\n console.log(`[${LogLevel[level].toUpperCase()}] ${logString}`);\n }\n}\n","import { EventData } from \"./events\";\nimport { Logger } from \"./logger\";\n\ntype Message<EventType extends keyof EventData> = {\n type: EventType;\n data: EventData[EventType];\n _meta: {\n id: string;\n origin: string;\n };\n};\n\nfunction getExtensionRuntime(): typeof chrome.runtime {\n if ((globalThis as any).browser?.runtime) {\n return (globalThis as any).browser.runtime;\n }\n if (globalThis.chrome?.runtime) {\n return globalThis.chrome.runtime;\n }\n throw new Error(\"No extension runtime found\");\n}\n\nfunction isExtensionHost() {\n return (\n !!(globalThis as any).browser?.runtime ||\n !!(globalThis as any).chrome?.runtime\n );\n}\n\nfunction isContentScript() {\n return isExtensionHost() && isBrowserHost();\n}\n\nfunction isBackgroundScript() {\n return isExtensionHost() && !isBrowserHost();\n}\n\nfunction isBrowserHost() {\n return !!(globalThis as any).window;\n}\n\ntype Handle = {\n addListener: (callback: (message: any) => void) => void;\n removeListener: (callback: (message: any) => void) => void;\n postMessage: (message: any) => void;\n};\n\nfunction getBackgroundScriptHandle(): Handle {\n const runtime = getExtensionRuntime();\n return {\n addListener: (callback: (message: any) => void) => {\n runtime.onMessage.addListener(callback);\n },\n removeListener: (callback: (message: any) => void) => {\n runtime.onMessage.removeListener(callback);\n },\n postMessage: async (message: any) => {\n const [tab] = await globalThis.chrome.tabs.query({\n active: true,\n lastFocusedWindow: true,\n });\n if (!tab.id || tab.id === globalThis.chrome.tabs.TAB_ID_NONE) {\n console.error(\"No active tab found\");\n return;\n }\n globalThis.chrome.tabs.sendMessage(tab.id, message);\n },\n };\n}\n\nfunction getContentScriptHandle(): Handle {\n const runtime = getExtensionRuntime();\n return {\n addListener: (callback: (message: any) => void) => {\n runtime.onMessage.addListener(callback);\n },\n removeListener: (callback: (message: any) => void) => {\n runtime.onMessage.removeListener(callback);\n },\n postMessage: async (message: any) => {\n runtime.sendMessage(message).catch((error) => {\n console.error(\"Failed to send message\", { message, error });\n });\n },\n };\n}\n\nfunction getBrowserHandle(): Handle {\n return {\n addListener: (callback: (message: any) => void) => {\n globalThis.window.addEventListener(\"message\", (event) => {\n if (event.source !== globalThis.window) {\n return;\n }\n callback(event.data);\n });\n },\n removeListener: (callback: (message: any) => void) => {\n globalThis.window.removeEventListener(\"message\", callback);\n },\n postMessage: (message: any) => {\n globalThis.window.postMessage(message, \"*\");\n },\n };\n}\n\n/**\n * Relay contains methods for implementing an event relay. It is used to\n * forward events between Javascript contexts:\n *\n * `<window>` <-> `<content script>` <-> `<background script>`\n *\n * Call `Relay.send` to send an event from one context to another. Call\n * `Relay.on` to listen for events in the current context. Call `Relay.run`\n * to start listening for events in the current context and forward them\n * to all other contexts.\n */\nexport class Relay {\n protected static handles: Handle[] = [];\n private static readonly logger = new Logger({ component: \"Relay\" });\n\n static {\n if (isBackgroundScript()) {\n Relay.logger.info(\"Detected background script\");\n Relay.handles.push(getBackgroundScriptHandle());\n } else if (isContentScript()) {\n Relay.logger.info(\"Detected content script\");\n Relay.handles.push(getContentScriptHandle());\n Relay.handles.push(getBrowserHandle());\n } else if (isBrowserHost()) {\n Relay.logger.info(\"Detected browser host\");\n Relay.handles.push(getBrowserHandle());\n }\n }\n\n public static send<EventType extends keyof EventData>(\n type: EventType,\n data: EventData[EventType]\n ) {\n const message = {\n type,\n data,\n _meta: {\n id: Math.random().toString(36).substring(2, 9),\n origin: globalThis.location.origin,\n },\n } satisfies Message<EventType>;\n\n Relay.logger.debug(\"Sending message\", message);\n\n for (const handle of Relay.handles) {\n handle.postMessage(message);\n }\n }\n\n public static run(events: (keyof EventData)[]) {\n const listeners = new Set<(message: any) => void>();\n\n for (const handle of Relay.handles) {\n const listener = Relay.handleMessage.bind(null, handle, events);\n listeners.add(listener);\n handle.addListener(listener);\n }\n\n Relay.logger.debug(\"Started relay\", { events });\n\n return () => {\n for (const handle of Relay.handles) {\n for (const listener of listeners) {\n handle.removeListener(listener);\n }\n }\n\n Relay.logger.debug(\"Stopped relay\", { events });\n };\n }\n\n private static handleMessage(\n sourceHandle: Handle,\n events: (keyof EventData)[],\n message: any\n ) {\n if (!events.includes(message.type)) {\n Relay.logger.debug(\"Ignoring message\", { message });\n return;\n }\n\n const destinationHandles = Relay.handles.filter(\n (handle) => handle !== sourceHandle\n );\n\n for (const destinationHandle of destinationHandles) {\n destinationHandle.postMessage(message);\n }\n }\n\n public static on<EventType extends keyof EventData>(\n targetEvent: EventType,\n callback: (data: EventData[EventType]) => void\n ) {\n const listeners = new Set<(message: any) => void>();\n for (const handle of Relay.handles) {\n const listener = (message: any) => {\n if (message.type !== targetEvent) {\n Relay.logger.debug(\"Ignoring message\", { message });\n return;\n }\n callback(message.data);\n };\n listeners.add(listener);\n handle.addListener(listener);\n }\n\n return () => {\n for (const handle of Relay.handles) {\n for (const listener of listeners) {\n handle.removeListener(listener);\n }\n }\n };\n }\n}\n","import { parse as parseDomain } from \"tldts\";\nimport normalizeUrl from \"normalize-url\";\nimport { z } from \"zod\";\n\nimport { AssetStatus, ChainPatrolClient } from \"./client\";\nimport { Memory } from \"./storage\";\nimport type { Storage } from \"./storage/types\";\nimport { Logger } from \"./logger\";\n\ntype Prettify<T> = T extends infer U ? (U extends string ? U : never) : never;\n\nexport type DetectorAssetStatus = Prettify<AssetStatus | \"IGNORED\">;\n\ntype Mode = \"cloud\" | \"local\";\n\n// Branded type for domain strings\ntype Domain = string & { __domain: never };\n\nexport type URLResult =\n | {\n ok: true;\n status: DetectorAssetStatus;\n url: string;\n redirectUrl?: string;\n }\n | {\n ok: false;\n url: string;\n error: string;\n };\n\nexport class DomainParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"DomainParseError\";\n }\n}\n\nexport class ThreatDetector {\n static StorageKeys = {\n ALLOWLIST: \"chainpatrol.allowed\",\n BLOCKLIST: \"chainpatrol.blocked\",\n IGNORELIST: \"chainpatrol.ignored\",\n };\n\n private static readonly CHAINPATROL_WARNING_URL =\n \"https://app.chainpatrol.io/warning\";\n private static readonly Schema = z.object({\n version: z.literal(1),\n data: z.object({\n domains: z.array(z.string()),\n }),\n });\n\n private mode: Mode;\n private client?: ChainPatrolClient;\n private storage: Storage;\n private redirectUrl?: string | ((url: string) => string);\n private readonly logger = new Logger({ component: \"ThreatDetector\" });\n\n constructor({\n mode,\n storage,\n redirectUrl,\n }: {\n mode: \"local\";\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n });\n\n constructor({\n mode,\n apiKey,\n storage,\n redirectUrl,\n }: {\n mode: \"cloud\";\n apiKey: string;\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n });\n\n constructor({\n mode,\n apiKey,\n proxyUrl,\n storage,\n redirectUrl,\n }: {\n mode: \"cloud\";\n apiKey: string;\n proxyUrl?: string;\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n });\n\n constructor({\n mode = \"cloud\",\n apiKey = \"\",\n storage = Memory(),\n proxyUrl,\n redirectUrl,\n }: {\n mode?: Mode;\n apiKey?: string;\n proxyUrl?: string;\n storage?: Storage;\n redirectUrl?: string | ((url: string) => string);\n }) {\n this.mode = mode;\n this.storage = storage;\n this.redirectUrl = redirectUrl ?? ThreatDetector.CHAINPATROL_WARNING_URL;\n if (mode === \"cloud\") {\n this.client = new ChainPatrolClient({\n apiKey: apiKey,\n baseUrl: proxyUrl,\n });\n }\n }\n\n public async url(url: string): Promise<URLResult> {\n this.logger.debug(\"Checking URL\", { url });\n\n let domains: Domain[];\n try {\n domains = this.generateDomains(url);\n } catch (e) {\n this.logger.error(\"Unable to parse domain\", { url, error: e });\n return {\n ok: false,\n url,\n error:\n e instanceof DomainParseError ? e.message : \"Unable to parse domain\",\n };\n }\n\n this.logger.debug(\"Generated domains\", { domains });\n\n let results = (\n await Promise.all(domains.map((domain) => this.urlHelper(domain, url)))\n ).filter(\n <T extends URLResult>(r: T): r is T extends { ok: true } ? T : never =>\n r.ok\n );\n\n if (results.length === 0) {\n return {\n ok: false,\n url,\n error: \"URL does not have a valid domain\",\n };\n }\n\n this.logger.debug(\"Results for domains\", { results });\n\n if (results.some((r) => r.status === \"IGNORED\")) {\n return {\n ok: true,\n status: \"IGNORED\",\n url,\n };\n }\n\n for (const result of results) {\n if (result.ok && result.status !== \"UNKNOWN\") {\n return result;\n }\n }\n\n return results[0];\n }\n\n private generateDomains(_url: string): Domain[] {\n const domain = this.parseDomainOrThrow(_url);\n\n const domains = [domain];\n\n const parsedDomain = parseDomain(domain);\n if (!parsedDomain.subdomain) {\n return domains;\n }\n\n const subdomainParts = parsedDomain.subdomain?.split(\".\") ?? [];\n\n for (let i = 0; i < subdomainParts.length; i++) {\n const subdomain = subdomainParts.slice(i).join(\".\");\n domains.unshift(`${subdomain}.${domain}` as Domain);\n }\n\n return domains;\n }\n\n private async urlHelper(domain: Domain, url: string): Promise<URLResult> {\n let status = await this.getStatusFromCache(domain);\n\n if (this.mode === \"cloud\" && this.client && status === \"UNKNOWN\") {\n try {\n const res = await this.client.asset.check({\n type: \"URL\",\n content: domain,\n });\n\n // Update cache storage\n this.logger.debug(\"Updating cache\", { domain, status: res.status });\n if (res.status === \"ALLOWED\") {\n this.addDomainToCache(domain, ThreatDetector.StorageKeys.ALLOWLIST);\n } else if (res.status === \"BLOCKED\") {\n this.addDomainToCache(domain, ThreatDetector.StorageKeys.BLOCKLIST);\n }\n\n status = res.status;\n } catch (e) {\n return {\n ok: false,\n url: domain,\n error: \"Unable to check URL\",\n };\n }\n }\n\n let redirectUrl: string | undefined;\n\n if (status === \"BLOCKED\") {\n if (typeof this.redirectUrl === \"function\") {\n redirectUrl = this.redirectUrl(url);\n } else if (typeof this.redirectUrl === \"string\") {\n const newUrl = new URL(this.redirectUrl);\n newUrl.searchParams.set(\"originUrl\", url);\n redirectUrl = newUrl.toString();\n }\n }\n\n return {\n ok: true,\n status,\n url: domain,\n redirectUrl,\n };\n }\n\n public async allow(\n url: string\n ): Promise<\n { ok: true; url: string } | { ok: false; url: string; error: string }\n > {\n try {\n const domain = this.parseDomainOrThrow(url);\n\n this.logger.debug(\"Allowing URL\", { url, domain });\n\n await this.invalidateDomainInCaches(domain);\n await this.addDomainToCache(domain, ThreatDetector.StorageKeys.ALLOWLIST);\n\n return {\n ok: true,\n url: domain,\n };\n } catch (e) {\n this.logger.error(\"Unable to allow URL\", { url, error: e });\n return {\n ok: false,\n url,\n error: \"Unable to allow URL\",\n };\n }\n }\n\n public async block(\n url: string\n ): Promise<\n { ok: true; url: string } | { ok: false; url: string; error: string }\n > {\n try {\n const domain = this.parseDomainOrThrow(url);\n\n this.logger.debug(\"Blocking URL\", { url, domain });\n\n await this.invalidateDomainInCaches(domain);\n await this.addDomainToCache(domain, ThreatDetector.StorageKeys.BLOCKLIST);\n\n return {\n ok: true,\n url: domain,\n };\n } catch (e) {\n this.logger.error(\"Unable to block URL\", { url, error: e });\n return {\n ok: false,\n url,\n error: \"Unable to block URL\",\n };\n }\n }\n\n public async ignore(\n url: string\n ): Promise<\n { ok: true; url: string } | { ok: false; url: string; error: string }\n > {\n try {\n const domain = this.parseDomainOrThrow(url);\n\n this.logger.debug(\"Ignoring URL\", { url, domain });\n\n await this.addDomainToCache(\n domain,\n ThreatDetector.StorageKeys.IGNORELIST\n );\n\n return {\n ok: true,\n url: domain,\n };\n } catch (e) {\n this.logger.error(\"Unable to ignore URL\", { url, error: e });\n return {\n ok: false,\n url,\n error: \"Unable to ignore URL\",\n };\n }\n }\n\n private parseDomainOrThrow(url: string): Domain {\n this.logger.debug(\"Parsing domain\", { url });\n try {\n const normalizedUrl = normalizeUrl(url, {\n stripWWW: false,\n removeTrailingSlash: false,\n });\n\n this.logger.debug(\"Normalized URL\", { from: url, to: normalizedUrl });\n\n const parsedURL = new URL(normalizedUrl);\n\n this.logger.debug(\"Extract domain from URL\", { url: parsedURL.hostname });\n\n const parsedSubdomainResult = parseDomain(parsedURL.hostname);\n\n this.logger.debug(\"Parsed domain\", { parsedSubdomainResult });\n\n if (parsedSubdomainResult.hostname === \"localhost\") {\n throw new DomainParseError(\"ThreatDetector does not support localhost\");\n }\n\n if (parsedSubdomainResult.isIp) {\n throw new DomainParseError(\n \"ThreatDetector does not support IP addresses\"\n );\n }\n\n if (!parsedSubdomainResult.domain) {\n throw new DomainParseError(\"Unable to parse domain\");\n }\n\n const domain = parsedSubdomainResult.domain as Domain;\n\n return domain;\n } catch (e) {\n if (e instanceof DomainParseError) {\n throw e;\n } else {\n this.logger.error(\"Unable to parse domain\", { url, error: e });\n throw new DomainParseError(\"Unable to parse domain\");\n }\n }\n }\n\n private async invalidateDomainInCaches(domain: Domain): Promise<void> {\n await this.invalidateDomainInCache(\n domain,\n ThreatDetector.StorageKeys.ALLOWLIST\n );\n await this.invalidateDomainInCache(\n domain,\n ThreatDetector.StorageKeys.BLOCKLIST\n );\n }\n\n private async invalidateDomainInCache(\n domain: Domain,\n key: string\n ): Promise<void> {\n const data = await this.storage.get(key);\n\n if (!data) {\n return;\n }\n\n const list = await this.getListFromStorage(key);\n\n if (!list.data.domains.includes(domain)) {\n return;\n }\n\n list.data.domains = list.data.domains.filter((u) => u !== domain);\n\n await this.setListInStorage(key, list);\n }\n\n private async addDomainToCache(domain: Domain, key: string): Promise<void> {\n const list = await this.getListFromStorage(key);\n\n if (list.data.domains.includes(domain)) {\n return;\n }\n\n list.data.domains.push(domain);\n\n await this.setListInStorage(key, list);\n }\n\n private async isDomainInList(domain: Domain, key: string): Promise<boolean> {\n const list = await this.getListFromStorage(key);\n return list.data.domains.includes(domain);\n }\n\n private async getListFromStorage(\n key: string\n ): Promise<z.infer<typeof ThreatDetector.Schema>> {\n const data = await this.storage.get(key);\n\n if (!data) {\n const list: z.infer<typeof ThreatDetector.Schema> = {\n version: 1,\n data: {\n domains: [],\n },\n };\n await this.setListInStorage(key, list);\n return list;\n }\n\n const list = ThreatDetector.Schema.parse(JSON.parse(data));\n\n return list;\n }\n\n private async setListInStorage(\n key: string,\n list: z.infer<typeof ThreatDetector.Schema>\n ): Promise<void> {\n await this.storage.set(key, JSON.stringify(list));\n }\n\n private async getStatusFromCache(\n domain: Domain\n ): Promise<DetectorAssetStatus> {\n if (\n await this.isDomainInList(domain, ThreatDetector.StorageKeys.IGNORELIST)\n ) {\n return \"IGNORED\";\n } else if (\n await this.isDomainInList(domain, ThreatDetector.StorageKeys.BLOCKLIST)\n ) {\n return \"BLOCKED\";\n } else if (\n await this.isDomainInList(domain, ThreatDetector.StorageKeys.ALLOWLIST)\n ) {\n return \"ALLOWED\";\n } else {\n return \"UNKNOWN\";\n }\n }\n}\n","// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs\nconst DATA_URL_DEFAULT_MIME_TYPE = 'text/plain';\nconst DATA_URL_DEFAULT_CHARSET = 'us-ascii';\n\nconst testParameter = (name, filters) => filters.some(filter => filter instanceof RegExp ? filter.test(name) : filter === name);\n\nconst supportedProtocols = new Set([\n\t'https:',\n\t'http:',\n\t'file:',\n]);\n\nconst hasCustomProtocol = urlString => {\n\ttry {\n\t\tconst {protocol} = new URL(urlString);\n\t\treturn protocol.endsWith(':') && !supportedProtocols.has(protocol);\n\t} catch {\n\t\treturn false;\n\t}\n};\n\nconst normalizeDataURL = (urlString, {stripHash}) => {\n\tconst match = /^data:(?<type>[^,]*?),(?<data>[^#]*?)(?:#(?<hash>.*))?$/.exec(urlString);\n\n\tif (!match) {\n\t\tthrow new Error(`Invalid URL: ${urlString}`);\n\t}\n\n\tlet {type, data, hash} = match.groups;\n\tconst mediaType = type.split(';');\n\thash = stripHash ? '' : hash;\n\n\tlet isBase64 = false;\n\tif (mediaType[mediaType.length - 1] === 'base64') {\n\t\tmediaType.pop();\n\t\tisBase64 = true;\n\t}\n\n\t// Lowercase MIME type\n\tconst mimeType = mediaType.shift()?.toLowerCase() ?? '';\n\tconst attributes = mediaType\n\t\t.map(attribute => {\n\t\t\tlet [key, value = ''] = attribute.split('=').map(string => string.trim());\n\n\t\t\t// Lowercase `charset`\n\t\t\tif (key === 'charset') {\n\t\t\t\tvalue = value.toLowerCase();\n\n\t\t\t\tif (value === DATA_URL_DEFAULT_CHARSET) {\n\t\t\t\t\treturn '';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn `${key}${value ? `=${value}` : ''}`;\n\t\t})\n\t\t.filter(Boolean);\n\n\tconst normalizedMediaType = [\n\t\t...attributes,\n\t];\n\n\tif (isBase64) {\n\t\tnormalizedMediaType.push('base64');\n\t}\n\n\tif (normalizedMediaType.length > 0 || (mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE)) {\n\t\tnormalizedMediaType.unshift(mimeType);\n\t}\n\n\treturn `data:${normalizedMediaType.join(';')},${isBase64 ? data.trim() : data}${hash ? `#${hash}` : ''}`;\n};\n\nexport default function normalizeUrl(urlString, options) {\n\toptions = {\n\t\tdefaultProtocol: 'http',\n\t\tnormalizeProtocol: true,\n\t\tforceHttp: false,\n\t\tforceHttps: false,\n\t\tstripAuthentication: true,\n\t\tstripHash: false,\n\t\tstripTextFragment: true,\n\t\tstripWWW: true,\n\t\tremoveQueryParameters: [/^utm_\\w+/i],\n\t\tremoveTrailingSlash: true,\n\t\tremoveSingleSlash: true,\n\t\tremoveDirectoryIndex: false,\n\t\tremoveExplicitPort: false,\n\t\tsortQueryParameters: true,\n\t\t...options,\n\t};\n\n\t// Legacy: Append `:` to the protocol if missing.\n\tif (typeof options.defaultProtocol === 'string' && !options.defaultProtocol.endsWith(':')) {\n\t\toptions.defaultProtocol = `${options.defaultProtocol}:`;\n\t}\n\n\turlString = urlString.trim();\n\n\t// Data URL\n\tif (/^data:/i.test(urlString)) {\n\t\treturn normalizeDataURL(urlString, options);\n\t}\n\n\tif (hasCustomProtocol(urlString)) {\n\t\treturn urlString;\n\t}\n\n\tconst hasRelativeProtocol = urlString.startsWith('//');\n\tconst isRelativeUrl = !hasRelativeProtocol && /^\\.*\\//.test(urlString);\n\n\t// Prepend protocol\n\tif (!isRelativeUrl) {\n\t\turlString = urlString.replace(/^(?!(?:\\w+:)?\\/\\/)|^\\/\\//, options.defaultProtocol);\n\t}\n\n\tconst urlObject = new URL(urlString);\n\n\tif (options.forceHttp && options.forceHttps) {\n\t\tthrow new Error('The `forceHttp` and `forceHttps` options cannot be used together');\n\t}\n\n\tif (options.forceHttp && urlObject.protocol === 'https:') {\n\t\turlObject.protocol = 'http:';\n\t}\n\n\tif (options.forceHttps && urlObject.protocol === 'http:') {\n\t\turlObject.protocol = 'https:';\n\t}\n\n\t// Remove auth\n\tif (options.stripAuthentication) {\n\t\turlObject.username = '';\n\t\turlObject.password = '';\n\t}\n\n\t// Remove hash\n\tif (options.stripHash) {\n\t\turlObject.hash = '';\n\t} else if (options.stripTextFragment) {\n\t\turlObject.hash = urlObject.hash.replace(/#?:~:text.*?$/i, '');\n\t}\n\n\t// Remove duplicate slashes if not preceded by a protocol\n\t// NOTE: This could be implemented using a single negative lookbehind\n\t// regex, but we avoid that to maintain compatibility with older js engines\n\t// which do not have support for that feature.\n\tif (urlObject.pathname) {\n\t\t// TODO: Replace everything below with `urlObject.pathname = urlObject.pathname.replace(/(?<!\\b[a-z][a-z\\d+\\-.]{1,50}:)\\/{2,}/g, '/');` when Safari supports negative lookbehind.\n\n\t\t// Split the string by occurrences of this protocol regex, and perform\n\t\t// duplicate-slash replacement on the strings between those occurrences\n\t\t// (if any).\n\t\tconst protocolRegex = /\\b[a-z][a-z\\d+\\-.]{1,50}:\\/\\//g;\n\n\t\tlet lastIndex = 0;\n\t\tlet result = '';\n\t\tfor (;;) {\n\t\t\tconst match = protocolRegex.exec(urlObject.pathname);\n\t\t\tif (!match) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst protocol = match[0];\n\t\t\tconst protocolAtIndex = match.index;\n\t\t\tconst intermediate = urlObject.pathname.slice(lastIndex, protocolAtIndex);\n\n\t\t\tresult += intermediate.replace(/\\/{2,}/g, '/');\n\t\t\tresult += protocol;\n\t\t\tlastIndex = protocolAtIndex + protocol.length;\n\t\t}\n\n\t\tconst remnant = urlObject.pathname.slice(lastIndex, urlObject.pathname.length);\n\t\tresult += remnant.replace(/\\/{2,}/g, '/');\n\n\t\turlObject.pathname = result;\n\t}\n\n\t// Decode URI octets\n\tif (urlObject.pathname) {\n\t\ttry {\n\t\t\turlObject.pathname = decodeURI(urlObject.pathname);\n\t\t} catch {}\n\t}\n\n\t// Remove directory index\n\tif (options.removeDirectoryIndex === true) {\n\t\toptions.removeDirectoryIndex = [/^index\\.[a-z]+$/];\n\t}\n\n\tif (Array.isArray(options.removeDirectoryIndex) && options.removeDirectoryIndex.length > 0) {\n\t\tlet pathComponents = urlObject.pathname.split('/');\n\t\tconst lastComponent = pathComponents[pathComponents.length - 1];\n\n\t\tif (testParameter(lastComponent, options.removeDirectoryIndex)) {\n\t\t\tpathComponents = pathComponents.slice(0, -1);\n\t\t\turlObject.pathname = pathComponents.slice(1).join('/') + '/';\n\t\t}\n\t}\n\n\tif (urlObject.hostname) {\n\t\t// Remove trailing dot\n\t\turlObject.hostname = urlObject.hostname.replace(/\\.$/, '');\n\n\t\t// Remove `www.`\n\t\tif (options.stripWWW && /^www\\.(?!www\\.)[a-z\\-\\d]{1,63}\\.[a-z.\\-\\d]{2,63}$/.test(urlObject.hostname)) {\n\t\t\t// Each label should be max 63 at length (min: 1).\n\t\t\t// Source: https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names\n\t\t\t// Each TLD should be up to 63 characters long (min: 2).\n\t\t\t// It is technically possible to have a single character TLD, but none currently exist.\n\t\t\turlObject.hostname = urlObject.hostname.replace(/^www\\./, '');\n\t\t}\n\t}\n\n\t// Remove query unwanted parameters\n\tif (Array.isArray(options.removeQueryParameters)) {\n\t\t// eslint-disable-next-line unicorn/no-useless-spread -- We are intentionally spreading to get a copy.\n\t\tfor (const key of [...urlObject.searchParams.keys()]) {\n\t\t\tif (testParameter(key, options.removeQueryParameters)) {\n\t\t\t\turlObject.searchParams.delete(key);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!Array.isArray(options.keepQueryParameters) && options.removeQueryParameters === true) {\n\t\turlObject.search = '';\n\t}\n\n\t// Keep wanted query parameters\n\tif (Array.isArray(options.keepQueryParameters) && options.keepQueryParameters.length > 0) {\n\t\t// eslint-disable-next-line unicorn/no-useless-spread -- We are intentionally spreading to get a copy.\n\t\tfor (const key of [...urlObject.searchParams.keys()]) {\n\t\t\tif (!testParameter(key, options.keepQueryParameters)) {\n\t\t\t\turlObject.searchParams.delete(key);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sort query parameters\n\tif (options.sortQueryParameters) {\n\t\turlObject.searchParams.sort();\n\n\t\t// Calling `.sort()` encodes the search parameters, so we need to decode them again.\n\t\ttry {\n\t\t\turlObject.search = decodeURIComponent(urlObject.search);\n\t\t} catch {}\n\t}\n\n\tif (options.removeTrailingSlash) {\n\t\turlObject.pathname = urlObject.pathname.replace(/\\/$/, '');\n\t}\n\n\t// Remove an explicit port number, excluding a default port number, if applicable\n\tif (options.removeExplicitPort && urlObject.port) {\n\t\turlObject.port = '';\n\t}\n\n\tconst oldUrlString = urlString;\n\n\t// Take advantage of many of the Node `url` normalizations\n\turlString = urlObject.toString();\n\n\tif (!options.removeSingleSlash && urlObject.pathname === '/' && !oldUrlString.endsWith('/') && urlObject.hash === '') {\n\t\turlString = urlString.replace(/\\/$/, '');\n\t}\n\n\t// Remove ending `/` unless removeSingleSlash is false\n\tif ((options.removeTrailingSlash || urlObject.pathname === '/') && urlObject.hash === '' && options.removeSingleSlash) {\n\t\turlString = urlString.replace(/\\/$/, '');\n\t}\n\n\t// Restore relative protocol, if applicable\n\tif (hasRelativeProtocol && !options.normalizeProtocol) {\n\t\turlString = urlString.replace(/^http:\\/\\//, '//');\n\t}\n\n\t// Remove http/https\n\tif (options.stripProtocol) {\n\t\turlString = urlString.replace(/^(?:https?:)?\\/\\//, '');\n\t}\n\n\treturn urlString;\n}\n","import { Logger } from \"./logger\";\n\nexport type ChainPatrolClientOptions = {\n apiKey: string;\n baseUrl?: string;\n};\n\ntype ApiRequest = {\n path: string[];\n method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n body?: unknown;\n};\n\nexport type AssetType = \"URL\" | \"PAGE\" | \"ADDRESS\";\nexport type AssetStatus = \"ALLOWED\" | \"BLOCKED\" | \"UNKNOWN\";\n\nfunction trimTrailingSlashes(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\nexport class ChainPatrolClient {\n public readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly logger = new Logger({ component: \"ChainPatrolClient\" });\n\n constructor(options: ChainPatrolClientOptions) {\n this.baseUrl = options.baseUrl ?? \"https://app.chainpatrol.io/api/\";\n if (!options.apiKey) {\n throw new Error(\"ChainPatrol API key is required\");\n }\n this.apiKey = options.apiKey;\n }\n\n private async fetch<TResult = unknown>(req: ApiRequest): Promise<TResult> {\n const url = `${trimTrailingSlashes(this.baseUrl)}/${req.path.join(\"/\")}`;\n this.logger.debug(\"fetch\", { url, req });\n const res = await fetch(url, {\n method: req.method,\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Api-Key\": this.apiKey,\n },\n body: JSON.stringify(req.body),\n });\n if (!res.ok) {\n throw new Error(await res.text());\n }\n return res.json();\n }\n\n public get asset() {\n return {\n check: async (req: {\n type: AssetType;\n content: string;\n }): Promise<{\n /**\n * \"ALLOWED\" - the asset is on the allowlist\n * \"BLOCKED\" - the asset is on the blocklist\n * \"UNKNOWN\" - the asset's status is not known\n */\n status: AssetStatus;\n /**\n * If the asset is allowed or blocked, this will be the reason why.\n * ChainPatrol aggregates data from multiple sources, so this will\n * tell you which source blocked or allowed the asset.\n *\n * ex. 'eth-phishing-detect' - the asset is on MetaMask's blocklist\n * 'reported' - the asset is on ChainPatrol's blocklist because\n * it was reported by a user\n */\n reason?: string;\n }> => {\n return await this.fetch<{\n status: AssetStatus;\n reason?: string;\n }>({\n path: [\"v2\", \"asset\", \"check\"],\n method: \"POST\",\n body: req,\n });\n },\n\n list: async (req: {\n /**\n * Asset type\n */\n type: AssetType;\n /**\n * Status of the assets to retrieve\n */\n status: AssetStatus;\n /**\n * The start date to list assets from. This should be in the format `YYYY-MM-DD` and is inclusive.\n */\n startDate?: string;\n /**\n * The end date to list assets from. This should be in the format `YYYY-MM-DD` and is inclusive.\n */\n endDate?: string;\n }): Promise<\n {\n content: string;\n type: AssetType;\n status: AssetStatus;\n }[]\n > => {\n return await this.fetch<\n {\n content: string;\n type: AssetType;\n status: AssetStatus;\n }[]\n >({\n path: [\"v2\", \"asset\", \"list\"],\n method: \"GET\",\n body: req,\n });\n },\n };\n }\n}\n","export * from \"./extension\";\nexport * from \"./browser\";\nexport * from \"./memory\";\nexport { defineStorage } from \"./define-storage\";\nexport * from \"./types\";\n","import { ThreatDetector } from \"../detector\";\nimport { Storage } from \"./types\";\n\nexport interface Context {\n keys: string[];\n}\n\nexport function defineStorage<T extends Storage>(\n config: (ctx: Context) => T\n): () => T {\n return () =>\n config({\n keys: Object.values(ThreatDetector.StorageKeys),\n });\n}\n","import { defineStorage } from \"./define-storage\";\n\nexport const Extension = defineStorage(({ keys }) => {\n return {\n get: async (key: string) => {\n const result = await chrome.storage.local.get(key);\n return result[key];\n },\n set: async (key: string, value: string) => {\n await chrome.storage.local.set({ [key]: value });\n },\n delete: async (key: string) => {\n await chrome.storage.local.remove(key);\n },\n size: async () => {\n const usageBytes = await chrome.storage.local.getBytesInUse(keys);\n return usageBytes;\n },\n };\n});\n","import { defineStorage } from \"./define-storage\";\n\nfunction isStorageAvailable(type: \"localStorage\" | \"sessionStorage\") {\n let storage;\n try {\n storage = window[type];\n const x = \"__storage_test__\";\n storage.setItem(x, x);\n storage.removeItem(x);\n return true;\n } catch (e) {\n return (\n e instanceof DOMException &&\n // everything except Firefox\n (e.code === 22 ||\n // Firefox\n e.code === 1014 ||\n // test name field too, because code might not be present\n // everything except Firefox\n e.name === \"QuotaExceededError\" ||\n // Firefox\n e.name === \"NS_ERROR_DOM_QUOTA_REACHED\") &&\n // acknowledge QuotaExceededError only if there's something already stored\n storage &&\n storage.length !== 0\n );\n }\n}\n\nexport const Browser = defineStorage(({ keys }) => {\n if (!isStorageAvailable(\"localStorage\")) {\n throw new Error(\"localStorage is not available\");\n }\n\n return {\n get: async (key: string) => {\n return localStorage.getItem(key);\n },\n set: async (key: string, value: string) => {\n localStorage.setItem(key, value);\n },\n delete: async (key: string) => {\n localStorage.removeItem(key);\n },\n size: async () => {\n let total = 0;\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key && keys.includes(key)) {\n total += localStorage.getItem(key)?.length ?? 0;\n }\n }\n return total;\n },\n };\n});\n","import { defineStorage } from \"./define-storage\";\n\nexport const Memory = defineStorage(() => {\n const storage = new Map<string, string>();\n return {\n get: async (key: string) => {\n return storage.get(key) || null;\n },\n set: async (key: string, value: string) => {\n storage.set(key, value);\n },\n delete: async (key: string) => {\n storage.delete(key);\n },\n size: async () => {\n let total = 0;\n for (const value of storage.values()) {\n total += value.length;\n }\n return total;\n },\n };\n});\n"]}
package/package.json CHANGED
@@ -2,11 +2,18 @@
2
2
  "name": "@chainpatrol/sdk",
3
3
  "description": "ChainPatrol SDK",
4
4
  "author": "ChainPatrol <support@chainpatrol.io>",
5
- "version": "0.1.2",
5
+ "version": "0.1.3",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
8
8
  "types": "./dist/index.d.ts",
9
9
  "sideEffects": false,
10
+ "exports": {
11
+ ".": {
12
+ "require": "./dist/index.js",
13
+ "import": "./dist/index.mjs",
14
+ "types": "./dist/index.d.ts"
15
+ }
16
+ },
10
17
  "files": [
11
18
  "dist/**"
12
19
  ],
@@ -18,12 +25,17 @@
18
25
  "clean": "rimraf .turbo node_modules dist",
19
26
  "dev": "tsup --watch"
20
27
  },
21
- "devDependencies": {
22
- "@types/chrome": "^0.0.238",
23
- "normalize-url": "^8.0.0"
24
- },
25
28
  "dependencies": {
26
29
  "tldts": "^6.0.8",
30
+ "tsup": "^7.0.0",
31
+ "typescript": "^5.1.3",
27
32
  "zod": "^3.21.4"
33
+ },
34
+ "devDependencies": {
35
+ "@chainpatrol/tsconfig": "workspace:*",
36
+ "@types/chrome": "^0.0.238",
37
+ "normalize-url": "^8.0.0",
38
+ "rimraf": "^5.0.1",
39
+ "tsup": "^7.0.0"
28
40
  }
29
41
  }