@socketsecurity/cli-with-sentry 0.14.52 → 0.14.53
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/bin/cli.js +10 -15
- package/dist/constants.d.ts +6 -6
- package/dist/constants.js +2 -2
- package/dist/constants.js.map +1 -1
- package/dist/instrument-with-sentry.js +2 -2
- package/dist/instrument-with-sentry.js.map +1 -1
- package/dist/module-sync/cli.js +81 -53
- package/dist/module-sync/cli.js.map +1 -1
- package/dist/module-sync/edge.d.ts +1 -1
- package/dist/module-sync/index.d.ts +22 -22
- package/dist/module-sync/index.js.map +1 -1
- package/dist/module-sync/npm-paths.js.map +1 -1
- package/dist/module-sync/reify.d.ts +8 -8
- package/dist/module-sync/settings.d.ts +1 -1
- package/dist/module-sync/shadow-bin.js +5 -10
- package/dist/module-sync/shadow-bin.js.map +1 -1
- package/dist/require/cli.js +81 -53
- package/dist/require/cli.js.map +1 -1
- package/package.json +2 -3
- package/dist/module-sync/npm.d.ts +0 -26
- package/dist/module-sync/npm.js +0 -114
- package/dist/module-sync/npm.js.map +0 -1
- package/dist/require/npm.js +0 -3
|
@@ -50,15 +50,15 @@ declare class SafeOverrideSet extends OverrideSet {
|
|
|
50
50
|
}
|
|
51
51
|
declare const depValid: (child: SafeNode, requested: string, accept: string | undefined, requester: SafeNode) => boolean;
|
|
52
52
|
declare function getSocketDevAlertUrl(alertType: string): string;
|
|
53
|
-
declare function getSocketDevPackageOverviewUrl(eco: string, name: string, version?: string): string;
|
|
53
|
+
declare function getSocketDevPackageOverviewUrl(eco: string, name: string, version?: string | undefined): string;
|
|
54
54
|
declare class ColorOrMarkdown {
|
|
55
55
|
useMarkdown: boolean;
|
|
56
56
|
constructor(useMarkdown: boolean);
|
|
57
57
|
bold(text: string): string;
|
|
58
58
|
header(text: string, level?: number): string;
|
|
59
59
|
hyperlink(text: string, url: string | undefined, { fallback, fallbackToUrl }?: {
|
|
60
|
-
fallback?: boolean;
|
|
61
|
-
fallbackToUrl?: boolean;
|
|
60
|
+
fallback?: boolean | undefined;
|
|
61
|
+
fallbackToUrl?: boolean | undefined;
|
|
62
62
|
}): string;
|
|
63
63
|
indent(...args: Parameters<typeof indentString>): ReturnType<typeof indentString>;
|
|
64
64
|
italic(text: string): string;
|
|
@@ -103,23 +103,23 @@ type SocketArtifactAlert = {
|
|
|
103
103
|
type: string;
|
|
104
104
|
severity: string;
|
|
105
105
|
category: string;
|
|
106
|
-
action?: string;
|
|
107
|
-
actionPolicyIndex?: number;
|
|
108
|
-
file?: string;
|
|
109
|
-
props?: any;
|
|
110
|
-
start?: number;
|
|
111
|
-
end?: number;
|
|
106
|
+
action?: string | undefined;
|
|
107
|
+
actionPolicyIndex?: number | undefined;
|
|
108
|
+
file?: string | undefined;
|
|
109
|
+
props?: any | undefined;
|
|
110
|
+
start?: number | undefined;
|
|
111
|
+
end?: number | undefined;
|
|
112
112
|
};
|
|
113
113
|
type SocketArtifact = {
|
|
114
114
|
type: string;
|
|
115
115
|
name: string;
|
|
116
|
-
namespace?: string;
|
|
117
|
-
version?: string;
|
|
118
|
-
subpath?: string;
|
|
119
|
-
release?: string;
|
|
120
|
-
id?: string;
|
|
116
|
+
namespace?: string | undefined;
|
|
117
|
+
version?: string | undefined;
|
|
118
|
+
subpath?: string | undefined;
|
|
119
|
+
release?: string | undefined;
|
|
120
|
+
id?: string | undefined;
|
|
121
121
|
author?: string[];
|
|
122
|
-
license?: string;
|
|
122
|
+
license?: string | undefined;
|
|
123
123
|
licenseDetails?: {
|
|
124
124
|
spdxDisj: string;
|
|
125
125
|
provenance: string;
|
|
@@ -144,8 +144,8 @@ type SocketArtifact = {
|
|
|
144
144
|
overall: number;
|
|
145
145
|
};
|
|
146
146
|
alerts?: SocketArtifactAlert[];
|
|
147
|
-
size?: number;
|
|
148
|
-
batchIndex?: number;
|
|
147
|
+
size?: number | undefined;
|
|
148
|
+
batchIndex?: number | undefined;
|
|
149
149
|
};
|
|
150
150
|
declare function batchScan(pkgIds: string[], concurrencyLimit?: number): AsyncGenerator<SocketArtifact>;
|
|
151
151
|
declare function isArtifactAlertCveFixable(alert: SocketArtifactAlert): alert is ArtifactAlertCveFixable;
|
|
@@ -156,18 +156,18 @@ type PackageDetail = {
|
|
|
156
156
|
existing?: SafeNode | undefined;
|
|
157
157
|
};
|
|
158
158
|
type GetPackagesToQueryFromDiffOptions = {
|
|
159
|
-
includeUnchanged?: boolean;
|
|
160
|
-
includeUnknownOrigin?: boolean;
|
|
159
|
+
includeUnchanged?: boolean | undefined;
|
|
160
|
+
includeUnknownOrigin?: boolean | undefined;
|
|
161
161
|
};
|
|
162
|
-
declare function getPackagesToQueryFromDiff(diff_: Diff | null, options?: GetPackagesToQueryFromDiffOptions): PackageDetail[];
|
|
162
|
+
declare function getPackagesToQueryFromDiff(diff_: Diff | null, options?: GetPackagesToQueryFromDiffOptions | undefined): PackageDetail[];
|
|
163
163
|
declare function findUp(name: string | string[], { cwd }: {
|
|
164
164
|
cwd: string;
|
|
165
165
|
}): Promise<string | undefined>;
|
|
166
166
|
type ReadFileOptions = ObjectEncodingOptions & Abortable & {
|
|
167
167
|
flag?: OpenMode | undefined;
|
|
168
168
|
};
|
|
169
|
-
declare function readFileBinary(filepath: PathLike | FileHandle, options?: ReadFileOptions): Promise<Buffer>;
|
|
170
|
-
declare function readFileUtf8(filepath: PathLike | FileHandle, options?: ReadFileOptions): Promise<string>;
|
|
169
|
+
declare function readFileBinary(filepath: PathLike | FileHandle, options?: ReadFileOptions | undefined): Promise<Buffer>;
|
|
170
|
+
declare function readFileUtf8(filepath: PathLike | FileHandle, options?: ReadFileOptions | undefined): Promise<string>;
|
|
171
171
|
declare function safeReadFile(...args: Parameters<typeof fs.readFile>): ReturnType<typeof fs.readFile> | undefined;
|
|
172
172
|
declare function safeReadFileSync(...args: Parameters<typeof fsReadFileSync>): ReturnType<typeof fsReadFileSync> | undefined;
|
|
173
173
|
declare const Arborist: ArboristClass;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/utils/errors.ts","../../src/utils/fs.ts","../../src/utils/settings.ts","../../src/utils/sdk.ts","../../src/shadow/arborist/lib/arborist/diff.ts","../../src/utils/alert/artifact.ts","../../src/utils/alert/rules.ts","../../src/utils/color-or-markdown.ts","../../src/utils/socket-url.ts","../../src/shadow/arborist/lib/dep-valid.ts","../../src/shadow/proc-log.ts","../../src/shadow/arborist/lib/override-set.ts","../../src/shadow/arborist/lib/node.ts","../../src/shadow/arborist/lib/edge.ts","../../src/shadow/arborist/lib/arborist/reify.ts","../../src/shadow/arborist/lib/arborist/index.ts"],"sourcesContent":["import { setTimeout as wait } from 'node:timers/promises'\n\nimport { debugLog } from '@socketsecurity/registry/lib/debug'\n\nimport constants from '../constants'\n\nconst {\n kInternalsSymbol,\n [kInternalsSymbol as unknown as 'Symbol(kInternalsSymbol)']: { getSentry }\n} = constants\n\ntype EventHintOrCaptureContext = { [key: string]: any } | Function\n\nexport class AuthError extends Error {}\n\nexport class InputError extends Error {\n public body: string | undefined\n\n constructor(message: string, body?: string) {\n super(message)\n this.body = body\n }\n}\n\nexport async function captureException(\n exception: unknown,\n hint?: EventHintOrCaptureContext | undefined\n): Promise<string> {\n const result = captureExceptionSync(exception, hint)\n // \"Sleep\" for a second, just in case, hopefully enough time to initiate fetch.\n await wait(1000)\n return result\n}\n\nexport function captureExceptionSync(\n exception: unknown,\n hint?: EventHintOrCaptureContext | undefined\n): string {\n const Sentry = getSentry()\n if (!Sentry) {\n return ''\n }\n debugLog('captureException: Sending exception to Sentry.')\n return <string>Sentry.captureException(exception, hint)\n}\n\nexport function isErrnoException(\n value: unknown\n): value is NodeJS.ErrnoException {\n if (!(value instanceof Error)) {\n return false\n }\n return (value as NodeJS.ErrnoException).code !== undefined\n}\n","import { promises as fs, readFileSync as fsReadFileSync } from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\n\nimport type { Abortable } from 'node:events'\nimport type { ObjectEncodingOptions, OpenMode, PathLike } from 'node:fs'\nimport type { FileHandle } from 'node:fs/promises'\n\nexport async function findUp(\n name: string | string[],\n { cwd = process.cwd() }: { cwd: string }\n): Promise<string | undefined> {\n let dir = path.resolve(cwd)\n const { root } = path.parse(dir)\n const names = [name].flat()\n while (dir && dir !== root) {\n for (const name of names) {\n const filePath = path.join(dir, name)\n try {\n // eslint-disable-next-line no-await-in-loop\n const stats = await fs.stat(filePath)\n if (stats.isFile()) {\n return filePath\n }\n } catch {}\n }\n dir = path.dirname(dir)\n }\n return undefined\n}\n\nexport type ReadFileOptions = ObjectEncodingOptions &\n Abortable & {\n flag?: OpenMode | undefined\n }\n\nexport async function readFileBinary(\n filepath: PathLike | FileHandle,\n options?: ReadFileOptions\n): Promise<Buffer> {\n return <Buffer>await fs.readFile(filepath, <ReadFileOptions>{\n ...options,\n encoding: 'binary'\n })\n}\n\nexport async function readFileUtf8(\n filepath: PathLike | FileHandle,\n options?: ReadFileOptions\n): Promise<string> {\n return <string>await fs.readFile(filepath, <ReadFileOptions>{\n ...options,\n encoding: 'utf8'\n })\n}\n\nexport function safeReadFile(\n ...args: Parameters<typeof fs.readFile>\n): ReturnType<typeof fs.readFile> | undefined {\n try {\n return fs.readFile(...args)\n } catch {}\n return undefined\n}\n\nexport function safeReadFileSync(\n ...args: Parameters<typeof fsReadFileSync>\n): ReturnType<typeof fsReadFileSync> | undefined {\n try {\n return fsReadFileSync(...args)\n } catch {}\n return undefined\n}\n","import { mkdirSync, writeFileSync } from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\n\nimport config from '@socketsecurity/config'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { safeReadFileSync } from './fs'\nimport constants from '../constants'\n\nconst LOCALAPPDATA = 'LOCALAPPDATA'\n\nconst supportedApiKeys = new Set([\n 'apiBaseUrl',\n 'apiKey',\n 'apiProxy',\n 'enforcedOrgs'\n])\n\ninterface Settings {\n apiBaseUrl?: string | null | undefined\n apiKey?: string | null | undefined\n apiProxy?: string | null | undefined\n enforcedOrgs?: string[] | null | undefined\n // apiToken is an alias for apiKey.\n apiToken?: string | null | undefined\n}\n\nlet _settings: Settings | undefined\nfunction getSettings(): Settings {\n if (_settings === undefined) {\n _settings = <Settings>{}\n const settingsPath = getSettingsPath()\n if (settingsPath) {\n const raw = <string | undefined>safeReadFileSync(settingsPath, 'utf8')\n if (raw) {\n try {\n Object.assign(\n _settings,\n JSON.parse(Buffer.from(raw, 'base64').toString())\n )\n } catch {\n logger.warn(`Failed to parse settings at ${settingsPath}`)\n }\n } else {\n mkdirSync(path.dirname(settingsPath), { recursive: true })\n }\n }\n }\n return _settings\n}\n\nlet _settingsPath: string | undefined\nlet _warnedSettingPathWin32Missing = false\nfunction getSettingsPath(): string | undefined {\n if (_settingsPath === undefined) {\n // Lazily access constants.WIN32.\n const { WIN32 } = constants\n let dataHome: string | undefined = WIN32\n ? process.env[LOCALAPPDATA]\n : process.env['XDG_DATA_HOME']\n if (!dataHome) {\n if (WIN32) {\n if (!_warnedSettingPathWin32Missing) {\n _warnedSettingPathWin32Missing = true\n logger.warn(`Missing %${LOCALAPPDATA}%`)\n }\n } else {\n dataHome = path.join(\n os.homedir(),\n ...(process.platform === 'darwin'\n ? ['Library', 'Application Support']\n : ['.local', 'share'])\n )\n }\n }\n _settingsPath = dataHome\n ? path.join(dataHome, 'socket/settings')\n : undefined\n }\n return _settingsPath\n}\n\nfunction normalizeSettingsKey(key: string): string {\n const normalizedKey = key === 'apiToken' ? 'apiKey' : key\n if (!supportedApiKeys.has(normalizedKey)) {\n throw new Error(`Invalid settings key: ${normalizedKey}`)\n }\n return normalizedKey\n}\n\nexport function findSocketYmlSync() {\n let prevDir = null\n let dir = process.cwd()\n while (dir !== prevDir) {\n let ymlPath = path.join(dir, 'socket.yml')\n let yml = <string | undefined>safeReadFileSync(ymlPath, 'utf8')\n if (yml === undefined) {\n ymlPath = path.join(dir, 'socket.yaml')\n yml = <string | undefined>safeReadFileSync(ymlPath, 'utf8')\n }\n if (typeof yml === 'string') {\n try {\n return {\n path: ymlPath,\n parsed: config.parseSocketConfig(yml)\n }\n } catch {\n throw new Error(`Found file but was unable to parse ${ymlPath}`)\n }\n }\n prevDir = dir\n dir = path.join(dir, '..')\n }\n return null\n}\n\nexport function getSetting<Key extends keyof Settings>(\n key: Key\n): Settings[Key] {\n return getSettings()[<Key>normalizeSettingsKey(key)]\n}\n\nlet pendingSave = false\nexport function updateSetting<Key extends keyof Settings>(\n key: Key,\n value: Settings[Key]\n): void {\n const settings = getSettings()\n ;(settings as any)[<Key>normalizeSettingsKey(key)] = value\n if (!pendingSave) {\n pendingSave = true\n process.nextTick(() => {\n pendingSave = false\n const settingsPath = getSettingsPath()\n if (settingsPath) {\n writeFileSync(\n settingsPath,\n Buffer.from(JSON.stringify(settings)).toString('base64')\n )\n }\n })\n }\n}\n","import process from 'node:process'\n\nimport { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'\n\nimport isInteractive from '@socketregistry/is-interactive/index.cjs'\nimport { SOCKET_PUBLIC_API_TOKEN } from '@socketsecurity/registry/lib/constants'\nimport { password } from '@socketsecurity/registry/lib/prompts'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\nimport { SocketSdk, createUserAgentFromPkgJson } from '@socketsecurity/sdk'\n\nimport { AuthError } from './errors'\nimport { getSetting } from './settings'\nimport constants from '../constants'\n\nconst { SOCKET_CLI_NO_API_TOKEN } = constants\n\n// The API server that should be used for operations.\nfunction getDefaultApiBaseUrl(): string | undefined {\n const baseUrl =\n process.env['SOCKET_SECURITY_API_BASE_URL'] || getSetting('apiBaseUrl')\n return isNonEmptyString(baseUrl) ? baseUrl : undefined\n}\n\n// The API server that should be used for operations.\nfunction getDefaultHttpProxy(): string | undefined {\n const apiProxy =\n process.env['SOCKET_SECURITY_API_PROXY'] || getSetting('apiProxy')\n return isNonEmptyString(apiProxy) ? apiProxy : undefined\n}\n\n// This API key should be stored globally for the duration of the CLI execution.\nlet _defaultToken: string | undefined\nexport function getDefaultToken(): string | undefined {\n // Lazily access constants.ENV[SOCKET_CLI_NO_API_TOKEN].\n if (constants.ENV[SOCKET_CLI_NO_API_TOKEN]) {\n _defaultToken = undefined\n } else {\n const key =\n process.env['SOCKET_SECURITY_API_TOKEN'] ||\n // Keep 'SOCKET_SECURITY_API_KEY' as an alias of 'SOCKET_SECURITY_API_TOKEN'.\n // TODO: Remove 'SOCKET_SECURITY_API_KEY' alias.\n process.env['SOCKET_SECURITY_API_KEY'] ||\n getSetting('apiToken') ||\n _defaultToken\n _defaultToken = isNonEmptyString(key) ? key : undefined\n }\n return _defaultToken\n}\n\nexport function getPublicToken(): string {\n return getDefaultToken() ?? SOCKET_PUBLIC_API_TOKEN\n}\n\nexport async function setupSdk(\n apiToken: string | undefined = getDefaultToken(),\n apiBaseUrl: string | undefined = getDefaultApiBaseUrl(),\n proxy: string | undefined = getDefaultHttpProxy()\n): Promise<SocketSdk> {\n if (typeof apiToken !== 'string' && isInteractive()) {\n apiToken = await password({\n message:\n 'Enter your Socket.dev API key (not saved, use socket login to persist)'\n })\n _defaultToken = apiToken\n }\n if (!apiToken) {\n throw new AuthError('You need to provide an API key')\n }\n return new SocketSdk(apiToken, {\n agent: proxy\n ? {\n http: new HttpProxyAgent({ proxy }),\n https: new HttpsProxyAgent({ proxy })\n }\n : undefined,\n baseUrl: apiBaseUrl,\n // Lazily access constants.rootPkgJsonPath.\n userAgent: createUserAgentFromPkgJson(require(constants.rootPkgJsonPath))\n })\n}\n","import constants from '../../../../constants'\n\nimport type { Diff } from './types'\nimport type { SafeNode } from '../node'\n\nconst { LOOP_SENTINEL, NPM_REGISTRY_URL } = constants\n\nfunction getUrlOrigin(input: string): string {\n try {\n return URL.parse(input)?.origin ?? ''\n } catch {}\n return ''\n}\n\nexport type PackageDetail = {\n node: SafeNode\n existing?: SafeNode | undefined\n}\n\ntype GetPackagesToQueryFromDiffOptions = {\n includeUnchanged?: boolean\n includeUnknownOrigin?: boolean\n}\n\nexport function getPackagesToQueryFromDiff(\n diff_: Diff | null,\n options?: GetPackagesToQueryFromDiffOptions\n): PackageDetail[] {\n const { includeUnchanged = false, includeUnknownOrigin = false } = <\n GetPackagesToQueryFromDiffOptions\n >{\n __proto__: null,\n ...options\n }\n const details: PackageDetail[] = []\n // `diff_` is `null` when `npm install --package-lock-only` is passed.\n if (!diff_) {\n return details\n }\n const queue: Diff[] = [...diff_.children]\n let pos = 0\n let { length: queueLength } = queue\n while (pos < queueLength) {\n if (pos === LOOP_SENTINEL) {\n throw new Error('Detected infinite loop while walking Arborist diff')\n }\n const diff = queue[pos++]!\n const { action } = diff\n if (action) {\n // The `pkgNode`, i.e. the `ideal` node, will be `undefined` if the diff\n // action is 'REMOVE'\n // The `oldNode`, i.e. the `actual` node, will be `undefined` if the diff\n // action is 'ADD'.\n const { actual: oldNode, ideal: pkgNode } = diff\n let existing: SafeNode | undefined\n let keep = false\n if (action === 'CHANGE') {\n if (pkgNode?.package.version !== oldNode?.package.version) {\n keep = true\n if (\n oldNode?.package.name &&\n oldNode.package.name === pkgNode?.package.name\n ) {\n existing = oldNode\n }\n } else {\n // TODO: Add proper debug mode.\n // logger.warn('SKIPPING META CHANGE ON', diff)\n }\n } else {\n keep = action !== 'REMOVE'\n }\n if (keep && pkgNode?.resolved && (!oldNode || oldNode.resolved)) {\n if (\n includeUnknownOrigin ||\n getUrlOrigin(pkgNode.resolved) === NPM_REGISTRY_URL\n ) {\n details.push({\n node: pkgNode,\n existing\n })\n }\n }\n }\n for (const child of diff.children) {\n queue[queueLength++] = child\n }\n }\n if (includeUnchanged) {\n const { unchanged } = diff_!\n for (let i = 0, { length } = unchanged; i < length; i += 1) {\n const pkgNode = unchanged[i]!\n if (\n includeUnknownOrigin ||\n getUrlOrigin(pkgNode.resolved!) === NPM_REGISTRY_URL\n ) {\n details.push({\n node: pkgNode,\n existing: pkgNode\n })\n }\n }\n }\n return details\n}\n","import events from 'node:events'\nimport https from 'node:https'\nimport readline from 'node:readline'\n\nimport constants from '../../constants'\nimport { getPublicToken } from '../sdk'\n\nimport type { IncomingMessage } from 'node:http'\n\nexport type CveAlertType = 'cve' | 'mediumCVE' | 'mildCVE' | 'criticalCVE'\n\nexport type ArtifactAlertCveFixable = Omit<\n SocketArtifactAlert,\n 'props' | 'title'\n> & {\n type: CveAlertType\n props: {\n firstPatchedVersionIdentifier: string\n vulnerableVersionRange: string\n [key: string]: any\n }\n}\n\nexport type ArtifactAlertFixable = ArtifactAlertCveFixable & {\n type: CveAlertType | 'socketUpgradeAvailable'\n}\n\nexport type SocketArtifactAlert = {\n key: string\n type: string\n severity: string\n category: string\n action?: string\n actionPolicyIndex?: number\n file?: string\n props?: any\n start?: number\n end?: number\n}\n\nexport type SocketArtifact = {\n type: string\n name: string\n namespace?: string\n version?: string\n subpath?: string\n release?: string\n id?: string\n author?: string[]\n license?: string\n licenseDetails?: {\n spdxDisj: string\n provenance: string\n filepath: string\n match_strength: number\n }[]\n licenseAttrib?: {\n attribText: string\n attribData: {\n purl: string\n foundInFilepath: string\n spdxExpr: string\n foundAuthors: string[]\n }[]\n }[]\n score?: {\n supplyChain: number\n quality: number\n maintenance: number\n vulnerability: number\n license: number\n overall: number\n }\n alerts?: SocketArtifactAlert[]\n size?: number\n batchIndex?: number\n}\n\nconst {\n ALERT_TYPE_CRITICAL_CVE,\n ALERT_TYPE_CVE,\n ALERT_TYPE_MEDIUM_CVE,\n ALERT_TYPE_MILD_CVE,\n ALERT_TYPE_SOCKET_UPGRADE_AVAILABLE,\n CVE_ALERT_PROPS_FIRST_PATCHED_VERSION_IDENTIFIER,\n CVE_ALERT_PROPS_VULNERABLE_VERSION_RANGE,\n abortSignal\n} = constants\n\nasync function* createBatchGenerator(\n chunk: string[]\n): AsyncGenerator<SocketArtifact> {\n // Adds the first 'abort' listener to abortSignal.\n const req = https\n // Lazily access constants.BATCH_PURL_ENDPOINT.\n .request(constants.BATCH_PURL_ENDPOINT, {\n method: 'POST',\n headers: {\n Authorization: `Basic ${btoa(`${getPublicToken()}:`)}`\n },\n signal: abortSignal\n })\n .end(\n JSON.stringify({\n components: chunk.map(id => ({ purl: `pkg:npm/${id}` }))\n })\n )\n // Adds the second 'abort' listener to abortSignal.\n const { 0: res } = <[IncomingMessage]>(\n await events.once(req, 'response', { signal: abortSignal })\n )\n const ok = res.statusCode! >= 200 && res.statusCode! <= 299\n if (!ok) {\n throw new Error(`Socket API Error: ${res.statusCode}`)\n }\n const rli = readline.createInterface({\n input: res,\n crlfDelay: Infinity,\n signal: abortSignal\n })\n for await (const line of rli) {\n yield <SocketArtifact>JSON.parse(line)\n }\n}\n\nexport async function* batchScan(\n pkgIds: string[],\n concurrencyLimit = 50\n): AsyncGenerator<SocketArtifact> {\n type GeneratorStep = {\n generator: AsyncGenerator<SocketArtifact>\n iteratorResult: IteratorResult<SocketArtifact>\n }\n type GeneratorEntry = {\n generator: AsyncGenerator<SocketArtifact>\n promise: Promise<GeneratorStep>\n }\n type ResolveFn = (value: GeneratorStep) => void\n\n // The createBatchGenerator method will add 2 'abort' event listeners to\n // abortSignal so we multiply the concurrencyLimit by 2.\n const neededMaxListeners = concurrencyLimit * 2\n // Increase abortSignal max listeners count to avoid Node's MaxListenersExceededWarning.\n const oldAbortSignalMaxListeners = events.getMaxListeners(abortSignal)\n let abortSignalMaxListeners = oldAbortSignalMaxListeners\n if (oldAbortSignalMaxListeners < neededMaxListeners) {\n abortSignalMaxListeners = oldAbortSignalMaxListeners + neededMaxListeners\n events.setMaxListeners(abortSignalMaxListeners, abortSignal)\n }\n const { length: pkgIdsCount } = pkgIds\n const running: GeneratorEntry[] = []\n let index = 0\n const enqueueGen = () => {\n if (index >= pkgIdsCount) {\n // No more work to do.\n return\n }\n const chunk = pkgIds.slice(index, index + 25)\n index += 25\n const generator = createBatchGenerator(chunk)\n continueGen(generator)\n }\n const continueGen = (generator: AsyncGenerator<SocketArtifact>) => {\n let resolveFn: ResolveFn\n running.push({\n generator,\n promise: new Promise<GeneratorStep>(resolve => (resolveFn = resolve))\n })\n void generator\n .next()\n .then(res => resolveFn!({ generator, iteratorResult: res }))\n }\n // Start initial batch of generators.\n while (running.length < concurrencyLimit && index < pkgIdsCount) {\n enqueueGen()\n }\n while (running.length > 0) {\n // eslint-disable-next-line no-await-in-loop\n const { generator, iteratorResult } = await Promise.race(\n running.map(entry => entry.promise)\n )\n // Remove generator.\n running.splice(\n running.findIndex(entry => entry.generator === generator),\n 1\n )\n if (iteratorResult.done) {\n // Start a new generator if available.\n enqueueGen()\n } else {\n yield iteratorResult.value\n // Keep fetching values from this generator.\n continueGen(generator)\n }\n }\n // Reset abortSignal max listeners count.\n if (abortSignalMaxListeners > oldAbortSignalMaxListeners) {\n events.setMaxListeners(oldAbortSignalMaxListeners, abortSignal)\n }\n}\n\nexport function isArtifactAlertCveFixable(\n alert: SocketArtifactAlert\n): alert is ArtifactAlertCveFixable {\n const { type } = alert\n return (\n (type === ALERT_TYPE_CVE ||\n type === ALERT_TYPE_MEDIUM_CVE ||\n type === ALERT_TYPE_MILD_CVE ||\n type === ALERT_TYPE_CRITICAL_CVE) &&\n !!alert.props?.[CVE_ALERT_PROPS_FIRST_PATCHED_VERSION_IDENTIFIER] &&\n !!alert.props?.[CVE_ALERT_PROPS_VULNERABLE_VERSION_RANGE]\n )\n}\n\nexport function isArtifactAlertUpgradeFixable(\n alert: SocketArtifactAlert\n): alert is ArtifactAlertFixable {\n return alert.type === ALERT_TYPE_SOCKET_UPGRADE_AVAILABLE\n}\n\nexport function isArtifactAlertFixable(\n alert: SocketArtifactAlert\n): alert is ArtifactAlertFixable {\n return (\n isArtifactAlertUpgradeFixable(alert) || isArtifactAlertCveFixable(alert)\n )\n}\n","import { setTimeout as wait } from 'node:timers/promises'\n\nimport { isObject } from '@socketsecurity/registry/lib/objects'\n\nimport constants from '../../constants'\nimport { isErrnoException } from '../errors'\nimport { getPublicToken, setupSdk } from '../sdk'\nimport { findSocketYmlSync, getSetting } from '../settings'\n\nimport type { SocketSdkResultType } from '@socketsecurity/sdk'\n\ntype AlertUxLookup = ReturnType<typeof createAlertUXLookup>\n\ntype AlertUxLookupSettings = Parameters<AlertUxLookup>[0]\n\ntype AlertUxLookupResult = ReturnType<AlertUxLookup>\n\ntype NonNormalizedRule =\n | NonNullable<\n NonNullable<\n NonNullable<\n (SocketSdkResultType<'postSettings'> & {\n success: true\n })['data']['entries'][number]['settings'][string]\n >['issueRules']\n >\n >[string]\n | boolean\n\ntype NonNormalizedResolvedRule =\n | (NonNullable<\n NonNullable<\n (SocketSdkResultType<'postSettings'> & {\n success: true\n })['data']['defaults']['issueRules']\n >[string]\n > & { action: string })\n | boolean\n\ntype RuleActionUX = { block: boolean; display: boolean }\n\nconst { abortSignal } = constants\n\nconst ERROR_UX: RuleActionUX = {\n block: true,\n display: true\n}\n\nconst IGNORE_UX: RuleActionUX = {\n block: false,\n display: false\n}\n\nconst WARN_UX: RuleActionUX = {\n block: false,\n display: true\n}\n\n// Iterates over all entries with ordered issue rule for deferral. Iterates over\n// all issue rules and finds the first defined value that does not defer otherwise\n// uses the defaultValue. Takes the value and converts into a UX workflow.\nfunction resolveAlertRuleUX(\n orderedRulesCollection: Iterable<Iterable<NonNormalizedRule>>,\n defaultValue: NonNormalizedResolvedRule\n): RuleActionUX {\n if (\n defaultValue === true ||\n defaultValue === null ||\n defaultValue === undefined\n ) {\n defaultValue = { action: 'error' }\n } else if (defaultValue === false) {\n defaultValue = { action: 'ignore' }\n }\n let block = false\n let display = false\n let needDefault = true\n iterate_entries: for (const rules of orderedRulesCollection) {\n for (const rule of rules) {\n if (ruleValueDoesNotDefer(rule)) {\n needDefault = false\n const narrowingFilter = uxForDefinedNonDeferValue(rule)\n block = block || narrowingFilter.block\n display = display || narrowingFilter.display\n continue iterate_entries\n }\n }\n const narrowingFilter = uxForDefinedNonDeferValue(defaultValue)\n block = block || narrowingFilter.block\n display = display || narrowingFilter.display\n }\n if (needDefault) {\n const narrowingFilter = uxForDefinedNonDeferValue(defaultValue)\n block = block || narrowingFilter.block\n display = display || narrowingFilter.display\n }\n return { block, display }\n}\n\n// Negative form because it is narrowing the type.\nfunction ruleValueDoesNotDefer(\n rule: NonNormalizedRule\n): rule is NonNormalizedResolvedRule {\n if (rule === undefined) {\n return false\n }\n if (isObject(rule)) {\n const { action } = rule\n if (action === undefined || action === 'defer') {\n return false\n }\n }\n return true\n}\n\n// Handles booleans for backwards compatibility.\nfunction uxForDefinedNonDeferValue(\n ruleValue: NonNormalizedResolvedRule\n): RuleActionUX {\n if (typeof ruleValue === 'boolean') {\n return ruleValue ? ERROR_UX : IGNORE_UX\n }\n const { action } = ruleValue\n if (action === 'warn') {\n return WARN_UX\n } else if (action === 'ignore') {\n return IGNORE_UX\n }\n return ERROR_UX\n}\n\ntype SettingsType = (SocketSdkResultType<'postSettings'> & {\n success: true\n})['data']\n\nexport function createAlertUXLookup(settings: SettingsType): (context: {\n package: { name: string; version: string }\n alert: { type: string }\n}) => RuleActionUX {\n const cachedUX: Map<keyof typeof settings.defaults.issueRules, RuleActionUX> =\n new Map()\n return context => {\n const { type } = context.alert\n let ux = cachedUX.get(type)\n if (ux) {\n return ux\n }\n const orderedRulesCollection: Array<Array<NonNormalizedRule>> = []\n for (const settingsEntry of settings.entries) {\n const orderedRules: NonNormalizedRule[] = []\n let target = settingsEntry.start\n while (target !== null) {\n const resolvedTarget = settingsEntry.settings[target]\n if (!resolvedTarget) {\n break\n }\n const issueRuleValue = resolvedTarget.issueRules?.[type]\n if (typeof issueRuleValue !== 'undefined') {\n orderedRules.push(issueRuleValue)\n }\n target = resolvedTarget.deferTo ?? null\n }\n orderedRulesCollection.push(orderedRules)\n }\n const defaultValue = settings.defaults.issueRules[type] as\n | { action: 'error' | 'ignore' | 'warn' }\n | boolean\n | undefined\n let resolvedDefaultValue: NonNormalizedResolvedRule = {\n action: 'error'\n }\n if (defaultValue === false) {\n resolvedDefaultValue = { action: 'ignore' }\n } else if (defaultValue && defaultValue !== true) {\n resolvedDefaultValue = { action: defaultValue.action ?? 'error' }\n }\n ux = resolveAlertRuleUX(orderedRulesCollection, resolvedDefaultValue)\n cachedUX.set(type, ux)\n return ux\n }\n}\n\nlet _uxLookup: AlertUxLookup | undefined\nexport async function uxLookup(\n settings: AlertUxLookupSettings\n): Promise<AlertUxLookupResult> {\n while (_uxLookup === undefined) {\n // eslint-disable-next-line no-await-in-loop\n await wait(1, { signal: abortSignal })\n }\n return _uxLookup(settings)\n}\n\n// Start initializing the AlertUxLookupResult immediately.\nvoid (async () => {\n const { orgs, settings } = await (async () => {\n try {\n const sockSdk = await setupSdk(getPublicToken())\n const orgResult = await sockSdk.getOrganizations()\n if (!orgResult.success) {\n throw new Error(\n `Failed to fetch Socket organization info: ${orgResult.error.message}`\n )\n }\n const orgs: Exclude<\n (typeof orgResult.data.organizations)[string],\n undefined\n >[] = []\n for (const org of Object.values(orgResult.data.organizations)) {\n if (org) {\n orgs.push(org)\n }\n }\n const result = await sockSdk.postSettings(\n orgs.map(org => ({ organization: org.id }))\n )\n if (!result.success) {\n throw new Error(\n `Failed to fetch API key settings: ${result.error.message}`\n )\n }\n return {\n orgs,\n settings: result.data\n }\n } catch (e: any) {\n const cause = isObject(e) && 'cause' in e ? e['cause'] : undefined\n if (\n isErrnoException(cause) &&\n (cause.code === 'ENOTFOUND' || cause.code === 'ECONNREFUSED')\n ) {\n throw new Error(\n 'Unable to connect to socket.dev, ensure internet connectivity before retrying',\n {\n cause: e\n }\n )\n }\n throw e\n }\n })()\n\n // Remove any organizations not being enforced.\n const enforcedOrgs = getSetting('enforcedOrgs') ?? []\n for (const { 0: i, 1: org } of orgs.entries()) {\n if (!enforcedOrgs.includes(org.id)) {\n settings.entries.splice(i, 1)\n }\n }\n\n const socketYml = findSocketYmlSync()\n if (socketYml) {\n settings.entries.push({\n start: socketYml.path,\n settings: {\n [socketYml.path]: {\n deferTo: null,\n // TODO: TypeScript complains about the type not matching. We should\n // figure out why are providing\n // issueRules: { [issueName: string]: boolean }\n // but expecting\n // issueRules: { [issueName: string]: { action: 'defer' | 'error' | 'ignore' | 'monitor' | 'warn' } }\n issueRules: (<unknown>socketYml.parsed.issueRules) as {\n [key: string]: {\n action: 'defer' | 'error' | 'ignore' | 'monitor' | 'warn'\n }\n }\n }\n }\n })\n }\n _uxLookup = createAlertUXLookup(settings)\n})()\n","import terminalLink from 'terminal-link'\nimport colors from 'yoctocolors-cjs'\n\nimport indentString from '@socketregistry/indent-string/index.cjs'\nimport { Logger } from '@socketsecurity/registry/lib/logger'\n\nimport type { LogSymbols } from '@socketsecurity/registry/lib/logger'\n\nconst markdownLogSymbols = <LogSymbols>Object.freeze({\n __proto__: null,\n info: ':information_source:',\n error: ':stop_sign:',\n success: ':white_check_mark:',\n warning: ':warning:'\n})\n\nexport class ColorOrMarkdown {\n public useMarkdown: boolean\n\n constructor(useMarkdown: boolean) {\n this.useMarkdown = !!useMarkdown\n }\n\n bold(text: string): string {\n return this.useMarkdown ? `**${text}**` : colors.bold(`${text}`)\n }\n\n header(text: string, level = 1): string {\n return this.useMarkdown\n ? `\\n${''.padStart(level, '#')} ${text}\\n`\n : colors.underline(`\\n${level === 1 ? colors.bold(text) : text}\\n`)\n }\n\n hyperlink(\n text: string,\n url: string | undefined,\n {\n fallback = true,\n fallbackToUrl\n }: {\n fallback?: boolean\n fallbackToUrl?: boolean\n } = {}\n ) {\n if (url) {\n return this.useMarkdown\n ? `[${text}](${url})`\n : terminalLink(text, url, {\n fallback: fallbackToUrl ? (_text, url) => url : fallback\n })\n }\n return text\n }\n\n indent(\n ...args: Parameters<typeof indentString>\n ): ReturnType<typeof indentString> {\n return indentString(...args)\n }\n\n italic(text: string): string {\n return this.useMarkdown ? `_${text}_` : colors.italic(`${text}`)\n }\n\n json(value: any): string {\n return this.useMarkdown\n ? '```json\\n' + JSON.stringify(value) + '\\n```'\n : JSON.stringify(value)\n }\n\n list(items: string[]): string {\n const indentedContent = items.map(item => this.indent(item).trimStart())\n return this.useMarkdown\n ? `* ${indentedContent.join('\\n* ')}\\n`\n : `${indentedContent.join('\\n')}\\n`\n }\n\n get logSymbols(): LogSymbols {\n return this.useMarkdown ? markdownLogSymbols : Logger.LOG_SYMBOLS\n }\n}\n","export function getSocketDevAlertUrl(alertType: string): string {\n return `https://socket.dev/alerts/${alertType}`\n}\n\nexport function getSocketDevPackageOverviewUrl(\n eco: string,\n name: string,\n version?: string\n): string {\n return `https://socket.dev/${eco}/package/${name}${version ? `/overview/${version}` : ''}`\n}\n","import { getArboristDepValidPath } from '../../npm-paths'\n\nimport type { SafeNode } from './node'\n\nexport const depValid: (\n child: SafeNode,\n requested: string,\n accept: string | undefined,\n requester: SafeNode\n) => boolean = require(getArboristDepValidPath())\n","import { getNpmRequire } from './npm-paths'\nimport constants from '../constants'\n\nconst { UNDEFINED_TOKEN } = constants\n\ninterface RequireKnownModules {\n npmlog: typeof import('npmlog')\n // The DefinitelyTyped definition of 'proc-log' does NOT have the log method.\n // The return type of the log method is the same as `typeof import('proc-log')`.\n 'proc-log': typeof import('proc-log')\n}\n\ntype RequireTransformer<T extends keyof RequireKnownModules> = (\n mod: RequireKnownModules[T]\n) => RequireKnownModules[T]\n\nfunction tryRequire<T extends keyof RequireKnownModules>(\n req: NodeJS.Require,\n ...ids: (T | [T, RequireTransformer<T>])[]\n): RequireKnownModules[T] | undefined {\n for (const data of ids) {\n let id: string | undefined\n let transformer: RequireTransformer<T> | undefined\n if (Array.isArray(data)) {\n id = data[0]\n transformer = <RequireTransformer<T>>data[1]\n } else {\n id = <keyof RequireKnownModules>data\n transformer = mod => mod\n }\n try {\n // Check that the transformed value isn't `undefined` because older\n // versions of packages like 'proc-log' may not export a `log` method.\n const exported = transformer(req(id))\n if (exported !== undefined) {\n return exported\n }\n } catch {}\n }\n return undefined\n}\n\nexport type Logger =\n | typeof import('npmlog')\n | typeof import('proc-log')\n | undefined\n\nlet _log: Logger | {} | undefined = UNDEFINED_TOKEN\nexport function getLogger(): Logger {\n if (_log === UNDEFINED_TOKEN) {\n _log = tryRequire(\n getNpmRequire(),\n [\n <'proc-log'>'proc-log/lib/index.js',\n // The proc-log DefinitelyTyped definition is incorrect. The type definition\n // is really that of its export log.\n mod => <RequireKnownModules['proc-log']>(mod as any).log\n ],\n <'npmlog'>'npmlog/lib/log.js'\n )\n }\n return <Logger | undefined>_log\n}\n","import npa from 'npm-package-arg'\nimport semver from 'semver'\n\nimport { getArboristOverrideSetClassPath } from '../../npm-paths'\nimport { getLogger } from '../../proc-log'\n\nimport type { SafeEdge } from './edge'\nimport type { SafeNode } from './node'\nimport type { AliasResult, RegistryResult } from 'npm-package-arg'\n\ninterface OverrideSetClass {\n children: Map<string, SafeOverrideSet>\n key: string | undefined\n keySpec: string | undefined\n name: string | undefined\n parent: SafeOverrideSet | undefined\n value: string | undefined\n version: string | undefined\n // eslint-disable-next-line @typescript-eslint/no-misused-new\n new (...args: any[]): OverrideSetClass\n get isRoot(): boolean\n get ruleset(): Map<string, SafeOverrideSet>\n ancestry(): Generator<SafeOverrideSet>\n childrenAreEqual(otherOverrideSet: SafeOverrideSet | undefined): boolean\n getEdgeRule(edge: SafeEdge): SafeOverrideSet\n getNodeRule(node: SafeNode): SafeOverrideSet\n getMatchingRule(node: SafeNode): SafeOverrideSet | null\n isEqual(otherOverrideSet: SafeOverrideSet | undefined): boolean\n}\n\nconst OverrideSet: OverrideSetClass = require(getArboristOverrideSetClassPath())\n\n// Implementation code not related to patch https://github.com/npm/cli/pull/8089\n// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/override-set.js:\nexport class SafeOverrideSet extends OverrideSet {\n // Patch adding doOverrideSetsConflict is based on\n // https://github.com/npm/cli/pull/8089.\n static doOverrideSetsConflict(\n first: SafeOverrideSet | undefined,\n second: SafeOverrideSet | undefined\n ) {\n // If override sets contain one another then we can try to use the more\n // specific one. If neither one is more specific, then we consider them to\n // be in conflict.\n return this.findSpecificOverrideSet(first, second) === undefined\n }\n\n // Patch adding findSpecificOverrideSet is based on\n // https://github.com/npm/cli/pull/8089.\n static findSpecificOverrideSet(\n first: SafeOverrideSet | undefined,\n second: SafeOverrideSet | undefined\n ) {\n for (\n let overrideSet = second;\n overrideSet;\n overrideSet = overrideSet.parent\n ) {\n if (overrideSet.isEqual(first)) {\n return second\n }\n }\n for (\n let overrideSet = first;\n overrideSet;\n overrideSet = overrideSet.parent\n ) {\n if (overrideSet.isEqual(second)) {\n return first\n }\n }\n // The override sets are incomparable. Neither one contains the other.\n const log = getLogger()\n log?.silly('Conflicting override sets', first, second)\n return undefined\n }\n\n // Patch adding childrenAreEqual is based on\n // https://github.com/npm/cli/pull/8089.\n override childrenAreEqual(otherOverrideSet: SafeOverrideSet) {\n if (this.children.size !== otherOverrideSet.children.size) {\n return false\n }\n for (const { 0: key, 1: childOverrideSet } of this.children) {\n const otherChildOverrideSet = otherOverrideSet.children.get(key)\n if (!otherChildOverrideSet) {\n return false\n }\n if (childOverrideSet.value !== otherChildOverrideSet.value) {\n return false\n }\n if (!childOverrideSet.childrenAreEqual(otherChildOverrideSet)) {\n return false\n }\n }\n return true\n }\n\n override getEdgeRule(edge: SafeEdge): SafeOverrideSet {\n for (const rule of this.ruleset.values()) {\n if (rule.name !== edge.name) {\n continue\n }\n // If keySpec is * we found our override.\n if (rule.keySpec === '*') {\n return rule\n }\n // Patch replacing\n // let spec = npa(`${edge.name}@${edge.spec}`)\n // is based on https://github.com/npm/cli/pull/8089.\n //\n // We need to use the rawSpec here, because the spec has the overrides\n // applied to it already. The rawSpec can be undefined, so we need to use\n // the fallback value of spec if it is.\n let spec = npa(`${edge.name}@${edge.rawSpec || edge.spec}`)\n if (spec.type === 'alias') {\n spec = (<AliasResult>spec).subSpec\n }\n if (spec.type === 'git') {\n if (spec.gitRange && semver.intersects(spec.gitRange, rule.keySpec!)) {\n return rule\n }\n continue\n }\n if (spec.type === 'range' || spec.type === 'version') {\n if (\n semver.intersects((<RegistryResult>spec).fetchSpec, rule.keySpec!)\n ) {\n return rule\n }\n continue\n }\n // If we got this far, the spec type is one of tag, directory or file\n // which means we have no real way to make version comparisons, so we\n // just accept the override.\n return rule\n }\n return this\n }\n\n // Patch adding isEqual is based on\n // https://github.com/npm/cli/pull/8089.\n override isEqual(otherOverrideSet: SafeOverrideSet | undefined): boolean {\n if (this === otherOverrideSet) {\n return true\n }\n if (!otherOverrideSet) {\n return false\n }\n if (\n this.key !== otherOverrideSet.key ||\n this.value !== otherOverrideSet.value\n ) {\n return false\n }\n if (!this.childrenAreEqual(otherOverrideSet)) {\n return false\n }\n if (!this.parent) {\n return !otherOverrideSet.parent\n }\n return this.parent.isEqual(otherOverrideSet.parent)\n }\n}\n","import semver from 'semver'\n\nimport { SafeOverrideSet } from './override-set'\nimport { getArboristNodeClassPath } from '../../npm-paths'\nimport { getLogger } from '../../proc-log'\n\nimport type { SafeEdge } from './edge'\nimport type { Node as BaseNode, Link } from '@npmcli/arborist'\n\ntype NodeClass = Omit<\n BaseNode,\n | 'addEdgeIn'\n | 'addEdgeOut'\n | 'canDedupe'\n | 'canReplace'\n | 'canReplaceWith'\n | 'children'\n | 'deleteEdgeIn'\n | 'edgesIn'\n | 'edgesOut'\n | 'from'\n | 'hasShrinkwrap'\n | 'inDepBundle'\n | 'inShrinkwrap'\n | 'integrity'\n | 'isTop'\n | 'matches'\n | 'meta'\n | 'name'\n | 'overrides'\n | 'packageName'\n | 'parent'\n | 'recalculateOutEdgesOverrides'\n | 'resolve'\n | 'resolveParent'\n | 'root'\n | 'updateOverridesEdgeInAdded'\n | 'updateOverridesEdgeInRemoved'\n | 'version'\n | 'versions'\n> & {\n name: string\n version: string\n children: Map<string, SafeNode | Link>\n edgesIn: Set<SafeEdge>\n edgesOut: Map<string, SafeEdge>\n from: SafeNode | null\n hasShrinkwrap: boolean\n inShrinkwrap: boolean | undefined\n integrity?: string | null\n isTop: boolean | undefined\n meta: BaseNode['meta'] & {\n addEdge(edge: SafeEdge): void\n }\n overrides: SafeOverrideSet | undefined\n versions: string[]\n get inDepBundle(): boolean\n get packageName(): string | null\n get parent(): SafeNode | null\n set parent(value: SafeNode | null)\n get resolveParent(): SafeNode | null\n get root(): SafeNode | null\n set root(value: SafeNode | null)\n new (...args: any): NodeClass\n addEdgeIn(edge: SafeEdge): void\n addEdgeOut(edge: SafeEdge): void\n canDedupe(preferDedupe?: boolean): boolean\n canReplace(node: SafeNode, ignorePeers?: string[]): boolean\n canReplaceWith(node: SafeNode, ignorePeers?: string[]): boolean\n deleteEdgeIn(edge: SafeEdge): void\n matches(node: SafeNode): boolean\n recalculateOutEdgesOverrides(): void\n resolve(name: string): SafeNode\n updateOverridesEdgeInAdded(\n otherOverrideSet: SafeOverrideSet | undefined\n ): boolean\n updateOverridesEdgeInRemoved(otherOverrideSet: SafeOverrideSet): boolean\n}\n\nconst Node: NodeClass = require(getArboristNodeClassPath())\n\n// Implementation code not related to patch https://github.com/npm/cli/pull/8089\n// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/node.js:\nexport class SafeNode extends Node {\n // Return true if it's safe to remove this node, because anything that is\n // depending on it would be fine with the thing that they would resolve to if\n // it was removed, or nothing is depending on it in the first place.\n override canDedupe(preferDedupe = false) {\n // Not allowed to mess with shrinkwraps or bundles.\n if (this.inDepBundle || this.inShrinkwrap) {\n return false\n }\n // It's a top level pkg, or a dep of one.\n if (!this.resolveParent?.resolveParent) {\n return false\n }\n // No one wants it, remove it.\n if (this.edgesIn.size === 0) {\n return true\n }\n const other = this.resolveParent.resolveParent.resolve(this.name)\n // Nothing else, need this one.\n if (!other) {\n return false\n }\n // If it's the same thing, then always fine to remove.\n if (other.matches(this)) {\n return true\n }\n // If the other thing can't replace this, then skip it.\n if (!other.canReplace(this)) {\n return false\n }\n // Patch replacing\n // if (preferDedupe || semver.gte(other.version, this.version)) {\n // return true\n // }\n // is based on https://github.com/npm/cli/pull/8089.\n //\n // If we prefer dedupe, or if the version is equal, take the other.\n if (preferDedupe || semver.eq(other.version, this.version)) {\n return true\n }\n // If our current version isn't the result of an override, then prefer to\n // take the greater version.\n if (!this.overridden && semver.gt(other.version, this.version)) {\n return true\n }\n return false\n }\n\n // Is it safe to replace one node with another? check the edges to\n // make sure no one will get upset. Note that the node might end up\n // having its own unmet dependencies, if the new node has new deps.\n // Note that there are cases where Arborist will opt to insert a node\n // into the tree even though this function returns false! This is\n // necessary when a root dependency is added or updated, or when a\n // root dependency brings peer deps along with it. In that case, we\n // will go ahead and create the invalid state, and then try to resolve\n // it with more tree construction, because it's a user request.\n override canReplaceWith(node: SafeNode, ignorePeers?: string[]): boolean {\n if (this.name !== node.name || this.packageName !== node.packageName) {\n return false\n }\n // Patch replacing\n // if (node.overrides !== this.overrides) {\n // return false\n // }\n // is based on https://github.com/npm/cli/pull/8089.\n //\n // If this node has no dependencies, then it's irrelevant to check the\n // override rules of the replacement node.\n if (this.edgesOut.size) {\n // XXX need to check for two root nodes?\n if (node.overrides) {\n if (!node.overrides.isEqual(this.overrides)) {\n return false\n }\n } else {\n if (this.overrides) {\n return false\n }\n }\n }\n // To satisfy the patch we ensure `node.overrides === this.overrides`\n // so that the condition we want to replace,\n // if (this.overrides !== node.overrides) {\n // , is not hit.`\n const oldOverrideSet = this.overrides\n let result = true\n if (oldOverrideSet !== node.overrides) {\n this.overrides = node.overrides\n }\n try {\n result = super.canReplaceWith(node, ignorePeers)\n this.overrides = oldOverrideSet\n } catch (e: any) {\n this.overrides = oldOverrideSet\n throw e\n }\n return result\n }\n\n // Patch adding deleteEdgeIn is based on https://github.com/npm/cli/pull/8089.\n override deleteEdgeIn(edge: SafeEdge) {\n this.edgesIn.delete(edge)\n const { overrides } = edge\n if (overrides) {\n this.updateOverridesEdgeInRemoved(overrides)\n }\n }\n\n override addEdgeIn(edge: SafeEdge): void {\n // Patch replacing\n // if (edge.overrides) {\n // this.overrides = edge.overrides\n // }\n // is based on https://github.com/npm/cli/pull/8089.\n //\n // We need to handle the case where the new edge in has an overrides field\n // which is different from the current value.\n if (!this.overrides || !this.overrides.isEqual(edge.overrides)) {\n this.updateOverridesEdgeInAdded(edge.overrides)\n }\n this.edgesIn.add(edge)\n // Try to get metadata from the yarn.lock file.\n this.root.meta?.addEdge(edge)\n }\n\n // @ts-ignore: Incorrectly typed as a property instead of an accessor.\n override get overridden() {\n // Patch replacing\n // return !!(this.overrides && this.overrides.value && this.overrides.name === this.name)\n // is based on https://github.com/npm/cli/pull/8089.\n if (\n !this.overrides ||\n !this.overrides.value ||\n this.overrides.name !== this.name\n ) {\n return false\n }\n // The overrides rule is for a package with this name, but some override\n // rules only apply to specific versions. To make sure this package was\n // actually overridden, we check whether any edge going in had the rule\n // applied to it, in which case its overrides set is different than its\n // source node.\n for (const edge of this.edgesIn) {\n if (\n edge.overrides &&\n edge.overrides.name === this.name &&\n edge.overrides.value === this.version\n ) {\n if (!edge.overrides.isEqual(edge.from?.overrides)) {\n return true\n }\n }\n }\n return false\n }\n\n override set parent(newParent: SafeNode) {\n // Patch removing\n // if (parent.overrides) {\n // this.overrides = parent.overrides.getNodeRule(this)\n // }\n // is based on https://github.com/npm/cli/pull/8089.\n //\n // The \"parent\" setter is a really large and complex function. To satisfy\n // the patch we hold on to the old overrides value and set `this.overrides`\n // to `undefined` so that the condition we want to remove is not hit.\n const { overrides } = this\n if (overrides) {\n this.overrides = undefined\n }\n try {\n super.parent = newParent\n this.overrides = overrides\n } catch (e: any) {\n this.overrides = overrides\n throw e\n }\n }\n\n // Patch adding recalculateOutEdgesOverrides is based on\n // https://github.com/npm/cli/pull/8089.\n override recalculateOutEdgesOverrides() {\n // For each edge out propagate the new overrides through.\n for (const edge of this.edgesOut.values()) {\n edge.reload(true)\n if (edge.to) {\n edge.to.updateOverridesEdgeInAdded(edge.overrides)\n }\n }\n }\n\n // @ts-ignore: Incorrectly typed to accept null.\n override set root(newRoot: SafeNode) {\n // Patch removing\n // if (!this.overrides && this.parent && this.parent.overrides) {\n // this.overrides = this.parent.overrides.getNodeRule(this)\n // }\n // is based on https://github.com/npm/cli/pull/8089.\n //\n // The \"root\" setter is a really large and complex function. To satisfy the\n // patch we add a dummy value to `this.overrides` so that the condition we\n // want to remove is not hit.\n if (!this.overrides) {\n this.overrides = new SafeOverrideSet({ overrides: '' })\n }\n try {\n super.root = newRoot\n this.overrides = undefined\n } catch (e: any) {\n this.overrides = undefined\n throw e\n }\n }\n\n // Patch adding updateOverridesEdgeInAdded is based on\n // https://github.com/npm/cli/pull/7025.\n //\n // This logic isn't perfect either. When we have two edges in that have\n // different override sets, then we have to decide which set is correct. This\n // function assumes the more specific override set is applicable, so if we have\n // dependencies A->B->C and A->C and an override set that specifies what happens\n // for C under A->B, this will work even if the new A->C edge comes along and\n // tries to change the override set. The strictly correct logic is not to allow\n // two edges with different overrides to point to the same node, because even\n // if this node can satisfy both, one of its dependencies might need to be\n // different depending on the edge leading to it. However, this might cause a\n // lot of duplication, because the conflict in the dependencies might never\n // actually happen.\n override updateOverridesEdgeInAdded(\n otherOverrideSet: SafeOverrideSet | undefined\n ) {\n if (!otherOverrideSet) {\n // Assuming there are any overrides at all, the overrides field is never\n // undefined for any node at the end state of the tree. So if the new edge's\n // overrides is undefined it will be updated later. So we can wait with\n // updating the node's overrides field.\n return false\n }\n if (!this.overrides) {\n this.overrides = otherOverrideSet\n this.recalculateOutEdgesOverrides()\n return true\n }\n if (this.overrides.isEqual(otherOverrideSet)) {\n return false\n }\n const newOverrideSet = SafeOverrideSet.findSpecificOverrideSet(\n this.overrides,\n otherOverrideSet\n )\n if (newOverrideSet) {\n if (this.overrides.isEqual(newOverrideSet)) {\n return false\n }\n this.overrides = newOverrideSet\n this.recalculateOutEdgesOverrides()\n return true\n }\n // This is an error condition. We can only get here if the new override set\n // is in conflict with the existing.\n const log = getLogger()\n log?.silly('Conflicting override sets', this.name)\n return false\n }\n\n // Patch adding updateOverridesEdgeInRemoved is based on\n // https://github.com/npm/cli/pull/7025.\n override updateOverridesEdgeInRemoved(otherOverrideSet: SafeOverrideSet) {\n // If this edge's overrides isn't equal to this node's overrides,\n // then removing it won't change newOverrideSet later.\n if (!this.overrides || !this.overrides.isEqual(otherOverrideSet)) {\n return false\n }\n let newOverrideSet\n for (const edge of this.edgesIn) {\n const { overrides: edgeOverrides } = edge\n if (newOverrideSet && edgeOverrides) {\n newOverrideSet = SafeOverrideSet.findSpecificOverrideSet(\n edgeOverrides,\n newOverrideSet\n )\n } else {\n newOverrideSet = edgeOverrides\n }\n }\n if (this.overrides.isEqual(newOverrideSet)) {\n return false\n }\n this.overrides = newOverrideSet\n if (newOverrideSet) {\n // Optimization: If there's any override set at all, then no non-extraneous\n // node has an empty override set. So if we temporarily have no override set\n // (for example, we removed all the edges in), there's no use updating all\n // the edges out right now. Let's just wait until we have an actual override\n // set later.\n this.recalculateOutEdgesOverrides()\n }\n return true\n }\n}\n","import { depValid } from './dep-valid'\nimport { SafeNode } from './node'\nimport { SafeOverrideSet } from './override-set'\nimport { getArboristEdgeClassPath } from '../../npm-paths'\n\nimport type { Edge as BaseEdge, DependencyProblem } from '@npmcli/arborist'\n\ntype EdgeClass = Omit<\n BaseEdge,\n | 'accept'\n | 'detach'\n | 'optional'\n | 'overrides'\n | 'peer'\n | 'peerConflicted'\n | 'rawSpec'\n | 'reload'\n | 'satisfiedBy'\n | 'spec'\n | 'to'\n> & {\n optional: boolean\n overrides: SafeOverrideSet | undefined\n peer: boolean\n peerConflicted: boolean\n rawSpec: string\n get accept(): string | undefined\n get spec(): string\n get to(): SafeNode | null\n new (...args: any): EdgeClass\n detach(): void\n reload(hard?: boolean): void\n satisfiedBy(node: SafeNode): boolean\n}\n\nexport type EdgeOptions = {\n type: string\n name: string\n spec: string\n from: SafeNode\n accept?: string | undefined\n overrides?: SafeOverrideSet | undefined\n to?: SafeNode\n}\n\nexport type ErrorStatus = DependencyProblem | 'OK'\n\nexport type Explanation = {\n type: string\n name: string\n spec: string\n bundled: boolean\n overridden: boolean\n error: ErrorStatus | undefined\n rawSpec: string | undefined\n from: object | undefined\n} | null\n\nexport const Edge: EdgeClass = require(getArboristEdgeClassPath())\n\n// The Edge class makes heavy use of private properties which subclasses do NOT\n// have access to. So we have to recreate any functionality that relies on those\n// private properties and use our own \"safe\" prefixed non-conflicting private\n// properties. Implementation code not related to patch https://github.com/npm/cli/pull/8089\n// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/edge.js.\n//\n// The npm application\n// Copyright (c) npm, Inc. and Contributors\n// Licensed on the terms of The Artistic License 2.0\n//\n// An edge in the dependency graph.\n// Represents a dependency relationship of some kind.\nexport class SafeEdge extends Edge {\n #safeError: ErrorStatus | null\n #safeExplanation: Explanation | undefined\n #safeFrom: SafeNode | null\n #safeTo: SafeNode | null\n\n constructor(options: EdgeOptions) {\n const { from } = options\n // Defer to supper to validate options and assign non-private values.\n super(options)\n if (from.constructor !== SafeNode) {\n Reflect.setPrototypeOf(from, SafeNode.prototype)\n }\n this.#safeError = null\n this.#safeExplanation = null\n this.#safeFrom = from\n this.#safeTo = null\n this.reload(true)\n }\n\n override get bundled() {\n return !!this.#safeFrom?.package?.bundleDependencies?.includes(this.name)\n }\n\n override get error() {\n if (!this.#safeError) {\n if (!this.#safeTo) {\n if (this.optional) {\n this.#safeError = null\n } else {\n this.#safeError = 'MISSING'\n }\n } else if (\n this.peer &&\n this.#safeFrom === this.#safeTo.parent &&\n // Patch adding \"?.\" use based on\n // https://github.com/npm/cli/pull/8089.\n !this.#safeFrom?.isTop\n ) {\n this.#safeError = 'PEER LOCAL'\n } else if (!this.satisfiedBy(this.#safeTo)) {\n this.#safeError = 'INVALID'\n }\n // Patch adding \"else if\" condition is based on\n // https://github.com/npm/cli/pull/8089.\n else if (\n this.overrides &&\n this.#safeTo.edgesOut.size &&\n SafeOverrideSet.doOverrideSetsConflict(\n this.overrides,\n this.#safeTo.overrides\n )\n ) {\n // Any inconsistency between the edge's override set and the target's\n // override set is potentially problematic. But we only say the edge is\n // in error if the override sets are plainly conflicting. Note that if\n // the target doesn't have any dependencies of their own, then this\n // inconsistency is irrelevant.\n this.#safeError = 'INVALID'\n } else {\n this.#safeError = 'OK'\n }\n }\n if (this.#safeError === 'OK') {\n return null\n }\n return this.#safeError\n }\n\n // @ts-ignore: Incorrectly typed as a property instead of an accessor.\n override get from() {\n return this.#safeFrom\n }\n\n // @ts-ignore: Incorrectly typed as a property instead of an accessor.\n override get spec(): string {\n if (\n this.overrides?.value &&\n this.overrides.value !== '*' &&\n this.overrides.name === this.name\n ) {\n if (this.overrides.value.startsWith('$')) {\n const ref = this.overrides.value.slice(1)\n // We may be a virtual root, if we are we want to resolve reference\n // overrides from the real root, not the virtual one.\n //\n // Patch adding \"?.\" use based on\n // https://github.com/npm/cli/pull/8089.\n const pkg = this.#safeFrom?.sourceReference\n ? this.#safeFrom?.sourceReference.root.package\n : this.#safeFrom?.root?.package\n if (pkg?.devDependencies?.[ref]) {\n return <string>pkg.devDependencies[ref]\n }\n if (pkg?.optionalDependencies?.[ref]) {\n return <string>pkg.optionalDependencies[ref]\n }\n if (pkg?.dependencies?.[ref]) {\n return <string>pkg.dependencies[ref]\n }\n if (pkg?.peerDependencies?.[ref]) {\n return <string>pkg.peerDependencies[ref]\n }\n throw new Error(`Unable to resolve reference ${this.overrides.value}`)\n }\n return this.overrides.value\n }\n return this.rawSpec\n }\n\n // @ts-ignore: Incorrectly typed as a property instead of an accessor.\n override get to() {\n return this.#safeTo\n }\n\n override detach() {\n this.#safeExplanation = null\n // Patch replacing\n // if (this.#to) {\n // this.#to.edgesIn.delete(this)\n // }\n // this.#from.edgesOut.delete(this.#name)\n // is based on https://github.com/npm/cli/pull/8089.\n this.#safeTo?.deleteEdgeIn(this)\n this.#safeFrom?.edgesOut.delete(this.name)\n this.#safeTo = null\n this.#safeError = 'DETACHED'\n this.#safeFrom = null\n }\n\n // Return the edge data, and an explanation of how that edge came to be here.\n // @ts-ignore: Edge#explain is defined with an unused `seen = []` param.\n override explain() {\n if (!this.#safeExplanation) {\n const explanation: Explanation = {\n type: this.type,\n name: this.name,\n spec: this.spec,\n bundled: false,\n overridden: false,\n error: undefined,\n from: undefined,\n rawSpec: undefined\n }\n if (this.rawSpec !== this.spec) {\n explanation.rawSpec = this.rawSpec\n explanation.overridden = true\n }\n if (this.bundled) {\n explanation.bundled = this.bundled\n }\n if (this.error) {\n explanation.error = this.error\n }\n if (this.#safeFrom) {\n explanation.from = this.#safeFrom.explain()\n }\n this.#safeExplanation = explanation\n }\n return this.#safeExplanation\n }\n\n override reload(hard = false) {\n this.#safeExplanation = null\n // Patch replacing\n // if (this.#from.overrides) {\n // is based on https://github.com/npm/cli/pull/8089.\n let needToUpdateOverrideSet = false\n let newOverrideSet\n let oldOverrideSet\n if (this.#safeFrom?.overrides) {\n newOverrideSet = this.#safeFrom.overrides.getEdgeRule(this)\n if (newOverrideSet && !newOverrideSet.isEqual(this.overrides)) {\n // If there's a new different override set we need to propagate it to\n // the nodes. If we're deleting the override set then there's no point\n // propagating it right now since it will be filled with another value\n // later.\n needToUpdateOverrideSet = true\n oldOverrideSet = this.overrides\n this.overrides = newOverrideSet\n }\n } else {\n this.overrides = undefined\n }\n // Patch adding \"?.\" use based on\n // https://github.com/npm/cli/pull/8089.\n const newTo = this.#safeFrom?.resolve(this.name)\n if (newTo !== this.#safeTo) {\n // Patch replacing\n // this.#to.edgesIn.delete(this)\n // is based on https://github.com/npm/cli/pull/8089.\n this.#safeTo?.deleteEdgeIn(this)\n this.#safeTo = <SafeNode>newTo ?? null\n this.#safeError = null\n this.#safeTo?.addEdgeIn(this)\n } else if (hard) {\n this.#safeError = null\n }\n // Patch adding \"else if\" condition based on\n // https://github.com/npm/cli/pull/8089.\n else if (needToUpdateOverrideSet && this.#safeTo) {\n // Propagate the new override set to the target node.\n this.#safeTo.updateOverridesEdgeInRemoved(oldOverrideSet!)\n this.#safeTo.updateOverridesEdgeInAdded(newOverrideSet)\n }\n }\n\n override satisfiedBy(node: SafeNode) {\n // Patch replacing\n // if (node.name !== this.#name) {\n // return false\n // }\n // is based on https://github.com/npm/cli/pull/8089.\n if (node.name !== this.name || !this.#safeFrom) {\n return false\n }\n // NOTE: this condition means we explicitly do not support overriding\n // bundled or shrinkwrapped dependencies\n if (node.hasShrinkwrap || node.inShrinkwrap || node.inBundle) {\n return depValid(node, this.rawSpec, this.accept, this.#safeFrom)\n }\n // Patch replacing\n // return depValid(node, this.spec, this.#accept, this.#from)\n // is based on https://github.com/npm/cli/pull/8089.\n //\n // If there's no override we just use the spec.\n if (!this.overrides?.keySpec) {\n return depValid(node, this.spec, this.accept, this.#safeFrom)\n }\n // There's some override. If the target node satisfies the overriding spec\n // then it's okay.\n if (depValid(node, this.spec, this.accept, this.#safeFrom)) {\n return true\n }\n // If it doesn't, then it should at least satisfy the original spec.\n if (!depValid(node, this.rawSpec, this.accept, this.#safeFrom)) {\n return false\n }\n // It satisfies the original spec, not the overriding spec. We need to make\n // sure it doesn't use the overridden spec.\n // For example:\n // we might have an ^8.0.0 rawSpec, and an override that makes\n // keySpec=8.23.0 and the override value spec=9.0.0.\n // If the node is 9.0.0, then it's okay because it's consistent with spec.\n // If the node is 8.24.0, then it's okay because it's consistent with the rawSpec.\n // If the node is 8.23.0, then it's not okay because even though it's consistent\n // with the rawSpec, it's also consistent with the keySpec.\n // So we're looking for ^8.0.0 or 9.0.0 and not 8.23.0.\n return !depValid(node, this.overrides.keySpec, this.accept, this.#safeFrom)\n }\n}\n","import path from 'node:path'\nimport process from 'node:process'\n\nimport semver from 'semver'\n\nimport { PackageURL } from '@socketregistry/packageurl-js'\nimport { getManifestData } from '@socketsecurity/registry'\nimport { arrayUnique } from '@socketsecurity/registry/lib/arrays'\nimport { hasOwn } from '@socketsecurity/registry/lib/objects'\nimport {\n fetchPackagePackument,\n resolvePackageName\n} from '@socketsecurity/registry/lib/packages'\nimport { confirm } from '@socketsecurity/registry/lib/prompts'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nimport { getPackagesToQueryFromDiff } from './diff'\nimport constants from '../../../../constants'\nimport {\n batchScan,\n isArtifactAlertCveFixable,\n isArtifactAlertUpgradeFixable\n} from '../../../../utils/alert/artifact'\nimport { uxLookup } from '../../../../utils/alert/rules'\nimport { ColorOrMarkdown } from '../../../../utils/color-or-markdown'\nimport { getSocketDevPackageOverviewUrl } from '../../../../utils/socket-url'\nimport { Edge, SafeEdge } from '../edge'\n\nimport type { ArboristClass, ArboristReifyOptions } from './types'\nimport type { SocketArtifact } from '../../../../utils/alert/artifact'\nimport type { SafeNode } from '../node'\nimport type { Writable } from 'node:stream'\n\ntype PackageJsonType = SafeNode['package']\n\ntype Packument = Exclude<\n Awaited<ReturnType<typeof fetchPackagePackument>>,\n null\n>\n\ntype SocketPackageAlert = {\n key: string\n type: string\n name: string\n version: string\n block: boolean\n fixable: boolean\n raw: any\n}\n\nconst {\n CVE_ALERT_PROPS_FIRST_PATCHED_VERSION_IDENTIFIER,\n LOOP_SENTINEL,\n NPM,\n NPM_REGISTRY_URL,\n OVERRIDES,\n PNPM,\n RESOLUTIONS\n} = constants\n\nconst formatter = new ColorOrMarkdown(false)\n\nfunction findBestPatchVersion(\n node: SafeNode,\n availableVersions: string[],\n vulnerableVersionRange?: string,\n _firstPatchedVersionIdentifier?: string\n): string | null {\n const manifestData = getManifestData(NPM, node.name)\n let eligibleVersions\n if (manifestData && manifestData.name === manifestData.package) {\n const major = semver.major(manifestData.version)\n eligibleVersions = availableVersions.filter(v => semver.major(v) === major)\n } else {\n const major = semver.major(node.version)\n eligibleVersions = availableVersions.filter(\n v =>\n // Filter for versions that are within the current major version\n // and are not in the vulnerable range\n semver.major(v) === major &&\n (!vulnerableVersionRange ||\n !semver.satisfies(v, vulnerableVersionRange))\n )\n }\n return semver.maxSatisfying(eligibleVersions, '*')\n}\n\nexport function findPackageNodes(\n tree: SafeNode,\n packageName: string\n): SafeNode[] {\n const queue: { node: typeof tree }[] = [{ node: tree }]\n const matches: SafeNode[] = []\n let sentinel = 0\n while (queue.length) {\n if (sentinel++ === LOOP_SENTINEL) {\n throw new Error('Detected infinite loop in findPackageNodes')\n }\n const { node: currentNode } = queue.pop()!\n const node = currentNode.children.get(packageName)\n if (node) {\n matches.push((<unknown>node) as SafeNode)\n }\n const children = [...currentNode.children.values()]\n for (let i = children.length - 1; i >= 0; i -= 1) {\n queue.push({ node: (<unknown>children[i]) as SafeNode })\n }\n }\n return matches\n}\n\nlet _translations: typeof import('../../../../../translations.json') | undefined\nfunction getTranslations() {\n if (_translations === undefined) {\n _translations = require(\n // Lazily access constants.rootPath.\n path.join(constants.rootPath, 'translations.json')\n )\n }\n return _translations!\n}\n\nfunction hasOverride(pkgJson: PackageJsonType, name: string): boolean {\n return !!(\n (pkgJson as any)?.[OVERRIDES]?.[name] ||\n (pkgJson as any)?.[RESOLUTIONS]?.[name] ||\n (pkgJson as any)?.[PNPM]?.[OVERRIDES]?.[name]\n )\n}\n\nexport function updateNode(\n node: SafeNode,\n packument: Packument,\n vulnerableVersionRange?: string,\n firstPatchedVersionIdentifier?: string\n): boolean {\n const availableVersions = Object.keys(packument.versions)\n // Find the highest non-vulnerable version within the same major range\n const targetVersion = findBestPatchVersion(\n node,\n availableVersions,\n vulnerableVersionRange,\n firstPatchedVersionIdentifier\n )\n const targetPackument = targetVersion\n ? packument.versions[targetVersion]\n : undefined\n // Check !targetVersion to make TypeScript happy.\n if (!targetVersion || !targetPackument) {\n // No suitable patch version found.\n return false\n }\n // Use Object.defineProperty to override the version.\n Object.defineProperty(node, 'version', {\n configurable: true,\n enumerable: true,\n get: () => targetVersion\n })\n node.package.version = targetVersion\n // Update resolved and clear integrity for the new version.\n const purlObj = PackageURL.fromString(`pkg:npm/${node.name}`)\n node.resolved = `${NPM_REGISTRY_URL}/${node.name}/-/${purlObj.name}-${targetVersion}.tgz`\n const { integrity } = targetPackument.dist\n if (integrity) {\n node.integrity = integrity\n } else {\n delete node.integrity\n }\n if ('deprecated' in targetPackument) {\n node.package['deprecated'] = <string>targetPackument.deprecated\n } else {\n delete node.package['deprecated']\n }\n const newDeps = { ...targetPackument.dependencies }\n const { dependencies: oldDeps } = node.package\n node.package.dependencies = newDeps\n if (oldDeps) {\n for (const oldDepName of Object.keys(oldDeps)) {\n if (!hasOwn(newDeps, oldDepName)) {\n node.edgesOut.get(oldDepName)?.detach()\n }\n }\n }\n for (const newDepName of Object.keys(newDeps)) {\n if (!hasOwn(oldDeps, newDepName)) {\n node.addEdgeOut((<unknown>new Edge({\n from: node,\n name: newDepName,\n spec: newDeps[newDepName],\n type: 'prod'\n })) as SafeEdge)\n }\n }\n return true\n}\n\ntype GetPackageAlertsOptions = {\n output?: Writable\n consolidate?: boolean\n includeExisting?: boolean\n includeUnfixable?: boolean\n}\n\nexport async function getPackagesAlerts(\n arb: SafeArborist,\n options?: GetPackageAlertsOptions\n): Promise<SocketPackageAlert[]> {\n const {\n consolidate = false,\n includeExisting = false,\n includeUnfixable = true,\n output\n } = <GetPackageAlertsOptions>{\n __proto__: null,\n ...options\n }\n const needInfoOn = getPackagesToQueryFromDiff(arb.diff, {\n includeUnchanged: includeExisting\n })\n const purls = arrayUnique(needInfoOn.map(d => d.node.pkgid))\n let { length: remaining } = purls\n const results: SocketPackageAlert[] = []\n if (!remaining) {\n return results\n }\n const pkgJson = (arb.actualTree ?? arb.idealTree)!.package\n const spinner = output ? new Spinner({ stream: output }) : undefined\n const getText = () => `Looking up data for ${remaining} packages`\n const decrementRemaining = () => {\n remaining -= 1\n if (spinner && remaining > 0) {\n spinner.start()\n spinner.setText(getText())\n }\n }\n spinner?.start(getText())\n for await (const artifact of batchScan(purls)) {\n if (!artifact.name || !artifact.version || !artifact.alerts?.length) {\n decrementRemaining()\n continue\n }\n const name = resolvePackageName(artifact)\n const { version } = artifact\n let displayWarning = false\n let sockPkgAlerts = []\n for (const alert of artifact.alerts) {\n // eslint-disable-next-line no-await-in-loop\n const ux = await uxLookup({\n package: { name, version },\n alert: { type: alert.type }\n })\n if (ux.display) {\n displayWarning = !!output\n }\n const fixableCve = isArtifactAlertCveFixable(alert)\n const fixableUpgrade = isArtifactAlertUpgradeFixable(alert)\n if (\n (fixableCve || fixableUpgrade || includeUnfixable) &&\n !(fixableUpgrade && hasOverride(pkgJson, name))\n ) {\n sockPkgAlerts.push({\n name,\n version,\n key: alert.key,\n type: alert.type,\n block: ux.block,\n raw: alert,\n fixable: fixableCve || fixableUpgrade\n })\n }\n }\n if (!includeExisting && sockPkgAlerts.length) {\n // Before we ask about problematic issues, check to see if they\n // already existed in the old version if they did, be quiet.\n const allExisting = needInfoOn.filter(d =>\n d.existing?.pkgid.startsWith(`${name}@`)\n )\n for (const { existing } of allExisting) {\n const oldAlerts: SocketArtifact['alerts'] | undefined =\n // eslint-disable-next-line no-await-in-loop\n (await batchScan([existing!.pkgid]).next()).value?.alerts\n if (oldAlerts?.length) {\n // SocketArtifactAlert and SocketPackageAlert both have the 'key' property.\n sockPkgAlerts = sockPkgAlerts.filter(\n ({ key }) => !oldAlerts.find(a => a.key === key)\n )\n }\n }\n }\n if (consolidate && sockPkgAlerts.length) {\n const highestForCve = new Map<number, SocketPackageAlert>()\n const highestForUpgrade = new Map<number, SocketPackageAlert>()\n const unfixableAlerts: SocketPackageAlert[] = []\n for (const sockPkgAlert of sockPkgAlerts) {\n if (isArtifactAlertCveFixable(sockPkgAlert.raw)) {\n const version =\n sockPkgAlert.raw.props[\n CVE_ALERT_PROPS_FIRST_PATCHED_VERSION_IDENTIFIER\n ]\n const major = semver.major(version)\n const highest =\n highestForCve.get(major)?.raw[\n CVE_ALERT_PROPS_FIRST_PATCHED_VERSION_IDENTIFIER\n ] ?? '0.0.0'\n if (semver.gt(version, highest)) {\n highestForCve.set(major, sockPkgAlert)\n }\n } else if (isArtifactAlertUpgradeFixable(sockPkgAlert.raw)) {\n const { version } = sockPkgAlert\n const major = semver.major(version)\n const highest = highestForUpgrade.get(major)?.version ?? '0.0.0'\n if (semver.gt(version, highest)) {\n highestForUpgrade.set(major, sockPkgAlert)\n }\n } else {\n unfixableAlerts.push(sockPkgAlert)\n }\n }\n sockPkgAlerts = [\n ...unfixableAlerts,\n ...highestForCve.values(),\n ...highestForUpgrade.values()\n ]\n }\n sockPkgAlerts.sort((a, b) => naturalCompare(a.type, b.type))\n spinner?.stop()\n if (displayWarning && sockPkgAlerts.length) {\n const lines = new Set()\n const translations = getTranslations()\n for (const sockPkgAlert of sockPkgAlerts) {\n const attributes = [\n ...(sockPkgAlert.fixable ? ['fixable'] : []),\n ...(sockPkgAlert.block ? [] : ['non-blocking'])\n ]\n const maybeAttributes = attributes.length\n ? ` (${attributes.join('; ')})`\n : ''\n // Based data from { pageProps: { alertTypes } } of:\n // https://socket.dev/_next/data/94666139314b6437ee4491a0864e72b264547585/en-US.json\n const info = (translations.alerts as any)[sockPkgAlert.type]\n const title = info?.title ?? sockPkgAlert.type\n const maybeDesc = info?.description ? ` - ${info.description}` : ''\n // TODO: emoji seems to mis-align terminals sometimes\n lines.add(` ${title}${maybeAttributes}${maybeDesc}\\n`)\n }\n output?.write(\n `(socket) ${formatter.hyperlink(\n `${name}@${version}`,\n getSocketDevPackageOverviewUrl(NPM, name, version)\n )} contains risks:\\n`\n )\n for (const line of lines) {\n output?.write(line)\n }\n }\n results.push(...sockPkgAlerts)\n decrementRemaining()\n }\n spinner?.stop()\n return results\n}\n\ntype CveInfoByPackage = Map<\n string,\n {\n firstPatchedVersionIdentifier: string\n vulnerableVersionRange: string\n }[]\n>\n\ntype GetCveInfoByPackageOptions = {\n excludeUpgrades?: boolean\n}\n\nexport function getCveInfoByPackage(\n alerts: SocketPackageAlert[],\n options?: GetCveInfoByPackageOptions\n): CveInfoByPackage | null {\n const { excludeUpgrades } = <GetCveInfoByPackageOptions>{\n __proto__: null,\n ...options\n }\n let infoByPkg: CveInfoByPackage | null = null\n for (const alert of alerts) {\n if (\n !isArtifactAlertCveFixable(alert.raw) ||\n (excludeUpgrades && getManifestData(NPM, alert.name))\n ) {\n continue\n }\n if (!infoByPkg) {\n infoByPkg = new Map()\n }\n const { name } = alert\n let infos = infoByPkg.get(name)\n if (!infos) {\n infos = []\n infoByPkg.set(name, infos)\n }\n const { firstPatchedVersionIdentifier, vulnerableVersionRange } =\n alert.raw.props\n infos.push({\n firstPatchedVersionIdentifier,\n vulnerableVersionRange\n })\n }\n return infoByPkg\n}\n\nexport async function updateAdvisoryNodes(\n arb: SafeArborist,\n alerts: SocketPackageAlert[]\n) {\n const infoByPkg = getCveInfoByPackage(alerts)\n if (!infoByPkg) {\n // No advisories to process.\n return\n }\n await arb.buildIdealTree()\n const tree = arb.idealTree!\n for (const { 0: name, 1: infos } of infoByPkg) {\n const nodes = findPackageNodes(tree, name)\n const packument =\n nodes.length && infos.length\n ? // eslint-disable-next-line no-await-in-loop\n await fetchPackagePackument(name)\n : null\n if (packument) {\n for (const node of nodes) {\n for (const {\n firstPatchedVersionIdentifier,\n vulnerableVersionRange\n } of infos) {\n updateNode(\n node,\n packument,\n vulnerableVersionRange,\n firstPatchedVersionIdentifier\n )\n }\n }\n }\n }\n}\n\nexport async function updateSocketRegistryNodes(arb: SafeArborist) {\n await arb.buildIdealTree()\n const tree = arb.idealTree!\n for (const { 1: data } of getManifestData(NPM)) {\n const nodes = findPackageNodes(tree, data.name)\n const packument = nodes.length\n ? // eslint-disable-next-line no-await-in-loop\n await fetchPackagePackument(data.name)\n : null\n if (packument) {\n for (const node of nodes) {\n updateNode(node, packument)\n }\n }\n }\n}\n\nexport const kRiskyReify = Symbol('riskyReify')\n\ntype SafeArborist = ArboristClass & {\n [kRiskyReify](options?: ArboristReifyOptions): Promise<SafeNode>\n}\n\nexport async function reify(\n this: SafeArborist,\n ...args: Parameters<InstanceType<ArboristClass>['reify']>\n): Promise<SafeNode> {\n const { stderr: output, stdin: input } = process\n const alerts = await getPackagesAlerts(this, { output })\n if (\n alerts.length &&\n !(await confirm(\n {\n message: 'Accept risks of installing these packages?',\n default: false\n },\n {\n input,\n output\n }\n ))\n ) {\n throw new Error('Socket npm exiting due to risks')\n }\n return await this[kRiskyReify](...args)\n}\n","import process from 'node:process'\n\nimport { kRiskyReify, reify } from './reify'\nimport constants from '../../../../constants'\nimport { getArboristClassPath } from '../../../npm-paths'\n\nimport type { ArboristClass, ArboristReifyOptions } from './types'\nimport type { SafeNode } from '../node'\n\nconst {\n SOCKET_CLI_SAFE_WRAPPER,\n kInternalsSymbol,\n [kInternalsSymbol as unknown as 'Symbol(kInternalsSymbol)']: { getIPC }\n} = constants\n\nexport const Arborist: ArboristClass = require(getArboristClassPath())\n\nexport const kCtorArgs = Symbol('ctorArgs')\n\nexport const SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES = {\n __proto__: null,\n audit: false,\n dryRun: true,\n fund: false,\n ignoreScripts: true,\n progress: false,\n save: false,\n saveBundle: false,\n silent: true\n}\n\n// Implementation code not related to our custom behavior is based on\n// https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/arborist/index.js:\nexport class SafeArborist extends Arborist {\n constructor(...ctorArgs: ConstructorParameters<ArboristClass>) {\n super(\n {\n path:\n (ctorArgs.length ? ctorArgs[0]?.path : undefined) ?? process.cwd(),\n ...(ctorArgs.length ? ctorArgs[0] : undefined),\n ...SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES\n },\n ...ctorArgs.slice(1)\n )\n ;(this as any)[kCtorArgs] = ctorArgs\n }\n\n async [kRiskyReify](\n ...args: Parameters<InstanceType<ArboristClass>['reify']>\n ): Promise<SafeNode> {\n const ctorArgs = (this as any)[kCtorArgs]\n const arb = new Arborist(\n {\n ...(ctorArgs.length ? ctorArgs[0] : undefined),\n progress: false\n },\n ...ctorArgs.slice(1)\n )\n arb.actualTree = this.actualTree\n arb.idealTree = this.idealTree\n const ret = await (arb.reify as (...args: any[]) => Promise<SafeNode>)(\n {\n ...(args.length ? args[0] : undefined),\n progress: false\n },\n ...args.slice(1)\n )\n Object.assign(this, arb)\n return ret\n }\n\n // @ts-ignore Incorrectly typed.\n override async reify(\n this: SafeArborist,\n ...args: Parameters<InstanceType<ArboristClass>['reify']>\n ): Promise<SafeNode> {\n const options = <ArboristReifyOptions>{\n __proto__: null,\n ...(args.length ? args[0] : undefined)\n }\n const safeArgs = [\n {\n ...options,\n progress: false\n },\n ...args.slice(1)\n ]\n if (options.dryRun || !(await getIPC(SOCKET_CLI_SAFE_WRAPPER))) {\n return await this[kRiskyReify](...safeArgs)\n }\n Object.assign(options, SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES)\n const old = args[0]\n args[0] = options\n await super.reify(...safeArgs)\n args[0] = old\n return await Reflect.apply(reify, this, safeArgs)\n }\n}\n"],"names":["getSentry","constructor","cwd","root","dir","encoding","logger","mkdirSync","recursive","WIN32","_warnedSettingPathWin32Missing","dataHome","_settingsPath","yml","path","parsed","prevDir","settings","pendingSave","writeFileSync","SOCKET_CLI_NO_API_TOKEN","_defaultToken","message","proxy","baseUrl","NPM_REGISTRY_URL","includeUnchanged","includeUnknownOrigin","__proto__","length","action","actual","ideal","keep","existing","node","queue","unchanged","abortSignal","method","headers","signal","components","input","crlfDelay","events","index","iteratorResult","enqueueGen","running","type","block","display","defaultValue","iterate_entries","needDefault","orderedRules","target","orderedRulesCollection","resolvedDefaultValue","ux","cachedUX","orgs","cause","deferTo","issueRules","_uxLookup","info","error","success","warning","header","hyperlink","fallback","fallbackToUrl","UNDEFINED_TOKEN","id","transformer","mod","canDedupe","canReplaceWith","overrides","recalculateOutEdgesOverrides","edge","newOverrideSet","from","detach","explain","bundled","overridden","rawSpec","explanation","reload","needToUpdateOverrideSet","RESOLUTIONS","eligibleVersions","matches","_translations","Object","configurable","enumerable","integrity","dependencies","name","spec","consolidate","includeExisting","includeUnfixable","output","stream","remaining","spinner","decrementRemaining","version","package","alert","raw","sockPkgAlerts","key","highestForCve","highestForUpgrade","unfixableAlerts","results","excludeUpgrades","infoByPkg","infos","vulnerableVersionRange","stderr","stdin","default","getIPC","audit","dryRun","fund","ignoreScripts","progress","save","saveBundle","silent","arb","args"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMA;;AAEE;AAA+DA;AAAU;AAC3E;AAIO;AAEA;AAGLC;;;AAGA;AACF;AAEO;AAIL;AACA;;AAEA;AACF;AAEO;AAIL;;AAEE;AACF;;AAEA;AACF;AAEO;AAGL;AACE;AACF;AACA;AACF;;AC7CO;AAEHC;AAAqC;AAEvC;;AACQC;AAAK;;AAEb;AACE;;;AAGI;;AAEA;AACE;AACF;;AAEJ;AACAC;AACF;AACA;AACF;AAOO;AAIL;AACE;AACAC;AACF;AACF;AAEO;AAIL;AACE;AACAA;AACF;AACF;AAEO;;AAIH;;AAEF;AACF;AAEO;;AAIH;;AAEF;AACF;;AC7DA;AAEA;AAgBA;AACA;;;AAGI;AACA;AACE;AACA;;;AAME;AACEC;AACF;AACF;AACEC;AAAwCC;AAAgB;AAC1D;AACF;AACF;AACA;AACF;AAEA;AACA;AACA;;AAEI;;AACQC;AAAM;AACd;;AAIE;;AAEIC;AACAJ;AACF;AACF;AACEK;AAMF;AACF;AACAC;AAGF;AACA;AACF;AAEA;;AAEE;AACE;AACF;AACA;AACF;AAEO;;AAEL;;;AAGE;;;AAGEC;AACF;AACA;;;AAGMC;AACAC;;AAEJ;AACE;AACF;AACF;AACAC;;AAEF;AACA;AACF;AAEO;;AAIP;AAEA;AACO;AAIL;AACEC;;AAEAC;;AAEEA;AACA;AACA;AACEC;AAIF;AACF;AACF;AACF;;AClIA;AAAQC;AAAwB;;AAEhC;AACA;AACE;AAEA;AACF;;AAEA;AACA;AACE;AAEA;AACF;;AAEA;AACA;AACO;AACL;AACA;AACEC;AACF;AACE;AAEE;AACA;;;AAKJ;AACA;AACF;AAEO;AACL;AACF;AAEO;;;AAODC;AAEF;AACAD;AACF;;AAEE;AACF;AACA;;;AAGmCE;AAAM;;AACJA;;AAC/B;AAEJC;AACA;;AAEF;AACF;;AC1EA;;AAAuBC;AAAiB;AAExC;;;;AAIE;AACF;AAYO;;AAIGC;AAA0BC;AAA6B;AAG7DC;;;;AAIF;;AAEE;AACF;AACA;;;AAEMC;AAAoB;;;AAGtB;AACF;AACA;;AACQC;AAAO;AACf;AACE;AACA;AACA;AACA;;AACQC;AAAiBC;AAAe;AACxC;;;;AAIIC;AACA;AAIEC;AACF;AACF;AAIF;;AAEA;AACA;;;AAMMC;AACAD;AACF;AACF;AACF;AACF;AACA;AACEE;AACF;AACF;AACA;;AACUC;AAAU;AAClB;AAAkBR;;AAChB;;;AAMIM;AACAD;AACF;AACF;AACF;AACF;AACA;AACF;;AC1BA;;;;;;;;AAQEI;AACF;AAEA;AAGE;AACA;AACE;AAAA;AAEEC;AACAC;;;AAGAC;AACF;AAGIC;;AAAqD;AACvD;AAEJ;;AACQ;;AAC+BD;AAAoB;AAE3D;;;AAGA;AACA;AACEE;AACAC;AACAH;AACF;AACA;AACE;AACF;AACF;AAEO;AAcL;AACA;AACA;AACA;AACA;;;;AAIEI;AACF;;AACQhB;AAAoB;;;;;AAKxB;AACA;AACF;;AAEAiB;AACA;;;;AAIA;;;;AAIA;;;AAGuCC;AAAoB;;AAE7D;;AAEEC;AACF;AACA;AACE;;;AACmBD;AAAe;AAGlC;AACAE;;AAKE;AACAD;AACF;;AAEE;;AAEF;AACF;AACA;;AAEEH;AACF;AACF;AAEO;;AAGGK;AAAK;AACb;AAQF;AAEO;AAGL;AACF;;AClLA;AAAQZ;AAAY;AAEpB;AACEa;AACAC;AACF;AAEA;AACED;AACAC;AACF;AAEA;AACED;AACAC;AACF;;AAEA;AACA;AACA;AACA;;AASIC;AAAiBvB;;AACnB;AACEuB;AAAiBvB;;AACnB;;;;AAIAwB;AACE;AACE;AACEC;AACA;AACAJ;AACAC;AACA;AACF;AACF;AACA;AACAD;AACAC;AACF;AACA;AACE;AACAD;AACAC;AACF;;;AACgBA;;AAClB;;AAEA;AACA;;AAII;AACF;AACA;;AACUtB;AAAO;AACf;AACE;AACF;AACF;AACA;AACF;;AAEA;AACA;AAGE;AACE;AACF;;AACQA;AAAO;;AAEb;AACF;AACE;AACF;AACA;AACF;AAMO;AAIL;AAEA;;AACUoB;;AACR;AACA;AACE;AACF;;AAEA;;AAEE;;AAEE;;AAEE;AACF;AACA;AACA;AACEM;AACF;AACAC;AACF;AACAC;AACF;;AAKA;AACE5B;;;AAGA6B;AAAyB7B;;AAC3B;AACE6B;AAAyB7B;;AAC3B;AACA8B;AACAC;AACA;;AAEJ;AAEA;AACO;;AAIH;;AACgBpB;AAAoB;AACtC;;AAEF;;AAEA;AACA;;;AACgBxB;;;;AAGV;AACA;;AAIA;;AAKA;AACE;AACE6C;AACF;AACF;AACA;;;AAGA;;AAIA;;;;;;AAMA;AACA;AAIE;AAGIC;AACF;AAEJ;AACA;AACF;AACF;;AAEA;AACA;AACA;AAAa;AAAM;AAAO;;;AAGxB;AACF;AAEA;AACA;AACE9C;;AAEEA;;AAEI+C;AACA;AACA;AACA;AACA;AACA;AACAC;AAKF;AACF;AACF;AACF;AACAC;AACF;;ACxQA;AACEtC;AACAuC;AACAC;AACAC;AACAC;AACF;AAEO;;AAIH;AACF;;AAGE;AACF;AAEAC;AACE;AAGF;AAEAC;AAIIC;AACAC;;AAMF;AACE;;AAII;AACN;AACA;AACF;;AAKE;AACF;;AAGE;AACF;;;AAMA;;AAGE;;AAIF;;;AAIA;AACF;;AChFO;;AAEP;AAEO;AAKL;AACF;;ACNO;;ACDP;AAAQC;AAAgB;AAaxB;AAIE;AACE;AACA;AACA;AACEC;AACAC;AACF;AACED;;AAEF;;AAEE;AACA;;;AAGE;AACF;;AAEJ;AACA;AACF;AAOA;AACO;;;AAMC;AACA;AACAE;AAIN;AACA;AACF;;AChCA;;AAEA;AACA;AACO;AACL;AACA;AACA;AAIE;AACA;AACA;;AAEF;;AAEA;AACA;AACA;AAIE;AAKE;AACE;AACF;AACF;AACA;AAKE;AACE;AACF;AACF;AACA;AACA;;AAEA;AACF;;AAEA;AACA;;;AAGI;AACF;AACA;AAAa;AAAQ;AAAoB;;;AAGrC;AACF;AACA;AACE;AACF;AACA;AACE;AACF;AACF;AACA;AACF;;;AAII;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACE;AACE;AACF;AACA;AACF;;AAEE;AAGE;AACF;AACA;AACF;AACA;AACA;AACA;AACA;AACF;AACA;AACF;;AAEA;AACA;;;AAGI;AACF;;AAEE;AACF;AACA;AAIE;AACF;AACA;AACE;AACF;AACA;;AAEA;;AAEF;AACF;;ACpFA;;AAEA;AACA;AACO;AACL;AACA;AACA;AACSC;AACP;AACA;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;;AAEE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACE;AACF;AACA;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACSC;AACP;AACE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;;;AAGI;AACF;AACF;;AAEI;AACF;AACF;AACF;AACA;AACA;AACA;AACA;AACA;;AAEA;AACE;AACF;;;;;;AAME;AACF;AACA;AACF;;AAEA;;AAEE;;AACQC;AAAU;AAClB;AACE;AACF;AACF;;AAGE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AACF;AACA;AACA;;AAEF;;AAEA;;AAEE;AACA;AACA;;AAME;AACF;AACA;AACA;AACA;AACA;AACA;AACA;;AAMI;AACE;AACF;AACF;AACF;AACA;AACF;;AAGE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACQA;AAAU;AAClB;;AAEA;;;;;;AAME;AACF;AACF;;AAEA;AACA;AACSC;AACP;;AAEEC;;;AAGA;AACF;AACF;;AAEA;;AAEE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AAAuCF;AAAc;AACvD;;;;;;AAME;AACF;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAKI;AACA;AACA;AACA;AACA;AACF;AACA;;;AAGE;AACF;;AAEE;AACF;;AAKA;;AAEI;AACF;;;AAGA;AACF;AACA;AACA;AACA;;AAEA;AACF;;AAEA;AACA;;AAEE;AACA;AACA;AACE;AACF;AACA;AACA;;AACUA;AAAyB;;;AAMjC;AACEG;AACF;AACF;;AAEE;AACF;;AAEA;AACE;AACA;AACA;AACA;AACA;;AAEF;AACA;AACF;AACF;;ACrUO;;AAEP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACL;AACA;AACA;AACA;;;AAGUC;AAAK;AACb;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACF;;AAGE;AACF;;AAGE;AACE;;AAEI;AACF;AACE;AACF;AACF;AAGE;AACA;AACA;AAEA;AACF;AACE;AACF;AACA;AACA;AAAA;AASE;AACA;AACA;AACA;AACA;AACA;AACF;AACE;AACF;AACF;AACA;AACE;AACF;;AAEF;;AAEA;;;AAGA;;AAEA;;;;;AASM;AACA;AACA;AACA;AACA;;AAIA;AACE;AACF;AACA;AACE;AACF;AACA;AACE;AACF;AACA;AACE;AACF;;AAEF;AACA;AACF;;AAEF;;AAEA;;;AAGA;AAESC;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACF;;AAEA;AACA;AACSC;AACP;AACE;;;;AAIEC;AACAC;AACArB;AACAiB;AACAK;;AAEF;AACEC;;AAEF;;AAEEA;AACF;;AAEEA;AACF;AACA;;AAEA;AACA;AACF;;AAEF;AAESC;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;;;AAGI;AACA;AACA;AACA;AACAC;;;AAGF;AACF;;AAEA;AACA;AACA;AACA;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACF;AACA;AACA;AAAA;AAEE;AACA;AACA;AACF;AACF;;AAGE;AACA;AACA;AACA;AACA;AACA;AACE;AACF;AACA;AACA;;AAEE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AACF;;AC/QA;;;;;;;AAOEC;AACF;AAEA;AAEA;;AAOE;;;AAGEC;AACF;;AAEEA;AAEI;AACA;;AAKN;AACA;AACF;AAEO;;AAIqC5D;AAAW;;;;AAInD;AACE;AACF;;AACQA;AAAkB;;AAE1B;AACE6D;AACF;;AAEA;;;AACwD;AACxD;AACF;AACA;AACF;AAEA;AACA;;AAEIC;AACE;;AAGJ;AACA;AACF;AAEA;AACE;AAKF;AAEO;;AAOL;;;AAUA;AACA;AACE;AACA;AACF;AACA;AACAC;AACEC;AACAC;;AAEF;AACAjE;AACA;;AAEAA;;AACQkE;;AACR;;AAEA;;AAEA;;;AAGA;AACE;AACF;AACA;AAAkB;;;AACVC;;AACRnE;AACA;;AAEI;;AAEA;AACF;AACF;;AAEE;AACEA;AACIkD;AACAkB;AACAC;AACAtD;AACF;AACJ;AACF;AACA;AACF;AASO;;AAKHuD;AACAC;AACAC;AACAC;AACF;AACEhF;;;AAGF;AACEF;AACF;AACA;;AACMG;AAAkB;;;AAGtB;AACF;;AAEA;AAAuCgF;;AACvC;;AAEEC;AACA;;AAEEC;AACF;;AAEFA;AACA;AACE;AACEC;AACA;AACF;AACA;;AACQC;AAAQ;;;AAGhB;AACE;AACA;AACEC;;AAAiBD;;AACjBE;;AAA0B;AAC5B;;;AAGA;AACA;AACA;AACA;;;;;;;AAUIC;;AAEF;AACF;AACF;AACA;AACE;AACA;;AAIA;AAAalF;;AACX;AACE;AACA;;AAEA;AACAmF;AACKC;AAAI;AAEX;AACF;AACF;AACA;AACE;AACA;;AAEA;AACE;;AAKE;AACA;;AAKEC;AACF;;;AAEQN;AAAQ;AAChB;;;AAGEO;AACF;AACF;AACEC;AACF;AACF;AACAJ;AAKF;AACAA;;AAEA;AACE;AACA;AACA;;AAKE;AAGA;AACA;;;AAGA;AACA;;AAEF;;AAOA;AACET;AACF;AACF;AACAc;AACAV;AACF;;AAEA;AACF;AAcO;;AAIGW;AAAgB;AACtB/F;;;;AAIF;AACE;AAIE;AACF;;AAEEgG;AACF;;AACQrB;AAAK;AACb;;AAEEsB;AACAD;AACF;;;AACuCE;AAAuB;;;AAI5DA;AACF;AACF;AACA;AACF;AAuDO;AAMA;;AAIGC;AAAgBC;AAAa;AACrC;AAA+CpB;AAAO;AACtD;AAIMtF;AACA2G;AACF;;AAGErB;;AAIJ;AACF;;AAEF;;ACleA;;;AAGE;AAA+DsB;AAAO;AACxE;AAEO;AAEA;AAEA;AACLtG;AACAuG;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACF;;AAEA;AACA;AACO;;AAEH;AAEI5H;;;;AAOF;AACJ;AAEA;AAGE;AACA;;AAGIyH;;AAIJI;AACAA;AACA;;AAGIJ;;AAIJrC;AACA;AACF;;AAEA;AACA;AAIE;AACEtE;;;;AAKE;AACA2G;;;;AAMJ;AACArC;AACA;AACA0C;AACA;AACAA;;AAEF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;","debugId":"30e57097-6100-437d-9b6d-904604eeaa8d"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/utils/errors.ts","../../src/utils/fs.ts","../../src/utils/settings.ts","../../src/utils/sdk.ts","../../src/shadow/arborist/lib/arborist/diff.ts","../../src/utils/alert/artifact.ts","../../src/utils/alert/rules.ts","../../src/utils/color-or-markdown.ts","../../src/utils/socket-url.ts","../../src/shadow/arborist/lib/dep-valid.ts","../../src/shadow/proc-log.ts","../../src/shadow/arborist/lib/override-set.ts","../../src/shadow/arborist/lib/node.ts","../../src/shadow/arborist/lib/edge.ts","../../src/shadow/arborist/lib/arborist/reify.ts","../../src/shadow/arborist/lib/arborist/index.ts"],"sourcesContent":["import { setTimeout as wait } from 'node:timers/promises'\n\nimport { debugLog } from '@socketsecurity/registry/lib/debug'\n\nimport constants from '../constants'\n\nconst {\n kInternalsSymbol,\n [kInternalsSymbol as unknown as 'Symbol(kInternalsSymbol)']: { getSentry }\n} = constants\n\ntype EventHintOrCaptureContext = { [key: string]: any } | Function\n\nexport class AuthError extends Error {}\n\nexport class InputError extends Error {\n public body: string | undefined\n\n constructor(message: string, body?: string) {\n super(message)\n this.body = body\n }\n}\n\nexport async function captureException(\n exception: unknown,\n hint?: EventHintOrCaptureContext | undefined\n): Promise<string> {\n const result = captureExceptionSync(exception, hint)\n // \"Sleep\" for a second, just in case, hopefully enough time to initiate fetch.\n await wait(1000)\n return result\n}\n\nexport function captureExceptionSync(\n exception: unknown,\n hint?: EventHintOrCaptureContext | undefined\n): string {\n const Sentry = getSentry()\n if (!Sentry) {\n return ''\n }\n debugLog('captureException: Sending exception to Sentry.')\n return <string>Sentry.captureException(exception, hint)\n}\n\nexport function isErrnoException(\n value: unknown\n): value is NodeJS.ErrnoException {\n if (!(value instanceof Error)) {\n return false\n }\n return (value as NodeJS.ErrnoException).code !== undefined\n}\n","import { promises as fs, readFileSync as fsReadFileSync } from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\n\nimport type { Abortable } from 'node:events'\nimport type { ObjectEncodingOptions, OpenMode, PathLike } from 'node:fs'\nimport type { FileHandle } from 'node:fs/promises'\n\nexport async function findUp(\n name: string | string[],\n { cwd = process.cwd() }: { cwd: string }\n): Promise<string | undefined> {\n let dir = path.resolve(cwd)\n const { root } = path.parse(dir)\n const names = [name].flat()\n while (dir && dir !== root) {\n for (const name of names) {\n const filePath = path.join(dir, name)\n try {\n // eslint-disable-next-line no-await-in-loop\n const stats = await fs.stat(filePath)\n if (stats.isFile()) {\n return filePath\n }\n } catch {}\n }\n dir = path.dirname(dir)\n }\n return undefined\n}\n\nexport type ReadFileOptions = ObjectEncodingOptions &\n Abortable & {\n flag?: OpenMode | undefined\n }\n\nexport async function readFileBinary(\n filepath: PathLike | FileHandle,\n options?: ReadFileOptions | undefined\n): Promise<Buffer> {\n return <Buffer>await fs.readFile(filepath, <ReadFileOptions>{\n ...options,\n encoding: 'binary'\n })\n}\n\nexport async function readFileUtf8(\n filepath: PathLike | FileHandle,\n options?: ReadFileOptions | undefined\n): Promise<string> {\n return <string>await fs.readFile(filepath, <ReadFileOptions>{\n ...options,\n encoding: 'utf8'\n })\n}\n\nexport function safeReadFile(\n ...args: Parameters<typeof fs.readFile>\n): ReturnType<typeof fs.readFile> | undefined {\n try {\n return fs.readFile(...args)\n } catch {}\n return undefined\n}\n\nexport function safeReadFileSync(\n ...args: Parameters<typeof fsReadFileSync>\n): ReturnType<typeof fsReadFileSync> | undefined {\n try {\n return fsReadFileSync(...args)\n } catch {}\n return undefined\n}\n","import { mkdirSync, writeFileSync } from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\n\nimport config from '@socketsecurity/config'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { safeReadFileSync } from './fs'\nimport constants from '../constants'\n\nconst LOCALAPPDATA = 'LOCALAPPDATA'\n\nconst supportedApiKeys = new Set([\n 'apiBaseUrl',\n 'apiKey',\n 'apiProxy',\n 'enforcedOrgs'\n])\n\ninterface Settings {\n apiBaseUrl?: string | null | undefined\n apiKey?: string | null | undefined\n apiProxy?: string | null | undefined\n enforcedOrgs?: string[] | Readonly<string[]> | null | undefined\n // apiToken is an alias for apiKey.\n apiToken?: string | null | undefined\n}\n\nlet _settings: Settings | undefined\nfunction getSettings(): Settings {\n if (_settings === undefined) {\n _settings = <Settings>{}\n const settingsPath = getSettingsPath()\n if (settingsPath) {\n const raw = <string | undefined>safeReadFileSync(settingsPath, 'utf8')\n if (raw) {\n try {\n Object.assign(\n _settings,\n JSON.parse(Buffer.from(raw, 'base64').toString())\n )\n } catch {\n logger.warn(`Failed to parse settings at ${settingsPath}`)\n }\n } else {\n mkdirSync(path.dirname(settingsPath), { recursive: true })\n }\n }\n }\n return _settings\n}\n\nlet _settingsPath: string | undefined\nlet _warnedSettingPathWin32Missing = false\nfunction getSettingsPath(): string | undefined {\n if (_settingsPath === undefined) {\n // Lazily access constants.WIN32.\n const { WIN32 } = constants\n let dataHome: string | undefined = WIN32\n ? process.env[LOCALAPPDATA]\n : process.env['XDG_DATA_HOME']\n if (!dataHome) {\n if (WIN32) {\n if (!_warnedSettingPathWin32Missing) {\n _warnedSettingPathWin32Missing = true\n logger.warn(`Missing %${LOCALAPPDATA}%`)\n }\n } else {\n dataHome = path.join(\n os.homedir(),\n ...(process.platform === 'darwin'\n ? ['Library', 'Application Support']\n : ['.local', 'share'])\n )\n }\n }\n _settingsPath = dataHome\n ? path.join(dataHome, 'socket/settings')\n : undefined\n }\n return _settingsPath\n}\n\nfunction normalizeSettingsKey(key: string): string {\n const normalizedKey = key === 'apiToken' ? 'apiKey' : key\n if (!supportedApiKeys.has(normalizedKey)) {\n throw new Error(`Invalid settings key: ${normalizedKey}`)\n }\n return normalizedKey\n}\n\nexport function findSocketYmlSync() {\n let prevDir = null\n let dir = process.cwd()\n while (dir !== prevDir) {\n let ymlPath = path.join(dir, 'socket.yml')\n let yml = <string | undefined>safeReadFileSync(ymlPath, 'utf8')\n if (yml === undefined) {\n ymlPath = path.join(dir, 'socket.yaml')\n yml = <string | undefined>safeReadFileSync(ymlPath, 'utf8')\n }\n if (typeof yml === 'string') {\n try {\n return {\n path: ymlPath,\n parsed: config.parseSocketConfig(yml)\n }\n } catch {\n throw new Error(`Found file but was unable to parse ${ymlPath}`)\n }\n }\n prevDir = dir\n dir = path.join(dir, '..')\n }\n return null\n}\n\nexport function getSetting<Key extends keyof Settings>(\n key: Key\n): Settings[Key] {\n return getSettings()[<Key>normalizeSettingsKey(key)]\n}\n\nlet pendingSave = false\nexport function updateSetting<Key extends keyof Settings>(\n key: Key,\n value: Settings[Key]\n): void {\n const settings = getSettings()\n ;(settings as any)[<Key>normalizeSettingsKey(key)] = value\n if (!pendingSave) {\n pendingSave = true\n process.nextTick(() => {\n pendingSave = false\n const settingsPath = getSettingsPath()\n if (settingsPath) {\n writeFileSync(\n settingsPath,\n Buffer.from(JSON.stringify(settings)).toString('base64')\n )\n }\n })\n }\n}\n","import process from 'node:process'\n\nimport { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'\n\nimport isInteractive from '@socketregistry/is-interactive/index.cjs'\nimport { SOCKET_PUBLIC_API_TOKEN } from '@socketsecurity/registry/lib/constants'\nimport { password } from '@socketsecurity/registry/lib/prompts'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\nimport { SocketSdk, createUserAgentFromPkgJson } from '@socketsecurity/sdk'\n\nimport { AuthError } from './errors'\nimport { getSetting } from './settings'\nimport constants from '../constants'\n\nconst { SOCKET_CLI_NO_API_TOKEN } = constants\n\n// The API server that should be used for operations.\nfunction getDefaultApiBaseUrl(): string | undefined {\n const baseUrl =\n process.env['SOCKET_SECURITY_API_BASE_URL'] || getSetting('apiBaseUrl')\n return isNonEmptyString(baseUrl) ? baseUrl : undefined\n}\n\n// The API server that should be used for operations.\nfunction getDefaultHttpProxy(): string | undefined {\n const apiProxy =\n process.env['SOCKET_SECURITY_API_PROXY'] || getSetting('apiProxy')\n return isNonEmptyString(apiProxy) ? apiProxy : undefined\n}\n\n// This API key should be stored globally for the duration of the CLI execution.\nlet _defaultToken: string | undefined\nexport function getDefaultToken(): string | undefined {\n // Lazily access constants.ENV[SOCKET_CLI_NO_API_TOKEN].\n if (constants.ENV[SOCKET_CLI_NO_API_TOKEN]) {\n _defaultToken = undefined\n } else {\n const key =\n process.env['SOCKET_SECURITY_API_TOKEN'] ||\n // Keep 'SOCKET_SECURITY_API_KEY' as an alias of 'SOCKET_SECURITY_API_TOKEN'.\n // TODO: Remove 'SOCKET_SECURITY_API_KEY' alias.\n process.env['SOCKET_SECURITY_API_KEY'] ||\n getSetting('apiToken') ||\n _defaultToken\n _defaultToken = isNonEmptyString(key) ? key : undefined\n }\n return _defaultToken\n}\n\nexport function getPublicToken(): string {\n return getDefaultToken() ?? SOCKET_PUBLIC_API_TOKEN\n}\n\nexport async function setupSdk(\n apiToken: string | undefined = getDefaultToken(),\n apiBaseUrl: string | undefined = getDefaultApiBaseUrl(),\n proxy: string | undefined = getDefaultHttpProxy()\n): Promise<SocketSdk> {\n if (typeof apiToken !== 'string' && isInteractive()) {\n apiToken = await password({\n message:\n 'Enter your Socket.dev API key (not saved, use socket login to persist)'\n })\n _defaultToken = apiToken\n }\n if (!apiToken) {\n throw new AuthError('You need to provide an API key')\n }\n return new SocketSdk(apiToken, {\n agent: proxy\n ? {\n http: new HttpProxyAgent({ proxy }),\n https: new HttpsProxyAgent({ proxy })\n }\n : undefined,\n baseUrl: apiBaseUrl,\n // Lazily access constants.rootPkgJsonPath.\n userAgent: createUserAgentFromPkgJson(require(constants.rootPkgJsonPath))\n })\n}\n","import constants from '../../../../constants'\n\nimport type { Diff } from './types'\nimport type { SafeNode } from '../node'\n\nconst { LOOP_SENTINEL, NPM_REGISTRY_URL } = constants\n\nfunction getUrlOrigin(input: string): string {\n try {\n return URL.parse(input)?.origin ?? ''\n } catch {}\n return ''\n}\n\nexport type PackageDetail = {\n node: SafeNode\n existing?: SafeNode | undefined\n}\n\ntype GetPackagesToQueryFromDiffOptions = {\n includeUnchanged?: boolean | undefined\n includeUnknownOrigin?: boolean | undefined\n}\n\nexport function getPackagesToQueryFromDiff(\n diff_: Diff | null,\n options?: GetPackagesToQueryFromDiffOptions | undefined\n): PackageDetail[] {\n const { includeUnchanged = false, includeUnknownOrigin = false } = <\n GetPackagesToQueryFromDiffOptions\n >{\n __proto__: null,\n ...options\n }\n const details: PackageDetail[] = []\n // `diff_` is `null` when `npm install --package-lock-only` is passed.\n if (!diff_) {\n return details\n }\n const queue: Diff[] = [...diff_.children]\n let pos = 0\n let { length: queueLength } = queue\n while (pos < queueLength) {\n if (pos === LOOP_SENTINEL) {\n throw new Error('Detected infinite loop while walking Arborist diff')\n }\n const diff = queue[pos++]!\n const { action } = diff\n if (action) {\n // The `pkgNode`, i.e. the `ideal` node, will be `undefined` if the diff\n // action is 'REMOVE'\n // The `oldNode`, i.e. the `actual` node, will be `undefined` if the diff\n // action is 'ADD'.\n const { actual: oldNode, ideal: pkgNode } = diff\n let existing: SafeNode | undefined\n let keep = false\n if (action === 'CHANGE') {\n if (pkgNode?.package.version !== oldNode?.package.version) {\n keep = true\n if (\n oldNode?.package.name &&\n oldNode.package.name === pkgNode?.package.name\n ) {\n existing = oldNode\n }\n } else {\n // TODO: Add proper debug mode.\n // logger.warn('SKIPPING META CHANGE ON', diff)\n }\n } else {\n keep = action !== 'REMOVE'\n }\n if (keep && pkgNode?.resolved && (!oldNode || oldNode.resolved)) {\n if (\n includeUnknownOrigin ||\n getUrlOrigin(pkgNode.resolved) === NPM_REGISTRY_URL\n ) {\n details.push({\n node: pkgNode,\n existing\n })\n }\n }\n }\n for (const child of diff.children) {\n queue[queueLength++] = child\n }\n }\n if (includeUnchanged) {\n const { unchanged } = diff_!\n for (let i = 0, { length } = unchanged; i < length; i += 1) {\n const pkgNode = unchanged[i]!\n if (\n includeUnknownOrigin ||\n getUrlOrigin(pkgNode.resolved!) === NPM_REGISTRY_URL\n ) {\n details.push({\n node: pkgNode,\n existing: pkgNode\n })\n }\n }\n }\n return details\n}\n","import events from 'node:events'\nimport https from 'node:https'\nimport readline from 'node:readline'\n\nimport constants from '../../constants'\nimport { getPublicToken } from '../sdk'\n\nimport type { IncomingMessage } from 'node:http'\n\nexport type CveAlertType = 'cve' | 'mediumCVE' | 'mildCVE' | 'criticalCVE'\n\nexport type ArtifactAlertCveFixable = Omit<\n SocketArtifactAlert,\n 'props' | 'title'\n> & {\n type: CveAlertType\n props: {\n firstPatchedVersionIdentifier: string\n vulnerableVersionRange: string\n [key: string]: any\n }\n}\n\nexport type ArtifactAlertFixable = ArtifactAlertCveFixable & {\n type: CveAlertType | 'socketUpgradeAvailable'\n}\n\nexport type SocketArtifactAlert = {\n key: string\n type: string\n severity: string\n category: string\n action?: string | undefined\n actionPolicyIndex?: number | undefined\n file?: string | undefined\n props?: any | undefined\n start?: number | undefined\n end?: number | undefined\n}\n\nexport type SocketArtifact = {\n type: string\n name: string\n namespace?: string | undefined\n version?: string | undefined\n subpath?: string | undefined\n release?: string | undefined\n id?: string | undefined\n author?: string[]\n license?: string | undefined\n licenseDetails?: {\n spdxDisj: string\n provenance: string\n filepath: string\n match_strength: number\n }[]\n licenseAttrib?: {\n attribText: string\n attribData: {\n purl: string\n foundInFilepath: string\n spdxExpr: string\n foundAuthors: string[]\n }[]\n }[]\n score?: {\n supplyChain: number\n quality: number\n maintenance: number\n vulnerability: number\n license: number\n overall: number\n }\n alerts?: SocketArtifactAlert[]\n size?: number | undefined\n batchIndex?: number | undefined\n}\n\nconst {\n ALERT_TYPE_CRITICAL_CVE,\n ALERT_TYPE_CVE,\n ALERT_TYPE_MEDIUM_CVE,\n ALERT_TYPE_MILD_CVE,\n ALERT_TYPE_SOCKET_UPGRADE_AVAILABLE,\n CVE_ALERT_PROPS_FIRST_PATCHED_VERSION_IDENTIFIER,\n CVE_ALERT_PROPS_VULNERABLE_VERSION_RANGE,\n abortSignal\n} = constants\n\nasync function* createBatchGenerator(\n chunk: string[]\n): AsyncGenerator<SocketArtifact> {\n // Adds the first 'abort' listener to abortSignal.\n const req = https\n // Lazily access constants.BATCH_PURL_ENDPOINT.\n .request(constants.BATCH_PURL_ENDPOINT, {\n method: 'POST',\n headers: {\n Authorization: `Basic ${btoa(`${getPublicToken()}:`)}`\n },\n signal: abortSignal\n })\n .end(\n JSON.stringify({\n components: chunk.map(id => ({ purl: `pkg:npm/${id}` }))\n })\n )\n // Adds the second 'abort' listener to abortSignal.\n const { 0: res } = <[IncomingMessage]>(\n await events.once(req, 'response', { signal: abortSignal })\n )\n const ok = res.statusCode! >= 200 && res.statusCode! <= 299\n if (!ok) {\n throw new Error(`Socket API Error: ${res.statusCode}`)\n }\n const rli = readline.createInterface({\n input: res,\n crlfDelay: Infinity,\n signal: abortSignal\n })\n for await (const line of rli) {\n yield <SocketArtifact>JSON.parse(line)\n }\n}\n\nexport async function* batchScan(\n pkgIds: string[],\n concurrencyLimit = 50\n): AsyncGenerator<SocketArtifact> {\n type GeneratorStep = {\n generator: AsyncGenerator<SocketArtifact>\n iteratorResult: IteratorResult<SocketArtifact>\n }\n type GeneratorEntry = {\n generator: AsyncGenerator<SocketArtifact>\n promise: Promise<GeneratorStep>\n }\n type ResolveFn = (value: GeneratorStep) => void\n\n // The createBatchGenerator method will add 2 'abort' event listeners to\n // abortSignal so we multiply the concurrencyLimit by 2.\n const neededMaxListeners = concurrencyLimit * 2\n // Increase abortSignal max listeners count to avoid Node's MaxListenersExceededWarning.\n const oldAbortSignalMaxListeners = events.getMaxListeners(abortSignal)\n let abortSignalMaxListeners = oldAbortSignalMaxListeners\n if (oldAbortSignalMaxListeners < neededMaxListeners) {\n abortSignalMaxListeners = oldAbortSignalMaxListeners + neededMaxListeners\n events.setMaxListeners(abortSignalMaxListeners, abortSignal)\n }\n const { length: pkgIdsCount } = pkgIds\n const running: GeneratorEntry[] = []\n let index = 0\n const enqueueGen = () => {\n if (index >= pkgIdsCount) {\n // No more work to do.\n return\n }\n const chunk = pkgIds.slice(index, index + 25)\n index += 25\n const generator = createBatchGenerator(chunk)\n continueGen(generator)\n }\n const continueGen = (generator: AsyncGenerator<SocketArtifact>) => {\n let resolveFn: ResolveFn\n running.push({\n generator,\n promise: new Promise<GeneratorStep>(resolve => (resolveFn = resolve))\n })\n void generator\n .next()\n .then(res => resolveFn!({ generator, iteratorResult: res }))\n }\n // Start initial batch of generators.\n while (running.length < concurrencyLimit && index < pkgIdsCount) {\n enqueueGen()\n }\n while (running.length > 0) {\n // eslint-disable-next-line no-await-in-loop\n const { generator, iteratorResult } = await Promise.race(\n running.map(entry => entry.promise)\n )\n // Remove generator.\n running.splice(\n running.findIndex(entry => entry.generator === generator),\n 1\n )\n if (iteratorResult.done) {\n // Start a new generator if available.\n enqueueGen()\n } else {\n yield iteratorResult.value\n // Keep fetching values from this generator.\n continueGen(generator)\n }\n }\n // Reset abortSignal max listeners count.\n if (abortSignalMaxListeners > oldAbortSignalMaxListeners) {\n events.setMaxListeners(oldAbortSignalMaxListeners, abortSignal)\n }\n}\n\nexport function isArtifactAlertCveFixable(\n alert: SocketArtifactAlert\n): alert is ArtifactAlertCveFixable {\n const { type } = alert\n return (\n (type === ALERT_TYPE_CVE ||\n type === ALERT_TYPE_MEDIUM_CVE ||\n type === ALERT_TYPE_MILD_CVE ||\n type === ALERT_TYPE_CRITICAL_CVE) &&\n !!alert.props?.[CVE_ALERT_PROPS_FIRST_PATCHED_VERSION_IDENTIFIER] &&\n !!alert.props?.[CVE_ALERT_PROPS_VULNERABLE_VERSION_RANGE]\n )\n}\n\nexport function isArtifactAlertUpgradeFixable(\n alert: SocketArtifactAlert\n): alert is ArtifactAlertFixable {\n return alert.type === ALERT_TYPE_SOCKET_UPGRADE_AVAILABLE\n}\n\nexport function isArtifactAlertFixable(\n alert: SocketArtifactAlert\n): alert is ArtifactAlertFixable {\n return (\n isArtifactAlertUpgradeFixable(alert) || isArtifactAlertCveFixable(alert)\n )\n}\n","import { setTimeout as wait } from 'node:timers/promises'\n\nimport { isObject } from '@socketsecurity/registry/lib/objects'\n\nimport constants from '../../constants'\nimport { isErrnoException } from '../errors'\nimport { getPublicToken, setupSdk } from '../sdk'\nimport { findSocketYmlSync, getSetting } from '../settings'\n\nimport type { SocketSdkResultType } from '@socketsecurity/sdk'\n\ntype AlertUxLookup = ReturnType<typeof createAlertUXLookup>\n\ntype AlertUxLookupSettings = Parameters<AlertUxLookup>[0]\n\ntype AlertUxLookupResult = ReturnType<AlertUxLookup>\n\ntype NonNormalizedRule =\n | NonNullable<\n NonNullable<\n NonNullable<\n (SocketSdkResultType<'postSettings'> & {\n success: true\n })['data']['entries'][number]['settings'][string]\n >['issueRules']\n >\n >[string]\n | boolean\n\ntype NonNormalizedResolvedRule =\n | (NonNullable<\n NonNullable<\n (SocketSdkResultType<'postSettings'> & {\n success: true\n })['data']['defaults']['issueRules']\n >[string]\n > & { action: string })\n | boolean\n\ntype RuleActionUX = { block: boolean; display: boolean }\n\nconst { abortSignal } = constants\n\nconst ERROR_UX: RuleActionUX = {\n block: true,\n display: true\n}\n\nconst IGNORE_UX: RuleActionUX = {\n block: false,\n display: false\n}\n\nconst WARN_UX: RuleActionUX = {\n block: false,\n display: true\n}\n\n// Iterates over all entries with ordered issue rule for deferral. Iterates over\n// all issue rules and finds the first defined value that does not defer otherwise\n// uses the defaultValue. Takes the value and converts into a UX workflow.\nfunction resolveAlertRuleUX(\n orderedRulesCollection: Iterable<Iterable<NonNormalizedRule>>,\n defaultValue: NonNormalizedResolvedRule\n): RuleActionUX {\n if (\n defaultValue === true ||\n defaultValue === null ||\n defaultValue === undefined\n ) {\n defaultValue = { action: 'error' }\n } else if (defaultValue === false) {\n defaultValue = { action: 'ignore' }\n }\n let block = false\n let display = false\n let needDefault = true\n iterate_entries: for (const rules of orderedRulesCollection) {\n for (const rule of rules) {\n if (ruleValueDoesNotDefer(rule)) {\n needDefault = false\n const narrowingFilter = uxForDefinedNonDeferValue(rule)\n block = block || narrowingFilter.block\n display = display || narrowingFilter.display\n continue iterate_entries\n }\n }\n const narrowingFilter = uxForDefinedNonDeferValue(defaultValue)\n block = block || narrowingFilter.block\n display = display || narrowingFilter.display\n }\n if (needDefault) {\n const narrowingFilter = uxForDefinedNonDeferValue(defaultValue)\n block = block || narrowingFilter.block\n display = display || narrowingFilter.display\n }\n return { block, display }\n}\n\n// Negative form because it is narrowing the type.\nfunction ruleValueDoesNotDefer(\n rule: NonNormalizedRule\n): rule is NonNormalizedResolvedRule {\n if (rule === undefined) {\n return false\n }\n if (isObject(rule)) {\n const { action } = rule\n if (action === undefined || action === 'defer') {\n return false\n }\n }\n return true\n}\n\n// Handles booleans for backwards compatibility.\nfunction uxForDefinedNonDeferValue(\n ruleValue: NonNormalizedResolvedRule\n): RuleActionUX {\n if (typeof ruleValue === 'boolean') {\n return ruleValue ? ERROR_UX : IGNORE_UX\n }\n const { action } = ruleValue\n if (action === 'warn') {\n return WARN_UX\n } else if (action === 'ignore') {\n return IGNORE_UX\n }\n return ERROR_UX\n}\n\ntype SettingsType = (SocketSdkResultType<'postSettings'> & {\n success: true\n})['data']\n\nexport function createAlertUXLookup(settings: SettingsType): (context: {\n package: { name: string; version: string }\n alert: { type: string }\n}) => RuleActionUX {\n const cachedUX: Map<keyof typeof settings.defaults.issueRules, RuleActionUX> =\n new Map()\n return context => {\n const { type } = context.alert\n let ux = cachedUX.get(type)\n if (ux) {\n return ux\n }\n const orderedRulesCollection: Array<Array<NonNormalizedRule>> = []\n for (const settingsEntry of settings.entries) {\n const orderedRules: NonNormalizedRule[] = []\n let target = settingsEntry.start\n while (target !== null) {\n const resolvedTarget = settingsEntry.settings[target]\n if (!resolvedTarget) {\n break\n }\n const issueRuleValue = resolvedTarget.issueRules?.[type]\n if (typeof issueRuleValue !== 'undefined') {\n orderedRules.push(issueRuleValue)\n }\n target = resolvedTarget.deferTo ?? null\n }\n orderedRulesCollection.push(orderedRules)\n }\n const defaultValue = settings.defaults.issueRules[type] as\n | { action: 'error' | 'ignore' | 'warn' }\n | boolean\n | undefined\n let resolvedDefaultValue: NonNormalizedResolvedRule = {\n action: 'error'\n }\n if (defaultValue === false) {\n resolvedDefaultValue = { action: 'ignore' }\n } else if (defaultValue && defaultValue !== true) {\n resolvedDefaultValue = { action: defaultValue.action ?? 'error' }\n }\n ux = resolveAlertRuleUX(orderedRulesCollection, resolvedDefaultValue)\n cachedUX.set(type, ux)\n return ux\n }\n}\n\nlet _uxLookup: AlertUxLookup | undefined\nexport async function uxLookup(\n settings: AlertUxLookupSettings\n): Promise<AlertUxLookupResult> {\n while (_uxLookup === undefined) {\n // eslint-disable-next-line no-await-in-loop\n await wait(1, { signal: abortSignal })\n }\n return _uxLookup(settings)\n}\n\n// Start initializing the AlertUxLookupResult immediately.\nvoid (async () => {\n const { orgs, settings } = await (async () => {\n try {\n const sockSdk = await setupSdk(getPublicToken())\n const orgResult = await sockSdk.getOrganizations()\n if (!orgResult.success) {\n throw new Error(\n `Failed to fetch Socket organization info: ${orgResult.error.message}`\n )\n }\n const orgs: Exclude<\n (typeof orgResult.data.organizations)[string],\n undefined\n >[] = []\n for (const org of Object.values(orgResult.data.organizations)) {\n if (org) {\n orgs.push(org)\n }\n }\n const result = await sockSdk.postSettings(\n orgs.map(org => ({ organization: org.id }))\n )\n if (!result.success) {\n throw new Error(\n `Failed to fetch API key settings: ${result.error.message}`\n )\n }\n return {\n orgs,\n settings: result.data\n }\n } catch (e: any) {\n const cause = isObject(e) && 'cause' in e ? e['cause'] : undefined\n if (\n isErrnoException(cause) &&\n (cause.code === 'ENOTFOUND' || cause.code === 'ECONNREFUSED')\n ) {\n throw new Error(\n 'Unable to connect to socket.dev, ensure internet connectivity before retrying',\n {\n cause: e\n }\n )\n }\n throw e\n }\n })()\n\n // Remove any organizations not being enforced.\n const enforcedOrgs = getSetting('enforcedOrgs') ?? []\n for (const { 0: i, 1: org } of orgs.entries()) {\n if (!enforcedOrgs.includes(org.id)) {\n settings.entries.splice(i, 1)\n }\n }\n\n const socketYml = findSocketYmlSync()\n if (socketYml) {\n settings.entries.push({\n start: socketYml.path,\n settings: {\n [socketYml.path]: {\n deferTo: null,\n // TODO: TypeScript complains about the type not matching. We should\n // figure out why are providing\n // issueRules: { [issueName: string]: boolean }\n // but expecting\n // issueRules: { [issueName: string]: { action: 'defer' | 'error' | 'ignore' | 'monitor' | 'warn' } }\n issueRules: (<unknown>socketYml.parsed.issueRules) as {\n [key: string]: {\n action: 'defer' | 'error' | 'ignore' | 'monitor' | 'warn'\n }\n }\n }\n }\n })\n }\n _uxLookup = createAlertUXLookup(settings)\n})()\n","import terminalLink from 'terminal-link'\nimport colors from 'yoctocolors-cjs'\n\nimport indentString from '@socketregistry/indent-string/index.cjs'\nimport { Logger } from '@socketsecurity/registry/lib/logger'\n\nimport type { LogSymbols } from '@socketsecurity/registry/lib/logger'\n\nconst markdownLogSymbols = <LogSymbols>Object.freeze({\n __proto__: null,\n info: ':information_source:',\n error: ':stop_sign:',\n success: ':white_check_mark:',\n warning: ':warning:'\n})\n\nexport class ColorOrMarkdown {\n public useMarkdown: boolean\n\n constructor(useMarkdown: boolean) {\n this.useMarkdown = !!useMarkdown\n }\n\n bold(text: string): string {\n return this.useMarkdown ? `**${text}**` : colors.bold(`${text}`)\n }\n\n header(text: string, level = 1): string {\n return this.useMarkdown\n ? `\\n${''.padStart(level, '#')} ${text}\\n`\n : colors.underline(`\\n${level === 1 ? colors.bold(text) : text}\\n`)\n }\n\n hyperlink(\n text: string,\n url: string | undefined,\n {\n fallback = true,\n fallbackToUrl\n }: {\n fallback?: boolean | undefined\n fallbackToUrl?: boolean | undefined\n } = {}\n ) {\n if (url) {\n return this.useMarkdown\n ? `[${text}](${url})`\n : terminalLink(text, url, {\n fallback: fallbackToUrl ? (_text, url) => url : fallback\n })\n }\n return text\n }\n\n indent(\n ...args: Parameters<typeof indentString>\n ): ReturnType<typeof indentString> {\n return indentString(...args)\n }\n\n italic(text: string): string {\n return this.useMarkdown ? `_${text}_` : colors.italic(`${text}`)\n }\n\n json(value: any): string {\n return this.useMarkdown\n ? '```json\\n' + JSON.stringify(value) + '\\n```'\n : JSON.stringify(value)\n }\n\n list(items: string[]): string {\n const indentedContent = items.map(item => this.indent(item).trimStart())\n return this.useMarkdown\n ? `* ${indentedContent.join('\\n* ')}\\n`\n : `${indentedContent.join('\\n')}\\n`\n }\n\n get logSymbols(): LogSymbols {\n return this.useMarkdown ? markdownLogSymbols : Logger.LOG_SYMBOLS\n }\n}\n","export function getSocketDevAlertUrl(alertType: string): string {\n return `https://socket.dev/alerts/${alertType}`\n}\n\nexport function getSocketDevPackageOverviewUrl(\n eco: string,\n name: string,\n version?: string | undefined\n): string {\n return `https://socket.dev/${eco}/package/${name}${version ? `/overview/${version}` : ''}`\n}\n","import { getArboristDepValidPath } from '../../npm-paths'\n\nimport type { SafeNode } from './node'\n\nexport const depValid: (\n child: SafeNode,\n requested: string,\n accept: string | undefined,\n requester: SafeNode\n) => boolean = require(getArboristDepValidPath())\n","import { getNpmRequire } from './npm-paths'\nimport constants from '../constants'\n\nconst { UNDEFINED_TOKEN } = constants\n\ninterface RequireKnownModules {\n npmlog: typeof import('npmlog')\n // The DefinitelyTyped definition of 'proc-log' does NOT have the log method.\n // The return type of the log method is the same as `typeof import('proc-log')`.\n 'proc-log': typeof import('proc-log')\n}\n\ntype RequireTransformer<T extends keyof RequireKnownModules> = (\n mod: RequireKnownModules[T]\n) => RequireKnownModules[T]\n\nfunction tryRequire<T extends keyof RequireKnownModules>(\n req: NodeJS.Require,\n ...ids: (T | [T, RequireTransformer<T>])[]\n): RequireKnownModules[T] | undefined {\n for (const data of ids) {\n let id: string | undefined\n let transformer: RequireTransformer<T> | undefined\n if (Array.isArray(data)) {\n id = data[0]\n transformer = <RequireTransformer<T>>data[1]\n } else {\n id = <keyof RequireKnownModules>data\n transformer = mod => mod\n }\n try {\n // Check that the transformed value isn't `undefined` because older\n // versions of packages like 'proc-log' may not export a `log` method.\n const exported = transformer(req(id))\n if (exported !== undefined) {\n return exported\n }\n } catch {}\n }\n return undefined\n}\n\nexport type Logger =\n | typeof import('npmlog')\n | typeof import('proc-log')\n | undefined\n\nlet _log: Logger | {} | undefined = UNDEFINED_TOKEN\nexport function getLogger(): Logger {\n if (_log === UNDEFINED_TOKEN) {\n _log = tryRequire(\n getNpmRequire(),\n [\n <'proc-log'>'proc-log/lib/index.js',\n // The proc-log DefinitelyTyped definition is incorrect. The type definition\n // is really that of its export log.\n mod => <RequireKnownModules['proc-log']>(mod as any).log\n ],\n <'npmlog'>'npmlog/lib/log.js'\n )\n }\n return <Logger | undefined>_log\n}\n","import npa from 'npm-package-arg'\nimport semver from 'semver'\n\nimport { getArboristOverrideSetClassPath } from '../../npm-paths'\nimport { getLogger } from '../../proc-log'\n\nimport type { SafeEdge } from './edge'\nimport type { SafeNode } from './node'\nimport type { AliasResult, RegistryResult } from 'npm-package-arg'\n\ninterface OverrideSetClass {\n children: Map<string, SafeOverrideSet>\n key: string | undefined\n keySpec: string | undefined\n name: string | undefined\n parent: SafeOverrideSet | undefined\n value: string | undefined\n version: string | undefined\n // eslint-disable-next-line @typescript-eslint/no-misused-new\n new (...args: any[]): OverrideSetClass\n get isRoot(): boolean\n get ruleset(): Map<string, SafeOverrideSet>\n ancestry(): Generator<SafeOverrideSet>\n childrenAreEqual(otherOverrideSet: SafeOverrideSet | undefined): boolean\n getEdgeRule(edge: SafeEdge): SafeOverrideSet\n getNodeRule(node: SafeNode): SafeOverrideSet\n getMatchingRule(node: SafeNode): SafeOverrideSet | null\n isEqual(otherOverrideSet: SafeOverrideSet | undefined): boolean\n}\n\nconst OverrideSet: OverrideSetClass = require(getArboristOverrideSetClassPath())\n\n// Implementation code not related to patch https://github.com/npm/cli/pull/8089\n// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/override-set.js:\nexport class SafeOverrideSet extends OverrideSet {\n // Patch adding doOverrideSetsConflict is based on\n // https://github.com/npm/cli/pull/8089.\n static doOverrideSetsConflict(\n first: SafeOverrideSet | undefined,\n second: SafeOverrideSet | undefined\n ) {\n // If override sets contain one another then we can try to use the more\n // specific one. If neither one is more specific, then we consider them to\n // be in conflict.\n return this.findSpecificOverrideSet(first, second) === undefined\n }\n\n // Patch adding findSpecificOverrideSet is based on\n // https://github.com/npm/cli/pull/8089.\n static findSpecificOverrideSet(\n first: SafeOverrideSet | undefined,\n second: SafeOverrideSet | undefined\n ) {\n for (\n let overrideSet = second;\n overrideSet;\n overrideSet = overrideSet.parent\n ) {\n if (overrideSet.isEqual(first)) {\n return second\n }\n }\n for (\n let overrideSet = first;\n overrideSet;\n overrideSet = overrideSet.parent\n ) {\n if (overrideSet.isEqual(second)) {\n return first\n }\n }\n // The override sets are incomparable. Neither one contains the other.\n const log = getLogger()\n log?.silly('Conflicting override sets', first, second)\n return undefined\n }\n\n // Patch adding childrenAreEqual is based on\n // https://github.com/npm/cli/pull/8089.\n override childrenAreEqual(otherOverrideSet: SafeOverrideSet) {\n if (this.children.size !== otherOverrideSet.children.size) {\n return false\n }\n for (const { 0: key, 1: childOverrideSet } of this.children) {\n const otherChildOverrideSet = otherOverrideSet.children.get(key)\n if (!otherChildOverrideSet) {\n return false\n }\n if (childOverrideSet.value !== otherChildOverrideSet.value) {\n return false\n }\n if (!childOverrideSet.childrenAreEqual(otherChildOverrideSet)) {\n return false\n }\n }\n return true\n }\n\n override getEdgeRule(edge: SafeEdge): SafeOverrideSet {\n for (const rule of this.ruleset.values()) {\n if (rule.name !== edge.name) {\n continue\n }\n // If keySpec is * we found our override.\n if (rule.keySpec === '*') {\n return rule\n }\n // Patch replacing\n // let spec = npa(`${edge.name}@${edge.spec}`)\n // is based on https://github.com/npm/cli/pull/8089.\n //\n // We need to use the rawSpec here, because the spec has the overrides\n // applied to it already. The rawSpec can be undefined, so we need to use\n // the fallback value of spec if it is.\n let spec = npa(`${edge.name}@${edge.rawSpec || edge.spec}`)\n if (spec.type === 'alias') {\n spec = (<AliasResult>spec).subSpec\n }\n if (spec.type === 'git') {\n if (spec.gitRange && semver.intersects(spec.gitRange, rule.keySpec!)) {\n return rule\n }\n continue\n }\n if (spec.type === 'range' || spec.type === 'version') {\n if (\n semver.intersects((<RegistryResult>spec).fetchSpec, rule.keySpec!)\n ) {\n return rule\n }\n continue\n }\n // If we got this far, the spec type is one of tag, directory or file\n // which means we have no real way to make version comparisons, so we\n // just accept the override.\n return rule\n }\n return this\n }\n\n // Patch adding isEqual is based on\n // https://github.com/npm/cli/pull/8089.\n override isEqual(otherOverrideSet: SafeOverrideSet | undefined): boolean {\n if (this === otherOverrideSet) {\n return true\n }\n if (!otherOverrideSet) {\n return false\n }\n if (\n this.key !== otherOverrideSet.key ||\n this.value !== otherOverrideSet.value\n ) {\n return false\n }\n if (!this.childrenAreEqual(otherOverrideSet)) {\n return false\n }\n if (!this.parent) {\n return !otherOverrideSet.parent\n }\n return this.parent.isEqual(otherOverrideSet.parent)\n }\n}\n","import semver from 'semver'\n\nimport { SafeOverrideSet } from './override-set'\nimport { getArboristNodeClassPath } from '../../npm-paths'\nimport { getLogger } from '../../proc-log'\n\nimport type { SafeEdge } from './edge'\nimport type { Node as BaseNode, Link } from '@npmcli/arborist'\n\ntype NodeClass = Omit<\n BaseNode,\n | 'addEdgeIn'\n | 'addEdgeOut'\n | 'canDedupe'\n | 'canReplace'\n | 'canReplaceWith'\n | 'children'\n | 'deleteEdgeIn'\n | 'edgesIn'\n | 'edgesOut'\n | 'from'\n | 'hasShrinkwrap'\n | 'inDepBundle'\n | 'inShrinkwrap'\n | 'integrity'\n | 'isTop'\n | 'matches'\n | 'meta'\n | 'name'\n | 'overrides'\n | 'packageName'\n | 'parent'\n | 'recalculateOutEdgesOverrides'\n | 'resolve'\n | 'resolveParent'\n | 'root'\n | 'updateOverridesEdgeInAdded'\n | 'updateOverridesEdgeInRemoved'\n | 'version'\n | 'versions'\n> & {\n name: string\n version: string\n children: Map<string, SafeNode | Link>\n edgesIn: Set<SafeEdge>\n edgesOut: Map<string, SafeEdge>\n from: SafeNode | null\n hasShrinkwrap: boolean\n inShrinkwrap: boolean | undefined\n integrity?: string | null\n isTop: boolean | undefined\n meta: BaseNode['meta'] & {\n addEdge(edge: SafeEdge): void\n }\n overrides: SafeOverrideSet | undefined\n versions: string[]\n get inDepBundle(): boolean\n get packageName(): string | null\n get parent(): SafeNode | null\n set parent(value: SafeNode | null)\n get resolveParent(): SafeNode | null\n get root(): SafeNode | null\n set root(value: SafeNode | null)\n new (...args: any): NodeClass\n addEdgeIn(edge: SafeEdge): void\n addEdgeOut(edge: SafeEdge): void\n canDedupe(preferDedupe?: boolean): boolean\n canReplace(node: SafeNode, ignorePeers?: string[]): boolean\n canReplaceWith(node: SafeNode, ignorePeers?: string[]): boolean\n deleteEdgeIn(edge: SafeEdge): void\n matches(node: SafeNode): boolean\n recalculateOutEdgesOverrides(): void\n resolve(name: string): SafeNode\n updateOverridesEdgeInAdded(\n otherOverrideSet: SafeOverrideSet | undefined\n ): boolean\n updateOverridesEdgeInRemoved(otherOverrideSet: SafeOverrideSet): boolean\n}\n\nconst Node: NodeClass = require(getArboristNodeClassPath())\n\n// Implementation code not related to patch https://github.com/npm/cli/pull/8089\n// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/node.js:\nexport class SafeNode extends Node {\n // Return true if it's safe to remove this node, because anything that is\n // depending on it would be fine with the thing that they would resolve to if\n // it was removed, or nothing is depending on it in the first place.\n override canDedupe(preferDedupe = false) {\n // Not allowed to mess with shrinkwraps or bundles.\n if (this.inDepBundle || this.inShrinkwrap) {\n return false\n }\n // It's a top level pkg, or a dep of one.\n if (!this.resolveParent?.resolveParent) {\n return false\n }\n // No one wants it, remove it.\n if (this.edgesIn.size === 0) {\n return true\n }\n const other = this.resolveParent.resolveParent.resolve(this.name)\n // Nothing else, need this one.\n if (!other) {\n return false\n }\n // If it's the same thing, then always fine to remove.\n if (other.matches(this)) {\n return true\n }\n // If the other thing can't replace this, then skip it.\n if (!other.canReplace(this)) {\n return false\n }\n // Patch replacing\n // if (preferDedupe || semver.gte(other.version, this.version)) {\n // return true\n // }\n // is based on https://github.com/npm/cli/pull/8089.\n //\n // If we prefer dedupe, or if the version is equal, take the other.\n if (preferDedupe || semver.eq(other.version, this.version)) {\n return true\n }\n // If our current version isn't the result of an override, then prefer to\n // take the greater version.\n if (!this.overridden && semver.gt(other.version, this.version)) {\n return true\n }\n return false\n }\n\n // Is it safe to replace one node with another? check the edges to\n // make sure no one will get upset. Note that the node might end up\n // having its own unmet dependencies, if the new node has new deps.\n // Note that there are cases where Arborist will opt to insert a node\n // into the tree even though this function returns false! This is\n // necessary when a root dependency is added or updated, or when a\n // root dependency brings peer deps along with it. In that case, we\n // will go ahead and create the invalid state, and then try to resolve\n // it with more tree construction, because it's a user request.\n override canReplaceWith(node: SafeNode, ignorePeers?: string[]): boolean {\n if (this.name !== node.name || this.packageName !== node.packageName) {\n return false\n }\n // Patch replacing\n // if (node.overrides !== this.overrides) {\n // return false\n // }\n // is based on https://github.com/npm/cli/pull/8089.\n //\n // If this node has no dependencies, then it's irrelevant to check the\n // override rules of the replacement node.\n if (this.edgesOut.size) {\n // XXX need to check for two root nodes?\n if (node.overrides) {\n if (!node.overrides.isEqual(this.overrides)) {\n return false\n }\n } else {\n if (this.overrides) {\n return false\n }\n }\n }\n // To satisfy the patch we ensure `node.overrides === this.overrides`\n // so that the condition we want to replace,\n // if (this.overrides !== node.overrides) {\n // , is not hit.`\n const oldOverrideSet = this.overrides\n let result = true\n if (oldOverrideSet !== node.overrides) {\n this.overrides = node.overrides\n }\n try {\n result = super.canReplaceWith(node, ignorePeers)\n this.overrides = oldOverrideSet\n } catch (e: any) {\n this.overrides = oldOverrideSet\n throw e\n }\n return result\n }\n\n // Patch adding deleteEdgeIn is based on https://github.com/npm/cli/pull/8089.\n override deleteEdgeIn(edge: SafeEdge) {\n this.edgesIn.delete(edge)\n const { overrides } = edge\n if (overrides) {\n this.updateOverridesEdgeInRemoved(overrides)\n }\n }\n\n override addEdgeIn(edge: SafeEdge): void {\n // Patch replacing\n // if (edge.overrides) {\n // this.overrides = edge.overrides\n // }\n // is based on https://github.com/npm/cli/pull/8089.\n //\n // We need to handle the case where the new edge in has an overrides field\n // which is different from the current value.\n if (!this.overrides || !this.overrides.isEqual(edge.overrides)) {\n this.updateOverridesEdgeInAdded(edge.overrides)\n }\n this.edgesIn.add(edge)\n // Try to get metadata from the yarn.lock file.\n this.root.meta?.addEdge(edge)\n }\n\n // @ts-ignore: Incorrectly typed as a property instead of an accessor.\n override get overridden() {\n // Patch replacing\n // return !!(this.overrides && this.overrides.value && this.overrides.name === this.name)\n // is based on https://github.com/npm/cli/pull/8089.\n if (\n !this.overrides ||\n !this.overrides.value ||\n this.overrides.name !== this.name\n ) {\n return false\n }\n // The overrides rule is for a package with this name, but some override\n // rules only apply to specific versions. To make sure this package was\n // actually overridden, we check whether any edge going in had the rule\n // applied to it, in which case its overrides set is different than its\n // source node.\n for (const edge of this.edgesIn) {\n if (\n edge.overrides &&\n edge.overrides.name === this.name &&\n edge.overrides.value === this.version\n ) {\n if (!edge.overrides.isEqual(edge.from?.overrides)) {\n return true\n }\n }\n }\n return false\n }\n\n override set parent(newParent: SafeNode) {\n // Patch removing\n // if (parent.overrides) {\n // this.overrides = parent.overrides.getNodeRule(this)\n // }\n // is based on https://github.com/npm/cli/pull/8089.\n //\n // The \"parent\" setter is a really large and complex function. To satisfy\n // the patch we hold on to the old overrides value and set `this.overrides`\n // to `undefined` so that the condition we want to remove is not hit.\n const { overrides } = this\n if (overrides) {\n this.overrides = undefined\n }\n try {\n super.parent = newParent\n this.overrides = overrides\n } catch (e: any) {\n this.overrides = overrides\n throw e\n }\n }\n\n // Patch adding recalculateOutEdgesOverrides is based on\n // https://github.com/npm/cli/pull/8089.\n override recalculateOutEdgesOverrides() {\n // For each edge out propagate the new overrides through.\n for (const edge of this.edgesOut.values()) {\n edge.reload(true)\n if (edge.to) {\n edge.to.updateOverridesEdgeInAdded(edge.overrides)\n }\n }\n }\n\n // @ts-ignore: Incorrectly typed to accept null.\n override set root(newRoot: SafeNode) {\n // Patch removing\n // if (!this.overrides && this.parent && this.parent.overrides) {\n // this.overrides = this.parent.overrides.getNodeRule(this)\n // }\n // is based on https://github.com/npm/cli/pull/8089.\n //\n // The \"root\" setter is a really large and complex function. To satisfy the\n // patch we add a dummy value to `this.overrides` so that the condition we\n // want to remove is not hit.\n if (!this.overrides) {\n this.overrides = new SafeOverrideSet({ overrides: '' })\n }\n try {\n super.root = newRoot\n this.overrides = undefined\n } catch (e: any) {\n this.overrides = undefined\n throw e\n }\n }\n\n // Patch adding updateOverridesEdgeInAdded is based on\n // https://github.com/npm/cli/pull/7025.\n //\n // This logic isn't perfect either. When we have two edges in that have\n // different override sets, then we have to decide which set is correct. This\n // function assumes the more specific override set is applicable, so if we have\n // dependencies A->B->C and A->C and an override set that specifies what happens\n // for C under A->B, this will work even if the new A->C edge comes along and\n // tries to change the override set. The strictly correct logic is not to allow\n // two edges with different overrides to point to the same node, because even\n // if this node can satisfy both, one of its dependencies might need to be\n // different depending on the edge leading to it. However, this might cause a\n // lot of duplication, because the conflict in the dependencies might never\n // actually happen.\n override updateOverridesEdgeInAdded(\n otherOverrideSet: SafeOverrideSet | undefined\n ) {\n if (!otherOverrideSet) {\n // Assuming there are any overrides at all, the overrides field is never\n // undefined for any node at the end state of the tree. So if the new edge's\n // overrides is undefined it will be updated later. So we can wait with\n // updating the node's overrides field.\n return false\n }\n if (!this.overrides) {\n this.overrides = otherOverrideSet\n this.recalculateOutEdgesOverrides()\n return true\n }\n if (this.overrides.isEqual(otherOverrideSet)) {\n return false\n }\n const newOverrideSet = SafeOverrideSet.findSpecificOverrideSet(\n this.overrides,\n otherOverrideSet\n )\n if (newOverrideSet) {\n if (this.overrides.isEqual(newOverrideSet)) {\n return false\n }\n this.overrides = newOverrideSet\n this.recalculateOutEdgesOverrides()\n return true\n }\n // This is an error condition. We can only get here if the new override set\n // is in conflict with the existing.\n const log = getLogger()\n log?.silly('Conflicting override sets', this.name)\n return false\n }\n\n // Patch adding updateOverridesEdgeInRemoved is based on\n // https://github.com/npm/cli/pull/7025.\n override updateOverridesEdgeInRemoved(otherOverrideSet: SafeOverrideSet) {\n // If this edge's overrides isn't equal to this node's overrides,\n // then removing it won't change newOverrideSet later.\n if (!this.overrides || !this.overrides.isEqual(otherOverrideSet)) {\n return false\n }\n let newOverrideSet\n for (const edge of this.edgesIn) {\n const { overrides: edgeOverrides } = edge\n if (newOverrideSet && edgeOverrides) {\n newOverrideSet = SafeOverrideSet.findSpecificOverrideSet(\n edgeOverrides,\n newOverrideSet\n )\n } else {\n newOverrideSet = edgeOverrides\n }\n }\n if (this.overrides.isEqual(newOverrideSet)) {\n return false\n }\n this.overrides = newOverrideSet\n if (newOverrideSet) {\n // Optimization: If there's any override set at all, then no non-extraneous\n // node has an empty override set. So if we temporarily have no override set\n // (for example, we removed all the edges in), there's no use updating all\n // the edges out right now. Let's just wait until we have an actual override\n // set later.\n this.recalculateOutEdgesOverrides()\n }\n return true\n }\n}\n","import { depValid } from './dep-valid'\nimport { SafeNode } from './node'\nimport { SafeOverrideSet } from './override-set'\nimport { getArboristEdgeClassPath } from '../../npm-paths'\n\nimport type { Edge as BaseEdge, DependencyProblem } from '@npmcli/arborist'\n\ntype EdgeClass = Omit<\n BaseEdge,\n | 'accept'\n | 'detach'\n | 'optional'\n | 'overrides'\n | 'peer'\n | 'peerConflicted'\n | 'rawSpec'\n | 'reload'\n | 'satisfiedBy'\n | 'spec'\n | 'to'\n> & {\n optional: boolean\n overrides: SafeOverrideSet | undefined\n peer: boolean\n peerConflicted: boolean\n rawSpec: string\n get accept(): string | undefined\n get spec(): string\n get to(): SafeNode | null\n new (...args: any): EdgeClass\n detach(): void\n reload(hard?: boolean): void\n satisfiedBy(node: SafeNode): boolean\n}\n\nexport type EdgeOptions = {\n type: string\n name: string\n spec: string\n from: SafeNode\n accept?: string | undefined\n overrides?: SafeOverrideSet | undefined\n to?: SafeNode | undefined\n}\n\nexport type ErrorStatus = DependencyProblem | 'OK'\n\nexport type Explanation = {\n type: string\n name: string\n spec: string\n bundled: boolean\n overridden: boolean\n error: ErrorStatus | undefined\n rawSpec: string | undefined\n from: object | undefined\n} | null\n\nexport const Edge: EdgeClass = require(getArboristEdgeClassPath())\n\n// The Edge class makes heavy use of private properties which subclasses do NOT\n// have access to. So we have to recreate any functionality that relies on those\n// private properties and use our own \"safe\" prefixed non-conflicting private\n// properties. Implementation code not related to patch https://github.com/npm/cli/pull/8089\n// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/edge.js.\n//\n// The npm application\n// Copyright (c) npm, Inc. and Contributors\n// Licensed on the terms of The Artistic License 2.0\n//\n// An edge in the dependency graph.\n// Represents a dependency relationship of some kind.\nexport class SafeEdge extends Edge {\n #safeError: ErrorStatus | null\n #safeExplanation: Explanation | undefined\n #safeFrom: SafeNode | null\n #safeTo: SafeNode | null\n\n constructor(options: EdgeOptions) {\n const { from } = options\n // Defer to supper to validate options and assign non-private values.\n super(options)\n if (from.constructor !== SafeNode) {\n Reflect.setPrototypeOf(from, SafeNode.prototype)\n }\n this.#safeError = null\n this.#safeExplanation = null\n this.#safeFrom = from\n this.#safeTo = null\n this.reload(true)\n }\n\n override get bundled() {\n return !!this.#safeFrom?.package?.bundleDependencies?.includes(this.name)\n }\n\n override get error() {\n if (!this.#safeError) {\n if (!this.#safeTo) {\n if (this.optional) {\n this.#safeError = null\n } else {\n this.#safeError = 'MISSING'\n }\n } else if (\n this.peer &&\n this.#safeFrom === this.#safeTo.parent &&\n // Patch adding \"?.\" use based on\n // https://github.com/npm/cli/pull/8089.\n !this.#safeFrom?.isTop\n ) {\n this.#safeError = 'PEER LOCAL'\n } else if (!this.satisfiedBy(this.#safeTo)) {\n this.#safeError = 'INVALID'\n }\n // Patch adding \"else if\" condition is based on\n // https://github.com/npm/cli/pull/8089.\n else if (\n this.overrides &&\n this.#safeTo.edgesOut.size &&\n SafeOverrideSet.doOverrideSetsConflict(\n this.overrides,\n this.#safeTo.overrides\n )\n ) {\n // Any inconsistency between the edge's override set and the target's\n // override set is potentially problematic. But we only say the edge is\n // in error if the override sets are plainly conflicting. Note that if\n // the target doesn't have any dependencies of their own, then this\n // inconsistency is irrelevant.\n this.#safeError = 'INVALID'\n } else {\n this.#safeError = 'OK'\n }\n }\n if (this.#safeError === 'OK') {\n return null\n }\n return this.#safeError\n }\n\n // @ts-ignore: Incorrectly typed as a property instead of an accessor.\n override get from() {\n return this.#safeFrom\n }\n\n // @ts-ignore: Incorrectly typed as a property instead of an accessor.\n override get spec(): string {\n if (\n this.overrides?.value &&\n this.overrides.value !== '*' &&\n this.overrides.name === this.name\n ) {\n if (this.overrides.value.startsWith('$')) {\n const ref = this.overrides.value.slice(1)\n // We may be a virtual root, if we are we want to resolve reference\n // overrides from the real root, not the virtual one.\n //\n // Patch adding \"?.\" use based on\n // https://github.com/npm/cli/pull/8089.\n const pkg = this.#safeFrom?.sourceReference\n ? this.#safeFrom?.sourceReference.root.package\n : this.#safeFrom?.root?.package\n if (pkg?.devDependencies?.[ref]) {\n return <string>pkg.devDependencies[ref]\n }\n if (pkg?.optionalDependencies?.[ref]) {\n return <string>pkg.optionalDependencies[ref]\n }\n if (pkg?.dependencies?.[ref]) {\n return <string>pkg.dependencies[ref]\n }\n if (pkg?.peerDependencies?.[ref]) {\n return <string>pkg.peerDependencies[ref]\n }\n throw new Error(`Unable to resolve reference ${this.overrides.value}`)\n }\n return this.overrides.value\n }\n return this.rawSpec\n }\n\n // @ts-ignore: Incorrectly typed as a property instead of an accessor.\n override get to() {\n return this.#safeTo\n }\n\n override detach() {\n this.#safeExplanation = null\n // Patch replacing\n // if (this.#to) {\n // this.#to.edgesIn.delete(this)\n // }\n // this.#from.edgesOut.delete(this.#name)\n // is based on https://github.com/npm/cli/pull/8089.\n this.#safeTo?.deleteEdgeIn(this)\n this.#safeFrom?.edgesOut.delete(this.name)\n this.#safeTo = null\n this.#safeError = 'DETACHED'\n this.#safeFrom = null\n }\n\n // Return the edge data, and an explanation of how that edge came to be here.\n // @ts-ignore: Edge#explain is defined with an unused `seen = []` param.\n override explain() {\n if (!this.#safeExplanation) {\n const explanation: Explanation = {\n type: this.type,\n name: this.name,\n spec: this.spec,\n bundled: false,\n overridden: false,\n error: undefined,\n from: undefined,\n rawSpec: undefined\n }\n if (this.rawSpec !== this.spec) {\n explanation.rawSpec = this.rawSpec\n explanation.overridden = true\n }\n if (this.bundled) {\n explanation.bundled = this.bundled\n }\n if (this.error) {\n explanation.error = this.error\n }\n if (this.#safeFrom) {\n explanation.from = this.#safeFrom.explain()\n }\n this.#safeExplanation = explanation\n }\n return this.#safeExplanation\n }\n\n override reload(hard = false) {\n this.#safeExplanation = null\n // Patch replacing\n // if (this.#from.overrides) {\n // is based on https://github.com/npm/cli/pull/8089.\n let needToUpdateOverrideSet = false\n let newOverrideSet\n let oldOverrideSet\n if (this.#safeFrom?.overrides) {\n newOverrideSet = this.#safeFrom.overrides.getEdgeRule(this)\n if (newOverrideSet && !newOverrideSet.isEqual(this.overrides)) {\n // If there's a new different override set we need to propagate it to\n // the nodes. If we're deleting the override set then there's no point\n // propagating it right now since it will be filled with another value\n // later.\n needToUpdateOverrideSet = true\n oldOverrideSet = this.overrides\n this.overrides = newOverrideSet\n }\n } else {\n this.overrides = undefined\n }\n // Patch adding \"?.\" use based on\n // https://github.com/npm/cli/pull/8089.\n const newTo = this.#safeFrom?.resolve(this.name)\n if (newTo !== this.#safeTo) {\n // Patch replacing\n // this.#to.edgesIn.delete(this)\n // is based on https://github.com/npm/cli/pull/8089.\n this.#safeTo?.deleteEdgeIn(this)\n this.#safeTo = <SafeNode>newTo ?? null\n this.#safeError = null\n this.#safeTo?.addEdgeIn(this)\n } else if (hard) {\n this.#safeError = null\n }\n // Patch adding \"else if\" condition based on\n // https://github.com/npm/cli/pull/8089.\n else if (needToUpdateOverrideSet && this.#safeTo) {\n // Propagate the new override set to the target node.\n this.#safeTo.updateOverridesEdgeInRemoved(oldOverrideSet!)\n this.#safeTo.updateOverridesEdgeInAdded(newOverrideSet)\n }\n }\n\n override satisfiedBy(node: SafeNode) {\n // Patch replacing\n // if (node.name !== this.#name) {\n // return false\n // }\n // is based on https://github.com/npm/cli/pull/8089.\n if (node.name !== this.name || !this.#safeFrom) {\n return false\n }\n // NOTE: this condition means we explicitly do not support overriding\n // bundled or shrinkwrapped dependencies\n if (node.hasShrinkwrap || node.inShrinkwrap || node.inBundle) {\n return depValid(node, this.rawSpec, this.accept, this.#safeFrom)\n }\n // Patch replacing\n // return depValid(node, this.spec, this.#accept, this.#from)\n // is based on https://github.com/npm/cli/pull/8089.\n //\n // If there's no override we just use the spec.\n if (!this.overrides?.keySpec) {\n return depValid(node, this.spec, this.accept, this.#safeFrom)\n }\n // There's some override. If the target node satisfies the overriding spec\n // then it's okay.\n if (depValid(node, this.spec, this.accept, this.#safeFrom)) {\n return true\n }\n // If it doesn't, then it should at least satisfy the original spec.\n if (!depValid(node, this.rawSpec, this.accept, this.#safeFrom)) {\n return false\n }\n // It satisfies the original spec, not the overriding spec. We need to make\n // sure it doesn't use the overridden spec.\n // For example:\n // we might have an ^8.0.0 rawSpec, and an override that makes\n // keySpec=8.23.0 and the override value spec=9.0.0.\n // If the node is 9.0.0, then it's okay because it's consistent with spec.\n // If the node is 8.24.0, then it's okay because it's consistent with the rawSpec.\n // If the node is 8.23.0, then it's not okay because even though it's consistent\n // with the rawSpec, it's also consistent with the keySpec.\n // So we're looking for ^8.0.0 or 9.0.0 and not 8.23.0.\n return !depValid(node, this.overrides.keySpec, this.accept, this.#safeFrom)\n }\n}\n","import path from 'node:path'\nimport process from 'node:process'\n\nimport semver from 'semver'\n\nimport { PackageURL } from '@socketregistry/packageurl-js'\nimport { getManifestData } from '@socketsecurity/registry'\nimport { arrayUnique } from '@socketsecurity/registry/lib/arrays'\nimport { hasOwn } from '@socketsecurity/registry/lib/objects'\nimport {\n fetchPackagePackument,\n resolvePackageName\n} from '@socketsecurity/registry/lib/packages'\nimport { confirm } from '@socketsecurity/registry/lib/prompts'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nimport { getPackagesToQueryFromDiff } from './diff'\nimport constants from '../../../../constants'\nimport {\n batchScan,\n isArtifactAlertCveFixable,\n isArtifactAlertUpgradeFixable\n} from '../../../../utils/alert/artifact'\nimport { uxLookup } from '../../../../utils/alert/rules'\nimport { ColorOrMarkdown } from '../../../../utils/color-or-markdown'\nimport { getSocketDevPackageOverviewUrl } from '../../../../utils/socket-url'\nimport { Edge, SafeEdge } from '../edge'\n\nimport type { ArboristClass, ArboristReifyOptions } from './types'\nimport type { SocketArtifact } from '../../../../utils/alert/artifact'\nimport type { SafeNode } from '../node'\nimport type { Writable } from 'node:stream'\n\ntype PackageJsonType = SafeNode['package']\n\ntype Packument = Exclude<\n Awaited<ReturnType<typeof fetchPackagePackument>>,\n null\n>\n\ntype SocketPackageAlert = {\n key: string\n type: string\n name: string\n version: string\n block: boolean\n fixable: boolean\n raw: any\n}\n\nconst {\n CVE_ALERT_PROPS_FIRST_PATCHED_VERSION_IDENTIFIER,\n LOOP_SENTINEL,\n NPM,\n NPM_REGISTRY_URL,\n OVERRIDES,\n PNPM,\n RESOLUTIONS\n} = constants\n\nconst formatter = new ColorOrMarkdown(false)\n\nfunction findBestPatchVersion(\n node: SafeNode,\n availableVersions: string[],\n vulnerableVersionRange?: string,\n _firstPatchedVersionIdentifier?: string | undefined\n): string | null {\n const manifestData = getManifestData(NPM, node.name)\n let eligibleVersions\n if (manifestData && manifestData.name === manifestData.package) {\n const major = semver.major(manifestData.version)\n eligibleVersions = availableVersions.filter(v => semver.major(v) === major)\n } else {\n const major = semver.major(node.version)\n eligibleVersions = availableVersions.filter(\n v =>\n // Filter for versions that are within the current major version\n // and are not in the vulnerable range\n semver.major(v) === major &&\n (!vulnerableVersionRange ||\n !semver.satisfies(v, vulnerableVersionRange))\n )\n }\n return semver.maxSatisfying(eligibleVersions, '*')\n}\n\nexport function findPackageNodes(\n tree: SafeNode,\n packageName: string\n): SafeNode[] {\n const queue: { node: typeof tree }[] = [{ node: tree }]\n const matches: SafeNode[] = []\n let sentinel = 0\n while (queue.length) {\n if (sentinel++ === LOOP_SENTINEL) {\n throw new Error('Detected infinite loop in findPackageNodes')\n }\n const { node: currentNode } = queue.pop()!\n const node = currentNode.children.get(packageName)\n if (node) {\n matches.push((<unknown>node) as SafeNode)\n }\n const children = [...currentNode.children.values()]\n for (let i = children.length - 1; i >= 0; i -= 1) {\n queue.push({ node: (<unknown>children[i]) as SafeNode })\n }\n }\n return matches\n}\n\nlet _translations: typeof import('../../../../../translations.json') | undefined\nfunction getTranslations() {\n if (_translations === undefined) {\n _translations = require(\n // Lazily access constants.rootPath.\n path.join(constants.rootPath, 'translations.json')\n )\n }\n return _translations!\n}\n\nfunction hasOverride(pkgJson: PackageJsonType, name: string): boolean {\n return !!(\n (pkgJson as any)?.[OVERRIDES]?.[name] ||\n (pkgJson as any)?.[RESOLUTIONS]?.[name] ||\n (pkgJson as any)?.[PNPM]?.[OVERRIDES]?.[name]\n )\n}\n\nexport function updateNode(\n node: SafeNode,\n packument: Packument,\n vulnerableVersionRange?: string,\n firstPatchedVersionIdentifier?: string | undefined\n): boolean {\n const availableVersions = Object.keys(packument.versions)\n // Find the highest non-vulnerable version within the same major range\n const targetVersion = findBestPatchVersion(\n node,\n availableVersions,\n vulnerableVersionRange,\n firstPatchedVersionIdentifier\n )\n const targetPackument = targetVersion\n ? packument.versions[targetVersion]\n : undefined\n // Check !targetVersion to make TypeScript happy.\n if (!targetVersion || !targetPackument) {\n // No suitable patch version found.\n return false\n }\n // Use Object.defineProperty to override the version.\n Object.defineProperty(node, 'version', {\n configurable: true,\n enumerable: true,\n get: () => targetVersion\n })\n node.package.version = targetVersion\n // Update resolved and clear integrity for the new version.\n const purlObj = PackageURL.fromString(`pkg:npm/${node.name}`)\n node.resolved = `${NPM_REGISTRY_URL}/${node.name}/-/${purlObj.name}-${targetVersion}.tgz`\n const { integrity } = targetPackument.dist\n if (integrity) {\n node.integrity = integrity\n } else {\n delete node.integrity\n }\n if ('deprecated' in targetPackument) {\n node.package['deprecated'] = <string>targetPackument.deprecated\n } else {\n delete node.package['deprecated']\n }\n const newDeps = { ...targetPackument.dependencies }\n const { dependencies: oldDeps } = node.package\n node.package.dependencies = newDeps\n if (oldDeps) {\n for (const oldDepName of Object.keys(oldDeps)) {\n if (!hasOwn(newDeps, oldDepName)) {\n node.edgesOut.get(oldDepName)?.detach()\n }\n }\n }\n for (const newDepName of Object.keys(newDeps)) {\n if (!hasOwn(oldDeps, newDepName)) {\n node.addEdgeOut((<unknown>new Edge({\n from: node,\n name: newDepName,\n spec: newDeps[newDepName],\n type: 'prod'\n })) as SafeEdge)\n }\n }\n return true\n}\n\ntype GetPackageAlertsOptions = {\n output?: Writable | undefined\n consolidate?: boolean | undefined\n includeExisting?: boolean | undefined\n includeUnfixable?: boolean | undefined\n}\n\nexport async function getPackagesAlerts(\n arb: SafeArborist,\n options?: GetPackageAlertsOptions | undefined\n): Promise<SocketPackageAlert[]> {\n const {\n consolidate = false,\n includeExisting = false,\n includeUnfixable = true,\n output\n } = <GetPackageAlertsOptions>{\n __proto__: null,\n ...options\n }\n const needInfoOn = getPackagesToQueryFromDiff(arb.diff, {\n includeUnchanged: includeExisting\n })\n const purls = arrayUnique(needInfoOn.map(d => d.node.pkgid))\n let { length: remaining } = purls\n const results: SocketPackageAlert[] = []\n if (!remaining) {\n return results\n }\n const pkgJson = (arb.actualTree ?? arb.idealTree)!.package\n const spinner = output ? new Spinner({ stream: output }) : undefined\n const getText = () => `Looking up data for ${remaining} packages`\n const decrementRemaining = () => {\n remaining -= 1\n if (spinner && remaining > 0) {\n spinner.start()\n spinner.setText(getText())\n }\n }\n spinner?.start(getText())\n for await (const artifact of batchScan(purls)) {\n if (!artifact.name || !artifact.version || !artifact.alerts?.length) {\n decrementRemaining()\n continue\n }\n const name = resolvePackageName(artifact)\n const { version } = artifact\n let displayWarning = false\n let sockPkgAlerts = []\n for (const alert of artifact.alerts) {\n // eslint-disable-next-line no-await-in-loop\n const ux = await uxLookup({\n package: { name, version },\n alert: { type: alert.type }\n })\n if (ux.display) {\n displayWarning = !!output\n }\n const fixableCve = isArtifactAlertCveFixable(alert)\n const fixableUpgrade = isArtifactAlertUpgradeFixable(alert)\n if (\n (fixableCve || fixableUpgrade || includeUnfixable) &&\n !(fixableUpgrade && hasOverride(pkgJson, name))\n ) {\n sockPkgAlerts.push({\n name,\n version,\n key: alert.key,\n type: alert.type,\n block: ux.block,\n raw: alert,\n fixable: fixableCve || fixableUpgrade\n })\n }\n }\n if (!includeExisting && sockPkgAlerts.length) {\n // Before we ask about problematic issues, check to see if they\n // already existed in the old version if they did, be quiet.\n const allExisting = needInfoOn.filter(d =>\n d.existing?.pkgid.startsWith(`${name}@`)\n )\n for (const { existing } of allExisting) {\n const oldAlerts: SocketArtifact['alerts'] | undefined =\n // eslint-disable-next-line no-await-in-loop\n (await batchScan([existing!.pkgid]).next()).value?.alerts\n if (oldAlerts?.length) {\n // SocketArtifactAlert and SocketPackageAlert both have the 'key' property.\n sockPkgAlerts = sockPkgAlerts.filter(\n ({ key }) => !oldAlerts.find(a => a.key === key)\n )\n }\n }\n }\n if (consolidate && sockPkgAlerts.length) {\n const highestForCve = new Map<number, SocketPackageAlert>()\n const highestForUpgrade = new Map<number, SocketPackageAlert>()\n const unfixableAlerts: SocketPackageAlert[] = []\n for (const sockPkgAlert of sockPkgAlerts) {\n if (isArtifactAlertCveFixable(sockPkgAlert.raw)) {\n const version =\n sockPkgAlert.raw.props[\n CVE_ALERT_PROPS_FIRST_PATCHED_VERSION_IDENTIFIER\n ]\n const major = semver.major(version)\n const highest =\n highestForCve.get(major)?.raw[\n CVE_ALERT_PROPS_FIRST_PATCHED_VERSION_IDENTIFIER\n ] ?? '0.0.0'\n if (semver.gt(version, highest)) {\n highestForCve.set(major, sockPkgAlert)\n }\n } else if (isArtifactAlertUpgradeFixable(sockPkgAlert.raw)) {\n const { version } = sockPkgAlert\n const major = semver.major(version)\n const highest = highestForUpgrade.get(major)?.version ?? '0.0.0'\n if (semver.gt(version, highest)) {\n highestForUpgrade.set(major, sockPkgAlert)\n }\n } else {\n unfixableAlerts.push(sockPkgAlert)\n }\n }\n sockPkgAlerts = [\n ...unfixableAlerts,\n ...highestForCve.values(),\n ...highestForUpgrade.values()\n ]\n }\n sockPkgAlerts.sort((a, b) => naturalCompare(a.type, b.type))\n spinner?.stop()\n if (displayWarning && sockPkgAlerts.length) {\n const lines = new Set()\n const translations = getTranslations()\n for (const sockPkgAlert of sockPkgAlerts) {\n const attributes = [\n ...(sockPkgAlert.fixable ? ['fixable'] : []),\n ...(sockPkgAlert.block ? [] : ['non-blocking'])\n ]\n const maybeAttributes = attributes.length\n ? ` (${attributes.join('; ')})`\n : ''\n // Based data from { pageProps: { alertTypes } } of:\n // https://socket.dev/_next/data/94666139314b6437ee4491a0864e72b264547585/en-US.json\n const info = (translations.alerts as any)[sockPkgAlert.type]\n const title = info?.title ?? sockPkgAlert.type\n const maybeDesc = info?.description ? ` - ${info.description}` : ''\n // TODO: emoji seems to mis-align terminals sometimes\n lines.add(` ${title}${maybeAttributes}${maybeDesc}\\n`)\n }\n output?.write(\n `(socket) ${formatter.hyperlink(\n `${name}@${version}`,\n getSocketDevPackageOverviewUrl(NPM, name, version)\n )} contains risks:\\n`\n )\n for (const line of lines) {\n output?.write(line)\n }\n }\n results.push(...sockPkgAlerts)\n decrementRemaining()\n }\n spinner?.stop()\n return results\n}\n\ntype CveInfoByPackage = Map<\n string,\n {\n firstPatchedVersionIdentifier: string\n vulnerableVersionRange: string\n }[]\n>\n\ntype GetCveInfoByPackageOptions = {\n excludeUpgrades?: boolean | undefined\n}\n\nexport function getCveInfoByPackage(\n alerts: SocketPackageAlert[],\n options?: GetCveInfoByPackageOptions | undefined\n): CveInfoByPackage | null {\n const { excludeUpgrades } = <GetCveInfoByPackageOptions>{\n __proto__: null,\n ...options\n }\n let infoByPkg: CveInfoByPackage | null = null\n for (const alert of alerts) {\n if (\n !isArtifactAlertCveFixable(alert.raw) ||\n (excludeUpgrades && getManifestData(NPM, alert.name))\n ) {\n continue\n }\n if (!infoByPkg) {\n infoByPkg = new Map()\n }\n const { name } = alert\n let infos = infoByPkg.get(name)\n if (!infos) {\n infos = []\n infoByPkg.set(name, infos)\n }\n const { firstPatchedVersionIdentifier, vulnerableVersionRange } =\n alert.raw.props\n infos.push({\n firstPatchedVersionIdentifier,\n vulnerableVersionRange\n })\n }\n return infoByPkg\n}\n\nexport async function updateAdvisoryNodes(\n arb: SafeArborist,\n alerts: SocketPackageAlert[]\n) {\n const infoByPkg = getCveInfoByPackage(alerts)\n if (!infoByPkg) {\n // No advisories to process.\n return\n }\n await arb.buildIdealTree()\n const tree = arb.idealTree!\n for (const { 0: name, 1: infos } of infoByPkg) {\n const nodes = findPackageNodes(tree, name)\n const packument =\n nodes.length && infos.length\n ? // eslint-disable-next-line no-await-in-loop\n await fetchPackagePackument(name)\n : null\n if (packument) {\n for (const node of nodes) {\n for (const {\n firstPatchedVersionIdentifier,\n vulnerableVersionRange\n } of infos) {\n updateNode(\n node,\n packument,\n vulnerableVersionRange,\n firstPatchedVersionIdentifier\n )\n }\n }\n }\n }\n}\n\nexport async function updateSocketRegistryNodes(arb: SafeArborist) {\n await arb.buildIdealTree()\n const tree = arb.idealTree!\n for (const { 1: data } of getManifestData(NPM)) {\n const nodes = findPackageNodes(tree, data.name)\n const packument = nodes.length\n ? // eslint-disable-next-line no-await-in-loop\n await fetchPackagePackument(data.name)\n : null\n if (packument) {\n for (const node of nodes) {\n updateNode(node, packument)\n }\n }\n }\n}\n\nexport const kRiskyReify = Symbol('riskyReify')\n\ntype SafeArborist = ArboristClass & {\n [kRiskyReify](options?: ArboristReifyOptions): Promise<SafeNode>\n}\n\nexport async function reify(\n this: SafeArborist,\n ...args: Parameters<InstanceType<ArboristClass>['reify']>\n): Promise<SafeNode> {\n const { stderr: output, stdin: input } = process\n const alerts = await getPackagesAlerts(this, { output })\n if (\n alerts.length &&\n !(await confirm(\n {\n message: 'Accept risks of installing these packages?',\n default: false\n },\n {\n input,\n output\n }\n ))\n ) {\n throw new Error('Socket npm exiting due to risks')\n }\n return await this[kRiskyReify](...args)\n}\n","import process from 'node:process'\n\nimport { kRiskyReify, reify } from './reify'\nimport constants from '../../../../constants'\nimport { getArboristClassPath } from '../../../npm-paths'\n\nimport type { ArboristClass, ArboristReifyOptions } from './types'\nimport type { SafeNode } from '../node'\n\nconst {\n SOCKET_CLI_SAFE_WRAPPER,\n kInternalsSymbol,\n [kInternalsSymbol as unknown as 'Symbol(kInternalsSymbol)']: { getIPC }\n} = constants\n\nexport const Arborist: ArboristClass = require(getArboristClassPath())\n\nexport const kCtorArgs = Symbol('ctorArgs')\n\nexport const SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES = {\n __proto__: null,\n audit: false,\n dryRun: true,\n fund: false,\n ignoreScripts: true,\n progress: false,\n save: false,\n saveBundle: false,\n silent: true\n}\n\n// Implementation code not related to our custom behavior is based on\n// https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/arborist/index.js:\nexport class SafeArborist extends Arborist {\n constructor(...ctorArgs: ConstructorParameters<ArboristClass>) {\n super(\n {\n path:\n (ctorArgs.length ? ctorArgs[0]?.path : undefined) ?? process.cwd(),\n ...(ctorArgs.length ? ctorArgs[0] : undefined),\n ...SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES\n },\n ...ctorArgs.slice(1)\n )\n ;(this as any)[kCtorArgs] = ctorArgs\n }\n\n async [kRiskyReify](\n ...args: Parameters<InstanceType<ArboristClass>['reify']>\n ): Promise<SafeNode> {\n const ctorArgs = (this as any)[kCtorArgs]\n const arb = new Arborist(\n {\n ...(ctorArgs.length ? ctorArgs[0] : undefined),\n progress: false\n },\n ...ctorArgs.slice(1)\n )\n arb.actualTree = this.actualTree\n arb.idealTree = this.idealTree\n const ret = await (arb.reify as (...args: any[]) => Promise<SafeNode>)(\n {\n ...(args.length ? args[0] : undefined),\n progress: false\n },\n ...args.slice(1)\n )\n Object.assign(this, arb)\n return ret\n }\n\n // @ts-ignore Incorrectly typed.\n override async reify(\n this: SafeArborist,\n ...args: Parameters<InstanceType<ArboristClass>['reify']>\n ): Promise<SafeNode> {\n const options = <ArboristReifyOptions>{\n __proto__: null,\n ...(args.length ? args[0] : undefined)\n }\n const safeArgs = [\n {\n ...options,\n progress: false\n },\n ...args.slice(1)\n ]\n if (options.dryRun || !(await getIPC(SOCKET_CLI_SAFE_WRAPPER))) {\n return await this[kRiskyReify](...safeArgs)\n }\n Object.assign(options, SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES)\n const old = args[0]\n args[0] = options\n await super.reify(...safeArgs)\n args[0] = old\n return await Reflect.apply(reify, this, safeArgs)\n }\n}\n"],"names":["getSentry","constructor","cwd","root","dir","encoding","logger","mkdirSync","recursive","WIN32","_warnedSettingPathWin32Missing","dataHome","_settingsPath","yml","path","parsed","prevDir","settings","pendingSave","writeFileSync","SOCKET_CLI_NO_API_TOKEN","_defaultToken","message","proxy","baseUrl","NPM_REGISTRY_URL","includeUnchanged","includeUnknownOrigin","__proto__","length","action","actual","ideal","keep","existing","node","queue","unchanged","abortSignal","method","headers","signal","components","input","crlfDelay","events","index","iteratorResult","enqueueGen","running","type","block","display","defaultValue","iterate_entries","needDefault","orderedRules","target","orderedRulesCollection","resolvedDefaultValue","ux","cachedUX","orgs","cause","deferTo","issueRules","_uxLookup","info","error","success","warning","header","hyperlink","fallback","fallbackToUrl","UNDEFINED_TOKEN","id","transformer","mod","canDedupe","canReplaceWith","overrides","recalculateOutEdgesOverrides","edge","newOverrideSet","from","detach","explain","bundled","overridden","rawSpec","explanation","reload","needToUpdateOverrideSet","RESOLUTIONS","eligibleVersions","matches","_translations","Object","configurable","enumerable","integrity","dependencies","name","spec","consolidate","includeExisting","includeUnfixable","output","stream","remaining","spinner","decrementRemaining","version","package","alert","raw","sockPkgAlerts","key","highestForCve","highestForUpgrade","unfixableAlerts","results","excludeUpgrades","infoByPkg","infos","vulnerableVersionRange","stderr","stdin","default","getIPC","audit","dryRun","fund","ignoreScripts","progress","save","saveBundle","silent","arb","args"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMA;;AAEE;AAA+DA;AAAU;AAC3E;AAIO;AAEA;AAGLC;;;AAGA;AACF;AAEO;AAIL;AACA;;AAEA;AACF;AAEO;AAIL;;AAEE;AACF;;AAEA;AACF;AAEO;AAGL;AACE;AACF;AACA;AACF;;AC7CO;AAEHC;AAAqC;AAEvC;;AACQC;AAAK;;AAEb;AACE;;;AAGI;;AAEA;AACE;AACF;;AAEJ;AACAC;AACF;AACA;AACF;AAOO;AAIL;AACE;AACAC;AACF;AACF;AAEO;AAIL;AACE;AACAA;AACF;AACF;AAEO;;AAIH;;AAEF;AACF;AAEO;;AAIH;;AAEF;AACF;;AC7DA;AAEA;AAgBA;AACA;;;AAGI;AACA;AACE;AACA;;;AAME;AACEC;AACF;AACF;AACEC;AAAwCC;AAAgB;AAC1D;AACF;AACF;AACA;AACF;AAEA;AACA;AACA;;AAEI;;AACQC;AAAM;AACd;;AAIE;;AAEIC;AACAJ;AACF;AACF;AACEK;AAMF;AACF;AACAC;AAGF;AACA;AACF;AAEA;;AAEE;AACE;AACF;AACA;AACF;AAEO;;AAEL;;;AAGE;;;AAGEC;AACF;AACA;;;AAGMC;AACAC;;AAEJ;AACE;AACF;AACF;AACAC;;AAEF;AACA;AACF;AAEO;;AAIP;AAEA;AACO;AAIL;AACEC;;AAEAC;;AAEEA;AACA;AACA;AACEC;AAIF;AACF;AACF;AACF;;AClIA;AAAQC;AAAwB;;AAEhC;AACA;AACE;AAEA;AACF;;AAEA;AACA;AACE;AAEA;AACF;;AAEA;AACA;AACO;AACL;AACA;AACEC;AACF;AACE;AAEE;AACA;;;AAKJ;AACA;AACF;AAEO;AACL;AACF;AAEO;;;AAODC;AAEF;AACAD;AACF;;AAEE;AACF;AACA;;;AAGmCE;AAAM;;AACJA;;AAC/B;AAEJC;AACA;;AAEF;AACF;;AC1EA;;AAAuBC;AAAiB;AAExC;;;;AAIE;AACF;AAYO;;AAIGC;AAA0BC;AAA6B;AAG7DC;;;;AAIF;;AAEE;AACF;AACA;;;AAEMC;AAAoB;;;AAGtB;AACF;AACA;;AACQC;AAAO;AACf;AACE;AACA;AACA;AACA;;AACQC;AAAiBC;AAAe;AACxC;;;;AAIIC;AACA;AAIEC;AACF;AACF;AAIF;;AAEA;AACA;;;AAMMC;AACAD;AACF;AACF;AACF;AACF;AACA;AACEE;AACF;AACF;AACA;;AACUC;AAAU;AAClB;AAAkBR;;AAChB;;;AAMIM;AACAD;AACF;AACF;AACF;AACF;AACA;AACF;;AC1BA;;;;;;;;AAQEI;AACF;AAEA;AAGE;AACA;AACE;AAAA;AAEEC;AACAC;;;AAGAC;AACF;AAGIC;;AAAqD;AACvD;AAEJ;;AACQ;;AAC+BD;AAAoB;AAE3D;;;AAGA;AACA;AACEE;AACAC;AACAH;AACF;AACA;AACE;AACF;AACF;AAEO;AAcL;AACA;AACA;AACA;AACA;;;;AAIEI;AACF;;AACQhB;AAAoB;;;;;AAKxB;AACA;AACF;;AAEAiB;AACA;;;;AAIA;;;;AAIA;;;AAGuCC;AAAoB;;AAE7D;;AAEEC;AACF;AACA;AACE;;;AACmBD;AAAe;AAGlC;AACAE;;AAKE;AACAD;AACF;;AAEE;;AAEF;AACF;AACA;;AAEEH;AACF;AACF;AAEO;;AAGGK;AAAK;AACb;AAQF;AAEO;AAGL;AACF;;AClLA;AAAQZ;AAAY;AAEpB;AACEa;AACAC;AACF;AAEA;AACED;AACAC;AACF;AAEA;AACED;AACAC;AACF;;AAEA;AACA;AACA;AACA;;AASIC;AAAiBvB;;AACnB;AACEuB;AAAiBvB;;AACnB;;;;AAIAwB;AACE;AACE;AACEC;AACA;AACAJ;AACAC;AACA;AACF;AACF;AACA;AACAD;AACAC;AACF;AACA;AACE;AACAD;AACAC;AACF;;;AACgBA;;AAClB;;AAEA;AACA;;AAII;AACF;AACA;;AACUtB;AAAO;AACf;AACE;AACF;AACF;AACA;AACF;;AAEA;AACA;AAGE;AACE;AACF;;AACQA;AAAO;;AAEb;AACF;AACE;AACF;AACA;AACF;AAMO;AAIL;AAEA;;AACUoB;;AACR;AACA;AACE;AACF;;AAEA;;AAEE;;AAEE;;AAEE;AACF;AACA;AACA;AACEM;AACF;AACAC;AACF;AACAC;AACF;;AAKA;AACE5B;;;AAGA6B;AAAyB7B;;AAC3B;AACE6B;AAAyB7B;;AAC3B;AACA8B;AACAC;AACA;;AAEJ;AAEA;AACO;;AAIH;;AACgBpB;AAAoB;AACtC;;AAEF;;AAEA;AACA;;;AACgBxB;;;;AAGV;AACA;;AAIA;;AAKA;AACE;AACE6C;AACF;AACF;AACA;;;AAGA;;AAIA;;;;;;AAMA;AACA;AAIE;AAGIC;AACF;AAEJ;AACA;AACF;AACF;;AAEA;AACA;AACA;AAAa;AAAM;AAAO;;;AAGxB;AACF;AAEA;AACA;AACE9C;;AAEEA;;AAEI+C;AACA;AACA;AACA;AACA;AACA;AACAC;AAKF;AACF;AACF;AACF;AACAC;AACF;;ACxQA;AACEtC;AACAuC;AACAC;AACAC;AACAC;AACF;AAEO;;AAIH;AACF;;AAGE;AACF;AAEAC;AACE;AAGF;AAEAC;AAIIC;AACAC;;AAMF;AACE;;AAII;AACN;AACA;AACF;;AAKE;AACF;;AAGE;AACF;;;AAMA;;AAGE;;AAIF;;;AAIA;AACF;;AChFO;;AAEP;AAEO;AAKL;AACF;;ACNO;;ACDP;AAAQC;AAAgB;AAaxB;AAIE;AACE;AACA;AACA;AACEC;AACAC;AACF;AACED;;AAEF;;AAEE;AACA;;;AAGE;AACF;;AAEJ;AACA;AACF;AAOA;AACO;;;AAMC;AACA;AACAE;AAIN;AACA;AACF;;AChCA;;AAEA;AACA;AACO;AACL;AACA;AACA;AAIE;AACA;AACA;;AAEF;;AAEA;AACA;AACA;AAIE;AAKE;AACE;AACF;AACF;AACA;AAKE;AACE;AACF;AACF;AACA;AACA;;AAEA;AACF;;AAEA;AACA;;;AAGI;AACF;AACA;AAAa;AAAQ;AAAoB;;;AAGrC;AACF;AACA;AACE;AACF;AACA;AACE;AACF;AACF;AACA;AACF;;;AAII;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACE;AACE;AACF;AACA;AACF;;AAEE;AAGE;AACF;AACA;AACF;AACA;AACA;AACA;AACA;AACF;AACA;AACF;;AAEA;AACA;;;AAGI;AACF;;AAEE;AACF;AACA;AAIE;AACF;AACA;AACE;AACF;AACA;;AAEA;;AAEF;AACF;;ACpFA;;AAEA;AACA;AACO;AACL;AACA;AACA;AACSC;AACP;AACA;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;;AAEE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACE;AACF;AACA;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACSC;AACP;AACE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;;;AAGI;AACF;AACF;;AAEI;AACF;AACF;AACF;AACA;AACA;AACA;AACA;AACA;;AAEA;AACE;AACF;;;;;;AAME;AACF;AACA;AACF;;AAEA;;AAEE;;AACQC;AAAU;AAClB;AACE;AACF;AACF;;AAGE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AACF;AACA;AACA;;AAEF;;AAEA;;AAEE;AACA;AACA;;AAME;AACF;AACA;AACA;AACA;AACA;AACA;AACA;;AAMI;AACE;AACF;AACF;AACF;AACA;AACF;;AAGE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACQA;AAAU;AAClB;;AAEA;;;;;;AAME;AACF;AACF;;AAEA;AACA;AACSC;AACP;;AAEEC;;;AAGA;AACF;AACF;;AAEA;;AAEE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AAAuCF;AAAc;AACvD;;;;;;AAME;AACF;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAKI;AACA;AACA;AACA;AACA;AACF;AACA;;;AAGE;AACF;;AAEE;AACF;;AAKA;;AAEI;AACF;;;AAGA;AACF;AACA;AACA;AACA;;AAEA;AACF;;AAEA;AACA;;AAEE;AACA;AACA;AACE;AACF;AACA;AACA;;AACUA;AAAyB;;;AAMjC;AACEG;AACF;AACF;;AAEE;AACF;;AAEA;AACE;AACA;AACA;AACA;AACA;;AAEF;AACA;AACF;AACF;;ACrUO;;AAEP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACL;AACA;AACA;AACA;;;AAGUC;AAAK;AACb;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACF;;AAGE;AACF;;AAGE;AACE;;AAEI;AACF;AACE;AACF;AACF;AAGE;AACA;AACA;AAEA;AACF;AACE;AACF;AACA;AACA;AAAA;AASE;AACA;AACA;AACA;AACA;AACA;AACF;AACE;AACF;AACF;AACA;AACE;AACF;;AAEF;;AAEA;;;AAGA;;AAEA;;;;;AASM;AACA;AACA;AACA;AACA;;AAIA;AACE;AACF;AACA;AACE;AACF;AACA;AACE;AACF;AACA;AACE;AACF;;AAEF;AACA;AACF;;AAEF;;AAEA;;;AAGA;AAESC;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACF;;AAEA;AACA;AACSC;AACP;AACE;;;;AAIEC;AACAC;AACArB;AACAiB;AACAK;;AAEF;AACEC;;AAEF;;AAEEA;AACF;;AAEEA;AACF;AACA;;AAEA;AACA;AACF;;AAEF;AAESC;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;;;AAGI;AACA;AACA;AACA;AACAC;;;AAGF;AACF;;AAEA;AACA;AACA;AACA;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACF;AACA;AACA;AAAA;AAEE;AACA;AACA;AACF;AACF;;AAGE;AACA;AACA;AACA;AACA;AACA;AACE;AACF;AACA;AACA;;AAEE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AACF;;AC/QA;;;;;;;AAOEC;AACF;AAEA;AAEA;;AAOE;;;AAGEC;AACF;;AAEEA;AAEI;AACA;;AAKN;AACA;AACF;AAEO;;AAIqC5D;AAAW;;;;AAInD;AACE;AACF;;AACQA;AAAkB;;AAE1B;AACE6D;AACF;;AAEA;;;AACwD;AACxD;AACF;AACA;AACF;AAEA;AACA;;AAEIC;AACE;;AAGJ;AACA;AACF;AAEA;AACE;AAKF;AAEO;;AAOL;;;AAUA;AACA;AACE;AACA;AACF;AACA;AACAC;AACEC;AACAC;;AAEF;AACAjE;AACA;;AAEAA;;AACQkE;;AACR;;AAEA;;AAEA;;;AAGA;AACE;AACF;AACA;AAAkB;;;AACVC;;AACRnE;AACA;;AAEI;;AAEA;AACF;AACF;;AAEE;AACEA;AACIkD;AACAkB;AACAC;AACAtD;AACF;AACJ;AACF;AACA;AACF;AASO;;AAKHuD;AACAC;AACAC;AACAC;AACF;AACEhF;;;AAGF;AACEF;AACF;AACA;;AACMG;AAAkB;;;AAGtB;AACF;;AAEA;AAAuCgF;;AACvC;;AAEEC;AACA;;AAEEC;AACF;;AAEFA;AACA;AACE;AACEC;AACA;AACF;AACA;;AACQC;AAAQ;;;AAGhB;AACE;AACA;AACEC;;AAAiBD;;AACjBE;;AAA0B;AAC5B;;;AAGA;AACA;AACA;AACA;;;;;;;AAUIC;;AAEF;AACF;AACF;AACA;AACE;AACA;;AAIA;AAAalF;;AACX;AACE;AACA;;AAEA;AACAmF;AACKC;AAAI;AAEX;AACF;AACF;AACA;AACE;AACA;;AAEA;AACE;;AAKE;AACA;;AAKEC;AACF;;;AAEQN;AAAQ;AAChB;;;AAGEO;AACF;AACF;AACEC;AACF;AACF;AACAJ;AAKF;AACAA;;AAEA;AACE;AACA;AACA;;AAKE;AAGA;AACA;;;AAGA;AACA;;AAEF;;AAOA;AACET;AACF;AACF;AACAc;AACAV;AACF;;AAEA;AACF;AAcO;;AAIGW;AAAgB;AACtB/F;;;;AAIF;AACE;AAIE;AACF;;AAEEgG;AACF;;AACQrB;AAAK;AACb;;AAEEsB;AACAD;AACF;;;AACuCE;AAAuB;;;AAI5DA;AACF;AACF;AACA;AACF;AAuDO;AAMA;;AAIGC;AAAgBC;AAAa;AACrC;AAA+CpB;AAAO;AACtD;AAIMtF;AACA2G;AACF;;AAGErB;;AAIJ;AACF;;AAEF;;ACleA;;;AAGE;AAA+DsB;AAAO;AACxE;AAEO;AAEA;AAEA;AACLtG;AACAuG;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACF;;AAEA;AACA;AACO;;AAEH;AAEI5H;;;;AAOF;AACJ;AAEA;AAGE;AACA;;AAGIyH;;AAIJI;AACAA;AACA;;AAGIJ;;AAIJrC;AACA;AACF;;AAEA;AACA;AAIE;AACEtE;;;;AAKE;AACA2G;;;;AAMJ;AACArC;AACA;AACA0C;AACA;AACAA;;AAEF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;","debugId":"30e57097-6100-437d-9b6d-904604eeaa8d"}
|