@socketsecurity/cli-with-sentry 1.0.111 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +148 -1295
- package/dist/cli.js.map +1 -1
- package/dist/constants.js +6 -5
- package/dist/constants.js.map +1 -1
- package/dist/shadow-npm-bin.js +10 -4
- package/dist/shadow-npm-bin.js.map +1 -1
- package/dist/shadow-npm-inject.js +21 -240
- package/dist/shadow-npm-inject.js.map +1 -1
- package/dist/tsconfig.dts.tsbuildinfo +1 -1
- package/dist/types/commands/fix/cmd-fix.d.mts.map +1 -1
- package/dist/types/commands/fix/coana-fix.d.mts +1 -1
- package/dist/types/commands/fix/coana-fix.d.mts.map +1 -1
- package/dist/types/commands/fix/handle-fix.d.mts +1 -1
- package/dist/types/commands/fix/handle-fix.d.mts.map +1 -1
- package/dist/types/commands/fix/types.d.mts +18 -0
- package/dist/types/commands/fix/types.d.mts.map +1 -0
- package/dist/types/constants.d.mts.map +1 -1
- package/dist/types/shadow/npm/arborist/lib/arborist/index.d.mts.map +1 -1
- package/dist/types/shadow/npm/arborist-helpers.d.mts +1 -1
- package/dist/types/shadow/npm/arborist-helpers.d.mts.map +1 -1
- package/dist/types/shadow/npm/bin.d.mts.map +1 -1
- package/dist/types/utils/alerts-map.d.mts.map +1 -1
- package/dist/types/utils/fs.d.mts +3 -1
- package/dist/types/utils/fs.d.mts.map +1 -1
- package/dist/utils.js +990 -1222
- package/dist/utils.js.map +1 -1
- package/dist/vendor.js +111951 -119286
- package/package.json +6 -6
- package/dist/types/commands/fix/agent-fix.d.mts +0 -42
- package/dist/types/commands/fix/agent-fix.d.mts.map +0 -1
- package/dist/types/commands/fix/get-actual-tree.d.mts +0 -3
- package/dist/types/commands/fix/get-actual-tree.d.mts.map +0 -1
- package/dist/types/commands/fix/npm-fix.d.mts +0 -7
- package/dist/types/commands/fix/npm-fix.d.mts.map +0 -1
- package/dist/types/commands/fix/pnpm-fix.d.mts +0 -7
- package/dist/types/commands/fix/pnpm-fix.d.mts.map +0 -1
- package/dist/types/commands/fix/shared.d.mts +0 -10
- package/dist/types/commands/fix/shared.d.mts.map +0 -1
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../src/utils/config.mts","../src/utils/sdk.mts","../src/utils/api.mts","../src/utils/fail-msg-with-badge.mts","../src/utils/markdown.mts","../src/utils/serialize-result-json.mts","../src/utils/check-input.mts","../src/utils/get-output-kind.mts","../src/utils/requirements.mts","../src/utils/output-formatting.mts","../src/utils/tildify.mts","../src/utils/meow-with-subcommands.mts","../src/utils/ms-at-home.mts","../src/commands/organization/fetch-organization-list.mts","../src/commands/scan/suggest-org-slug.mts","../src/commands/scan/suggest-to-persist-orgslug.mts","../src/utils/determine-org-slug.mts","../src/commands/ci/fetch-default-org-slug.mts","../src/utils/git.mts","../src/utils/purl.mts","../src/utils/socket-url.mts","../src/utils/map-to-object.mts","../src/utils/walk-nested-map.mts","../src/utils/glob.mts","../src/utils/path-resolve.mts","../src/utils/npm-paths.mts","../src/utils/cmd.mts","../src/utils/coana.mts","../src/utils/organization.mts","../src/utils/socket-json.mts","../src/utils/alert/artifact.mts","../src/utils/objects.mts","../src/utils/alert/fix.mts","../src/utils/alert/severity.mts","../src/utils/color-or-markdown.mts","../src/utils/filter-config.mts","../src/utils/semver.mts","../src/utils/translations.mts","../src/utils/socket-package-alert.mts","../src/utils/spec.mts","../src/utils/pnpm.mts","../src/utils/alerts-map.mts","../src/utils/npm-package-arg.mts","../src/utils/fs.mts","../src/shadow/npm/install.mts","../src/utils/agent.mts","../src/utils/npm-config.mts","../src/utils/lockfile.mts","../src/utils/package-environment.mts","../src/utils/completion.mts","../src/utils/errors.mts","../src/utils/ecosystem.mts"],"sourcesContent":["import { mkdirSync, writeFileSync } from 'node:fs'\nimport path from 'node:path'\n\nimport config from '@socketsecurity/config'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { safeReadFileSync } from '@socketsecurity/registry/lib/fs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\n\nimport constants from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { SocketYml } from '@socketsecurity/config'\n\nexport interface LocalConfig {\n apiBaseUrl?: string | null | undefined\n // @deprecated ; use apiToken. when loading a config, if this prop exists it\n // is deleted and set to apiToken instead, and then persisted.\n // should only happen once for legacy users.\n apiKey?: string | null | undefined\n apiProxy?: string | null | undefined\n apiToken?: string | null | undefined\n defaultOrg?: string\n enforcedOrgs?: string[] | readonly string[] | null | undefined\n skipAskToPersistDefaultOrg?: boolean\n org?: string // convenience alias for defaultOrg\n}\n\nconst sensitiveConfigKeyLookup: Set<keyof LocalConfig> = new Set(['apiToken'])\n\nconst supportedConfig: Map<keyof LocalConfig, string> = new Map([\n ['apiBaseUrl', 'Base URL of the Socket API endpoint'],\n ['apiProxy', 'A proxy through which to access the Socket API'],\n [\n 'apiToken',\n 'The Socket API token required to access most Socket API endpoints',\n ],\n [\n 'defaultOrg',\n 'The default org slug to use; usually the org your Socket API token has access to. When set, all orgSlug arguments are implied to be this value.',\n ],\n [\n 'enforcedOrgs',\n 'Orgs in this list have their security policies enforced on this machine',\n ],\n [\n 'skipAskToPersistDefaultOrg',\n 'This flag prevents the Socket CLI from asking you to persist the org slug when you selected one interactively',\n ],\n ['org', 'Alias for defaultOrg'],\n])\n\nconst supportedConfigEntries = [...supportedConfig.entries()].sort((a, b) =>\n naturalCompare(a[0], b[0]),\n)\nconst supportedConfigKeys = supportedConfigEntries.map(p => p[0])\n\nfunction getConfigValues(): LocalConfig {\n if (_cachedConfig === undefined) {\n // Order: env var > --config flag > file\n _cachedConfig = {} as LocalConfig\n const { socketAppDataPath } = constants\n if (socketAppDataPath) {\n const raw = safeReadFileSync(socketAppDataPath)\n if (raw) {\n try {\n Object.assign(\n _cachedConfig,\n JSON.parse(Buffer.from(raw, 'base64').toString()),\n )\n } catch {\n logger.warn(`Failed to parse config at ${socketAppDataPath}`)\n }\n // Normalize apiKey to apiToken and persist it.\n // This is a one time migration per user.\n if (_cachedConfig['apiKey']) {\n const token = _cachedConfig['apiKey']\n delete _cachedConfig['apiKey']\n updateConfigValue('apiToken', token)\n }\n } else {\n mkdirSync(path.dirname(socketAppDataPath), { recursive: true })\n }\n }\n }\n return _cachedConfig\n}\n\nfunction normalizeConfigKey(\n key: keyof LocalConfig,\n): CResult<keyof LocalConfig> {\n // Note: apiKey was the old name of the token. When we load a config with\n // property apiKey, we'll copy that to apiToken and delete the old property.\n // We added `org` as a convenience alias for `defaultOrg`\n const normalizedKey =\n key === 'apiKey' ? 'apiToken' : key === 'org' ? 'defaultOrg' : key\n if (!isSupportedConfigKey(normalizedKey)) {\n return {\n ok: false,\n message: `Invalid config key: ${normalizedKey}`,\n data: undefined,\n }\n }\n return { ok: true, data: normalizedKey }\n}\n\nexport type FoundSocketYml = {\n path: string\n parsed: SocketYml\n}\n\nexport function findSocketYmlSync(\n dir = process.cwd(),\n): FoundSocketYml | undefined {\n let prevDir = null\n while (dir !== prevDir) {\n let ymlPath = path.join(dir, 'socket.yml')\n let yml = safeReadFileSync(ymlPath)\n if (yml === undefined) {\n ymlPath = path.join(dir, 'socket.yaml')\n yml = safeReadFileSync(ymlPath)\n }\n if (typeof yml === 'string') {\n try {\n return {\n path: ymlPath,\n parsed: config.parseSocketConfig(yml),\n }\n } catch (e) {\n debugDir('inspect', { error: e })\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 undefined\n}\n\nexport function getConfigValue<Key extends keyof LocalConfig>(\n key: Key,\n): CResult<LocalConfig[Key]> {\n const localConfig = getConfigValues()\n const keyResult = normalizeConfigKey(key)\n if (!keyResult.ok) {\n return keyResult\n }\n return { ok: true, data: localConfig[keyResult.data as Key] }\n}\n\n// This version squashes errors, returning undefined instead.\n// Should be used when we can reasonably predict the call can't fail.\nexport function getConfigValueOrUndef<Key extends keyof LocalConfig>(\n key: Key,\n): LocalConfig[Key] | undefined {\n const localConfig = getConfigValues()\n const keyResult = normalizeConfigKey(key)\n if (!keyResult.ok) {\n return undefined\n }\n return localConfig[keyResult.data as Key]\n}\n\n// Ensure export because dist/utils.js is required in src/constants.mts.\n// eslint-disable-next-line n/exports-style\nexports.getConfigValueOrUndef = getConfigValueOrUndef\n\nexport function getSupportedConfigEntries() {\n return [...supportedConfigEntries]\n}\n\nexport function getSupportedConfigKeys() {\n return [...supportedConfigKeys]\n}\n\nexport function isReadOnlyConfig() {\n return _readOnlyConfig\n}\n\nexport function isSensitiveConfigKey(key: string): key is keyof LocalConfig {\n return sensitiveConfigKeyLookup.has(key as keyof LocalConfig)\n}\n\nexport function isSupportedConfigKey(key: string): key is keyof LocalConfig {\n return supportedConfig.has(key as keyof LocalConfig)\n}\n\nlet _cachedConfig: LocalConfig | undefined\n// When using --config or SOCKET_CLI_CONFIG, do not persist the config.\nlet _readOnlyConfig = false\n\nexport function overrideCachedConfig(jsonConfig: unknown): CResult<undefined> {\n debugFn('notice', 'override: full config (not stored)')\n\n let config\n try {\n config = JSON.parse(String(jsonConfig))\n if (!config || typeof config !== 'object') {\n // `null` is valid json, so are primitive values.\n // They're not valid config objects :)\n return {\n ok: false,\n message: 'Could not parse Config as JSON',\n cause:\n \"Could not JSON parse the config override. Make sure it's a proper JSON object (double-quoted keys and strings, no unquoted `undefined`) and try again.\",\n }\n }\n } catch {\n // Force set an empty config to prevent accidentally using system settings.\n _cachedConfig = {} as LocalConfig\n _readOnlyConfig = true\n\n return {\n ok: false,\n message: 'Could not parse Config as JSON',\n cause:\n \"Could not JSON parse the config override. Make sure it's a proper JSON object (double-quoted keys and strings, no unquoted `undefined`) and try again.\",\n }\n }\n\n // @ts-ignore Override an illegal object.\n _cachedConfig = config as LocalConfig\n _readOnlyConfig = true\n\n // Normalize apiKey to apiToken.\n if (_cachedConfig['apiKey']) {\n if (_cachedConfig['apiToken']) {\n logger.warn(\n 'Note: The config override had both apiToken and apiKey. Using the apiToken value. Remove the apiKey to get rid of this message.',\n )\n }\n _cachedConfig['apiToken'] = _cachedConfig['apiKey']\n delete _cachedConfig['apiKey']\n }\n\n return { ok: true, data: undefined }\n}\n\nexport function overrideConfigApiToken(apiToken: unknown) {\n debugFn('notice', 'override: Socket API token (not stored)')\n // Set token to the local cached config and mark it read-only so it doesn't persist.\n _cachedConfig = {\n ...config,\n ...(apiToken === undefined ? {} : { apiToken: String(apiToken) }),\n } as LocalConfig\n _readOnlyConfig = true\n}\n\nlet _pendingSave = false\nexport function updateConfigValue<Key extends keyof LocalConfig>(\n configKey: keyof LocalConfig,\n value: LocalConfig[Key],\n): CResult<undefined | string> {\n const localConfig = getConfigValues()\n const keyResult = normalizeConfigKey(configKey)\n if (!keyResult.ok) {\n return keyResult\n }\n const key: Key = keyResult.data as Key\n // Implicitly deleting when serializing.\n let wasDeleted = value === undefined\n if (key === 'skipAskToPersistDefaultOrg') {\n if (value === 'true' || value === 'false') {\n localConfig['skipAskToPersistDefaultOrg'] = value === 'true'\n } else {\n delete localConfig['skipAskToPersistDefaultOrg']\n wasDeleted = true\n }\n } else {\n if (value === 'undefined' || value === 'true' || value === 'false') {\n logger.warn(\n `Note: The value is set to \"${value}\", as a string (!). Use \\`socket config unset\\` to reset a key.`,\n )\n }\n localConfig[key] = value\n }\n if (_readOnlyConfig) {\n return {\n ok: true,\n message: `Config key '${key}' was ${wasDeleted ? 'deleted' : `updated`}`,\n data: 'Change applied but not persisted; current config is overridden through env var or flag',\n }\n }\n\n if (!_pendingSave) {\n _pendingSave = true\n process.nextTick(() => {\n _pendingSave = false\n const { socketAppDataPath } = constants\n if (socketAppDataPath) {\n writeFileSync(\n socketAppDataPath,\n Buffer.from(JSON.stringify(localConfig)).toString('base64'),\n )\n }\n })\n }\n\n return {\n ok: true,\n message: `Config key '${key}' was ${wasDeleted ? 'deleted' : `updated`}`,\n data: undefined,\n }\n}\n","import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'\n\nimport isInteractive from '@socketregistry/is-interactive/index.cjs'\nimport { password } from '@socketsecurity/registry/lib/prompts'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\nimport { SocketSdk, createUserAgentFromPkgJson } from '@socketsecurity/sdk'\n\nimport { getConfigValueOrUndef } from './config.mts'\nimport constants from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\n\nconst TOKEN_PREFIX = 'sktsec_'\n\nconst TOKEN_PREFIX_LENGTH = TOKEN_PREFIX.length\n\nconst TOKEN_VISIBLE_LENGTH = 5\n\n// The Socket API server that should be used for operations.\nfunction getDefaultApiBaseUrl(): string | undefined {\n const baseUrl =\n constants.ENV.SOCKET_CLI_API_BASE_URL || getConfigValueOrUndef('apiBaseUrl')\n return isUrl(baseUrl) ? baseUrl : undefined\n}\n\n// The Socket API server that should be used for operations.\nfunction getDefaultProxyUrl(): string | undefined {\n const apiProxy =\n constants.ENV.SOCKET_CLI_API_PROXY || getConfigValueOrUndef('apiProxy')\n return isUrl(apiProxy) ? apiProxy : undefined\n}\n\nfunction isUrl(value: any): value is string {\n if (isNonEmptyString(value)) {\n try {\n // eslint-disable-next-line no-new\n new URL(value)\n return true\n } catch {}\n }\n return false\n}\n\n// This Socket API token should be stored globally for the duration of the CLI execution.\nlet _defaultToken: string | undefined\nexport function getDefaultApiToken(): string | undefined {\n if (constants.ENV.SOCKET_CLI_NO_API_TOKEN) {\n _defaultToken = undefined\n return _defaultToken\n }\n\n const key =\n constants.ENV.SOCKET_CLI_API_TOKEN ||\n getConfigValueOrUndef('apiToken') ||\n _defaultToken\n\n _defaultToken = isNonEmptyString(key) ? key : undefined\n return _defaultToken\n}\n\nexport function getPublicApiToken(): string {\n return (\n getDefaultApiToken() ||\n constants.ENV.SOCKET_CLI_API_TOKEN ||\n constants.SOCKET_PUBLIC_API_TOKEN\n )\n}\n\nexport function getVisibleTokenPrefix(): string {\n const apiToken = getDefaultApiToken()\n return apiToken\n ? apiToken.slice(\n TOKEN_PREFIX_LENGTH,\n TOKEN_PREFIX_LENGTH + TOKEN_VISIBLE_LENGTH,\n )\n : ''\n}\n\nexport function hasDefaultApiToken(): boolean {\n return !!getDefaultApiToken()\n}\n\nexport type SetupSdkOptions = {\n apiBaseUrl?: string | undefined\n apiProxy?: string | undefined\n apiToken?: string | undefined\n}\n\nexport async function setupSdk(\n options?: SetupSdkOptions | undefined,\n): Promise<CResult<SocketSdk>> {\n const opts = { __proto__: null, ...options } as SetupSdkOptions\n let { apiToken = getDefaultApiToken() } = opts\n\n if (typeof apiToken !== 'string' && isInteractive()) {\n apiToken = await password({\n message:\n 'Enter your Socket.dev API token (not saved, use socket login to persist)',\n })\n _defaultToken = apiToken\n }\n\n if (!apiToken) {\n return {\n ok: false,\n message: 'Auth Error',\n cause: 'You need to provide an API token. Run `socket login` first.',\n }\n }\n\n let { apiProxy } = opts\n if (!isUrl(apiProxy)) {\n apiProxy = getDefaultProxyUrl()\n }\n\n const { apiBaseUrl = getDefaultApiBaseUrl() } = opts\n const ProxyAgent = apiProxy?.startsWith('http:')\n ? HttpProxyAgent\n : HttpsProxyAgent\n\n return {\n ok: true,\n data: new SocketSdk(apiToken, {\n agent: apiProxy ? new ProxyAgent({ proxy: apiProxy }) : undefined,\n baseUrl: apiBaseUrl,\n timeout: constants.ENV.SOCKET_CLI_API_TIMEOUT,\n userAgent: createUserAgentFromPkgJson({\n name: constants.ENV.INLINED_SOCKET_CLI_NAME,\n version: constants.ENV.INLINED_SOCKET_CLI_VERSION,\n homepage: constants.ENV.INLINED_SOCKET_CLI_HOMEPAGE,\n }),\n }),\n }\n}\n","import { messageWithCauses } from 'pony-cause'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { getConfigValueOrUndef } from './config.mts'\nimport constants from '../constants.mts'\nimport { getDefaultApiToken } from './sdk.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\nimport type {\n SocketSdkErrorResult,\n SocketSdkOperations,\n SocketSdkResult,\n SocketSdkSuccessResult,\n} from '@socketsecurity/sdk'\n\nconst NO_ERROR_MESSAGE = 'No error message returned'\n\n// The Socket API server that should be used for operations.\nexport function getDefaultApiBaseUrl(): string | undefined {\n const baseUrl =\n constants.ENV.SOCKET_CLI_API_BASE_URL || getConfigValueOrUndef('apiBaseUrl')\n if (isNonEmptyString(baseUrl)) {\n return baseUrl\n }\n const API_V0_URL = constants.API_V0_URL\n return API_V0_URL\n}\n\nexport async function getErrorMessageForHttpStatusCode(code: number) {\n if (code === 400) {\n return 'One of the options passed might be incorrect'\n }\n if (code === 403 || code === 401) {\n return 'Your Socket API token may not have the required permissions for this command or you might be trying to access (data from) an organization that is not linked to the API token you are logged in with'\n }\n if (code === 404) {\n return 'The requested Socket API endpoint was not found (404) or there was no result for the requested parameters. If unexpected, this could be a temporary problem caused by an incident or a bug in the CLI. If the problem persists please let us know.'\n }\n if (code === 500) {\n return 'There was an unknown server side problem with your request. This ought to be temporary. Please let us know if this problem persists.'\n }\n return `Server responded with status code ${code}`\n}\n\nexport type HandleApiCallOptions = {\n desc?: string | undefined\n spinner?: Spinner | undefined\n}\n\nexport type ApiCallResult<T extends SocketSdkOperations> = CResult<\n SocketSdkSuccessResult<T>['data']\n>\n\nexport async function handleApiCall<T extends SocketSdkOperations>(\n value: Promise<SocketSdkResult<T>>,\n options?: HandleApiCallOptions | undefined,\n): Promise<ApiCallResult<T>> {\n const { desc, spinner } = {\n __proto__: null,\n ...options,\n } as HandleApiCallOptions\n\n if (desc) {\n spinner?.start(`Requesting ${desc} from API...`)\n } else {\n spinner?.start()\n }\n\n let sdkResult: SocketSdkResult<T>\n try {\n sdkResult = await value\n spinner?.stop()\n if (desc) {\n const message = `Received Socket API response (after requesting ${desc}).`\n if (sdkResult.success) {\n logger.success(message)\n } else {\n logger.info(message)\n }\n }\n } catch (e) {\n spinner?.stop()\n const socketSdkErrorResult: ApiCallResult<T> = {\n ok: false,\n message: 'Socket API returned an error',\n cause: messageWithCauses(e as Error),\n }\n if (desc) {\n logger.fail(`An error was thrown while requesting ${desc}`)\n debugFn('error', `caught: ${desc} error`)\n } else {\n debugFn('error', `caught: Socket API request error`)\n }\n debugDir('inspect', { error: e, socketSdkErrorResult })\n return socketSdkErrorResult\n }\n\n // Note: TS can't narrow down the type of result due to generics.\n if (sdkResult.success === false) {\n const errorResult = sdkResult as SocketSdkErrorResult<T>\n const message = `${errorResult.error || NO_ERROR_MESSAGE}`\n const { cause: reason } = errorResult\n const socketSdkErrorResult: ApiCallResult<T> = {\n ok: false,\n message: 'Socket API returned an error',\n cause: `${message}${reason ? ` ( Reason: ${reason} )` : ''}`,\n data: {\n code: sdkResult.status,\n },\n }\n debugFn('error', `fail:${desc ? ` ${desc}` : ''} bad response`)\n debugDir('inspect', { sdkResult })\n return socketSdkErrorResult\n }\n const socketSdkSuccessResult: ApiCallResult<T> = {\n ok: true,\n data: (sdkResult as SocketSdkSuccessResult<T>).data,\n }\n return socketSdkSuccessResult\n}\n\nexport async function handleApiCallNoSpinner<T extends SocketSdkOperations>(\n value: Promise<SocketSdkResult<T>>,\n description: string,\n): Promise<CResult<SocketSdkSuccessResult<T>['data']>> {\n let result: SocketSdkResult<T>\n try {\n result = await value\n } catch (e) {\n const message = `${e || NO_ERROR_MESSAGE}`\n const reason = `${e || NO_ERROR_MESSAGE}`\n\n debugFn('error', `caught: ${description} error`)\n debugDir('inspect', { error: e })\n\n return {\n ok: false,\n message: 'Socket API returned an error',\n cause: `${message}${reason ? ` ( Reason: ${reason} )` : ''}`,\n }\n }\n\n // Note: TS can't narrow down the type of result due to generics\n if (result.success === false) {\n const error = result as SocketSdkErrorResult<T>\n const message = `${error.error || NO_ERROR_MESSAGE}`\n\n debugFn('error', `fail: ${description} bad response`)\n debugDir('inspect', { error })\n\n return {\n ok: false,\n message: 'Socket API returned an error',\n cause: `${message}${error.cause ? ` ( Reason: ${error.cause} )` : ''}`,\n data: {\n code: result.status,\n },\n }\n } else {\n const ok = result as SocketSdkSuccessResult<T>\n return {\n ok: true,\n data: ok.data,\n }\n }\n}\n\nexport async function queryApi(path: string, apiToken: string) {\n const baseUrl = getDefaultApiBaseUrl() || ''\n if (!baseUrl) {\n logger.warn(\n 'API endpoint is not set and default was empty. Request is likely to fail.',\n )\n }\n\n return await fetch(`${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}${path}`, {\n method: 'GET',\n headers: {\n Authorization: `Basic ${btoa(`${apiToken}:`)}`,\n },\n })\n}\n\nexport async function queryApiSafeText(\n path: string,\n desc?: string | undefined,\n): Promise<CResult<string>> {\n const apiToken = getDefaultApiToken()\n if (!apiToken) {\n return {\n ok: false,\n message: 'Authentication Error',\n cause:\n 'User must be authenticated to run this command. Run `socket login` and enter your Socket API token.',\n }\n }\n\n const { spinner } = constants\n\n if (desc) {\n spinner.start(`Requesting ${desc} from API...`)\n }\n\n let result\n try {\n result = await queryApi(path, apiToken)\n if (desc) {\n spinner.successAndStop(\n `Received Socket API response (after requesting ${desc}).`,\n )\n }\n } catch (e) {\n if (desc) {\n spinner.failAndStop(`An error was thrown while requesting ${desc}.`)\n }\n\n const cause = (e as undefined | { message: string })?.message\n\n debugFn('error', 'caught: await queryApi() error')\n debugDir('inspect', { error: e })\n\n return {\n ok: false,\n message: 'API Request failed to complete',\n ...(cause ? { cause } : {}),\n }\n }\n\n if (!result.ok) {\n const cause = await getErrorMessageForHttpStatusCode(result.status)\n return {\n ok: false,\n message: 'Socket API returned an error',\n cause: `${result.statusText}${cause ? ` (cause: ${cause})` : ''}`,\n }\n }\n\n try {\n const data = await result.text()\n return {\n ok: true,\n data,\n }\n } catch (e) {\n debugFn('error', 'caught: await result.text() error')\n debugDir('inspect', { error: e })\n return {\n ok: false,\n message: 'API Request failed to complete',\n cause: 'There was an unexpected error trying to read the response text',\n }\n }\n}\n\nexport async function queryApiSafeJson<T>(\n path: string,\n desc = '',\n): Promise<CResult<T>> {\n const result = await queryApiSafeText(path, desc)\n\n if (!result.ok) {\n return result\n }\n\n try {\n return {\n ok: true,\n data: JSON.parse(result.data) as T,\n }\n } catch (e) {\n return {\n ok: false,\n message: 'Server returned invalid JSON',\n cause: `Please report this. JSON.parse threw an error over the following response: \\`${(result.data?.slice?.(0, 100) || '<empty>').trim() + (result.data?.length > 100 ? '...' : '')}\\``,\n }\n }\n}\n\nexport async function sendApiRequest<T>(\n path: string,\n options: {\n method: 'POST' | 'PUT'\n body?: unknown\n desc?: string\n },\n): Promise<CResult<T>> {\n const apiToken = getDefaultApiToken()\n if (!apiToken) {\n return {\n ok: false,\n message: 'Authentication Error',\n cause:\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your Socket API token.',\n }\n }\n\n const baseUrl = getDefaultApiBaseUrl() || ''\n if (!baseUrl) {\n logger.warn(\n 'API endpoint is not set and default was empty. Request is likely to fail.',\n )\n }\n\n const { spinner } = constants\n\n if (options.desc) {\n spinner.start(`Requesting ${options.desc} from API...`)\n }\n\n let result\n try {\n const fetchOptions = {\n method: options.method,\n headers: {\n Authorization: `Basic ${btoa(`${apiToken}:`)}`,\n 'Content-Type': 'application/json',\n },\n ...(options.body ? { body: JSON.stringify(options.body) } : {}),\n }\n\n result = await fetch(\n `${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}${path}`,\n fetchOptions,\n )\n if (options.desc) {\n spinner.successAndStop(\n `Received Socket API response (after requesting ${options.desc}).`,\n )\n }\n } catch (e) {\n if (options.desc) {\n spinner.failAndStop(\n `An error was thrown while requesting ${options.desc}.`,\n )\n }\n\n const cause = (e as undefined | { message: string })?.message\n\n debugFn('error', `caught: await fetch() ${options.method} error`)\n debugDir('inspect', { error: e })\n\n return {\n ok: false,\n message: 'API Request failed to complete',\n ...(cause ? { cause } : {}),\n }\n }\n\n if (!result.ok) {\n const cause = await getErrorMessageForHttpStatusCode(result.status)\n return {\n ok: false,\n message: 'Socket API returned an error',\n cause: `${result.statusText}${cause ? ` (cause: ${cause})` : ''}`,\n data: {\n code: result.status,\n },\n }\n }\n\n try {\n const data = await result.json()\n return {\n ok: true,\n data: data as T,\n }\n } catch (e) {\n debugFn('error', 'caught: await result.json() error')\n debugDir('inspect', { error: e })\n return {\n ok: false,\n message: 'API Request failed to complete',\n cause: 'There was an unexpected error trying to parse the response JSON',\n }\n }\n}\n","import colors from 'yoctocolors-cjs'\n\nexport function failMsgWithBadge(\n badge: string,\n message: string | undefined,\n): string {\n const prefix = colors.bgRed(\n colors.bold(colors.white(` ${badge}${message ? ': ' : ''}`)),\n )\n const postfix = message ? ` ${colors.bold(message)}` : ''\n return `${prefix}${postfix}`\n}\n","export function mdTableStringNumber(\n title1: string,\n title2: string,\n obj: Record<string, number | string>,\n): string {\n // | Date | Counts |\n // | ----------- | ------ |\n // | Header | 201464 |\n // | Paragraph | 18 |\n let mw1 = title1.length\n let mw2 = title2.length\n for (const [key, value] of Object.entries(obj)) {\n mw1 = Math.max(mw1, key.length)\n mw2 = Math.max(mw2, String(value ?? '').length)\n }\n\n const lines = []\n lines.push(`| ${title1.padEnd(mw1, ' ')} | ${title2.padEnd(mw2)} |`)\n lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)\n for (const [key, value] of Object.entries(obj)) {\n lines.push(\n `| ${key.padEnd(mw1, ' ')} | ${String(value ?? '').padStart(mw2, ' ')} |`,\n )\n }\n lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)\n\n return lines.join('\\n')\n}\n\nexport function mdTable<T extends Array<Record<string, string>>>(\n logs: T,\n // This is saying \"an array of strings and the strings are a valid key of elements of T\"\n // In turn, T is defined above as the audit log event type from our OpenAPI docs.\n cols: Array<string & keyof T[number]>,\n titles: string[] = cols,\n): string {\n // Max col width required to fit all data in that column\n const cws = cols.map(col => col.length)\n\n for (const log of logs) {\n for (let i = 0, { length } = cols; i < length; i += 1) {\n // @ts-ignore\n const val: unknown = log[cols[i] ?? ''] ?? ''\n cws[i] = Math.max(\n cws[i] ?? 0,\n String(val).length,\n (titles[i] || '').length,\n )\n }\n }\n\n let div = '|'\n for (const cw of cws) {\n div += ' ' + '-'.repeat(cw) + ' |'\n }\n\n let header = '|'\n for (let i = 0, { length } = titles; i < length; i += 1) {\n header += ' ' + String(titles[i]).padEnd(cws[i] ?? 0, ' ') + ' |'\n }\n\n let body = ''\n for (const log of logs) {\n body += '|'\n for (let i = 0, { length } = cols; i < length; i += 1) {\n // @ts-ignore\n const val: unknown = log[cols[i] ?? ''] ?? ''\n body += ' ' + String(val).padEnd(cws[i] ?? 0, ' ') + ' |'\n }\n body += '\\n'\n }\n\n return [div, header, div, body.trim(), div].filter(s => s.trim()).join('\\n')\n}\n\nexport function mdTableOfPairs(\n arr: Array<[string, string]>,\n // This is saying \"an array of strings and the strings are a valid key of elements of T\"\n // In turn, T is defined above as the audit log event type from our OpenAPI docs.\n cols: string[],\n): string {\n // Max col width required to fit all data in that column\n const cws = cols.map(col => col.length)\n\n for (const [key, val] of arr) {\n cws[0] = Math.max(cws[0] ?? 0, String(key).length)\n cws[1] = Math.max(cws[1] ?? 0, String(val ?? '').length)\n }\n\n let div = '|'\n for (const cw of cws) {\n div += ' ' + '-'.repeat(cw) + ' |'\n }\n\n let header = '|'\n for (let i = 0, { length } = cols; i < length; i += 1) {\n header += ' ' + String(cols[i]).padEnd(cws[i] ?? 0, ' ') + ' |'\n }\n\n let body = ''\n for (const [key, val] of arr) {\n body += '|'\n body += ' ' + String(key).padEnd(cws[0] ?? 0, ' ') + ' |'\n body += ' ' + String(val ?? '').padEnd(cws[1] ?? 0, ' ') + ' |'\n body += '\\n'\n }\n\n return [div, header, div, body.trim(), div].filter(s => s.trim()).join('\\n')\n}\n","import { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { isObject } from '@socketsecurity/registry/lib/objects'\n\nimport type { CResult } from '../types.mts'\n\n// Serialize the final result object before printing it\n// All commands that support the --json flag should call this before printing\nexport function serializeResultJson(data: CResult<unknown>): string {\n if (!isObject(data)) {\n process.exitCode = 1\n\n debugFn('inspect', { data })\n\n // We should not allow the JSON value to be \"null\", or a boolean/number/string,\n // even if they are valid \"json\".\n return `${JSON.stringify({\n ok: false,\n message: 'Unable to serialize JSON',\n cause:\n 'There was a problem converting the data set to JSON. The JSON was not an object. Please try again without --json',\n }).trim()}\\n`\n }\n\n try {\n return `${JSON.stringify(data, null, 2).trim()}\\n`\n } catch (e) {\n process.exitCode = 1\n\n const message =\n 'There was a problem converting the data set to JSON. Please try again without --json'\n\n logger.fail(message)\n debugDir('inspect', { error: e })\n\n // This could be caused by circular references, which is an \"us\" problem.\n return `${JSON.stringify({\n ok: false,\n message: 'Unable to serialize JSON',\n cause: message,\n }).trim()}\\n`\n }\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { LOG_SYMBOLS, logger } from '@socketsecurity/registry/lib/logger'\nimport { stripAnsi } from '@socketsecurity/registry/lib/strings'\n\nimport { failMsgWithBadge } from './fail-msg-with-badge.mts'\nimport { serializeResultJson } from './serialize-result-json.mts'\n\nimport type { OutputKind } from '../types.mts'\n\nexport function checkCommandInput(\n outputKind: OutputKind,\n ...checks: Array<{\n fail: string\n message: string\n test: boolean\n nook?: boolean | undefined\n pass?: string | undefined\n }>\n): boolean {\n if (checks.every(d => d.test)) {\n return true\n }\n\n const msg = ['Please review the input requirements and try again', '']\n for (const d of checks) {\n // If nook, then ignore when test is ok\n if (d.nook && d.test) {\n continue\n }\n const lines = d.message.split('\\n')\n const { length: lineCount } = lines\n if (!lineCount) {\n continue\n }\n // If the message has newlines then format the first line with the input\n // expectation and the rest indented below it.\n const logSymbol = d.test ? LOG_SYMBOLS.success : LOG_SYMBOLS.fail\n const reason = d.test ? d.pass : d.fail\n let listItem = ` ${logSymbol} ${lines[0]}`\n if (reason) {\n const styledReason = d.test ? colors.green(reason) : colors.red(reason)\n listItem += ` (${styledReason})`\n }\n msg.push(listItem)\n if (lineCount > 1) {\n msg.push(...lines.slice(1).map(str => ` ${str}`))\n }\n }\n\n // Use exit status of 2 to indicate incorrect usage, generally invalid\n // options or missing arguments.\n // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html\n process.exitCode = 2\n\n if (outputKind === 'json') {\n logger.log(\n serializeResultJson({\n ok: false,\n message: 'Input error',\n data: stripAnsi(msg.join('\\n')),\n }),\n )\n } else {\n logger.fail(failMsgWithBadge('Input error', msg.join('\\n')))\n }\n\n return false\n}\n","import type { OutputKind } from '../types.mts'\n\nexport function getOutputKind(json: unknown, markdown: unknown): OutputKind {\n if (json) {\n return 'json'\n }\n if (markdown) {\n return 'markdown'\n }\n return 'text'\n}\n","import { createRequire } from 'node:module'\nimport path from 'node:path'\n\nimport constants from '../constants.mts'\n\nconst require = createRequire(import.meta.url)\n\nlet _requirements:\n | Readonly<typeof import('../../requirements.json')>\n | undefined\n\nexport function getRequirements() {\n if (_requirements === undefined) {\n _requirements = /*@__PURE__*/ require(\n path.join(constants.rootPath, 'requirements.json'),\n )\n }\n return _requirements!\n}\n","import { isObject } from '@socketsecurity/registry/lib/objects'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport { indentString } from '@socketsecurity/registry/lib/strings'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { getRequirements } from './requirements.mts'\n\nimport type { MeowFlags } from '../flags.mts'\n\ntype ApiRequirementsOptions = {\n indent?: number | undefined\n}\n\ntype HelpListOptions = {\n indent?: number | undefined\n keyPrefix?: string | undefined\n padName?: number | undefined\n}\n\ntype ListDescription =\n | { description: string }\n | { description: string; hidden: boolean }\n\nfunction camelToKebab(string: string): string {\n return string.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()\n}\n\nexport function getFlagApiRequirementsOutput(\n cmdPath: string,\n options?: ApiRequirementsOptions | undefined,\n): string {\n const { indent = 6 } = {\n __proto__: null,\n ...options,\n } as ApiRequirementsOptions\n const key = cmdPath.replace(/^socket[: ]/, '').replace(/ +/g, ':')\n const requirements = getRequirements()\n const data = (requirements.api as any)[key]\n let result = ''\n if (data) {\n const quota: number = data?.quota\n const perms: string[] = data?.permissions\n const padding = ''.padEnd(indent)\n const lines = []\n if (typeof quota === 'number') {\n lines.push(`${padding}- Quota: ${quota} ${pluralize('unit', quota)}`)\n }\n if (Array.isArray(perms) && perms.length) {\n lines.push(`${padding}- Permissions: ${perms.join(' ')}`)\n }\n result += lines.join('\\n')\n }\n return result.trim() || '(none)'\n}\n\nexport function getFlagListOutput(\n list: MeowFlags,\n options?: HelpListOptions | undefined,\n): string {\n const { keyPrefix = '--' } = {\n __proto__: null,\n ...options,\n } as HelpListOptions\n return getHelpListOutput(\n {\n ...list,\n },\n { ...options, keyPrefix },\n )\n}\n\nexport function getHelpListOutput(\n list: Record<string, ListDescription>,\n options?: HelpListOptions | undefined,\n): string {\n const {\n indent = 6,\n keyPrefix = '',\n padName = 20,\n } = {\n __proto__: null,\n ...options,\n } as HelpListOptions\n let result = ''\n const names = Object.keys(list).sort(naturalCompare)\n for (const name of names) {\n const entry = list[name]\n const entryIsObj = isObject(entry)\n if (entryIsObj && 'hidden' in entry && entry?.hidden) {\n continue\n }\n const printedName = `${keyPrefix}${camelToKebab(name)}`\n const preDescription = `${''.padEnd(indent)}${printedName.padEnd(Math.max(printedName.length + 2, padName))}`\n\n result += preDescription\n\n const description = entryIsObj ? entry.description : String(entry)\n if (description) {\n result += indentString(description, preDescription.length).trimStart()\n }\n result += '\\n'\n }\n return result.trim() || '(none)'\n}\n","import path from 'node:path'\n\nimport { escapeRegExp } from '@socketsecurity/registry/lib/regexps'\n\nimport constants from '../constants.mts'\n\n// Replace the start of a path with ~/ when it starts with your home dir.\n// A common way to abbreviate the user home dir (though not strictly posix).\nexport function tildify(cwd: string) {\n return cwd.replace(\n new RegExp(`^${escapeRegExp(constants.homePath)}(?:${path.sep}|$)`, 'i'),\n '~/',\n )\n}\n","import meow from 'meow'\nimport terminalLink from 'terminal-link'\nimport colors from 'yoctocolors-cjs'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { hasOwn, toSortedObject } from '@socketsecurity/registry/lib/objects'\nimport { normalizePath } from '@socketsecurity/registry/lib/path'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport {\n indentString,\n trimNewlines,\n} from '@socketsecurity/registry/lib/strings'\n\nimport {\n getConfigValueOrUndef,\n isReadOnlyConfig,\n overrideCachedConfig,\n overrideConfigApiToken,\n} from './config.mts'\nimport { getFlagListOutput, getHelpListOutput } from './output-formatting.mts'\nimport constants from '../constants.mts'\nimport { commonFlags } from '../flags.mts'\nimport { getVisibleTokenPrefix } from './sdk.mts'\nimport { tildify } from './tildify.mts'\n\nimport type { MeowFlags } from '../flags.mts'\nimport type { Options, Result } from 'meow'\n\nexport interface CliAlias {\n description: string\n argv: readonly string[]\n hidden?: boolean | undefined\n}\n\nexport type CliAliases = Record<string, CliAlias>\n\nexport type CliSubcommandRun = (\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n context: { parentName: string },\n) => Promise<void> | void\n\nexport interface CliSubcommand {\n description: string\n hidden?: boolean | undefined\n run: CliSubcommandRun\n}\n\n// Property names are picked such that the name is at the top when the props\n// get ordered by alphabet while flags is near the bottom and the help text\n// at the bottom, because they tend ot occupy the most lines of code.\nexport interface CliCommandConfig {\n commandName: string\n description: string\n hidden: boolean\n flags: MeowFlags\n help: (command: string, config: CliCommandConfig) => string\n}\n\nexport interface MeowOptions extends Options<any> {\n aliases?: CliAliases | undefined\n argv: readonly string[]\n name: string\n // When no sub-command is given, default to this sub-command.\n defaultSub?: string\n}\n\nconst HELP_INDENT = 2\n\nconst HELP_PAD_NAME = 28\n\nfunction description(command: CliSubcommand | undefined): string {\n const description = command?.description\n const str =\n typeof description === 'string' ? description : String(description)\n return indentString(str, HELP_PAD_NAME).trimStart()\n}\n\n/**\n * Find the best matching command name for a typo.\n */\nfunction findBestCommandMatch(\n input: string,\n subcommands: Record<string, unknown>,\n aliases: Record<string, unknown>,\n): string | null {\n let bestMatch = null\n let bestScore = Infinity\n const allCommands = [...Object.keys(subcommands), ...Object.keys(aliases)]\n for (const command of allCommands) {\n const distance = levenshteinDistance(\n input.toLowerCase(),\n command.toLowerCase(),\n )\n const maxLength = Math.max(input.length, command.length)\n // Only suggest if the similarity is reasonable (more than 50% similar).\n if (distance < maxLength * 0.5 && distance < bestScore) {\n bestScore = distance\n bestMatch = command\n }\n }\n return bestMatch\n}\n\n/**\n * Calculate Levenshtein distance between two strings for fuzzy matching.\n */\nfunction levenshteinDistance(a: string, b: string): number {\n const matrix = Array.from({ length: a.length + 1 }, () =>\n Array(b.length + 1).fill(0),\n )\n for (let i = 0; i <= a.length; i++) {\n matrix[i]![0] = i\n }\n for (let j = 0; j <= b.length; j++) {\n matrix[0]![j] = j\n }\n for (let i = 1; i <= a.length; i++) {\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n matrix[i]![j] = Math.min(\n matrix[i - 1]![j]! + 1, // Deletion.\n matrix[i]![j - 1]! + 1, // Insertion.\n matrix[i - 1]![j - 1]! + cost, // Substitution.\n )\n }\n }\n return matrix[a.length]![b.length]!\n}\n\nfunction shouldSuppressBanner(flags: Record<string, unknown>): boolean {\n return Boolean(flags['json'] || flags['markdown'] || flags['nobanner'])\n}\n\n// For debugging. Whenever you call meowOrExit it will store the command here\n// This module exports a getter that returns the current value.\nlet lastSeenCommand = ''\n\nexport function getLastSeenCommand(): string {\n return lastSeenCommand\n}\n\nexport async function meowWithSubcommands(\n subcommands: Record<string, CliSubcommand>,\n options: MeowOptions,\n): Promise<void> {\n const {\n aliases = {},\n argv,\n defaultSub,\n importMeta,\n name,\n ...additionalOptions\n } = { __proto__: null, ...options }\n const [commandOrAliasName_, ...rawCommandArgv] = argv\n let commandOrAliasName = commandOrAliasName_\n if (!commandOrAliasName && defaultSub) {\n commandOrAliasName = defaultSub\n }\n\n const flags: MeowFlags = {\n ...commonFlags,\n version: {\n type: 'boolean',\n hidden: true,\n description: 'Print the app version',\n },\n ...additionalOptions.flags,\n }\n\n // No further args or first arg is a flag (shrug)\n const isRootCommand =\n name === 'socket' &&\n (!commandOrAliasName || commandOrAliasName?.startsWith('-'))\n\n // Try to support `socket <purl>` as a shorthand for `socket package score <purl>`\n if (!isRootCommand) {\n if (commandOrAliasName?.startsWith('pkg:')) {\n logger.info('Invoking `socket package score`.')\n return await meowWithSubcommands(subcommands, {\n ...options,\n argv: ['package', 'deep', ...argv],\n })\n }\n // Support `socket npm/lodash` or whatever as a shorthand, too.\n // Accept any ecosystem and let the remote sort it out.\n if (/^[a-z]+\\//.test(commandOrAliasName || '')) {\n logger.info('Invoking `socket package score`.')\n return await meowWithSubcommands(subcommands, {\n ...options,\n argv: [\n 'package',\n 'deep',\n `pkg:${commandOrAliasName}`,\n ...rawCommandArgv,\n ],\n })\n }\n }\n\n if (isRootCommand) {\n flags['help'] = {\n ...flags['help'],\n hidden: false,\n } as (typeof flags)['help']\n\n flags['config'] = {\n ...flags['config'],\n hidden: false,\n } as (typeof flags)['config']\n\n flags['dryRun'] = {\n ...flags['dryRun'],\n hidden: false,\n } as (typeof flags)['dryRun']\n\n flags['maxOldSpaceSize'] = {\n ...flags['maxOldSpaceSize'],\n hidden: false,\n } as (typeof flags)['maxOldSpaceSize']\n\n flags['maxSemiSpaceSize'] = {\n ...flags['maxSemiSpaceSize'],\n hidden: false,\n } as (typeof flags)['maxSemiSpaceSize']\n\n flags['version'] = {\n ...flags['version'],\n hidden: false,\n } as (typeof flags)['version']\n\n delete flags['json']\n delete flags['markdown']\n } else {\n delete flags['help']\n delete flags['version']\n }\n\n // This is basically a dry-run parse of cli args and flags. We use this to\n // determine config overrides and expected output mode.\n const cli1 = meow({\n argv,\n importMeta,\n ...additionalOptions,\n flags,\n // Ensure we don't check unknown flags.\n allowUnknownFlags: true,\n // Prevent meow from potentially exiting early.\n autoHelp: false,\n autoVersion: false,\n // We want to detect whether a bool flag is given at all.\n booleanDefault: undefined,\n })\n\n const orgFlag = String(cli1.flags['org'] || '') || undefined\n\n // Hard override the config if instructed to do so.\n // The env var overrides the --flag, which overrides the persisted config\n // Also, when either of these are used, config updates won't persist.\n let configOverrideResult\n if (constants.ENV.SOCKET_CLI_CONFIG) {\n configOverrideResult = overrideCachedConfig(constants.ENV.SOCKET_CLI_CONFIG)\n } else if (cli1.flags['config']) {\n configOverrideResult = overrideCachedConfig(\n String(cli1.flags['config'] || ''),\n )\n }\n\n if (constants.ENV.SOCKET_CLI_NO_API_TOKEN) {\n // This overrides the config override and even the explicit token env var.\n // The config will be marked as readOnly to prevent persisting it.\n overrideConfigApiToken(undefined)\n } else {\n const tokenOverride = constants.ENV.SOCKET_CLI_API_TOKEN\n if (tokenOverride) {\n // This will set the token (even if there was a config override) and\n // set it to readOnly, making sure the temp token won't be persisted.\n overrideConfigApiToken(tokenOverride)\n }\n }\n\n if (configOverrideResult?.ok === false) {\n if (!shouldSuppressBanner(cli1.flags)) {\n emitBanner(name, orgFlag)\n // Add newline in stderr.\n logger.error('')\n }\n logger.fail(configOverrideResult.message)\n process.exitCode = 2\n return\n }\n\n // If we have got some args, then lets find out if we can find a command.\n if (commandOrAliasName) {\n const alias = aliases[commandOrAliasName]\n // First: Resolve argv data from alias if its an alias that's been given.\n const [commandName, ...commandArgv] = alias\n ? [...alias.argv, ...rawCommandArgv]\n : [commandOrAliasName, ...rawCommandArgv]\n // Second: Find a command definition using that data.\n const commandDefinition = commandName ? subcommands[commandName] : undefined\n // Third: If a valid command has been found, then we run it...\n if (commandDefinition) {\n return await commandDefinition.run(commandArgv, importMeta, {\n parentName: name,\n })\n }\n\n // Suggest similar commands for typos.\n if (commandName && !commandDefinition) {\n const suggestion = findBestCommandMatch(commandName, subcommands, aliases)\n if (suggestion) {\n process.exitCode = 2\n logger.fail(\n `Unknown command \"${commandName}\". Did you mean \"${suggestion}\"?`,\n )\n return\n }\n }\n }\n\n const lines = ['', 'Usage', ` $ ${name} <command>`]\n if (isRootCommand) {\n lines.push(\n ` $ ${name} scan create --json`,\n ` $ ${name} package score npm lodash --markdown`,\n )\n }\n lines.push('')\n if (isRootCommand) {\n // \"Bucket\" some commands for easier usage.\n const commands = new Set([\n 'analytics',\n 'audit-log',\n 'ci',\n 'cdxgen',\n 'config',\n 'dependencies',\n 'fix',\n 'install',\n //'json',\n 'license',\n 'login',\n 'logout',\n 'manifest',\n 'npm',\n 'npx',\n 'optimize',\n 'organization',\n 'package',\n //'patch',\n 'raw-npm',\n 'raw-npx',\n 'repository',\n 'scan',\n //'security',\n 'threat-feed',\n 'uninstall',\n 'wrapper',\n ])\n Object.entries(subcommands)\n .filter(([_name, subcommand]) => !subcommand.hidden)\n .map(([name]) => name)\n .forEach(name => {\n if (commands.has(name)) {\n commands.delete(name)\n } else {\n logger.fail('Received an unknown command:', name)\n }\n })\n if (commands.size) {\n logger.fail(\n 'Found commands in the list that were not marked as public or not defined at all:',\n // Node < 22 will print 'Object (n)' before the array. So to have consistent\n // test snapshots we use joinAnd.\n joinAnd(\n Array.from(commands)\n .sort(naturalCompare)\n .map(c => `'${c}'`),\n ),\n )\n }\n lines.push(\n 'Note: All commands have their own --help',\n '',\n 'Main commands',\n ` socket login ${description(subcommands['login'])}`,\n ` socket scan create Create a new Socket scan and report`,\n ` socket npm/lodash@4.17.21 Request the Socket score of a package`,\n ` socket ci ${description(subcommands['ci'])}`,\n ``,\n 'Socket API',\n ` analytics ${description(subcommands['analytics'])}`,\n ` audit-log ${description(subcommands['audit-log'])}`,\n ` organization ${description(subcommands['organization'])}`,\n ` package ${description(subcommands['package'])}`,\n ` repository ${description(subcommands['repository'])}`,\n ` scan ${description(subcommands['scan'])}`,\n ` threat-feed ${description(subcommands['threat-feed'])}`,\n ``,\n 'Local tools',\n ` fix ${description(subcommands['fix'])}`,\n ` manifest ${description(subcommands['manifest'])}`,\n ` npm ${description(subcommands['npm'])}`,\n ` npx ${description(subcommands['npx'])}`,\n ` optimize ${description(subcommands['optimize'])}`,\n ` raw-npm ${description(subcommands['raw-npm'])}`,\n ` raw-npx ${description(subcommands['raw-npx'])}`,\n '',\n 'CLI configuration',\n ` config ${description(subcommands['config'])}`,\n ` install ${description(subcommands['install'])}`,\n ` login Socket API login and CLI setup`,\n ` logout ${description(subcommands['logout'])}`,\n ` uninstall ${description(subcommands['uninstall'])}`,\n ` wrapper ${description(subcommands['wrapper'])}`,\n )\n } else {\n lines.push('Commands')\n lines.push(\n ` ${getHelpListOutput(\n {\n ...toSortedObject(\n Object.fromEntries(\n Object.entries(subcommands).filter(\n ({ 1: subcommand }) => !subcommand.hidden,\n ),\n ),\n ),\n ...toSortedObject(\n Object.fromEntries(\n Object.entries(aliases).filter(({ 1: alias }) => {\n const { hidden } = alias\n const cmdName = hidden ? '' : alias.argv[0]\n const subcommand = cmdName ? subcommands[cmdName] : undefined\n return subcommand && !subcommand.hidden\n }),\n ),\n ),\n },\n { indent: HELP_INDENT, padName: HELP_PAD_NAME },\n )}`,\n )\n }\n\n lines.push('', 'Options')\n if (isRootCommand) {\n lines.push(\n ' Note: All commands have these flags even when not displayed in their help',\n '',\n )\n } else {\n lines.push('')\n }\n lines.push(\n ` ${getFlagListOutput(flags, { indent: HELP_INDENT, padName: HELP_PAD_NAME })}`,\n )\n if (isRootCommand) {\n lines.push(\n '',\n 'Environment variables',\n ' SOCKET_CLI_API_TOKEN Set the Socket API token',\n ' SOCKET_CLI_CONFIG A JSON stringified Socket configuration object',\n ' SOCKET_CLI_GITHUB_API_URL Change the base URL for GitHub REST API calls',\n ' SOCKET_CLI_GIT_USER_EMAIL The git config `user.email` used by Socket CLI',\n ` ${colors.italic('Defaults:')} github-actions[bot]@users.noreply.github.com`,\n ' SOCKET_CLI_GIT_USER_NAME The git config `user.name` used by Socket CLI',\n ` ${colors.italic('Defaults:')} github-actions[bot]`,\n ` SOCKET_CLI_GITHUB_TOKEN A classic or fine-grained ${terminalLink('GitHub personal access token', 'https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens')}`,\n ` ${colors.italic('Aliases:')} GITHUB_TOKEN`,\n ' SOCKET_CLI_NO_API_TOKEN Make the default API token `undefined`',\n ' SOCKET_CLI_NPM_PATH The absolute location of the npm directory',\n ' SOCKET_CLI_ORG_SLUG Specify the Socket organization slug',\n '',\n ' SOCKET_CLI_ACCEPT_RISKS Accept risks of a Socket wrapped npm/npx run',\n ' SOCKET_CLI_VIEW_ALL_RISKS View all risks of a Socket wrapped npm/npx run',\n '',\n 'Environment variables for development',\n ' SOCKET_CLI_API_BASE_URL Change the base URL for Socket API calls',\n ` ${colors.italic('Defaults:')} The \"apiBaseUrl\" value of socket/settings local app data`,\n ' if present, else https://api.socket.dev/v0/',\n ' SOCKET_CLI_API_PROXY Set the proxy Socket API requests are routed through, e.g. if set to',\n ` ${terminalLink('http://127.0.0.1:9090', 'https://docs.proxyman.io/troubleshooting/couldnt-see-any-requests-from-3rd-party-network-libraries')} then all request are passed through that proxy`,\n ` ${colors.italic('Aliases:')} HTTPS_PROXY, https_proxy, HTTP_PROXY, and http_proxy`,\n ' SOCKET_CLI_API_TIMEOUT Set the timeout in milliseconds for Socket API requests',\n ' SOCKET_CLI_DEBUG Enable debug logging in Socket CLI',\n ` DEBUG Enable debug logging based on the ${terminalLink('debug', 'https://socket.dev/npm/package/debug')} package`,\n )\n }\n\n // Parse it again. Config overrides should now be applied (may affect help).\n // Note: this is displayed as help screen if the command does not override it\n // (which is the case for most sub-commands with sub-commands)\n const cli2 = meow({\n argv,\n importMeta,\n ...additionalOptions,\n flags,\n // Do not strictly check for flags here.\n allowUnknownFlags: true,\n // We will emit help when we're ready.\n // Plus, if we allow this then meow may exit here.\n autoHelp: false,\n autoVersion: false,\n // We want to detect whether a bool flag is given at all.\n booleanDefault: undefined,\n help: lines.map(l => indentString(l, HELP_INDENT)).join('\\n'),\n })\n\n // ...else we provide basic instructions and help.\n if (!shouldSuppressBanner(cli2.flags)) {\n emitBanner(name, orgFlag)\n // meow will add newline so don't add stderr spacing here\n }\n if (!cli2.flags['help'] && cli2.flags['dryRun']) {\n process.exitCode = 0\n logger.log(`${constants.DRY_RUN_LABEL}: No-op, call a sub-command; ok`)\n } else {\n // When you explicitly request --help, the command should be successful\n // so we exit(0). If we do it because we need more input, we exit(2).\n cli2.showHelp(cli2.flags['help'] ? 0 : 2)\n }\n}\n\n/**\n * Note: meow will exit immediately if it calls its .showHelp()\n */\nexport function meowOrExit({\n allowUnknownFlags = true,\n argv,\n config,\n importMeta,\n parentName,\n}: {\n allowUnknownFlags?: boolean | undefined\n argv: readonly string[]\n config: CliCommandConfig\n parentName: string\n importMeta: ImportMeta\n}): Result<MeowFlags> {\n const command = `${parentName} ${config.commandName}`\n lastSeenCommand = command\n\n // This exits if .printHelp() is called either by meow itself or by us.\n const cli = meow({\n argv,\n // Prevent meow from potentially exiting early.\n autoHelp: false,\n autoVersion: false,\n // We want to detect whether a bool flag is given at all.\n booleanDefault: undefined,\n collectUnknownFlags: true,\n description: config.description,\n flags: config.flags,\n help: trimNewlines(config.help(command, config)),\n importMeta,\n })\n\n if (!shouldSuppressBanner(cli.flags)) {\n emitBanner(command, String(cli.flags['org'] || '') || undefined)\n // Add newline in stderr.\n // Meow help adds a newline too so we do it here.\n logger.error('')\n }\n\n // As per https://github.com/sindresorhus/meow/issues/178\n // Setting `allowUnknownFlags: false` makes it reject camel cased flags.\n // if (!allowUnknownFlags) {\n // // Run meow specifically with the flag setting. It will exit(2) if an\n // // invalid flag is set and print a message.\n // meow({\n // argv,\n // allowUnknownFlags: false,\n // // Prevent meow from potentially exiting early.\n // autoHelp: false,\n // autoVersion: false,\n // description: config.description,\n // flags: config.flags,\n // help: trimNewlines(config.help(command, config)),\n // importMeta,\n // })\n // }\n\n if (cli.flags['help']) {\n cli.showHelp(0)\n }\n\n // meow doesn't detect 'version' as an unknown flag, so we do the leg work here.\n if (!hasOwn(config.flags, 'version') && cli.flags['version']) {\n // Use `console.error` here instead of `logger.error` to match meow behavior.\n console.error('Unknown flag\\n--version')\n // eslint-disable-next-line n/no-process-exit\n process.exit(2)\n }\n\n // Now test for help state. Run meow again. If it exits now, it must be due\n // to wanting to print the help screen. But it would exit(0) and we want a\n // consistent exit(2) for that case (missing input).\n // TODO: Move away from meow.\n process.exitCode = 2\n meow({\n argv,\n // As per https://github.com/sindresorhus/meow/issues/178\n // Setting `allowUnknownFlags: false` makes it reject camel cased flags.\n allowUnknownFlags: Boolean(allowUnknownFlags),\n // Prevent meow from potentially exiting early.\n autoHelp: false,\n autoVersion: false,\n description: config.description,\n help: trimNewlines(config.help(command, config)),\n importMeta,\n flags: config.flags,\n })\n // Ok, no help, reset to default.\n process.exitCode = 0\n\n return cli\n}\n\nexport function emitBanner(name: string, orgFlag: string | undefined) {\n // Print a banner at the top of each command.\n // This helps with brand recognition and marketing.\n // It also helps with debugging since it contains version and command details.\n // Note: print over stderr to preserve stdout for flags like --json and\n // --markdown. If we don't do this, you can't use --json in particular\n // and pipe the result to other tools. By emitting the banner over stderr\n // you can do something like `socket scan view xyz | jq | process`.\n // The spinner also emits over stderr for example.\n logger.error(getAsciiHeader(name, orgFlag))\n}\n\nfunction getAsciiHeader(command: string, orgFlag: string | undefined) {\n // Note: In tests we return <redacted> because otherwise snapshots will fail.\n const { REDACTED } = constants\n const redacting = constants.ENV.VITEST\n const cliVersion = redacting\n ? REDACTED\n : constants.ENV.INLINED_SOCKET_CLI_VERSION_HASH\n const nodeVersion = redacting ? REDACTED : process.version\n const defaultOrg = getConfigValueOrUndef('defaultOrg')\n const readOnlyConfig = isReadOnlyConfig() ? '*' : '.'\n const shownToken = redacting\n ? REDACTED\n : getVisibleTokenPrefix() || '(not set)'\n const relCwd = redacting ? REDACTED : normalizePath(tildify(process.cwd()))\n // Note: we must redact org when creating snapshots because dev machine probably\n // has a default org set but CI won't. Showing --org is fine either way.\n const orgPart = orgFlag\n ? `--org: ${orgFlag}`\n : redacting\n ? 'org: <redacted>'\n : defaultOrg\n ? `default org: ${defaultOrg}`\n : '(org not set)'\n // Note: We could draw these with ascii box art instead but I worry about\n // portability and paste-ability. \"simple\" ascii chars just work.\n const body = `\n _____ _ _ /---------------\n | __|___ ___| |_ ___| |_ | Socket.dev CLI ver ${cliVersion}\n |__ | ${readOnlyConfig} | _| '_| -_| _| | Node: ${nodeVersion}, API token: ${shownToken}, ${orgPart}\n |_____|___|___|_,_|___|_|.dev | Command: \\`${command}\\`, cwd: ${relCwd}\n `.trim()\n // Note: logger will auto-append a newline.\n return ` ${body}`\n}\n","export function msAtHome(isoTimeStamp: string): string {\n const timeStart = Date.parse(isoTimeStamp)\n const timeEnd = Date.now()\n\n const rtf = new Intl.RelativeTimeFormat('en', {\n numeric: 'always',\n style: 'short',\n })\n\n const delta = timeEnd - timeStart\n if (delta < 60 * 60 * 1000) {\n return rtf.format(-Math.round(delta / (60 * 1000)), 'minute')\n // return Math.round(delta / (60 * 1000)) + ' min ago'\n } else if (delta < 24 * 60 * 60 * 1000) {\n return rtf.format(-(delta / (60 * 60 * 1000)).toFixed(1), 'hour')\n // return (delta / (60 * 60 * 1000)).toFixed(1) + ' hr ago'\n } else if (delta < 7 * 24 * 60 * 60 * 1000) {\n return rtf.format(-(delta / (24 * 60 * 60 * 1000)).toFixed(1), 'day')\n // return (delta / (24 * 60 * 60 * 1000)).toFixed(1) + ' day ago'\n } else {\n return isoTimeStamp.slice(0, 10)\n }\n}\n","import { handleApiCall } from '../../utils/api.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { SetupSdkOptions } from '../../utils/sdk.mts'\nimport type { SocketSdk, SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchOrganizationOptions = {\n desc?: string | undefined\n sdk?: SocketSdk | undefined\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport type EnterpriseOrganization = Omit<Organization, 'plan'> & {\n plan: 'enterprise'\n}\n\nexport type EnterpriseOrganizations = EnterpriseOrganization[]\n\nexport type Organization =\n SocketSdkSuccessResult<'getOrganizations'>['data']['organizations'][string]\n\nexport type Organizations = Organization[]\n\nexport type OrganizationsData = { organizations: Organizations }\n\nexport type OrganizationsCResult = CResult<OrganizationsData>\n\nexport async function fetchOrganization(\n options?: FetchOrganizationOptions | undefined,\n): Promise<OrganizationsCResult> {\n const {\n desc = 'organization list',\n sdk,\n sdkOpts,\n } = {\n __proto__: null,\n ...options,\n } as FetchOrganizationOptions\n\n let sockSdk = sdk\n if (!sockSdk) {\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n sockSdk = sockSdkCResult.data\n }\n\n const orgsCResult = await handleApiCall(sockSdk.getOrganizations(), { desc })\n if (!orgsCResult.ok) {\n return orgsCResult\n }\n\n return {\n ...orgsCResult,\n data: {\n organizations: Object.values(orgsCResult.data.organizations),\n },\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\nimport { select } from '@socketsecurity/registry/lib/prompts'\n\nimport { fetchOrganization } from '../organization/fetch-organization-list.mts'\n\nexport async function suggestOrgSlug(): Promise<string | void> {\n const orgsCResult = await fetchOrganization()\n if (!orgsCResult.ok) {\n logger.fail(\n 'Failed to lookup organization list from API, unable to suggest',\n )\n return undefined\n }\n\n // Ignore a failed request here. It was not the primary goal of\n // running this command and reporting it only leads to end-user confusion.\n const { organizations } = orgsCResult.data\n const proceed = await select<string>({\n message:\n 'Missing org name; do you want to use any of these orgs for this scan?',\n choices: [\n ...organizations.map(o => {\n const name = o.name ?? o.slug\n return {\n name: `Yes [${name}]`,\n value: name,\n description: `Use \"${name}\" as the organization`,\n }\n }),\n {\n name: 'No',\n value: '',\n description:\n 'Do not use any of these organizations (will end in a no-op)',\n },\n ],\n })\n\n if (proceed) {\n return proceed\n }\n return undefined\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\nimport { select } from '@socketsecurity/registry/lib/prompts'\n\nimport { getConfigValue, updateConfigValue } from '../../utils/config.mts'\n\nexport async function suggestToPersistOrgSlug(orgSlug: string): Promise<void> {\n const skipAsk = getConfigValue('skipAskToPersistDefaultOrg')\n if (!skipAsk.ok || skipAsk.data) {\n // Don't ask to store it when disabled before, or when reading config fails.\n return\n }\n\n const result = await select<string>({\n message: `Would you like to use this org (${orgSlug}) as the default org for future calls?`,\n choices: [\n {\n name: 'Yes',\n value: 'yes',\n description: 'Stores it in your config',\n },\n {\n name: 'No',\n value: 'no',\n description: 'Do not persist this org as default org',\n },\n {\n name: \"No and don't ask again\",\n value: 'sush',\n description:\n 'Do not store as default org and do not ask again to persist it',\n },\n ],\n })\n if (result === 'yes') {\n const updateResult = updateConfigValue('defaultOrg', orgSlug)\n if (updateResult.ok) {\n logger.success('Updated default org config to:', orgSlug)\n } else {\n logger.fail(\n '(Non blocking) Failed to update default org in config:',\n updateResult.cause,\n )\n }\n } else if (result === 'sush') {\n const updateResult = updateConfigValue('skipAskToPersistDefaultOrg', true)\n if (updateResult.ok) {\n logger.info('Default org not changed. Will not ask to persist again.')\n } else {\n logger.fail(\n `(Non blocking) Failed to store preference; will ask to persist again next time. Reason: ${updateResult.cause}`,\n )\n }\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getConfigValueOrUndef } from './config.mts'\nimport { suggestOrgSlug } from '../commands/scan/suggest-org-slug.mts'\nimport { suggestToPersistOrgSlug } from '../commands/scan/suggest-to-persist-orgslug.mts'\n\nexport async function determineOrgSlug(\n orgFlag: string,\n interactive: boolean,\n dryRun: boolean,\n): Promise<[string, string | undefined]> {\n const defaultOrgSlug = getConfigValueOrUndef('defaultOrg')\n let orgSlug = String(orgFlag || defaultOrgSlug || '')\n if (!orgSlug) {\n if (!interactive) {\n logger.warn(\n 'Note: This command requires an org slug because the Socket API endpoint does.',\n )\n logger.warn('')\n logger.warn(\n 'It seems no default org was setup and the `--org` flag was not used.',\n )\n logger.warn(\n \"Additionally, `--no-interactive` was set so we can't ask for it.\",\n )\n logger.warn(\n 'Since v1.0.0 the org _argument_ for all commands was dropped in favor of an',\n )\n logger.warn(\n 'implicit default org setting, which will be setup when you run `socket login`.',\n )\n logger.warn('')\n logger.warn(\n 'Note: When running in CI, you probably want to set the `--org` flag.',\n )\n logger.warn('')\n logger.warn(\n 'For details, see: https://docs.socket.dev/docs/v1-migration-guide',\n )\n logger.warn('')\n logger.warn(\n 'This command will exit now because the org slug is required to proceed.',\n )\n return ['', undefined]\n }\n\n logger.warn(\n 'Unable to determine the target org. Trying to auto-discover it now...',\n )\n logger.info('Note: Run `socket login` to set a default org.')\n logger.error(' Use the --org flag to override the default org.')\n logger.error('')\n if (dryRun) {\n logger.fail('Skipping auto-discovery of org in dry-run mode')\n } else {\n orgSlug = (await suggestOrgSlug()) || ''\n if (orgSlug) {\n await suggestToPersistOrgSlug(orgSlug)\n }\n }\n }\n\n return [orgSlug, defaultOrgSlug]\n}\n","import { debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport constants from '../../constants.mts'\nimport { getConfigValueOrUndef } from '../../utils/config.mts'\nimport { fetchOrganization } from '../organization/fetch-organization-list.mts'\n\nimport type { CResult } from '../../types.mts'\n\n// Use the config defaultOrg when set, otherwise discover from remote.\nexport async function getDefaultOrgSlug(): Promise<CResult<string>> {\n const defaultOrgResult = getConfigValueOrUndef('defaultOrg')\n if (defaultOrgResult) {\n debugFn(\n 'notice',\n 'use: org from \"defaultOrg\" value of socket/settings local app data',\n defaultOrgResult,\n )\n return { ok: true, data: defaultOrgResult }\n }\n\n const envOrgSlug = constants.ENV.SOCKET_CLI_ORG_SLUG\n if (envOrgSlug) {\n debugFn(\n 'notice',\n 'use: org from SOCKET_CLI_ORG_SLUG environment variable',\n envOrgSlug,\n )\n return { ok: true, data: envOrgSlug }\n }\n\n const orgsCResult = await fetchOrganization()\n if (!orgsCResult.ok) {\n return orgsCResult\n }\n\n const { organizations } = orgsCResult.data\n const keys = Object.keys(organizations)\n if (!keys.length) {\n return {\n ok: false,\n message: 'Failed to establish identity',\n data: `No organization associated with the Socket API token. Unable to continue.`,\n }\n }\n\n const slug = (organizations as any)[keys[0]!]?.name ?? undefined\n if (!slug) {\n return {\n ok: false,\n message: 'Failed to establish identity',\n data: `Cannot determine the default organization for the API token. Unable to continue.`,\n }\n }\n\n debugFn('notice', 'resolve: org from Socket API', slug)\n\n return {\n ok: true,\n message: 'Retrieved default org from server',\n data: slug,\n }\n}\n","import { debugDir, debugFn, isDebug } from '@socketsecurity/registry/lib/debug'\nimport { normalizePath } from '@socketsecurity/registry/lib/path'\nimport { isSpawnError, spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { SpawnOptions } from '@socketsecurity/registry/lib/spawn'\n\n// Listed in order of check preference.\nconst COMMON_DEFAULT_BRANCH_NAMES = [\n // Modern default (GitHub, GitLab, Bitbucket have switched to this).\n 'main',\n // Historic default in Git (pre-2020, still used in many repos).\n 'master',\n // Common in Git Flow workflows (main for stable, develop for ongoing work).\n 'develop',\n // Used by teams adopting trunk-based development practices.\n 'trunk',\n // Used in some older enterprise setups and tools.\n 'default',\n]\n\nexport async function getBaseBranch(cwd = process.cwd()): Promise<string> {\n const { GITHUB_BASE_REF, GITHUB_REF_NAME, GITHUB_REF_TYPE } = constants.ENV\n // 1. In a pull request, this is always the base branch.\n if (GITHUB_BASE_REF) {\n return GITHUB_BASE_REF\n }\n // 2. If it's a branch (not a tag), GITHUB_REF_TYPE should be 'branch'.\n if (GITHUB_REF_TYPE === 'branch' && GITHUB_REF_NAME) {\n return GITHUB_REF_NAME\n }\n // 3. Try to resolve the default remote branch using 'git remote show origin'.\n // This handles detached HEADs or workflows triggered by tags/releases.\n try {\n const originDetails = (\n await spawn('git', ['remote', 'show', 'origin'], { cwd })\n ).stdout\n\n const match = /(?<=HEAD branch: ).+/.exec(originDetails)\n if (match?.[0]) {\n return match[0].trim()\n }\n } catch {}\n // GitHub and GitLab default to branch name \"main\"\n // https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches#about-the-default-branch\n return 'main'\n}\n\nexport type RepoInfo = {\n owner: string\n repo: string\n}\n\nexport async function getRepoInfo(\n cwd = process.cwd(),\n): Promise<RepoInfo | null> {\n let info = null\n const quotedCmd = '`git remote get-url origin`'\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n const remoteUrl = (\n await spawn('git', ['remote', 'get-url', 'origin'], { cwd })\n ).stdout\n info = parseGitRemoteUrl(remoteUrl)\n if (!info) {\n debugFn('error', 'git: unmatched git remote URL format')\n debugDir('inspect', { remoteUrl })\n }\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n return info\n}\n\nexport async function getRepoName(cwd = process.cwd()): Promise<string> {\n const repoInfo = await getRepoInfo(cwd)\n return repoInfo?.repo ?? constants.SOCKET_DEFAULT_REPOSITORY\n}\n\nexport async function getRepoOwner(\n cwd = process.cwd(),\n): Promise<string | null> {\n const repoInfo = await getRepoInfo(cwd)\n return repoInfo?.owner ?? null\n}\n\nexport async function gitBranch(cwd = process.cwd()): Promise<string | null> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n let quotedCmd = '`git symbolic-ref --short HEAD`'\n debugFn('stdio', `spawn: ${quotedCmd}`)\n // Try symbolic-ref first which returns the branch name or fails in a\n // detached HEAD state.\n try {\n const gitSymbolicRefResult = await spawn(\n 'git',\n ['symbolic-ref', '--short', 'HEAD'],\n stdioPipeOptions,\n )\n debugDir('stdio', { gitSymbolicRefResult })\n return gitSymbolicRefResult.stdout\n } catch (e) {\n if (isDebug('stdio')) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n }\n // Fallback to using rev-parse to get the short commit hash in a\n // detached HEAD state.\n quotedCmd = '`git rev-parse --short HEAD`'\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n const gitRevParseResult = await spawn(\n 'git',\n ['rev-parse', '--short', 'HEAD'],\n stdioPipeOptions,\n )\n debugDir('stdio', { gitRevParseResult })\n return gitRevParseResult.stdout\n } catch (e) {\n if (isDebug('stdio')) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n }\n return null\n}\n\n/**\n * Try to detect the default branch name by checking common patterns.\n * Returns the first branch that exists in the repository.\n */\nexport async function detectDefaultBranch(\n cwd = process.cwd(),\n): Promise<string> {\n // First pass: check all local branches\n for (const branch of COMMON_DEFAULT_BRANCH_NAMES) {\n // eslint-disable-next-line no-await-in-loop\n if (await gitLocalBranchExists(branch, cwd)) {\n return branch\n }\n }\n // Second pass: check remote branches only if no local branch found\n for (const branch of COMMON_DEFAULT_BRANCH_NAMES) {\n // eslint-disable-next-line no-await-in-loop\n if (await gitRemoteBranchExists(branch, cwd)) {\n return branch\n }\n }\n return constants.SOCKET_DEFAULT_BRANCH\n}\n\nexport type GitCreateAndPushBranchOptions = {\n cwd?: string | undefined\n email?: string | undefined\n user?: string | undefined\n}\n\nexport async function gitCleanFdx(cwd = process.cwd()): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = '`git clean -fdx`'\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n await spawn('git', ['clean', '-fdx'], stdioIgnoreOptions)\n return true\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n return false\n}\n\nexport async function gitCheckoutBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git checkout ${branch}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n await spawn('git', ['checkout', branch], stdioIgnoreOptions)\n return true\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n return false\n}\n\nexport async function gitCreateBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n if (await gitLocalBranchExists(branch)) {\n return true\n }\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git branch ${branch}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n await spawn('git', ['branch', branch], stdioIgnoreOptions)\n return true\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n return false\n}\n\nexport async function gitPushBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git push --force --set-upstream origin ${branch}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n await spawn(\n 'git',\n ['push', '--force', '--set-upstream', 'origin', branch],\n stdioIgnoreOptions,\n )\n return true\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n if (isSpawnError(e) && e.code === 128) {\n debugFn(\n 'error',\n \"denied: token requires write permissions for 'contents' and 'pull-requests'\",\n )\n }\n debugDir('inspect', { error: e })\n }\n return false\n}\n\nexport async function gitCommit(\n commitMsg: string,\n filepaths: string[],\n options?: GitCreateAndPushBranchOptions | undefined,\n): Promise<boolean> {\n if (!filepaths.length) {\n debugFn('notice', `miss: no filepaths to add`)\n return false\n }\n const {\n cwd = process.cwd(),\n email = constants.ENV.SOCKET_CLI_GIT_USER_EMAIL,\n user = constants.ENV.SOCKET_CLI_GIT_USER_NAME,\n } = { __proto__: null, ...options } as GitCreateAndPushBranchOptions\n\n await gitEnsureIdentity(user, email, cwd)\n\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedAddCmd = `\\`git add ${filepaths.join(' ')}\\``\n debugFn('stdio', `spawn: ${quotedAddCmd}`)\n try {\n await spawn('git', ['add', ...filepaths], stdioIgnoreOptions)\n } catch (e) {\n debugFn('error', `caught: ${quotedAddCmd} failed`)\n debugDir('inspect', { error: e })\n }\n\n const quotedCommitCmd = `\\`git commit -m ${commitMsg}\\``\n debugFn('stdio', `spawn: ${quotedCommitCmd}`)\n try {\n await spawn('git', ['commit', '-m', commitMsg], stdioIgnoreOptions)\n return true\n } catch (e) {\n debugFn('error', `caught: ${quotedCommitCmd} failed`)\n debugDir('inspect', { error: e })\n }\n return false\n}\n\nexport async function gitDeleteBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git branch -D ${branch}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n // Will throw with exit code 1 if branch does not exist.\n await spawn('git', ['branch', '-D', branch], stdioIgnoreOptions)\n return true\n } catch (e) {\n if (isDebug('stdio')) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n }\n return false\n}\n\nexport async function gitEnsureIdentity(\n name: string,\n email: string,\n cwd = process.cwd(),\n): Promise<void> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n const identEntries: Array<[string, string]> = [\n ['user.email', email],\n ['user.name', name],\n ]\n await Promise.all(\n identEntries.map(async ({ 0: prop, 1: value }) => {\n let configValue\n {\n const quotedCmd = `\\`git config --get ${prop}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n // Will throw with exit code 1 if the config property is not set.\n const gitConfigResult = await spawn(\n 'git',\n ['config', '--get', prop],\n stdioPipeOptions,\n )\n debugDir('stdio', { gitConfigResult })\n configValue = gitConfigResult.stdout\n } catch (e) {\n if (isDebug('stdio')) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n }\n }\n if (configValue !== value) {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git config ${prop} ${value}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n await spawn('git', ['config', prop, value], stdioIgnoreOptions)\n } catch (e) {\n if (isDebug('stdio')) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n }\n }\n }),\n )\n}\n\nexport async function gitLocalBranchExists(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git show-ref --quiet refs/heads/${branch}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n // Will throw with exit code 1 if the branch does not exist.\n await spawn(\n 'git',\n ['show-ref', '--quiet', `refs/heads/${branch}`],\n stdioIgnoreOptions,\n )\n return true\n } catch (e) {\n if (isDebug('stdio')) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n }\n return false\n}\n\nexport async function gitRemoteBranchExists(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n const quotedCmd = `\\`git ls-remote --heads origin ${branch}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n const lsRemoteResult = await spawn(\n 'git',\n ['ls-remote', '--heads', 'origin', branch],\n stdioPipeOptions,\n )\n debugDir('stdio', { lsRemoteResult })\n return lsRemoteResult.stdout.length > 0\n } catch (e) {\n if (isDebug('stdio')) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n }\n return false\n}\n\nexport async function gitResetAndClean(\n branch = 'HEAD',\n cwd = process.cwd(),\n): Promise<void> {\n // Discards tracked changes.\n await gitResetHard(branch, cwd)\n // Deletes all untracked files and directories.\n await gitCleanFdx(cwd)\n}\n\nexport async function gitResetHard(\n branch = 'HEAD',\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git reset --hard ${branch}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n await spawn('git', ['reset', '--hard', branch], stdioIgnoreOptions)\n return true\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n return false\n}\n\nexport async function gitUnstagedModifiedFiles(\n cwd = process.cwd(),\n): Promise<CResult<string[]>> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n const quotedCmd = `\\`git diff --name-only\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n const gitDiffResult = await spawn(\n 'git',\n ['diff', '--name-only'],\n stdioPipeOptions,\n )\n debugDir('stdio', { gitDiffResult })\n const changedFilesDetails = gitDiffResult.stdout\n const relPaths = changedFilesDetails.split('\\n')\n return {\n ok: true,\n data: relPaths.map(p => normalizePath(p)),\n }\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n return {\n ok: false,\n message: 'Git Error',\n cause: 'Unexpected error while trying to ask git whether repo is dirty',\n }\n }\n}\n\nconst parsedGitRemoteUrlCache = new Map<string, RepoInfo | null>()\n\nexport function parseGitRemoteUrl(remoteUrl: string): RepoInfo | null {\n let result = parsedGitRemoteUrlCache.get(remoteUrl) ?? null\n if (result) {\n return { ...result }\n }\n // Handle SSH-style\n const sshMatch = /^git@[^:]+:([^/]+)\\/(.+?)(?:\\.git)?$/.exec(remoteUrl)\n // 1. Handle SSH-style, e.g. git@github.com:owner/repo.git\n if (sshMatch) {\n result = { owner: sshMatch[1]!, repo: sshMatch[2]! }\n } else {\n // 2. Handle HTTPS/URL-style, e.g. https://github.com/owner/repo.git\n try {\n const parsed = new URL(remoteUrl)\n // Remove leading slashes from pathname and split by \"/\" to extract segments.\n const segments = parsed.pathname.replace(/^\\/+/, '').split('/')\n // The second-to-last segment is expected to be the owner (e.g., \"owner\" in /owner/repo.git).\n const owner = segments.at(-2)\n // The last segment is expected to be the repo name, so we remove the \".git\" suffix if present.\n const repo = segments.at(-1)?.replace(/\\.git$/, '')\n if (owner && repo) {\n result = { owner, repo }\n }\n } catch {}\n }\n parsedGitRemoteUrlCache.set(remoteUrl, result)\n return result ? { ...result } : result\n}\n","import { PackageURL } from '@socketregistry/packageurl-js'\n\nimport type { SocketArtifact } from './alert/artifact.mts'\nimport type { PURL_Type } from './ecosystem.mts'\n\nexport function getPurlObject(purl: string): PackageURL & { type: PURL_Type }\nexport function getPurlObject(\n purl: PackageURL,\n): PackageURL & { type: PURL_Type }\nexport function getPurlObject(\n purl: SocketArtifact,\n): SocketArtifact & { type: PURL_Type }\nexport function getPurlObject(\n purl: string | PackageURL | SocketArtifact,\n): (PackageURL | SocketArtifact) & { type: PURL_Type }\nexport function getPurlObject(\n purl: string | PackageURL | SocketArtifact,\n): (PackageURL | SocketArtifact) & { type: PURL_Type } {\n return typeof purl === 'string'\n ? (PackageURL.fromString(purl) as PackageURL & { type: PURL_Type })\n : (purl as (PackageURL | SocketArtifact) & { type: PURL_Type })\n}\n","import constants from '../constants.mts'\nimport { getPurlObject } from './purl.mts'\n\nimport type { SocketArtifact } from './alert/artifact.mts'\nimport type { PURL_Type } from './ecosystem.mts'\nimport type { PackageURL } from '@socketregistry/packageurl-js'\n\nexport function getPkgFullNameFromPurl(\n purl: string | PackageURL | SocketArtifact,\n): string {\n const purlObj = getPurlObject(purl)\n const { name, namespace } = purlObj\n return namespace\n ? `${namespace}${purlObj.type === 'maven' ? ':' : '/'}${name}`\n : name!\n}\n\nexport function getSocketDevAlertUrl(alertType: string): string {\n return `${constants.SOCKET_WEBSITE_URL}/alerts/${alertType}`\n}\n\nexport function getSocketDevPackageOverviewUrlFromPurl(\n purl: string | PackageURL | SocketArtifact,\n): string {\n const purlObj = getPurlObject(purl)\n const fullName = getPkgFullNameFromPurl(purlObj)\n return getSocketDevPackageOverviewUrl(purlObj.type, fullName, purlObj.version)\n}\n\nexport function getSocketDevPackageOverviewUrl(\n ecosystem: PURL_Type,\n fullName: string,\n version?: string | undefined,\n): string {\n const url = `${constants.SOCKET_WEBSITE_URL}/${ecosystem}/package/${fullName}`\n return ecosystem === 'golang'\n ? `${url}${version ? `?section=overview&version=${version}` : ''}`\n : `${url}${version ? `/overview/${version}` : ''}`\n}\n","interface NestedRecord<T> {\n [key: string]: T | NestedRecord<T>\n}\n\n/**\n * Convert a Map<string, Map|string> to a nested object of similar shape.\n * The goal is to serialize it with JSON.stringify, which Map can't do.\n */\nexport function mapToObject<T>(\n map: Map<string, T | Map<string, T | Map<string, T>>>,\n): NestedRecord<T> {\n return Object.fromEntries(\n Array.from(map.entries()).map(([k, v]) => [\n k,\n v instanceof Map ? mapToObject(v) : v,\n ]),\n )\n}\n","type NestedMap<T> = Map<string, T | NestedMap<T>>\n\nexport function* walkNestedMap<T>(\n map: NestedMap<T>,\n keys: string[] = [],\n): Generator<{ keys: string[]; value: T }> {\n for (const [key, value] of map.entries()) {\n if (value instanceof Map) {\n yield* walkNestedMap(value as NestedMap<T>, [...keys, key])\n } else {\n yield { keys: [...keys, key], value: value }\n }\n }\n}\n","import path from 'node:path'\n\nimport { glob, globStream } from 'fast-glob'\nimport ignore from 'ignore'\nimport micromatch from 'micromatch'\nimport { parse as yamlParse } from 'yaml'\n\nimport { safeReadFile } from '@socketsecurity/registry/lib/fs'\nimport { defaultIgnore } from '@socketsecurity/registry/lib/globs'\nimport { readPackageJson } from '@socketsecurity/registry/lib/packages'\nimport { transform } from '@socketsecurity/registry/lib/streams'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport type { Agent } from './package-environment.mts'\nimport type { SocketYml } from '@socketsecurity/config'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\nimport type { Options as GlobOptions } from 'fast-glob'\n\nconst DEFAULT_IGNORE_FOR_GIT_IGNORE = defaultIgnore.filter(\n p => !p.endsWith('.gitignore'),\n)\n\nconst IGNORED_DIRS = [\n // Taken from ignore-by-default:\n // https://github.com/novemberborn/ignore-by-default/blob/v2.1.0/index.js\n '.git', // Git repository files, see <https://git-scm.com/>\n '.log', // Log files emitted by tools such as `tsserver`, see <https://github.com/Microsoft/TypeScript/wiki/Standalone-Server-%28tsserver%29>\n '.nyc_output', // Temporary directory where nyc stores coverage data, see <https://github.com/bcoe/nyc>\n '.sass-cache', // Cache folder for node-sass, see <https://github.com/sass/node-sass>\n '.yarn', // Where node modules are installed when using Yarn, see <https://yarnpkg.com/>\n 'bower_components', // Where Bower packages are installed, see <http://bower.io/>\n 'coverage', // Standard output directory for code coverage reports, see <https://github.com/gotwarlost/istanbul>\n 'node_modules', // Where Node modules are installed, see <https://nodejs.org/>\n // Taken from globby:\n // https://github.com/sindresorhus/globby/blob/v14.0.2/ignore.js#L11-L16\n 'flow-typed',\n] as const\n\nconst IGNORED_DIR_PATTERNS = IGNORED_DIRS.map(i => `**/${i}`)\n\nasync function getWorkspaceGlobs(\n agent: Agent,\n cwd = process.cwd(),\n): Promise<string[]> {\n let workspacePatterns\n if (agent === 'pnpm') {\n for (const workspacePath of [\n path.join(cwd, 'pnpm-workspace.yaml'),\n path.join(cwd, 'pnpm-workspace.yml'),\n ]) {\n // eslint-disable-next-line no-await-in-loop\n const yml = await safeReadFile(workspacePath)\n if (yml) {\n try {\n workspacePatterns = yamlParse(yml)?.packages\n } catch {}\n if (workspacePatterns) {\n break\n }\n }\n }\n } else {\n workspacePatterns = (await readPackageJson(cwd, { throws: false }))?.[\n 'workspaces'\n ]\n }\n return Array.isArray(workspacePatterns)\n ? workspacePatterns\n .filter(isNonEmptyString)\n .map(workspacePatternToGlobPattern)\n : []\n}\n\nfunction ignoreFileLinesToGlobPatterns(\n lines: string[] | readonly string[],\n filepath: string,\n cwd: string,\n): string[] {\n const base = path.relative(cwd, path.dirname(filepath)).replace(/\\\\/g, '/')\n const patterns = []\n for (let i = 0, { length } = lines; i < length; i += 1) {\n const pattern = lines[i]!.trim()\n if (pattern.length > 0 && pattern.charCodeAt(0) !== 35 /*'#'*/) {\n patterns.push(\n ignorePatternToMinimatch(\n pattern.length && pattern.charCodeAt(0) === 33 /*'!'*/\n ? `!${path.posix.join(base, pattern.slice(1))}`\n : path.posix.join(base, pattern),\n ),\n )\n }\n }\n return patterns\n}\n\nfunction ignoreFileToGlobPatterns(\n content: string,\n filepath: string,\n cwd: string,\n): string[] {\n return ignoreFileLinesToGlobPatterns(content.split(/\\r?\\n/), filepath, cwd)\n}\n\n// Based on `@eslint/compat` convertIgnorePatternToMinimatch.\n// Apache v2.0 licensed\n// Copyright Nicholas C. Zakas\n// https://github.com/eslint/rewrite/blob/compat-v1.2.1/packages/compat/src/ignore-file.js#L28\nfunction ignorePatternToMinimatch(pattern: string): string {\n const isNegated = pattern.startsWith('!')\n const negatedPrefix = isNegated ? '!' : ''\n const patternToTest = (isNegated ? pattern.slice(1) : pattern).trimEnd()\n // Special cases.\n if (\n patternToTest === '' ||\n patternToTest === '**' ||\n patternToTest === '/**' ||\n patternToTest === '**'\n ) {\n return `${negatedPrefix}${patternToTest}`\n }\n const firstIndexOfSlash = patternToTest.indexOf('/')\n const matchEverywherePrefix =\n firstIndexOfSlash === -1 || firstIndexOfSlash === patternToTest.length - 1\n ? '**/'\n : ''\n const patternWithoutLeadingSlash =\n firstIndexOfSlash === 0 ? patternToTest.slice(1) : patternToTest\n // Escape `{` and `(` because in gitignore patterns they are just\n // literal characters without any specific syntactic meaning,\n // while in minimatch patterns they can form brace expansion or extglob syntax.\n //\n // For example, gitignore pattern `src/{a,b}.js` ignores file `src/{a,b}.js`.\n // But, the same minimatch pattern `src/{a,b}.js` ignores files `src/a.js` and `src/b.js`.\n // Minimatch pattern `src/\\{a,b}.js` is equivalent to gitignore pattern `src/{a,b}.js`.\n const escapedPatternWithoutLeadingSlash =\n patternWithoutLeadingSlash.replaceAll(\n /(?=((?:\\\\.|[^{(])*))\\1([{(])/guy,\n '$1\\\\$2',\n )\n const matchInsideSuffix = patternToTest.endsWith('/**') ? '/*' : ''\n return `${negatedPrefix}${matchEverywherePrefix}${escapedPatternWithoutLeadingSlash}${matchInsideSuffix}`\n}\n\nfunction workspacePatternToGlobPattern(workspace: string): string {\n const { length } = workspace\n if (!length) {\n return ''\n }\n // If the workspace ends with \"/\"\n if (workspace.charCodeAt(length - 1) === 47 /*'/'*/) {\n return `${workspace}/*/package.json`\n }\n // If the workspace ends with \"/**\"\n if (\n workspace.charCodeAt(length - 1) === 42 /*'*'*/ &&\n workspace.charCodeAt(length - 2) === 42 /*'*'*/ &&\n workspace.charCodeAt(length - 3) === 47 /*'/'*/\n ) {\n return `${workspace}/*/**/package.json`\n }\n // Things like \"packages/a\" or \"packages/*\"\n return `${workspace}/package.json`\n}\n\nexport function filterBySupportedScanFiles(\n filepaths: string[] | readonly string[],\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n): string[] {\n const patterns = getSupportedFilePatterns(supportedFiles)\n return filepaths.filter(p => micromatch.some(p, patterns))\n}\n\nexport function getSupportedFilePatterns(\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n): string[] {\n const patterns: string[] = []\n for (const key of Object.keys(supportedFiles)) {\n const supported = supportedFiles[key]\n if (supported) {\n patterns.push(...Object.values(supported).map(p => `**/${p.pattern}`))\n }\n }\n return patterns\n}\n\ntype GlobWithGitIgnoreOptions = GlobOptions & {\n socketConfig?: SocketYml | undefined\n}\n\nexport async function globWithGitIgnore(\n patterns: string[] | readonly string[],\n options: GlobWithGitIgnoreOptions,\n): Promise<string[]> {\n const {\n cwd = process.cwd(),\n socketConfig,\n ...additionalOptions\n } = { __proto__: null, ...options } as GlobWithGitIgnoreOptions\n\n const ignores = new Set<string>(IGNORED_DIR_PATTERNS)\n\n const projectIgnorePaths = socketConfig?.projectIgnorePaths\n if (Array.isArray(projectIgnorePaths)) {\n const ignorePatterns = ignoreFileLinesToGlobPatterns(\n projectIgnorePaths,\n path.join(cwd, '.gitignore'),\n cwd,\n )\n for (const pattern of ignorePatterns) {\n ignores.add(pattern)\n }\n }\n\n const gitIgnoreStream = globStream(['**/.gitignore'], {\n absolute: true,\n cwd,\n ignore: DEFAULT_IGNORE_FOR_GIT_IGNORE,\n })\n for await (const ignorePatterns of transform(\n gitIgnoreStream,\n async (filepath: string) =>\n ignoreFileToGlobPatterns(\n (await safeReadFile(filepath)) ?? '',\n filepath,\n cwd,\n ),\n { concurrency: 8 },\n )) {\n for (const p of ignorePatterns) {\n ignores.add(p)\n }\n }\n\n let hasNegatedPattern = false\n for (const p of ignores) {\n if (p.charCodeAt(0) === 33 /*'!'*/) {\n hasNegatedPattern = true\n break\n }\n }\n\n const globOptions = {\n __proto__: null,\n absolute: true,\n cwd,\n dot: true,\n ignore: hasNegatedPattern ? defaultIgnore : [...ignores],\n ...additionalOptions,\n } as GlobOptions\n\n if (!hasNegatedPattern) {\n return await glob(patterns as string[], globOptions)\n }\n\n // Add support for negated \"ignore\" patterns which many globbing libraries,\n // including 'fast-glob', 'globby', and 'tinyglobby', lack support for.\n const filtered: string[] = []\n const ig = ignore().add([...ignores])\n const stream = globStream(\n patterns as string[],\n globOptions,\n ) as AsyncIterable<string>\n for await (const p of stream) {\n // Note: the input files must be INSIDE the cwd. If you get strange looking\n // relative path errors here, most likely your path is outside the given cwd.\n const relPath = globOptions.absolute ? path.relative(cwd, p) : p\n if (!ig.ignores(relPath)) {\n filtered.push(p)\n }\n }\n return filtered\n}\n\nexport async function globStreamNodeModules(\n cwd = process.cwd(),\n): Promise<NodeJS.ReadableStream> {\n return globStream('**/node_modules', {\n absolute: true,\n cwd,\n onlyDirectories: true,\n })\n}\n\nexport async function globWorkspace(\n agent: Agent,\n cwd = process.cwd(),\n): Promise<string[]> {\n const workspaceGlobs = await getWorkspaceGlobs(agent, cwd)\n return workspaceGlobs.length\n ? await glob(workspaceGlobs, {\n absolute: true,\n cwd,\n ignore: defaultIgnore,\n })\n : []\n}\n\nexport function isReportSupportedFile(\n filepath: string,\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n) {\n const patterns = getSupportedFilePatterns(supportedFiles)\n return micromatch.some(filepath, patterns)\n}\n\nexport function pathsToGlobPatterns(\n paths: string[] | readonly string[],\n): string[] {\n // TODO: Does not support `~/` paths.\n return paths.map(p => (p === '.' || p === './' ? '**/*' : p))\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport which from 'which'\n\nimport { isDirSync } from '@socketsecurity/registry/lib/fs'\nimport { resolveBinPathSync } from '@socketsecurity/registry/lib/npm'\n\nimport constants from '../constants.mts'\nimport {\n filterBySupportedScanFiles,\n globWithGitIgnore,\n pathsToGlobPatterns,\n} from './glob.mts'\n\nimport type { SocketYml } from '@socketsecurity/config'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport function findBinPathDetailsSync(binName: string): {\n name: string\n path: string | undefined\n shadowed: boolean\n} {\n const binPaths =\n which.sync(binName, {\n all: true,\n nothrow: true,\n }) ?? []\n const { shadowBinPath } = constants\n let shadowIndex = -1\n let theBinPath: string | undefined\n for (let i = 0, { length } = binPaths; i < length; i += 1) {\n const binPath = binPaths[i]!\n // Skip our bin directory if it's in the front.\n if (path.dirname(binPath) === shadowBinPath) {\n shadowIndex = i\n } else {\n theBinPath = resolveBinPathSync(binPath)\n break\n }\n }\n return { name: binName, path: theBinPath, shadowed: shadowIndex !== -1 }\n}\n\nexport function findNpmDirPathSync(npmBinPath: string): string | undefined {\n const { WIN32 } = constants\n let thePath = npmBinPath\n while (true) {\n const libNmNpmPath = path.join(thePath, 'lib/node_modules/npm')\n // mise, which uses opaque binaries, puts its npm bin in a path like:\n // /Users/SomeUsername/.local/share/mise/installs/node/vX.X.X/bin/npm.\n // HOWEVER, the location of the npm install is:\n // /Users/SomeUsername/.local/share/mise/installs/node/vX.X.X/lib/node_modules/npm.\n if (\n // Use existsSync here because statsSync, even with { throwIfNoEntry: false },\n // will throw an ENOTDIR error for paths like ./a-file-that-exists/a-directory-that-does-not.\n // See https://github.com/nodejs/node/issues/56993.\n isDirSync(libNmNpmPath)\n ) {\n thePath = libNmNpmPath\n }\n const hasNmInCurrPath = isDirSync(path.join(thePath, 'node_modules'))\n const hasNmInParentPath =\n !hasNmInCurrPath && isDirSync(path.join(thePath, '../node_modules'))\n if (\n // npm bin paths may look like:\n // /usr/local/share/npm/bin/npm\n // /Users/SomeUsername/.nvm/versions/node/vX.X.X/bin/npm\n // C:\\Users\\SomeUsername\\AppData\\Roaming\\npm\\bin\\npm.cmd\n // OR\n // C:\\Program Files\\nodejs\\npm.cmd\n //\n // In practically all cases the npm path contains a node_modules folder:\n // /usr/local/share/npm/bin/npm/node_modules\n // C:\\Program Files\\nodejs\\node_modules\n (hasNmInCurrPath ||\n // In some bespoke cases the node_modules folder is in the parent directory.\n hasNmInParentPath) &&\n // Optimistically look for the default location.\n (path.basename(thePath) === 'npm' ||\n // Chocolatey installs npm bins in the same directory as node bins.\n (WIN32 && existsSync(path.join(thePath, 'npm.cmd'))))\n ) {\n return hasNmInParentPath ? path.dirname(thePath) : thePath\n }\n const parent = path.dirname(thePath)\n if (parent === thePath) {\n return undefined\n }\n thePath = parent\n }\n}\n\nexport type PackageFilesForScanOptions = {\n cwd?: string | undefined\n config?: SocketYml | undefined\n}\n\nexport async function getPackageFilesForScan(\n inputPaths: string[],\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n options?: PackageFilesForScanOptions | undefined,\n): Promise<string[]> {\n const { config: socketConfig, cwd = process.cwd() } = {\n __proto__: null,\n ...options,\n } as PackageFilesForScanOptions\n\n const filepaths = await globWithGitIgnore(pathsToGlobPatterns(inputPaths), {\n cwd,\n socketConfig,\n })\n\n return filterBySupportedScanFiles(filepaths!, supportedFiles)\n}\n","import { existsSync } from 'node:fs'\nimport Module from 'node:module'\nimport path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../constants.mts'\nimport { findBinPathDetailsSync, findNpmDirPathSync } from './path-resolve.mts'\n\nfunction exitWithBinPathError(binName: string): never {\n logger.fail(\n `Socket unable to locate ${binName}; ensure it is available in the PATH environment variable`,\n )\n // The exit code 127 indicates that the command or binary being executed\n // could not be found.\n // eslint-disable-next-line n/no-process-exit\n process.exit(127)\n}\n\nlet _npmBinPath: string | undefined\nexport function getNpmBinPath(): string {\n if (_npmBinPath === undefined) {\n _npmBinPath = getNpmBinPathDetails().path\n if (!_npmBinPath) {\n exitWithBinPathError('npm')\n }\n }\n return _npmBinPath\n}\n\nlet _npmBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined\nfunction getNpmBinPathDetails(): ReturnType<typeof findBinPathDetailsSync> {\n if (_npmBinPathDetails === undefined) {\n _npmBinPathDetails = findBinPathDetailsSync('npm')\n }\n return _npmBinPathDetails\n}\n\nlet _npmDirPath: string | undefined\nexport function getNpmDirPath() {\n if (_npmDirPath === undefined) {\n const npmBinPath = getNpmBinPath()\n _npmDirPath = npmBinPath ? findNpmDirPathSync(npmBinPath) : undefined\n if (!_npmDirPath) {\n _npmDirPath = constants.ENV.SOCKET_CLI_NPM_PATH || undefined\n }\n if (!_npmDirPath) {\n let message = 'Unable to find npm CLI install directory.'\n if (npmBinPath) {\n message += `\\nSearched parent directories of ${path.dirname(npmBinPath)}.`\n }\n message +=\n '\\n\\nThis is may be a bug with socket-npm related to changes to the npm CLI.'\n message += `\\nPlease report to ${constants.SOCKET_CLI_ISSUES_URL}.`\n logger.fail(message)\n // The exit code 127 indicates that the command or binary being executed\n // could not be found.\n // eslint-disable-next-line n/no-process-exit\n process.exit(127)\n }\n }\n return _npmDirPath\n}\n\nlet _npmRequire: NodeJS.Require | undefined\nexport function getNpmRequire(): NodeJS.Require {\n if (_npmRequire === undefined) {\n const npmDirPath = getNpmDirPath()\n const npmNmPath = path.join(npmDirPath, 'node_modules/npm')\n _npmRequire = Module.createRequire(\n path.join(\n existsSync(npmNmPath) ? npmNmPath : npmDirPath,\n '<dummy-basename>',\n ),\n )\n }\n return _npmRequire\n}\n\nlet _npxBinPath: string | undefined\nexport function getNpxBinPath(): string {\n if (_npxBinPath === undefined) {\n _npxBinPath = getNpxBinPathDetails().path\n if (!_npxBinPath) {\n exitWithBinPathError('npx')\n }\n }\n return _npxBinPath\n}\n\nlet _npxBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined\nfunction getNpxBinPathDetails(): ReturnType<typeof findBinPathDetailsSync> {\n if (_npxBinPathDetails === undefined) {\n _npxBinPathDetails = findBinPathDetailsSync('npx')\n }\n return _npxBinPathDetails\n}\n\nexport function isNpmBinPathShadowed() {\n return getNpmBinPathDetails().shadowed\n}\n\nexport function isNpxBinPathShadowed() {\n return getNpxBinPathDetails().shadowed\n}\n","const helpFlags = new Set(['--help', '-h'])\n\nexport function cmdFlagsToString(args: string[]) {\n const result = []\n for (let i = 0, { length } = args; i < length; i += 1) {\n if (args[i]!.startsWith('--')) {\n // Check if the next item exists and is NOT another flag.\n if (i + 1 < length && !args[i + 1]!.startsWith('--')) {\n result.push(`${args[i]}=${args[i + 1]}`)\n i += 1\n } else {\n result.push(args[i])\n }\n }\n }\n return result.join(' ')\n}\n\nexport function cmdFlagValueToArray(value: any): string[] {\n if (typeof value === 'string') {\n return value.trim().split(/, */).filter(Boolean)\n }\n if (Array.isArray(value)) {\n return value.flatMap(cmdFlagValueToArray)\n }\n return []\n}\n\nexport function cmdPrefixMessage(cmdName: string, text: string): string {\n const cmdPrefix = cmdName ? `${cmdName}: ` : ''\n return `${cmdPrefix}${text}`\n}\n\nexport function isHelpFlag(cmdArg: string) {\n return helpFlags.has(cmdArg)\n}\n","import { readJsonSync } from '@socketsecurity/registry/lib/fs'\n\nimport { getDefaultOrgSlug } from '../commands/ci/fetch-default-org-slug.mts'\nimport constants from '../constants.mts'\nimport { getDefaultApiToken } from './sdk.mts'\nimport shadowBin from '../shadow/npm/bin.mts'\n\nimport type { ShadowBinOptions } from '../shadow/npm/bin.mts'\nimport type { CResult } from '../types.mts'\nimport type { SpawnExtra } from '@socketsecurity/registry/lib/spawn'\n\nexport function extractTier1ReachabilityScanId(\n socketFactsFile: string,\n): string | undefined {\n const json = readJsonSync(socketFactsFile, { throws: false })\n const tier1ReachabilityScanId = String(\n json?.['tier1ReachabilityScanId'] ?? '',\n ).trim()\n return tier1ReachabilityScanId.length > 0\n ? tier1ReachabilityScanId\n : undefined\n}\n\nexport async function spawnCoana(\n args: string[] | readonly string[],\n orgSlug?: string,\n options?: ShadowBinOptions | undefined,\n extra?: SpawnExtra | undefined,\n): Promise<CResult<string>> {\n const {\n env: spawnEnv,\n ipc,\n ...spawnOpts\n } = {\n __proto__: null,\n ...options,\n } as ShadowBinOptions\n const mixinsEnv: Record<string, string> = {\n SOCKET_CLI_VERSION: constants.ENV.INLINED_SOCKET_CLI_VERSION,\n }\n const defaultApiToken = getDefaultApiToken()\n if (defaultApiToken) {\n mixinsEnv['SOCKET_CLI_API_TOKEN'] = defaultApiToken\n }\n\n if (orgSlug) {\n mixinsEnv['SOCKET_ORG_SLUG'] = orgSlug\n } else {\n const orgSlugCResult = await getDefaultOrgSlug()\n if (orgSlugCResult.ok) {\n mixinsEnv['SOCKET_ORG_SLUG'] = orgSlugCResult.data\n }\n }\n\n try {\n const { spawnPromise } = await shadowBin(\n 'npx',\n [\n '--yes',\n `@coana-tech/cli@~${constants.ENV.INLINED_SOCKET_CLI_COANA_TECH_CLI_VERSION}`,\n ...args,\n ],\n {\n ...spawnOpts,\n env: {\n ...mixinsEnv,\n ...spawnEnv,\n },\n ipc: {\n [constants.SOCKET_CLI_SHADOW_ACCEPT_RISKS]: true,\n [constants.SOCKET_CLI_SHADOW_API_TOKEN]:\n constants.SOCKET_PUBLIC_API_TOKEN,\n [constants.SOCKET_CLI_SHADOW_SILENT]: true,\n ...ipc,\n },\n },\n extra,\n )\n const output = await spawnPromise\n return { ok: true, data: output.stdout }\n } catch (e) {\n const stderr = (e as any)?.stderr\n const message = stderr ? stderr : (e as Error)?.message\n return { ok: false, data: e, message }\n }\n}\n","import type {\n EnterpriseOrganizations,\n Organizations,\n} from '../commands/organization/fetch-organization-list.mts'\n\nexport function getEnterpriseOrgs(\n orgs: Organizations,\n): EnterpriseOrganizations {\n return orgs.filter(o => o.plan === 'enterprise') as EnterpriseOrganizations\n}\n\nexport function getOrgSlugs(orgs: Organizations): string[] {\n return orgs.map(o => o.slug)\n}\n\nexport function hasEnterpriseOrgPlan(orgs: Organizations): boolean {\n return orgs.some(o => o.plan === 'enterprise')\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport type { CResult } from '../types.mts'\n\nexport interface SocketJson {\n ' _____ _ _ ': string\n '| __|___ ___| |_ ___| |_ ': string\n \"|__ | . | _| '_| -_| _| \": string\n '|_____|___|___|_,_|___|_|.dev': string\n version: number\n\n defaults?: {\n manifest?: {\n conda?: {\n disabled?: boolean\n infile?: string\n outfile?: string\n stdin?: boolean\n stdout?: boolean\n target?: string\n verbose?: boolean\n }\n gradle?: {\n disabled?: boolean\n bin?: string\n gradleOpts?: string\n verbose?: boolean\n }\n sbt?: {\n disabled?: boolean\n infile?: string\n stdin?: boolean\n bin?: string\n outfile?: string\n sbtOpts?: string\n stdout?: boolean\n verbose?: boolean\n }\n }\n scan?: {\n create?: {\n autoManifest?: boolean\n repo?: string\n report?: boolean\n branch?: string\n }\n github?: {\n all?: boolean\n githubApiUrl?: string\n orgGithub?: string\n repos?: string\n }\n }\n }\n}\n\nexport function readOrDefaultSocketJson(cwd: string): SocketJson {\n const jsonCResult = readSocketJsonSync(cwd, true)\n return jsonCResult.ok\n ? jsonCResult.data\n : // This should be unreachable but it makes TS happy.\n getDefaultSocketJson()\n}\n\nexport function getDefaultSocketJson(): SocketJson {\n return {\n ' _____ _ _ ':\n 'Local config file for Socket CLI tool ( https://npmjs.org/socket ), to work with https://socket.dev',\n '| __|___ ___| |_ ___| |_ ':\n ' The config in this file is used to set as defaults for flags or command args when using the CLI',\n \"|__ | . | _| '_| -_| _| \":\n ' in this dir, often a repo root. You can choose commit or .ignore this file, both works.',\n '|_____|___|___|_,_|___|_|.dev':\n 'Warning: This file may be overwritten without warning by `socket manifest setup` or other commands',\n version: 1,\n }\n}\n\nexport function readSocketJsonSync(\n cwd: string,\n defaultOnError = false,\n): CResult<SocketJson> {\n const sockJsonPath = path.join(cwd, 'socket.json')\n if (!fs.existsSync(sockJsonPath)) {\n debugFn('notice', `miss: socket.json not found at ${cwd}`)\n return { ok: true, data: getDefaultSocketJson() }\n }\n let json = null\n try {\n json = fs.readFileSync(sockJsonPath, 'utf8')\n } catch (e) {\n if (defaultOnError) {\n logger.warn('Failed to read socket.json, using default')\n debugDir('inspect', { error: e })\n return { ok: true, data: getDefaultSocketJson() }\n }\n const msg = (e as Error)?.message\n debugDir('inspect', { error: e })\n return {\n ok: false,\n message: 'Failed to read socket.json',\n cause: `An error occurred while trying to read socket.json${msg ? `: ${msg}` : ''}`,\n }\n }\n\n let obj\n try {\n obj = JSON.parse(json)\n } catch (e) {\n debugFn('error', 'caught: JSON.parse error')\n debugDir('inspect', { error: e, json })\n if (defaultOnError) {\n logger.warn('Failed to parse socket.json, using default')\n return { ok: true, data: getDefaultSocketJson() }\n }\n return {\n ok: false,\n message: 'Failed to parse socket.json',\n cause: 'socket.json does not contain valid JSON, please verify',\n }\n }\n\n if (!obj) {\n logger.warn('Warning: file contents was empty, using default')\n return { ok: true, data: getDefaultSocketJson() }\n }\n\n // Do we really care to validate? All properties are optional so code will have\n // to check every step of the way regardless. Who cares about validation here...?\n return { ok: true, data: obj }\n}\n\nexport async function writeSocketJson(\n cwd: string,\n sockJson: SocketJson,\n): Promise<CResult<undefined>> {\n let json = ''\n try {\n json = JSON.stringify(sockJson, null, 2)\n } catch (e) {\n debugFn('error', 'caught: JSON.stringify error')\n debugDir('inspect', { error: e, sockJson })\n return {\n ok: false,\n message: 'Failed to serialize to JSON',\n cause:\n 'There was an unexpected problem converting the socket json object to a JSON string. Unable to store it.',\n }\n }\n\n const filepath = path.join(cwd, 'socket.json')\n await fs.promises.writeFile(filepath, json + '\\n', 'utf8')\n\n return { ok: true, data: undefined }\n}\n","import constants from '../../constants.mts'\n\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\nimport type {\n ALERT_ACTION,\n ALERT_TYPE,\n CompactSocketArtifact,\n CompactSocketArtifactAlert,\n SocketArtifact,\n SocketArtifactAlert,\n} from '@socketsecurity/sdk'\n\nexport type {\n ALERT_ACTION,\n ALERT_TYPE,\n CompactSocketArtifact,\n CompactSocketArtifactAlert,\n SocketArtifact,\n SocketArtifactAlert,\n}\n\nexport type CVE_ALERT_TYPE = 'cve' | 'mediumCVE' | 'mildCVE' | 'criticalCVE'\n\nexport type ArtifactAlertCve = Remap<\n Omit<CompactSocketArtifactAlert, 'type'> & {\n type: CVE_ALERT_TYPE\n }\n>\n\nexport type ArtifactAlertCveFixable = Remap<\n Omit<CompactSocketArtifactAlert, 'props' | 'type'> & {\n type: CVE_ALERT_TYPE\n props: CveProps\n }\n>\n\nexport type ArtifactAlertUpgrade = Remap<\n Omit<CompactSocketArtifactAlert, 'type'> & {\n type: 'socketUpgradeAvailable'\n }\n>\n\nexport type CveProps = {\n firstPatchedVersionIdentifier?: string\n vulnerableVersionRange: string\n [key: string]: any\n}\n\nexport function isArtifactAlertCve(\n alert: CompactSocketArtifactAlert,\n): alert is ArtifactAlertCve {\n const { type } = alert\n return (\n type === constants.ALERT_TYPE_CVE ||\n type === constants.ALERT_TYPE_MEDIUM_CVE ||\n type === constants.ALERT_TYPE_MILD_CVE ||\n type === constants.ALERT_TYPE_CRITICAL_CVE\n )\n}\n","export function createEnum<const T extends Record<string, any>>(\n obj: T,\n): Readonly<T> {\n return Object.freeze({ __proto__: null, ...obj }) as any\n}\n\nexport function pick<T extends Record<string, any>, K extends keyof T>(\n input: T,\n keys: K[] | readonly K[],\n): Pick<T, K> {\n const result: Partial<Pick<T, K>> = {}\n for (const key of keys) {\n result[key] = input[key]\n }\n return result as Pick<T, K>\n}\n","import { createEnum } from '../objects.mts'\n\nexport const ALERT_FIX_TYPE = createEnum({\n cve: 'cve',\n remove: 'remove',\n upgrade: 'upgrade',\n})\n","import { joinAnd } from '@socketsecurity/registry/lib/arrays'\n\nimport { createEnum, pick } from '../objects.mts'\n\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport const ALERT_SEVERITY = createEnum({\n critical: 'critical',\n high: 'high',\n middle: 'middle',\n low: 'low',\n})\n\nexport type SocketSdkAlertList =\n SocketSdkSuccessResult<'getIssuesByNPMPackage'>['data']\n\nexport type SocketSdkAlert = SocketSdkAlertList[number]['value'] extends\n | infer U\n | undefined\n ? U\n : never\n\n// Ordered from most severe to least.\nexport const ALERT_SEVERITIES_SORTED: ReadonlyArray<\n SocketSdkAlert['severity']\n> = Object.freeze(['critical', 'high', 'middle', 'low'])\n\nfunction getDesiredSeverities(\n lowestToInclude: SocketSdkAlert['severity'] | undefined,\n): Array<SocketSdkAlert['severity']> {\n const result: Array<SocketSdkAlert['severity']> = []\n for (const severity of ALERT_SEVERITIES_SORTED) {\n result.push(severity)\n if (severity === lowestToInclude) {\n break\n }\n }\n return result\n}\n\nexport function formatSeverityCount(\n severityCount: Record<SocketSdkAlert['severity'], number>,\n): string {\n const summary: string[] = []\n for (const severity of ALERT_SEVERITIES_SORTED) {\n if (severityCount[severity]) {\n summary.push(`${severityCount[severity]} ${severity}`)\n }\n }\n return joinAnd(summary)\n}\n\nexport function getSeverityCount(\n issues: SocketSdkAlertList,\n lowestToInclude: SocketSdkAlert['severity'] | undefined,\n): Record<SocketSdkAlert['severity'], number> {\n const severityCount = pick(\n { low: 0, middle: 0, high: 0, critical: 0 },\n getDesiredSeverities(lowestToInclude),\n ) as Record<SocketSdkAlert['severity'], number>\n\n for (const issue of issues) {\n const { value } = issue\n if (!value) {\n continue\n }\n const { severity } = value\n if (severityCount[severity] !== undefined) {\n severityCount[severity] += 1\n }\n }\n return severityCount\n}\n","import terminalLink from 'terminal-link'\nimport colors from 'yoctocolors-cjs'\n\nimport indentString from '@socketregistry/indent-string/index.cjs'\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","import { isObject } from '@socketsecurity/registry/lib/objects'\n\nexport type FilterConfig = {\n [key: string]: boolean | string[]\n}\n\nexport function toFilterConfig(obj: any): FilterConfig {\n const normalized = { __proto__: null } as unknown as FilterConfig\n const keys = isObject(obj) ? Object.keys(obj) : []\n for (const key of keys) {\n const value = obj[key]\n if (typeof value === 'boolean' || Array.isArray(value)) {\n normalized[key] = value\n }\n }\n return normalized\n}\n","import semver from 'semver'\n\nimport type { SemVer } from 'semver'\n\nexport const RangeStyles = [\n 'caret',\n 'gt',\n 'gte',\n 'lt',\n 'lte',\n 'pin',\n 'preserve',\n 'tilde',\n]\n\nexport type RangeStyle =\n | 'caret'\n | 'gt'\n | 'gte'\n | 'lt'\n | 'lte'\n | 'pin'\n | 'preserve'\n | 'tilde'\n\nexport type { SemVer }\n\nexport function applyRange(\n refRange: string,\n version: string,\n style: RangeStyle = 'preserve',\n): string {\n switch (style) {\n case 'caret':\n return `^${version}`\n case 'gt':\n return `>${version}`\n case 'gte':\n return `>=${version}`\n case 'lt':\n return `<${version}`\n case 'lte':\n return `<=${version}`\n case 'preserve': {\n const range = new semver.Range(refRange)\n const { raw } = range\n const comparators = range.set.flat()\n const { length } = comparators\n if (length === 1) {\n const char = /^[<>]=?/.exec(raw)?.[0]\n if (char) {\n return `${char}${version}`\n }\n } else if (length === 2) {\n const char = /^[~^]/.exec(raw)?.[0]\n if (char) {\n return `${char}${version}`\n }\n }\n return version\n }\n case 'tilde':\n return `~${version}`\n case 'pin':\n default:\n return version\n }\n}\n\nexport function getMajor(version: unknown): number | null {\n try {\n const coerced = semver.coerce(version as string)\n return coerced ? semver.major(coerced) : null\n } catch {}\n return null\n}\n\nexport function getMinVersion(range: unknown): SemVer | null {\n try {\n return semver.minVersion(range as string)\n } catch {}\n return null\n}\n","import { createRequire } from 'node:module'\nimport path from 'node:path'\n\nimport constants from '../constants.mts'\n\nconst require = createRequire(import.meta.url)\n\nlet _translations:\n | Readonly<typeof import('../../translations.json')>\n | undefined\n\nexport function getTranslations() {\n if (_translations === undefined) {\n _translations = /*@__PURE__*/ require(\n path.join(constants.rootPath, 'translations.json'),\n )\n }\n return _translations!\n}\n","import semver from 'semver'\nimport colors from 'yoctocolors-cjs'\n\nimport { PackageURL } from '@socketregistry/packageurl-js'\nimport { getManifestData } from '@socketsecurity/registry'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { getOwn, hasOwn } from '@socketsecurity/registry/lib/objects'\nimport { resolvePackageName } from '@socketsecurity/registry/lib/packages'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\n\nimport { isArtifactAlertCve } from './alert/artifact.mts'\nimport { ALERT_FIX_TYPE } from './alert/fix.mts'\nimport { ALERT_SEVERITY } from './alert/severity.mts'\nimport { ColorOrMarkdown } from './color-or-markdown.mts'\nimport { toFilterConfig } from './filter-config.mts'\nimport { createEnum } from './objects.mts'\nimport { getPurlObject } from './purl.mts'\nimport { getMajor } from './semver.mts'\nimport { getSocketDevPackageOverviewUrl } from './socket-url.mts'\nimport { getTranslations } from './translations.mts'\n\nimport type {\n ALERT_ACTION,\n ALERT_TYPE,\n CompactSocketArtifact,\n CompactSocketArtifactAlert,\n CveProps,\n} from './alert/artifact.mts'\nimport type { PURL_Type } from './ecosystem.mts'\nimport type { SocketYml } from '@socketsecurity/config'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nexport const ALERT_SEVERITY_COLOR = createEnum({\n critical: 'magenta',\n high: 'red',\n middle: 'yellow',\n low: 'white',\n})\n\nexport const ALERT_SEVERITY_ORDER = createEnum({\n critical: 0,\n high: 1,\n middle: 2,\n low: 3,\n none: 4,\n})\n\nexport type SocketPackageAlert = {\n name: string\n version: string\n key: string\n type: string\n blocked: boolean\n critical: boolean\n ecosystem: PURL_Type\n fixable: boolean\n raw: CompactSocketArtifactAlert\n upgradable: boolean\n}\n\nexport type AlertsByPurl = Map<string, SocketPackageAlert[]>\n\nconst MIN_ABOVE_THE_FOLD_COUNT = 3\n\nconst MIN_ABOVE_THE_FOLD_ALERT_COUNT = 1\n\nconst format = new ColorOrMarkdown(false)\n\nexport type RiskCounts = {\n critical: number\n high: number\n middle: number\n low: number\n}\n\nfunction getHiddenRiskCounts(hiddenAlerts: SocketPackageAlert[]): RiskCounts {\n const riskCounts = {\n critical: 0,\n high: 0,\n middle: 0,\n low: 0,\n }\n for (const alert of hiddenAlerts) {\n switch (getAlertSeverityOrder(alert)) {\n case ALERT_SEVERITY_ORDER.critical:\n riskCounts.critical += 1\n break\n case ALERT_SEVERITY_ORDER.high:\n riskCounts.high += 1\n break\n case ALERT_SEVERITY_ORDER.middle:\n riskCounts.middle += 1\n break\n case ALERT_SEVERITY_ORDER.low:\n riskCounts.low += 1\n break\n }\n }\n return riskCounts\n}\n\nfunction getHiddenRisksDescription(riskCounts: RiskCounts): string {\n const descriptions: string[] = []\n if (riskCounts.critical) {\n descriptions.push(`${riskCounts.critical} ${getSeverityLabel('critical')}`)\n }\n if (riskCounts.high) {\n descriptions.push(`${riskCounts.high} ${getSeverityLabel('high')}`)\n }\n if (riskCounts.middle) {\n descriptions.push(`${riskCounts.middle} ${getSeverityLabel('middle')}`)\n }\n if (riskCounts.low) {\n descriptions.push(`${riskCounts.low} ${getSeverityLabel('low')}`)\n }\n return `(${descriptions.join('; ')})`\n}\n\nexport type AlertFilter = {\n actions?: ALERT_ACTION[] | undefined\n blocked?: boolean | undefined\n critical?: boolean | undefined\n cve?: boolean | undefined\n existing?: boolean | undefined\n fixable?: boolean | undefined\n upgradable?: boolean | undefined\n}\n\nexport type AddArtifactToAlertsMapOptions = {\n consolidate?: boolean | undefined\n filter?: AlertFilter | undefined\n overrides?: { [key: string]: string } | undefined\n socketYml?: SocketYml | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function addArtifactToAlertsMap<T extends AlertsByPurl>(\n artifact: CompactSocketArtifact,\n alertsByPurl: T,\n options?: AddArtifactToAlertsMapOptions | undefined,\n): Promise<T> {\n // Make TypeScript happy.\n if (!artifact.name || !artifact.version || !artifact.alerts?.length) {\n return alertsByPurl\n }\n\n const { type: ecosystem, version } = artifact\n\n const {\n consolidate = false,\n overrides,\n socketYml,\n } = {\n __proto__: null,\n ...options,\n } as AddArtifactToAlertsMapOptions\n\n const name = resolvePackageName(\n artifact as {\n name: string\n namespace?: string | undefined\n },\n )\n\n const filterConfig = toFilterConfig({\n blocked: true,\n critical: true,\n cve: true,\n ...getOwn(options, 'filter'),\n }) as AlertFilter\n\n const enabledState = {\n __proto__: null,\n ...socketYml?.issueRules,\n } as Partial<Record<ALERT_TYPE, boolean>>\n\n let sockPkgAlerts: SocketPackageAlert[] = []\n for (const alert of artifact.alerts) {\n const action = alert.action ?? ''\n const enabledFlag = enabledState[alert.type]\n if (\n (action === 'ignore' && enabledFlag !== true) ||\n enabledFlag === false\n ) {\n continue\n }\n const blocked = action === 'error'\n const critical = alert.severity === ALERT_SEVERITY.critical\n const cve = isArtifactAlertCve(alert)\n const fixType = alert.fix?.type ?? ''\n const fixableCve = fixType === ALERT_FIX_TYPE.cve\n const fixableUpgrade = fixType === ALERT_FIX_TYPE.upgrade\n const fixable = fixableCve || fixableUpgrade\n const upgradable = fixableUpgrade && !hasOwn(overrides, name)\n if (\n (filterConfig.blocked && blocked) ||\n (filterConfig.critical && critical) ||\n (filterConfig.cve && cve) ||\n (filterConfig.fixable && fixable) ||\n (filterConfig.upgradable && upgradable)\n ) {\n sockPkgAlerts.push({\n name,\n version,\n key: alert.key,\n type: alert.type,\n blocked,\n critical,\n ecosystem,\n fixable,\n raw: alert,\n upgradable,\n })\n }\n }\n if (!sockPkgAlerts.length) {\n return alertsByPurl\n }\n const purl = `pkg:${ecosystem}/${name}@${version}`\n const major = getMajor(version)!\n if (consolidate) {\n type HighestVersionByMajor = Map<\n number,\n { alert: SocketPackageAlert; version: string }\n >\n const highestForCve: HighestVersionByMajor = new Map()\n const highestForUpgrade: HighestVersionByMajor = new Map()\n const unfixableAlerts: SocketPackageAlert[] = []\n for (const sockPkgAlert of sockPkgAlerts) {\n const alert = sockPkgAlert.raw\n const fixType = alert.fix?.type ?? ''\n if (fixType === ALERT_FIX_TYPE.cve) {\n // An alert with alert.fix.type of 'cve' should have a\n // alert.props.firstPatchedVersionIdentifier property value.\n // We're just being cautious.\n const firstPatchedVersionIdentifier = (alert.props as CveProps)\n ?.firstPatchedVersionIdentifier\n const patchedMajor = firstPatchedVersionIdentifier\n ? getMajor(firstPatchedVersionIdentifier)\n : null\n if (typeof patchedMajor === 'number') {\n // Consolidate to the highest \"first patched version\" by each major\n // version number.\n const highest = highestForCve.get(patchedMajor)?.version ?? '0.0.0'\n if (semver.gt(firstPatchedVersionIdentifier!, highest)) {\n highestForCve.set(patchedMajor, {\n alert: sockPkgAlert,\n version: firstPatchedVersionIdentifier!,\n })\n }\n } else {\n unfixableAlerts.push(sockPkgAlert)\n }\n } else if (fixType === ALERT_FIX_TYPE.upgrade) {\n // For Socket Optimize upgrades we assume the highest version available\n // is compatible. This may change in the future.\n const highest = highestForUpgrade.get(major)?.version ?? '0.0.0'\n if (semver.gt(version, highest)) {\n highestForUpgrade.set(major, { alert: sockPkgAlert, version })\n }\n } else {\n unfixableAlerts.push(sockPkgAlert)\n }\n }\n sockPkgAlerts = [\n // Sort CVE alerts by severity: critical, high, middle, then low.\n ...Array.from(highestForCve.values())\n .map(d => d.alert)\n .sort(alertSeverityComparator),\n ...Array.from(highestForUpgrade.values()).map(d => d.alert),\n ...unfixableAlerts,\n ]\n } else {\n sockPkgAlerts.sort((a, b) => naturalCompare(a.type, b.type))\n }\n if (sockPkgAlerts.length) {\n alertsByPurl.set(purl, sockPkgAlerts)\n }\n return alertsByPurl\n}\n\nexport function alertsHaveBlocked(alerts: SocketPackageAlert[]): boolean {\n return alerts.find(a => a.blocked) !== undefined\n}\n\nexport function alertsHaveSeverity(\n alerts: SocketPackageAlert[],\n severity: `${keyof typeof ALERT_SEVERITY}`,\n): boolean {\n return alerts.find(a => a.raw.severity === severity) !== undefined\n}\n\nexport function alertSeverityComparator(\n a: SocketPackageAlert,\n b: SocketPackageAlert,\n): number {\n // Put the most severe first.\n return getAlertSeverityOrder(a) - getAlertSeverityOrder(b)\n}\n\nexport function getAlertSeverityOrder(alert: SocketPackageAlert): number {\n // The more severe, the lower the sort number.\n const { severity } = alert.raw\n return severity === ALERT_SEVERITY.critical\n ? 0\n : severity === ALERT_SEVERITY.high\n ? 1\n : severity === ALERT_SEVERITY.middle\n ? 2\n : severity === ALERT_SEVERITY.low\n ? 3\n : 4\n}\n\nexport function getAlertsSeverityOrder(alerts: SocketPackageAlert[]): number {\n return alertsHaveBlocked(alerts) ||\n alertsHaveSeverity(alerts, ALERT_SEVERITY.critical)\n ? 0\n : alertsHaveSeverity(alerts, ALERT_SEVERITY.high)\n ? 1\n : alertsHaveSeverity(alerts, ALERT_SEVERITY.middle)\n ? 2\n : alertsHaveSeverity(alerts, ALERT_SEVERITY.low)\n ? 3\n : 4\n}\n\nexport type CveFilter = {\n upgradable?: boolean | undefined\n}\n\nexport type CveInfoByAlertKey = Map<\n string,\n {\n firstPatchedVersionIdentifier: string\n vulnerableVersionRange: string\n }\n>\n\nexport type CveInfoByPartialPurl = Map<string, CveInfoByAlertKey>\n\nexport type GetCveInfoByPackageOptions = {\n filter?: CveFilter | undefined\n}\n\nexport function getCveInfoFromAlertsMap(\n alertsMap: AlertsByPurl,\n options?: GetCveInfoByPackageOptions | undefined,\n): CveInfoByPartialPurl | null {\n const filterConfig = toFilterConfig(getOwn(options, 'filter')) as CveFilter\n\n let infoByPartialPurl: CveInfoByPartialPurl | null = null\n // eslint-disable-next-line no-unused-labels\n alertsMapLoop: for (const { 0: purl, 1: sockPkgAlerts } of alertsMap) {\n const purlObj = getPurlObject(purl)\n const partialPurl = new PackageURL(\n purlObj.type,\n purlObj.namespace,\n purlObj.name,\n ).toString()\n const name = resolvePackageName(purlObj)\n sockPkgAlertsLoop: for (const sockPkgAlert of sockPkgAlerts) {\n const alert = sockPkgAlert.raw\n if (\n alert.fix?.type !== ALERT_FIX_TYPE.cve ||\n (filterConfig.upgradable === false &&\n getManifestData(sockPkgAlert.ecosystem as any, name))\n ) {\n continue sockPkgAlertsLoop\n }\n if (!infoByPartialPurl) {\n infoByPartialPurl = new Map()\n }\n let infos = infoByPartialPurl.get(partialPurl)\n if (!infos) {\n infos = new Map()\n infoByPartialPurl.set(partialPurl, infos)\n }\n const { key } = alert\n if (!infos.has(key)) {\n // An alert with alert.fix.type of 'cve' should have a\n // alert.props.firstPatchedVersionIdentifier property value.\n // We're just being cautious.\n const firstPatchedVersionIdentifier = (alert.props as CveProps)\n ?.firstPatchedVersionIdentifier\n const vulnerableVersionRange = (alert.props as CveProps)\n ?.vulnerableVersionRange\n let error: unknown\n if (firstPatchedVersionIdentifier && vulnerableVersionRange) {\n try {\n infos.set(key, {\n firstPatchedVersionIdentifier,\n vulnerableVersionRange: new semver.Range(\n // Replace ', ' in a range like '>= 1.0.0, < 1.8.2' with ' ' so that\n // semver.Range will parse it without erroring.\n vulnerableVersionRange\n .replace(/, +/g, ' ')\n .replace(/; +/g, ' || '),\n ).format(),\n })\n continue sockPkgAlertsLoop\n } catch (e) {\n error = e\n }\n }\n debugFn('error', 'fail: invalid SocketPackageAlert')\n debugDir('inspect', { alert, error })\n }\n }\n }\n return infoByPartialPurl\n}\n\nexport function getSeverityLabel(\n severity: `${keyof typeof ALERT_SEVERITY}`,\n): string {\n return severity === 'middle' ? 'moderate' : severity\n}\n\nexport type LogAlertsMapOptions = {\n hideAt?: `${keyof typeof ALERT_SEVERITY}` | 'none' | undefined\n output?: NodeJS.WriteStream | undefined\n}\n\nexport function logAlertsMap(\n alertsMap: AlertsByPurl,\n options: LogAlertsMapOptions,\n) {\n const { hideAt = 'middle', output = process.stderr } = {\n __proto__: null,\n ...options,\n } as LogAlertsMapOptions\n\n const translations = getTranslations()\n const sortedEntries = Array.from(alertsMap.entries()).sort(\n (a, b) => getAlertsSeverityOrder(a[1]) - getAlertsSeverityOrder(b[1]),\n )\n\n const aboveTheFoldPurls = new Set<string>()\n const viewableAlertsByPurl = new Map<string, SocketPackageAlert[]>()\n const hiddenAlertsByPurl = new Map<string, SocketPackageAlert[]>()\n\n for (let i = 0, { length } = sortedEntries; i < length; i += 1) {\n const { 0: purl, 1: alerts } = sortedEntries[i]!\n const hiddenAlerts: typeof alerts = []\n const viewableAlerts = alerts.filter(a => {\n const keep =\n a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER[hideAt]\n if (!keep) {\n hiddenAlerts.push(a)\n }\n return keep\n })\n if (hiddenAlerts.length) {\n hiddenAlertsByPurl.set(purl, hiddenAlerts.sort(alertSeverityComparator))\n }\n if (!viewableAlerts.length) {\n continue\n }\n viewableAlerts.sort(alertSeverityComparator)\n viewableAlertsByPurl.set(purl, viewableAlerts)\n if (\n viewableAlerts.find(\n (a: SocketPackageAlert) =>\n a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER.middle,\n )\n ) {\n aboveTheFoldPurls.add(purl)\n }\n }\n\n // If MIN_ABOVE_THE_FOLD_COUNT is NOT met add more from viewable pkg ids.\n for (const { 0: purl } of viewableAlertsByPurl.entries()) {\n if (aboveTheFoldPurls.size >= MIN_ABOVE_THE_FOLD_COUNT) {\n break\n }\n aboveTheFoldPurls.add(purl)\n }\n // If MIN_ABOVE_THE_FOLD_COUNT is STILL NOT met add more from hidden pkg ids.\n for (const { 0: purl, 1: hiddenAlerts } of hiddenAlertsByPurl.entries()) {\n if (aboveTheFoldPurls.size >= MIN_ABOVE_THE_FOLD_COUNT) {\n break\n }\n aboveTheFoldPurls.add(purl)\n const viewableAlerts = viewableAlertsByPurl.get(purl) ?? []\n if (viewableAlerts.length < MIN_ABOVE_THE_FOLD_ALERT_COUNT) {\n const neededCount = MIN_ABOVE_THE_FOLD_ALERT_COUNT - viewableAlerts.length\n let removedHiddenAlerts: SocketPackageAlert[] | undefined\n if (hiddenAlerts.length - neededCount > 0) {\n removedHiddenAlerts = hiddenAlerts.splice(\n 0,\n MIN_ABOVE_THE_FOLD_ALERT_COUNT,\n )\n } else {\n removedHiddenAlerts = hiddenAlerts\n hiddenAlertsByPurl.delete(purl)\n }\n viewableAlertsByPurl.set(purl, [\n ...viewableAlerts,\n ...removedHiddenAlerts,\n ])\n }\n }\n\n const mentionedPurlsWithHiddenAlerts = new Set<string>()\n for (\n let i = 0,\n prevAboveTheFold = true,\n entries = Array.from(viewableAlertsByPurl.entries()),\n { length } = entries;\n i < length;\n i += 1\n ) {\n const { 0: purl, 1: alerts } = entries[i]!\n const lines = new Set<string>()\n for (const alert of alerts) {\n const { type } = alert\n const severity = alert.raw.severity ?? ''\n const attributes = [\n ...(severity\n ? [colors[ALERT_SEVERITY_COLOR[severity]](getSeverityLabel(severity))]\n : []),\n ...(alert.blocked ? [colors.bold(colors.red('blocked'))] : []),\n ...(alert.fixable ? ['fixable'] : []),\n ]\n const maybeAttributes = attributes.length\n ? ` ${colors.italic(`(${attributes.join('; ')})`)}`\n : ''\n // Based data from { pageProps: { alertTypes } } of:\n // https://socket.dev/_next/data/9a6db8224b68b6da0eb9f7dbb17aff7e51568ac2/en-US.json\n const info = (translations.alerts as any)[type]\n const title = info?.title ?? type\n const maybeDesc = info?.description ? ` - ${info.description}` : ''\n const content = `${title}${maybeAttributes}${maybeDesc}`\n // TODO: An added emoji seems to mis-align terminals sometimes.\n lines.add(` ${content}`)\n }\n const purlObj = getPurlObject(purl)\n const pkgName = resolvePackageName(purlObj)\n const hyperlink = format.hyperlink(\n pkgName,\n getSocketDevPackageOverviewUrl(purlObj.type, pkgName, purlObj.version),\n )\n const isAboveTheFold = aboveTheFoldPurls.has(purl)\n if (isAboveTheFold) {\n aboveTheFoldPurls.add(purl)\n output.write(`${i ? '\\n' : ''}${hyperlink}:\\n`)\n } else {\n output.write(`${prevAboveTheFold ? '\\n' : ''}${hyperlink}:\\n`)\n }\n for (const line of lines) {\n output.write(`${line}\\n`)\n }\n const hiddenAlerts = hiddenAlertsByPurl.get(purl) ?? []\n const { length: hiddenAlertsCount } = hiddenAlerts\n if (hiddenAlertsCount) {\n mentionedPurlsWithHiddenAlerts.add(purl)\n if (hiddenAlertsCount === 1) {\n output.write(\n ` ${colors.dim(`+1 Hidden ${getSeverityLabel(hiddenAlerts[0]!.raw.severity ?? 'low')} risk alert`)}\\n`,\n )\n } else {\n output.write(\n ` ${colors.dim(`+${hiddenAlertsCount} Hidden alerts ${colors.italic(getHiddenRisksDescription(getHiddenRiskCounts(hiddenAlerts)))}`)}\\n`,\n )\n }\n }\n prevAboveTheFold = isAboveTheFold\n }\n\n const additionalHiddenCount =\n hiddenAlertsByPurl.size - mentionedPurlsWithHiddenAlerts.size\n if (additionalHiddenCount) {\n const totalRiskCounts = {\n critical: 0,\n high: 0,\n middle: 0,\n low: 0,\n }\n for (const { 0: purl, 1: alerts } of hiddenAlertsByPurl.entries()) {\n if (mentionedPurlsWithHiddenAlerts.has(purl)) {\n continue\n }\n const riskCounts = getHiddenRiskCounts(alerts)\n totalRiskCounts.critical += riskCounts.critical\n totalRiskCounts.high += riskCounts.high\n totalRiskCounts.middle += riskCounts.middle\n totalRiskCounts.low += riskCounts.low\n }\n output.write(\n `${aboveTheFoldPurls.size ? '\\n' : ''}${colors.dim(`${aboveTheFoldPurls.size ? '+' : ''}${additionalHiddenCount} Packages with hidden alerts ${colors.italic(getHiddenRisksDescription(totalRiskCounts))}`)}\\n`,\n )\n }\n output.write('\\n')\n}\n","import semver from 'semver'\n\nimport { PackageURL } from '@socketregistry/packageurl-js'\n\nimport { stripPnpmPeerSuffix } from './pnpm.mts'\n\nexport function idToNpmPurl(id: string): string {\n return `pkg:npm/${id}`\n}\n\nexport function idToPurl(id: string, type: string): string {\n return `pkg:${type}/${id}`\n}\n\nexport function resolvePackageVersion(purlObj: PackageURL): string {\n const { version } = purlObj\n if (!version) {\n return ''\n }\n const { type } = purlObj\n return (\n semver.coerce(type === 'npm' ? stripPnpmPeerSuffix(version) : version)\n ?.version ?? ''\n )\n}\n","import { existsSync } from 'node:fs'\n\nimport yaml from 'js-yaml'\nimport semver from 'semver'\n\nimport { readFileUtf8 } from '@socketsecurity/registry/lib/fs'\nimport { isObjectObject } from '@socketsecurity/registry/lib/objects'\nimport { stripBom } from '@socketsecurity/registry/lib/strings'\n\nimport { idToNpmPurl } from './spec.mts'\n\nimport type { LockfileObject, PackageSnapshot } from '@pnpm/lockfile.fs'\nimport type { SemVer } from 'semver'\n\nexport function extractOverridesFromPnpmLockSrc(lockfileContent: any): string {\n let match\n if (typeof lockfileContent === 'string') {\n match = /^overrides:(?:\\r?\\n {2}.+)+(?:\\r?\\n)*/m.exec(lockfileContent)?.[0]\n }\n return match ?? ''\n}\n\nexport async function extractPurlsFromPnpmLockfile(\n lockfile: LockfileObject,\n): Promise<string[]> {\n const packages = lockfile?.packages ?? {}\n const seen = new Set<string>()\n const visit = (pkgPath: string) => {\n if (seen.has(pkgPath)) {\n return\n }\n const pkg = (packages as any)[pkgPath] as PackageSnapshot\n if (!pkg) {\n return\n }\n seen.add(pkgPath)\n const deps: { [name: string]: string } = {\n __proto__: null,\n ...pkg.dependencies,\n ...pkg.optionalDependencies,\n ...(pkg as any).devDependencies,\n }\n for (const depName in deps) {\n const ref = deps[depName]!\n const subKey = isPnpmDepPath(ref) ? ref : `/${depName}@${ref}`\n visit(subKey)\n }\n }\n for (const pkgPath of Object.keys(packages)) {\n visit(pkgPath)\n }\n return Array.from(seen).map(p =>\n idToNpmPurl(stripPnpmPeerSuffix(stripLeadingPnpmDepPathSlash(p))),\n )\n}\n\nexport function isPnpmDepPath(maybeDepPath: string): boolean {\n return maybeDepPath.length > 0 && maybeDepPath.charCodeAt(0) === 47 /*'/'*/\n}\n\nexport function parsePnpmLockfile(\n lockfileContent: unknown,\n): LockfileObject | null {\n let result\n if (typeof lockfileContent === 'string') {\n try {\n result = yaml.load(stripBom(lockfileContent))\n } catch {}\n }\n return isObjectObject(result) ? (result as LockfileObject) : null\n}\n\nexport function parsePnpmLockfileVersion(version: unknown): SemVer | null {\n try {\n return semver.coerce(version as string)\n } catch {}\n return null\n}\n\nexport async function readPnpmLockfile(\n lockfilePath: string,\n): Promise<string | null> {\n return existsSync(lockfilePath) ? await readFileUtf8(lockfilePath) : null\n}\n\nexport function stripLeadingPnpmDepPathSlash(depPath: string): string {\n return isPnpmDepPath(depPath) ? depPath.slice(1) : depPath\n}\n\nexport function stripPnpmPeerSuffix(depPath: string): string {\n const parenIndex = depPath.indexOf('(')\n const index = parenIndex === -1 ? depPath.indexOf('_') : parenIndex\n return index === -1 ? depPath : depPath.slice(0, index)\n}\n","import { arrayUnique } from '@socketsecurity/registry/lib/arrays'\nimport { debugDir } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { getOwn } from '@socketsecurity/registry/lib/objects'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { findSocketYmlSync } from './config.mts'\nimport { toFilterConfig } from './filter-config.mts'\nimport { extractPurlsFromPnpmLockfile } from './pnpm.mts'\nimport { getPublicApiToken, setupSdk } from './sdk.mts'\nimport { addArtifactToAlertsMap } from './socket-package-alert.mts'\n\nimport type { CompactSocketArtifact } from './alert/artifact.mts'\nimport type { AlertFilter, AlertsByPurl } from './socket-package-alert.mts'\nimport type { LockfileObject } from '@pnpm/lockfile.fs'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nexport type GetAlertsMapFromPnpmLockfileOptions = {\n apiToken?: string | undefined\n consolidate?: boolean | undefined\n filter?: AlertFilter | undefined\n overrides?: { [key: string]: string } | undefined\n nothrow?: boolean | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function getAlertsMapFromPnpmLockfile(\n lockfile: LockfileObject,\n options?: GetAlertsMapFromPnpmLockfileOptions | undefined,\n): Promise<AlertsByPurl> {\n const purls = await extractPurlsFromPnpmLockfile(lockfile)\n return await getAlertsMapFromPurls(purls, {\n overrides: lockfile.overrides,\n ...options,\n })\n}\n\nexport type GetAlertsMapFromPurlsOptions = {\n apiToken?: string | undefined\n consolidate?: boolean | undefined\n filter?: AlertFilter | undefined\n onlyFixable?: boolean | undefined\n overrides?: { [key: string]: string } | undefined\n nothrow?: boolean | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function getAlertsMapFromPurls(\n purls: string[] | readonly string[],\n options?: GetAlertsMapFromPurlsOptions | undefined,\n): Promise<AlertsByPurl> {\n const uniqPurls = arrayUnique(purls)\n debugDir('silly', { purls: uniqPurls })\n\n let { length: remaining } = uniqPurls\n const alertsByPurl: AlertsByPurl = new Map()\n\n if (!remaining) {\n return alertsByPurl\n }\n\n const opts = {\n __proto__: null,\n consolidate: false,\n nothrow: false,\n ...options,\n filter: toFilterConfig(getOwn(options, 'filter')),\n } as GetAlertsMapFromPurlsOptions & { filter: AlertFilter }\n\n if (opts.onlyFixable) {\n opts.filter.fixable = true\n }\n\n const { apiToken = getPublicApiToken(), spinner } = opts\n\n const getText = () => `Looking up data for ${remaining} packages`\n\n spinner?.start(getText())\n\n const sockSdkCResult = await setupSdk({ apiToken })\n if (!sockSdkCResult.ok) {\n spinner?.stop()\n throw new Error('Auth error: Run `socket login` first')\n }\n const sockSdk = sockSdkCResult.data\n const socketYml = findSocketYmlSync()?.parsed\n\n const alertsMapOptions = {\n overrides: opts.overrides,\n consolidate: opts.consolidate,\n filter: opts.filter,\n socketYml,\n spinner,\n }\n\n for await (const batchResult of sockSdk.batchPackageStream(\n {\n components: uniqPurls.map(purl => ({ purl })),\n },\n {\n queryParams: {\n alerts: 'true',\n compact: 'true',\n ...(opts.onlyFixable ? { fixable: 'true ' } : {}),\n ...(Array.isArray(opts.filter.actions)\n ? { actions: opts.filter.actions.join(',') }\n : {}),\n },\n },\n )) {\n if (batchResult.success) {\n const artifact = batchResult.data as CompactSocketArtifact\n await addArtifactToAlertsMap(artifact, alertsByPurl, alertsMapOptions)\n } else if (!opts.nothrow) {\n spinner?.stop()\n if (isNonEmptyString(batchResult.error)) {\n throw new Error(batchResult.error)\n }\n const statusCode = batchResult.status ?? 'unknown'\n throw new Error(\n `Socket API server error (${statusCode}): No status message`,\n )\n } else {\n spinner?.stop()\n logger.fail(\n `Received a ${batchResult.status} response from Socket API which we consider a permanent failure:`,\n batchResult.error,\n batchResult.cause ? `( ${batchResult.cause} )` : '',\n )\n debugDir('inspect', { batchResult })\n break\n }\n remaining -= 1\n if (remaining > 0) {\n spinner?.start(getText())\n }\n }\n\n spinner?.stop()\n\n return alertsByPurl\n}\n","import npmPackageArg from 'npm-package-arg'\n\nexport type {\n AliasResult,\n FileResult,\n HostedGit,\n HostedGitResult,\n RegistryResult,\n Result,\n URLResult,\n} from 'npm-package-arg'\n\nexport function npa(\n ...args: Parameters<typeof npmPackageArg>\n): ReturnType<typeof npmPackageArg> | null {\n try {\n return Reflect.apply(npmPackageArg, undefined, args)\n } catch {}\n return null\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nimport { remove } from '@socketsecurity/registry/lib/fs'\nimport { parallelEach } from '@socketsecurity/registry/lib/streams'\n\nimport constants from '../constants.mts'\nimport { globStreamNodeModules } from './glob.mts'\n\nexport async function removeNodeModules(cwd = process.cwd()) {\n const stream = await globStreamNodeModules(cwd)\n await parallelEach(stream, p => remove(p, { force: true, recursive: true }), {\n concurrency: 8,\n })\n}\n\nexport type FindUpOptions = {\n cwd?: string | undefined\n signal?: AbortSignal | undefined\n}\n\nexport async function findUp(\n name: string | string[],\n { cwd = process.cwd(), signal = constants.abortSignal }: FindUpOptions,\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 if (signal?.aborted) {\n return undefined\n }\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","import { isDebug } from '@socketsecurity/registry/lib/debug'\nimport {\n isNpmAuditFlag,\n isNpmFundFlag,\n isNpmLoglevelFlag,\n isNpmProgressFlag,\n resolveBinPathSync,\n} from '@socketsecurity/registry/lib/npm'\nimport { getOwn, isObject } from '@socketsecurity/registry/lib/objects'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants.mts'\nimport { getNpmBinPath } from '../../utils/npm-paths.mts'\n\nimport type { SpawnResult } from '@socketsecurity/registry/lib/spawn'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\ntype SpawnOption = Exclude<Parameters<typeof spawn>[2], undefined>\n\nexport type ShadowNpmInstallOptions = SpawnOption & {\n agentExecPath?: string | undefined\n args?: string[] | readonly string[] | undefined\n ipc?: object | undefined\n spinner?: Spinner | undefined\n}\n\nexport function shadowNpmInstall(\n options?: ShadowNpmInstallOptions,\n): SpawnResult<string, Record<any, any> | undefined> {\n const {\n agentExecPath = getNpmBinPath(),\n args = [],\n ipc,\n spinner,\n ...spawnOpts\n } = { __proto__: null, ...options } as ShadowNpmInstallOptions\n const useDebug = isDebug('stdio')\n const terminatorPos = args.indexOf('--')\n const rawBinArgs = terminatorPos === -1 ? args : args.slice(0, terminatorPos)\n const binArgs = rawBinArgs.filter(\n a => !isNpmAuditFlag(a) && !isNpmFundFlag(a) && !isNpmProgressFlag(a),\n )\n const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos)\n const progressArg = rawBinArgs.findLast(isNpmProgressFlag) !== '--no-progress'\n const isSilent = !useDebug && !binArgs.some(isNpmLoglevelFlag)\n const logLevelArgs = isSilent ? ['--loglevel', 'silent'] : []\n const useIpc = isObject(ipc)\n\n // Include 'ipc' in the spawnOpts.stdio when an options.ipc object is provided.\n // See https://github.com/nodejs/node/blob/v23.6.0/lib/child_process.js#L161-L166\n // and https://github.com/nodejs/node/blob/v23.6.0/lib/internal/child_process.js#L238.\n let stdio = getOwn(spawnOpts, 'stdio')\n if (typeof stdio === 'string') {\n stdio = useIpc ? [stdio, stdio, stdio, 'ipc'] : [stdio, stdio, stdio]\n } else if (Array.isArray(stdio)) {\n if (useIpc && !stdio.includes('ipc')) {\n stdio = stdio.concat('ipc')\n }\n } else {\n stdio = useIpc ? ['pipe', 'pipe', 'pipe', 'ipc'] : 'pipe'\n }\n\n const spawnPromise = spawn(\n constants.execPath,\n [\n ...constants.nodeNoWarningsFlags,\n ...constants.nodeDebugFlags,\n ...constants.nodeHardenFlags,\n ...constants.nodeMemoryFlags,\n ...(constants.ENV.INLINED_SOCKET_CLI_SENTRY_BUILD\n ? ['--require', constants.instrumentWithSentryPath]\n : []),\n '--require',\n constants.shadowNpmInjectPath,\n resolveBinPathSync(agentExecPath),\n 'install',\n // Avoid code paths for 'audit' and 'fund'.\n '--no-audit',\n '--no-fund',\n // Add '--no-progress' to fix input being swallowed by the npm spinner.\n '--no-progress',\n // Add '--loglevel=silent' if a loglevel flag is not provided and the\n // SOCKET_CLI_DEBUG environment variable is not truthy.\n ...logLevelArgs,\n ...binArgs,\n ...otherArgs,\n ],\n {\n ...spawnOpts,\n env: {\n ...process.env,\n ...constants.processEnv,\n ...getOwn(spawnOpts, 'env'),\n },\n spinner,\n stdio,\n },\n )\n\n if (useIpc) {\n spawnPromise.process.send({\n [constants.SOCKET_IPC_HANDSHAKE]: {\n [constants.SOCKET_CLI_SHADOW_BIN]: 'npm',\n [constants.SOCKET_CLI_SHADOW_PROGRESS]: progressArg,\n ...ipc,\n },\n })\n }\n\n return spawnPromise\n}\n","import { getOwn } from '@socketsecurity/registry/lib/objects'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\nimport { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nimport constants from '../constants.mts'\nimport { cmdFlagsToString } from './cmd.mts'\nimport { shadowNpmInstall } from '../shadow/npm/install.mts'\n\nimport type { EnvDetails } from './package-environment.mts'\n\ntype SpawnOption = Exclude<Parameters<typeof spawn>[2], undefined>\n\nexport type AgentInstallOptions = SpawnOption & {\n args?: string[] | readonly string[] | undefined\n spinner?: Spinner | undefined\n}\n\nexport type AgentSpawnResult = ReturnType<typeof spawn>\n\nexport function runAgentInstall(\n pkgEnvDetails: EnvDetails,\n options?: AgentInstallOptions | undefined,\n): AgentSpawnResult {\n const { agent, agentExecPath } = pkgEnvDetails\n const isNpm = agent === 'npm'\n const isPnpm = agent === 'pnpm'\n // All package managers support the \"install\" command.\n if (isNpm) {\n return shadowNpmInstall({\n agentExecPath,\n ...options,\n })\n }\n const {\n args = [],\n spinner,\n ...spawnOpts\n } = { __proto__: null, ...options } as AgentInstallOptions\n const skipNodeHardenFlags = isPnpm && pkgEnvDetails.agentVersion.major < 11\n return spawn(agentExecPath, ['install', ...args], {\n shell: constants.WIN32,\n spinner,\n stdio: 'inherit',\n ...spawnOpts,\n env: {\n ...process.env,\n ...constants.processEnv,\n NODE_OPTIONS: cmdFlagsToString([\n ...(skipNodeHardenFlags ? [] : constants.nodeHardenFlags),\n ...constants.nodeNoWarningsFlags,\n ]),\n ...getOwn(spawnOpts, 'env'),\n },\n })\n}\n","import NpmConfig from '@npmcli/config'\nimport {\n definitions as npmConfigDefinitions,\n flatten as npmConfigFlatten,\n shorthands as npmConfigShorthands,\n // @ts-ignore: TypeScript types unavailable.\n} from '@npmcli/config/lib/definitions'\n\nimport { getNpmDirPath } from './npm-paths.mts'\n\nimport type { ArboristOptions } from '../shadow/npm/arborist/types.mts'\nimport type { SemVer } from 'semver'\n\nexport type NpmConfigOptions = {\n cwd?: string | undefined\n env?: NodeJS.ProcessEnv | undefined\n execPath?: string | undefined\n nodeVersion?: string | undefined\n npmCommand?: string | undefined\n npmPath?: string | undefined\n npmVersion?: SemVer | string | undefined\n platform?: NodeJS.Platform | undefined\n}\n\nexport async function getNpmConfig(\n options?: NpmConfigOptions | undefined,\n): Promise<ArboristOptions> {\n const {\n cwd = process.cwd(),\n env = process.env,\n execPath = process.execPath,\n nodeVersion = process.version,\n npmCommand = 'install',\n npmPath = getNpmDirPath(),\n npmVersion,\n platform = process.platform,\n } = { __proto__: null, ...options } as NpmConfigOptions\n const config = new NpmConfig({\n argv: [],\n cwd,\n definitions: npmConfigDefinitions,\n execPath,\n env: { ...env },\n flatten: npmConfigFlatten,\n npmPath,\n platform,\n shorthands: npmConfigShorthands,\n })\n await config.load()\n const flatConfig = { __proto__: null, ...config.flat } as ArboristOptions\n\n if (nodeVersion) {\n flatConfig.nodeVersion = nodeVersion\n }\n if (npmCommand) {\n flatConfig.npmCommand = npmCommand\n }\n if (npmVersion) {\n flatConfig.npmVersion = npmVersion.toString()\n }\n return flatConfig\n}\n","import { existsSync } from 'node:fs'\n\nimport { readFileUtf8 } from '@socketsecurity/registry/lib/fs'\n\nexport async function readLockfile(\n lockfilePath: string,\n): Promise<string | null> {\n return existsSync(lockfilePath) ? await readFileUtf8(lockfilePath) : null\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport browserslist from 'browserslist'\nimport semver from 'semver'\nimport which from 'which'\n\nimport { parse as parseBunLockb } from '@socketregistry/hyrious__bun.lockb/index.cjs'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { readFileBinary, readFileUtf8 } from '@socketsecurity/registry/lib/fs'\nimport { Logger } from '@socketsecurity/registry/lib/logger'\nimport { readPackageJson } from '@socketsecurity/registry/lib/packages'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { cmdPrefixMessage } from './cmd.mts'\nimport { findUp } from './fs.mts'\nimport constants from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\nimport type { EditablePackageJson } from '@socketsecurity/registry/lib/packages'\nimport type { SemVer } from 'semver'\n\nconst {\n BINARY_LOCK_EXT,\n BUN,\n HIDDEN_PACKAGE_LOCK_JSON,\n LOCK_EXT,\n NPM,\n NPM_BUGGY_OVERRIDES_PATCHED_VERSION,\n PACKAGE_JSON,\n PNPM,\n VLT,\n YARN,\n YARN_BERRY,\n YARN_CLASSIC,\n} = constants\n\nexport const AGENTS = [BUN, NPM, PNPM, YARN_BERRY, YARN_CLASSIC, VLT] as const\n\nconst binByAgent = new Map<Agent, string>([\n [BUN, BUN],\n [NPM, NPM],\n [PNPM, PNPM],\n [YARN_BERRY, YARN],\n [YARN_CLASSIC, YARN],\n [VLT, VLT],\n])\n\nexport type Agent = (typeof AGENTS)[number]\n\nexport type EnvBase = {\n agent: Agent\n agentExecPath: string\n agentSupported: boolean\n features: {\n // Fixed by https://github.com/npm/cli/pull/8089.\n // Landed in npm v11.2.0.\n npmBuggyOverrides: boolean\n }\n nodeSupported: boolean\n nodeVersion: SemVer\n npmExecPath: string\n pkgRequirements: {\n agent: string\n node: string\n }\n pkgSupports: {\n agent: boolean\n node: boolean\n }\n}\n\nexport type EnvDetails = Readonly<\n Remap<\n EnvBase & {\n agentVersion: SemVer\n editablePkgJson: EditablePackageJson\n lockName: string\n lockPath: string\n lockSrc: string\n pkgPath: string\n }\n >\n>\n\nexport type DetectAndValidateOptions = {\n cmdName?: string | undefined\n logger?: Logger | undefined\n prod?: boolean | undefined\n}\n\nexport type DetectOptions = {\n cwd?: string | undefined\n onUnknown?: (pkgManager: string | undefined) => void\n}\n\nexport type PartialEnvDetails = Readonly<\n Remap<\n EnvBase & {\n agentVersion: SemVer | undefined\n editablePkgJson: EditablePackageJson | undefined\n lockName: string | undefined\n lockPath: string | undefined\n lockSrc: string | undefined\n pkgPath: string | undefined\n }\n >\n>\n\nexport type ReadLockFile =\n | ((lockPath: string) => Promise<string | undefined>)\n | ((lockPath: string, agentExecPath: string) => Promise<string | undefined>)\n | ((\n lockPath: string,\n agentExecPath: string,\n cwd: string,\n ) => Promise<string | undefined>)\n\nconst readLockFileByAgent: Map<Agent, ReadLockFile> = (() => {\n function wrapReader<T extends (...args: any[]) => Promise<any>>(\n reader: T,\n ): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>> | undefined> {\n return async (...args: any[]): Promise<any> => {\n try {\n return await reader(...args)\n } catch {}\n return undefined\n }\n }\n\n const binaryReader = wrapReader(readFileBinary)\n\n const defaultReader = wrapReader(\n async (lockPath: string) => await readFileUtf8(lockPath),\n )\n\n return new Map([\n [\n BUN,\n wrapReader(\n async (\n lockPath: string,\n agentExecPath: string,\n cwd = process.cwd(),\n ) => {\n const ext = path.extname(lockPath)\n if (ext === LOCK_EXT) {\n return await defaultReader(lockPath)\n }\n if (ext === BINARY_LOCK_EXT) {\n const lockBuffer = await binaryReader(lockPath)\n if (lockBuffer) {\n try {\n return parseBunLockb(lockBuffer)\n } catch {}\n }\n // To print a Yarn lockfile to your console without writing it to disk\n // use `bun bun.lockb`.\n // https://bun.sh/guides/install/yarnlock\n return (\n await spawn(agentExecPath, [lockPath], {\n cwd,\n shell: constants.WIN32,\n })\n ).stdout\n }\n return undefined\n },\n ),\n ],\n [NPM, defaultReader],\n [PNPM, defaultReader],\n [VLT, defaultReader],\n [YARN_BERRY, defaultReader],\n [YARN_CLASSIC, defaultReader],\n ])\n})()\n\n// The order of LOCKS properties IS significant as it affects iteration order.\nconst LOCKS: Record<string, Agent> = {\n [`bun${LOCK_EXT}`]: BUN,\n [`bun${BINARY_LOCK_EXT}`]: BUN,\n // If both package-lock.json and npm-shrinkwrap.json are present in the root\n // of a project, npm-shrinkwrap.json will take precedence and package-lock.json\n // will be ignored.\n // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#package-lockjson-vs-npm-shrinkwrapjson\n 'npm-shrinkwrap.json': NPM,\n 'package-lock.json': NPM,\n 'pnpm-lock.yaml': PNPM,\n 'pnpm-lock.yml': PNPM,\n [`yarn${LOCK_EXT}`]: YARN_CLASSIC,\n 'vlt-lock.json': VLT,\n // Lastly, look for a hidden lock file which is present if .npmrc has package-lock=false:\n // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#hidden-lockfiles\n //\n // Unlike the other LOCKS keys this key contains a directory AND filename so\n // it has to be handled differently.\n 'node_modules/.package-lock.json': NPM,\n}\n\nasync function getAgentExecPath(agent: Agent): Promise<string> {\n const binName = binByAgent.get(agent)!\n if (binName === NPM) {\n return constants.npmExecPath\n }\n return (await which(binName, { nothrow: true })) ?? binName\n}\n\nasync function getAgentVersion(\n agent: Agent,\n agentExecPath: string,\n cwd: string,\n): Promise<SemVer | undefined> {\n let result\n const quotedCmd = `\\`${agent} --version\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n result =\n // Coerce version output into a valid semver version by passing it through\n // semver.coerce which strips leading v's, carets (^), comparators (<,<=,>,>=,=),\n // and tildes (~).\n semver.coerce(\n // All package managers support the \"--version\" flag.\n (\n await spawn(agentExecPath, ['--version'], {\n cwd,\n shell: constants.WIN32,\n })\n ).stdout,\n ) ?? undefined\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n return result\n}\n\nexport async function detectPackageEnvironment({\n cwd = process.cwd(),\n onUnknown,\n}: DetectOptions = {}): Promise<EnvDetails | PartialEnvDetails> {\n let lockPath = await findUp(Object.keys(LOCKS), { cwd })\n let lockName = lockPath ? path.basename(lockPath) : undefined\n const isHiddenLockFile = lockName === HIDDEN_PACKAGE_LOCK_JSON\n const pkgJsonPath = lockPath\n ? path.resolve(\n lockPath,\n `${isHiddenLockFile ? '../' : ''}../${PACKAGE_JSON}`,\n )\n : await findUp(PACKAGE_JSON, { cwd })\n const pkgPath =\n pkgJsonPath && existsSync(pkgJsonPath)\n ? path.dirname(pkgJsonPath)\n : undefined\n const editablePkgJson = pkgPath\n ? await readPackageJson(pkgPath, { editable: true })\n : undefined\n // Read Corepack `packageManager` field in package.json:\n // https://nodejs.org/api/packages.html#packagemanager\n const pkgManager = isNonEmptyString(editablePkgJson?.content?.packageManager)\n ? editablePkgJson.content.packageManager\n : undefined\n\n let agent: Agent | undefined\n if (pkgManager) {\n // A valid \"packageManager\" field value is \"<package manager name>@<version>\".\n // https://nodejs.org/api/packages.html#packagemanager\n const atSignIndex = pkgManager.lastIndexOf('@')\n if (atSignIndex !== -1) {\n const name = pkgManager.slice(0, atSignIndex) as Agent\n const version = pkgManager.slice(atSignIndex + 1)\n if (version && AGENTS.includes(name)) {\n agent = name\n }\n }\n }\n if (\n agent === undefined &&\n !isHiddenLockFile &&\n typeof pkgJsonPath === 'string' &&\n typeof lockName === 'string'\n ) {\n agent = LOCKS[lockName] as Agent\n }\n if (agent === undefined) {\n agent = NPM\n onUnknown?.(pkgManager)\n }\n const agentExecPath = await getAgentExecPath(agent)\n const agentVersion = await getAgentVersion(agent, agentExecPath, cwd)\n if (agent === YARN_CLASSIC && (agentVersion?.major ?? 0) > 1) {\n agent = YARN_BERRY\n }\n const { maintainedNodeVersions } = constants\n const minSupportedAgentVersion = constants.minimumVersionByAgent.get(agent)!\n const minSupportedNodeMajor = semver.major(maintainedNodeVersions.last)\n const minSupportedNodeVersion = `${minSupportedNodeMajor}.0.0`\n const minSupportedNodeRange = `>=${minSupportedNodeMajor}`\n const nodeVersion = semver.coerce(process.version)!\n let lockSrc: string | undefined\n let pkgAgentRange: string | undefined\n let pkgNodeRange: string | undefined\n let pkgMinAgentVersion = minSupportedAgentVersion\n let pkgMinNodeVersion = minSupportedNodeVersion\n if (editablePkgJson?.content) {\n const { engines } = editablePkgJson.content\n const engineAgentRange = engines?.[agent]\n const engineNodeRange = engines?.['node']\n if (isNonEmptyString(engineAgentRange)) {\n pkgAgentRange = engineAgentRange\n // Roughly check agent range as semver.coerce will strip leading\n // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).\n const coerced = semver.coerce(pkgAgentRange)\n if (coerced && semver.lt(coerced, pkgMinAgentVersion)) {\n pkgMinAgentVersion = coerced.version\n }\n }\n if (isNonEmptyString(engineNodeRange)) {\n pkgNodeRange = engineNodeRange\n // Roughly check Node range as semver.coerce will strip leading\n // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).\n const coerced = semver.coerce(pkgNodeRange)\n if (coerced && semver.lt(coerced, pkgMinNodeVersion)) {\n pkgMinNodeVersion = coerced.version\n }\n }\n const browserslistQuery = editablePkgJson.content['browserslist'] as\n | string[]\n | undefined\n if (Array.isArray(browserslistQuery)) {\n // List Node targets in ascending version order.\n const browserslistNodeTargets = browserslist(browserslistQuery)\n .filter(v => /^node /i.test(v))\n .map(v => v.slice(5 /*'node '.length*/))\n .sort(naturalCompare)\n if (browserslistNodeTargets.length) {\n // browserslistNodeTargets[0] is the lowest Node target version.\n const coerced = semver.coerce(browserslistNodeTargets[0])\n if (coerced && semver.lt(coerced, pkgMinNodeVersion)) {\n pkgMinNodeVersion = coerced.version\n }\n }\n }\n lockSrc =\n typeof lockPath === 'string'\n ? await readLockFileByAgent.get(agent)!(lockPath, agentExecPath, cwd)\n : undefined\n } else {\n lockName = undefined\n lockPath = undefined\n }\n\n // Does the system agent version meet our minimum supported agent version?\n const agentSupported =\n !!agentVersion &&\n semver.satisfies(agentVersion, `>=${minSupportedAgentVersion}`)\n // Does the system Node version meet our minimum supported Node version?\n const nodeSupported = semver.satisfies(nodeVersion, minSupportedNodeRange)\n\n const npmExecPath =\n agent === NPM ? agentExecPath : await getAgentExecPath(NPM)\n const npmBuggyOverrides =\n agent === NPM &&\n !!agentVersion &&\n semver.lt(agentVersion, NPM_BUGGY_OVERRIDES_PATCHED_VERSION)\n\n const pkgMinAgentRange = `>=${pkgMinAgentVersion}`\n const pkgMinNodeRange = `>=${semver.major(pkgMinNodeVersion)}`\n\n return {\n agent,\n agentExecPath,\n agentSupported,\n agentVersion,\n editablePkgJson,\n features: { npmBuggyOverrides },\n lockName,\n lockPath,\n lockSrc,\n nodeSupported,\n nodeVersion,\n npmExecPath,\n pkgPath,\n pkgRequirements: {\n agent: pkgAgentRange ?? pkgMinAgentRange,\n node: pkgNodeRange ?? pkgMinNodeRange,\n },\n pkgSupports: {\n // Does our minimum supported agent version meet the package's requirements?\n agent: semver.satisfies(minSupportedAgentVersion, pkgMinAgentRange),\n // Does our supported Node versions meet the package's requirements?\n node: maintainedNodeVersions.some(v =>\n semver.satisfies(v, pkgMinNodeRange),\n ),\n },\n }\n}\n\nexport async function detectAndValidatePackageEnvironment(\n cwd: string,\n options?: DetectAndValidateOptions | undefined,\n): Promise<CResult<EnvDetails>> {\n const {\n cmdName = '',\n logger,\n prod,\n } = {\n __proto__: null,\n ...options,\n } as DetectAndValidateOptions\n const details = await detectPackageEnvironment({\n cwd,\n onUnknown(pkgManager: string | undefined) {\n logger?.warn(\n cmdPrefixMessage(\n cmdName,\n `Unknown package manager${pkgManager ? ` ${pkgManager}` : ''}, defaulting to npm`,\n ),\n )\n },\n })\n const { agent, nodeVersion, pkgRequirements } = details\n const agentVersion = details.agentVersion ?? 'unknown'\n if (!details.agentSupported) {\n const minVersion = constants.minimumVersionByAgent.get(agent)!\n return {\n ok: false,\n message: 'Version mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Requires ${agent} >=${minVersion}. Current version: ${agentVersion}.`,\n ),\n }\n }\n if (!details.nodeSupported) {\n const minVersion = constants.maintainedNodeVersions.last\n return {\n ok: false,\n message: 'Version mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Requires Node >=${minVersion}. Current version: ${nodeVersion}.`,\n ),\n }\n }\n if (!details.pkgSupports.agent) {\n return {\n ok: false,\n message: 'Engine mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Package engine \"${agent}\" requires ${pkgRequirements.agent}. Current version: ${agentVersion}`,\n ),\n }\n }\n if (!details.pkgSupports.node) {\n return {\n ok: false,\n message: 'Version mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Package engine \"node\" requires ${pkgRequirements.node}. Current version: ${nodeVersion}`,\n ),\n }\n }\n const lockName = details.lockName ?? 'lock file'\n if (details.lockName === undefined || details.lockSrc === undefined) {\n return {\n ok: false,\n message: 'Missing lockfile',\n cause: cmdPrefixMessage(cmdName, `No ${lockName} found`),\n }\n }\n if (details.lockSrc.trim() === '') {\n return {\n ok: false,\n message: 'Empty lockfile',\n cause: cmdPrefixMessage(cmdName, `${lockName} is empty`),\n }\n }\n if (details.pkgPath === undefined) {\n return {\n ok: false,\n message: 'Missing package.json',\n cause: cmdPrefixMessage(cmdName, `No ${PACKAGE_JSON} found`),\n }\n }\n if (prod && (agent === BUN || agent === YARN_BERRY)) {\n return {\n ok: false,\n message: 'Bad input',\n cause: cmdPrefixMessage(\n cmdName,\n `--prod not supported for ${agent}${agentVersion ? `@${agentVersion}` : ''}`,\n ),\n }\n }\n if (\n details.lockPath &&\n path.relative(cwd, details.lockPath).startsWith('.')\n ) {\n // Note: In tests we return <redacted> because otherwise snapshots will fail.\n logger?.warn(\n cmdPrefixMessage(\n cmdName,\n `Package ${lockName} found at ${constants.ENV.VITEST ? constants.REDACTED : details.lockPath}`,\n ),\n )\n }\n return { ok: true, data: details as EnvDetails }\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nimport constants from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\n\nexport const COMPLETION_CMD_PREFIX = 'complete -F _socket_completion'\n\nexport function getCompletionSourcingCommand(): CResult<string> {\n // Note: this is exported to distPath in .config/rollup.dist.config.mjs\n const completionScriptExportPath = path.join(\n constants.distPath,\n 'socket-completion.bash',\n )\n\n if (!fs.existsSync(completionScriptExportPath)) {\n return {\n ok: false,\n message: 'Tab Completion script not found',\n cause: `Expected to find completion script at \\`${completionScriptExportPath}\\` but it was not there`,\n }\n }\n\n return { ok: true, data: `source ${completionScriptExportPath}` }\n}\n\nexport function getBashrcDetails(targetCommandName: string): CResult<{\n completionCommand: string\n sourcingCommand: string\n toAddToBashrc: string\n targetName: string\n targetPath: string\n}> {\n const sourcingCommand = getCompletionSourcingCommand()\n if (!sourcingCommand.ok) {\n return sourcingCommand\n }\n\n const { socketAppDataPath } = constants\n if (!socketAppDataPath) {\n return {\n ok: false,\n message: 'Could not determine config directory',\n cause: 'Failed to get config path',\n }\n }\n\n // _socket_completion is the function defined in our completion bash script\n const completionCommand = `${COMPLETION_CMD_PREFIX} ${targetCommandName}`\n\n // Location of completion script in config after installing\n const completionScriptPath = path.join(\n path.dirname(socketAppDataPath),\n 'completion',\n 'socket-completion.bash',\n )\n\n const bashrcContent = `# Socket CLI completion for \"${targetCommandName}\"\nif [ -f \"${completionScriptPath}\" ]; then\n # Load the tab completion script\n source \"${completionScriptPath}\"\n # Tell bash to use this function for tab completion of this function\n ${completionCommand}\nfi\n`\n\n return {\n ok: true,\n data: {\n sourcingCommand: sourcingCommand.data,\n completionCommand,\n toAddToBashrc: bashrcContent,\n targetName: targetCommandName,\n targetPath: completionScriptPath,\n },\n }\n}\n","import { setTimeout as wait } from 'node:timers/promises'\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport constants from '../constants.mts'\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 debugFn('notice', 'send: exception to Sentry')\n return Sentry.captureException(exception, hint) as string\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 type { EcosystemString } from '@socketsecurity/registry'\nimport type { components } from '@socketsecurity/sdk/types/api'\n\nexport type PURL_Type = components['schemas']['SocketPURL_Type']\n\ntype ExpectNever<T extends never> = T\n\ntype MissingInEcosystemString = Exclude<PURL_Type, EcosystemString>\ntype ExtraInEcosystemString = Exclude<EcosystemString, PURL_Type>\n\nexport type _Check_EcosystemString_has_all_purl_types =\n ExpectNever<MissingInEcosystemString>\nexport type _Check_EcosystemString_has_no_extras =\n ExpectNever<ExtraInEcosystemString>\n\nexport const ALL_ECOSYSTEMS = [\n 'apk',\n 'bitbucket',\n 'cargo',\n 'chrome',\n 'cocoapods',\n 'composer',\n 'conan',\n 'conda',\n 'cran',\n 'deb',\n 'docker',\n 'gem',\n 'generic',\n 'github',\n 'golang',\n 'hackage',\n 'hex',\n 'huggingface',\n 'maven',\n 'mlflow',\n 'npm',\n 'nuget',\n 'oci',\n 'pub',\n 'pypi',\n 'qpkg',\n 'rpm',\n 'swift',\n 'swid',\n 'unknown',\n] as const satisfies readonly PURL_Type[]\n\ntype AllEcosystemsUnion = (typeof ALL_ECOSYSTEMS)[number]\ntype MissingInAllEcosystems = Exclude<PURL_Type, AllEcosystemsUnion>\ntype ExtraInAllEcosystems = Exclude<AllEcosystemsUnion, PURL_Type>\n\nexport type _Check_ALL_ECOSYSTEMS_has_all_purl_types =\n ExpectNever<MissingInAllEcosystems>\nexport type _Check_ALL_ECOSYSTEMS_has_no_extras =\n ExpectNever<ExtraInAllEcosystems>\n\nexport const ALL_SUPPORTED_ECOSYSTEMS = new Set<string>(ALL_ECOSYSTEMS)\n\nexport function getEcosystemChoicesForMeow(): string[] {\n return [...ALL_ECOSYSTEMS]\n}\n\nexport function isValidEcosystem(value: string): value is PURL_Type {\n return ALL_SUPPORTED_ECOSYSTEMS.has(value)\n}\n\nexport function parseEcosystems(\n value: string | string[] | undefined,\n): PURL_Type[] {\n if (!value) {\n return []\n }\n const values =\n typeof value === 'string'\n ? value.split(',').map(v => v.trim().toLowerCase())\n : value.map(v => String(v).toLowerCase())\n\n return values.filter(isValidEcosystem)\n}\n"],"names":["socketAppDataPath","logger","updateConfigValue","mkdirSync","recursive","ok","data","yml","path","parsed","error","prevDir","exports","debugFn","message","cause","_readOnlyConfig","_cachedConfig","localConfig","wasDeleted","_pendingSave","writeFileSync","_defaultToken","__proto__","apiProxy","agent","proxy","baseUrl","timeout","name","version","homepage","spinner","socketSdkErrorResult","sdkResult","method","headers","Authorization","result","body","mw2","lines","cols","length","cws","msg","_requirements","indent","keyPrefix","padName","bestScore","bestMatch","matrix","commandOrAliasName","type","hidden","description","argv","allowUnknownFlags","autoHelp","autoVersion","booleanDefault","configOverrideResult","emitBanner","parentName","Object","commands","help","cli2","collectUnknownFlags","importMeta","cli","console","process","meow","REDACTED","numeric","style","desc","sdkOpts","organizations","value","choices","orgSlug","GITHUB_REF_TYPE","cwd","info","remoteUrl","gitSymbolicRefResult","quotedCmd","gitRevParseResult","stdio","email","user","gitConfigResult","lsRemoteResult","gitDiffResult","owner","repo","parsedGitRemoteUrlCache","namespace","keys","workspacePatterns","throws","ignores","absolute","ignore","concurrency","hasNegatedPattern","dot","filtered","onlyDirectories","all","nothrow","shadowBinPath","shadowIndex","theBinPath","WIN32","thePath","config","socketConfig","_npmBinPath","_npmBinPathDetails","_npmDirPath","_npxBinPath","_npxBinPathDetails","i","env","SOCKET_CLI_VERSION","mixinsEnv","spawnPromise","ipc","getDefaultSocketJson","obj","json","sockJson","cve","remove","upgrade","critical","high","middle","low","header","hyperlink","fallback","fallbackToUrl","normalized","raw","_translations","none","descriptions","consolidate","socketYml","blocked","upgradable","highestForCve","alert","unfixableAlerts","highestForUpgrade","sockPkgAlerts","alertsByPurl","severity","sockPkgAlertsLoop","infoByPartialPurl","infos","key","vulnerableVersionRange","hideAt","hiddenAlerts","viewableAlerts","viewableAlertsByPurl","aboveTheFoldPurls","removedHiddenAlerts","hiddenAlertsByPurl","output","mentionedPurlsWithHiddenAlerts","prevAboveTheFold","totalRiskCounts","seen","purls","opts","apiToken","components","purl","queryParams","alerts","compact","fixable","batchResult","remaining","force","root","dir","args","agentExecPath","NODE_OPTIONS","npmCommand","definitions","flatten","shorthands","flatConfig","YARN_CLASSIC","semver","onUnknown","editable","maintainedNodeVersions","engines","pkgAgentRange","pkgNodeRange","lockName","lockPath","features","npmBuggyOverrides","pkgRequirements","pkgSupports","node","cmdName","prod","toAddToBashrc","targetName","targetPath","getSentry","constructor"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA;AAEA;AAsBA;AAGA;AAEA;;AAEI;;;AAEQA;AAAkB;AAC1B;AACE;AACA;;;AAME;AACEC;AACF;AACA;AACA;AACA;AACE;;AAEAC;AACF;AACF;AACEC;AAA6CC;AAAgB;AAC/D;AACF;AACF;AACA;AACF;AAEA;AAGE;AACA;AACA;AACA;AAEA;;AAEIC;;AAEAC;;AAEJ;;AACSD;AAAUC;;AACrB;AAOO;;;;AAMH;;;AAGEC;AACF;AACA;;;AAGMC;AACAC;;;;AAGoBC;AAAS;AAC/B;AACF;AACF;AACAC;;AAEF;AACA;AACF;AAEO;AAGL;AACA;AACA;AACE;AACF;;AACSN;AAAUC;;AACrB;;AAEA;AACA;AACO;AAGL;AACA;AACA;AACE;AACF;AACA;AACF;;AAEA;AACA;AACAM;AAEO;;AAEP;AAEO;;AAEP;AAEO;AACL;AACF;AAEO;AACL;AACF;AAEO;AACL;AACF;AAEA;AACA;AACA;AAEO;AACLC;AAEA;;;AAGE;AACE;AACA;;AAEER;AACAS;AACAC;;AAGJ;AACF;AACE;;AAEAC;;AAGEX;AACAS;AACAC;;AAGJ;;AAEA;AACAE;AACAD;;AAEA;AACA;AACE;AACEf;AAGF;AACAgB;;AAEF;;AAESZ;AAAUC;;AACrB;AAEO;AACLO;AACA;AACAI;AACE;AACA;;;;AAEFD;AACF;AAEA;AACO;AAIL;AACA;AACA;AACE;AACF;AACA;AACA;AACA;;AAEE;AACEE;AACF;;AAEEC;AACF;AACF;;AAEIlB;AAGF;AACAiB;AACF;AACA;;AAEIb;;AAEAC;;AAEJ;;AAGEc;;AAEEA;;AACQpB;AAAkB;AAC1B;AACEqB;AAIF;AACF;AACF;;AAGEhB;;AAEAC;;AAEJ;;ACnSA;AAEA;AAEA;;AAEA;AACA;;AAGE;AACF;;AAEA;AACA;;AAGE;AACF;AAEA;AACE;;AAEI;;AAEA;;AAEJ;AACA;AACF;;AAEA;AACA;AACO;AACL;AACEgB;AACA;AACF;AAEA;;AAMA;AACF;AAEO;AACL;AAKF;AAEO;AACL;AACA;AAMF;AAEO;AACL;AACF;AAQO;AAGL;AAAeC;;;;;AACuB;;;AAIlCT;AAEF;AACAQ;AACF;;;AAIIjB;AACAS;AACAC;;AAEJ;;AAEMS;AAAS;AACf;;AAEA;;;AAE4C;;;AAM1CnB;AACAC;AACEmB;AAAmCC;;AACnCC;AACAC;;AAEEC;AACAC;AACAC;;;;AAIR;;AClHA;;AAEA;AACO;;AAGL;AACE;AACF;AACA;AACA;AACF;AAEO;;AAEH;AACF;AACA;AACE;AACF;;AAEE;AACF;;AAEE;AACF;;AAEF;AAWO;;;AAISC;AAAQ;AACpBT;;;AAIF;AACES;AACF;;AAEA;AAEA;;;;AAIE;AACE;;AAEE/B;AACF;AACEA;AACF;AACF;;;AAGA;AACEI;AACAS;;;AAGF;AACEb;AACAY;AACF;AACEA;AACF;;AACsBH;AAAUuB;AAAqB;AACrD;AACF;;AAEA;AACA;;;;AAGUlB;AAAc;AACtB;AACEV;AACAS;;AAEAR;;AAEA;;AAEFO;;AACsBqB;AAAU;AAChC;AACF;AACA;AACE7B;;;AAGF;AACF;AAEO;AAIL;;;;AAIE;AACA;AAEAQ;;AACsBH;AAAS;;AAG7BL;AACAS;;;AAGJ;;AAEA;AACA;;;AAIED;;AACsBH;AAAM;;AAG1BL;AACAS;AACAC;AACAT;;AAEA;;AAEJ;;;AAGID;;;AAGJ;AACF;AAEO;AACL;;AAEEJ;AAGF;AAEA;AACEkC;AACAC;AACEC;AACF;AACF;AACF;AAEO;AAIL;;;AAGIhC;AACAS;AACAC;;AAGJ;;AAEQiB;AAAQ;AAEhB;AACEA;AACF;AAEA;;AAEEM;AACA;AACEN;AAGF;;AAEA;AACEA;AACF;AAEA;AAEAnB;;AACsBH;AAAS;;AAG7BL;AACAS;AACA;AAAcC;;;AAElB;AAEA;;;AAGIV;AACAS;AACAC;;AAEJ;;AAGE;;AAEEV;AACAC;;;AAGFO;;AACsBH;AAAS;;AAE7BL;AACAS;AACAC;;AAEJ;AACF;AAEO;;AAML;AACE;AACF;;;AAIIV;AACAC;;;;AAIAD;AACAS;AACAC;;AAEJ;AACF;AAEO;AAQL;;;AAGIV;AACAS;AACAC;;AAGJ;AAEA;;AAEEd;AAGF;;AAEQ+B;AAAQ;;;AAIhB;AAEA;;AAEE;;AAEEI;;AAEE;;;AAEmBG;;;;;;AAWvB;;;;AAMA;AAEA;;;AAGsB7B;AAAS;;AAG7BL;AACAS;AACA;AAAcC;;;AAElB;AAEA;;;AAGIV;AACAS;AACAC;AACAT;;AAEA;;AAEJ;;AAGE;;AAEED;AACAC;;;AAGFO;;AACsBH;AAAS;;AAE7BL;AACAS;AACAC;;AAEJ;AACF;;ACzXO;;AAOL;AACA;AACF;;ACXO;AAKL;AACA;AACA;AACA;AACA;AACA;AACA;;AAEEyB;AACF;;;AAIAC;AACA;;AAIA;AACAA;AAEA;AACF;AAEO;AAEL;AACA;AACAC;AAGA;;AAGA;AACE;AAAkBC;;AAChB;AACA;AACAC;AAKF;AACF;;AAGA;;AAEA;;AAGA;AAAkBD;;;AAElB;;AAGA;AACEJ;AACA;AAAkBI;;AAChB;AACA;;AAEF;AACAJ;AACF;AAEA;AACF;AAEO;AAEL;AACA;AACAG;AAEA;;;;;AAMA;;AAGA;;AAEA;;AAGA;AAAkBC;;;AAElB;;;AAIEJ;;;AAGAA;AACF;AAEA;AACF;;ACtGA;AACA;AACO;AACL;;;AAGuBjC;AAAK;;AAE1B;AACA;AACA;AACED;AACAS;AACAC;AAEF;AACF;;AAGE;;;;AAOAd;;AACsBS;AAAS;;AAE/B;AACA;AACEL;AACAS;AACAC;AACF;AACF;AACF;;AChCO;;AAWH;AACF;AAEA;AACA;AACE;AACA;AACE;AACF;;;AAEQ4B;AAAkB;;AAExB;AACF;AACA;AACA;AACA;AACA;;AAEA;AACE;;AAEF;AACAE;;AAEEA;AACF;AACF;;AAEA;AACA;AACA;;;AAIE5C;AAEII;AACAS;;AAEF;AAEJ;AACEb;AACF;AAEA;AACF;;AClEO;AACL;AACE;AACF;AACA;AACE;AACF;AACA;AACF;;ACLA;AAEA;AAIO;;AAEH6C;AAGF;AACA;AACF;;ACKA;;AAEA;AAEO;;AAIGC;AAAW;AACjBxB;;;AAGF;AACA;AACA;;AAEA;AACE;AACA;AACA;;AAEA;AACEkB;AACF;;AAEEA;AACF;AACAH;AACF;AACA;AACF;AAEO;;AAIGU;AAAiB;AACvBzB;;;AAGF;;AAGE;AACE;AAAYyB;AAAU;AAE5B;AAEO;;AAKHD;AACAC;AACAC;AACF;AACE1B;;;;AAIF;AACA;AACE;AACA;;AAEE;AACF;;;AAIAe;;AAGA;AACEA;AACF;AACAA;AACF;AACA;AACF;;ACjGA;AACA;AACO;;AAKP;;ACoCA;AACA;AACA;;AAiBA;AAEA;AAEA;AACE;AACA;;AAGF;;AAEA;AACA;AACA;AACA;;;AAOE;AACA;AACE;AAIA;AACA;;AAEEY;AACAC;AACF;AACF;AACA;AACF;;AAEA;AACA;AACA;AACA;AACE;AAA4BR;AAAqB;AAGjD;AACES;AACF;AACA;AACEA;AACF;AACA;AACE;AACE;;AAE0B;;AACA;AACxBA;;AAEJ;AACF;;AAEF;AAEA;AACE;AACF;AAUO;;;;;;;;AAWL;AAAM7B;;;AACN;;AAEA;AACE8B;AACF;AAEA;AACE;AACAvB;AACEwB;AACAC;AACAC;;AAEF;;;AAGF;AACA;;AAIA;;AAEE;AACEvD;AACA;AACE;AACAwD;AACF;AACF;AACA;AACA;;AAEExD;AACA;AACE;;AAOF;AACF;AACF;AAEA;;;AAGIsD;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKJ;;;AAGA;;AAEA;AACA;;;;AAIE;;AAEA;AACAG;AACA;AACAC;AACAC;AACA;AACAC;AACF;AAEA;;AAEA;AACA;AACA;AACA;AACA;;;AAGEC;AAGF;AAEA;AACE;AACA;;AAEF;AACE;AACA;AACE;AACA;;AAEF;AACF;AAEA;AACE;AACEC;AACA;AACA9D;AACF;AACAA;;AAEA;AACF;;AAEA;AACA;AACE;AACA;;AAIA;;AAEA;AACA;;AAEI+D;AACF;AACF;;AAEA;AACA;;AAEE;;;AAKE;AACF;AACF;AACF;;AAGA;;AAKA;AACAvB;AACA;AACE;;AAUE;AACA;AASA;AACA;AAIA;AACA;AAIFwB;AAII;AACEC;AACF;AACEjE;AACF;AACF;;;AAIE;AACA;;AAOJ;;AAoCF;AACEwC;AACAA;AAGM;AAGS;AAAc;AAIvB;AAEsC;AAAS;;AACjCc;AAAO;;;AAGf;AACF;AAGN;AACER;AAAqBE;;AAG7B;AAEAR;AACA;AACEA;AAIF;AACEA;AACF;AACAA;AACkCM;AAAqBE;;AAEvD;;AA+BA;;AAEA;AACA;AACA;;;;AAIE;;AAEA;AACAS;AACA;AACA;AACAC;AACAC;AACA;AACAC;AACAM;AACF;;AAEA;AACA;AACEJ;AACA;AACF;AACA;;;AAGA;AACE;AACA;AACAK;AACF;AACF;;AAEA;AACA;AACA;AACO;AACLV;;;;AAIAM;AAOF;;;AAIE;;;AAGE;AACAL;AACAC;AACA;AACAC;AACAQ;;;;AAIAC;AACF;AAEA;AACEP;AACA;AACA;AACA9D;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACEsE;AACF;;AAEA;AACA;AACE;AACAC;AACA;AACAC;AACF;;AAEA;AACA;AACA;AACA;;AAEAC;;AAEE;AACA;AACAhB;AACA;AACAC;AACAC;;;;;AAKF;AACA;;AAGA;AACF;AAEO;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEF;AAEA;AACE;;AACQe;AAAS;AACjB;;;AAKA;;;AAKA;AACA;AACA;AACA;AAOA;AACA;AACA;AACF;AACA;AACA;AACA;AACA;AACE;;AAEF;;ACzpBO;AACL;AACA;;AAGEC;AACAC;AACF;AAEA;AACA;AACE;AACA;;;AAGA;AACF;;AAEE;AACF;AACE;AACF;AACF;;ACMO;;AAIHC;;AAEAC;AACF;AACExD;;;;;AAMA;AACA;AACE;AACF;;AAEF;;AAEsEuD;AAAK;AAC3E;AACE;AACF;;AAGE;AACAxE;;AAEA;;AAEJ;;ACvDO;AACL;AACA;AACEL;AAGA;AACF;;AAEA;AACA;;AACQ+E;;AACR;AACElE;;;;;AAOMmE;;;AAGJ;AAEEpD;AACAoD;AACAzB;;AAIN;AAEA;AACE;AACF;AACA;AACF;;ACrCO;AACL;;AAEE;AACA;AACF;AAEA;;AAEE0B;AAEIrD;AACAoD;AACAzB;AACF;AAEE3B;AACAoD;AACAzB;AACF;AAEE3B;AACAoD;AACAzB;;AAIN;;AAEE;;AAEEvD;AACF;;AAKA;AACF;AACE;;AAEEA;AACF;;AAIA;AACF;AACF;;AC/CO;AAKL;;;;AAIIA;AAGAA;AACAA;AAGAA;AAGAA;AAGAA;AAGAA;AACAA;AAGAA;AACAA;AAGAA;AACAA;AAGA;AACF;AAEAA;AAGAA;AACAA;AACAA;AACA;AACEA;AACF;AACEkF;AACA;;AAEA;AACF;AACF;AAEA;AACF;;ACvDA;AACO;AACL;AACA;AACEtE;;AAKSR;AAAUC;;AACrB;AAEA;AACA;AACEO;;AAKSR;AAAUC;;AACrB;AAEA;AACA;AACE;AACF;;AAEQ0E;;AACR;AACA;;AAEI3E;AACAS;AACAR;;AAEJ;AAEA;;;AAGID;AACAS;AACAR;;AAEJ;AAEAO;;AAGER;AACAS;AACAR;;AAEJ;;ACpDA;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGK;;;;AACqC8E;;AAC1C;AACA;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;;AAEE;AACqDC;;AAGrD;AACA;AACE;AACF;;AAEF;AACA;AACA;AACF;AAOO;;;AAKLxE;;AAEE;AACwDwE;;AAExDC;;AAEEzE;;AACsB0E;AAAU;AAClC;;AAEA1E;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AACL;AACA;AACF;AASO;AACL;AAAyC2E;;;AAEzCxE;AACA;AACA;;AAEE;;AAKoB2E;AAAqB;;;AAGzC;AACE3E;;AACsBH;AAAS;AACjC;AACF;AACA;AACA;AACA+E;AACA5E;;AAEE;;AAKoB6E;AAAkB;;;AAGtC;AACE7E;;AACsBH;AAAS;AACjC;AACF;AACA;AACF;;AAEA;AACA;AACA;AACA;AACO;AAGL;AACA;AACE;AACA;AACE;AACF;AACF;AACA;AACA;AACE;AACA;AACE;AACF;AACF;;AAEF;AAQO;AACL;;AAEEiF;;;AAGF9E;;;AAGE;;AAEAA;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AAIL;;AAEEiF;;AAEF;AACA9E;;;AAGE;;AAEAA;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AAIL;AACE;AACF;AACA;;AAEEiF;;AAEF;AACA9E;;;AAGE;;AAEAA;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AAIL;;AAEEiF;;AAEF;AACA9E;;AAEE;AAKA;;AAEAA;;AAEEA;AAIF;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AAKL;AACEG;AACA;AACF;;AAEEwE;AACAO;AACAC;AACF;AAAMtE;;;AAEN;AAEA;;AAEEoE;;;AAGF9E;;AAEE;;AAEAA;;AACsBH;AAAS;AACjC;AAEA;AACAG;;AAEE;AACA;;AAEAA;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AAIL;;AAEEiF;;AAEF;AACA9E;;AAEE;AACA;AACA;;AAEA;AACEA;;AACsBH;AAAS;AACjC;AACF;AACA;AACF;AAEO;AAKL;AAAyC2E;;AACzC;;AAK4B;AAAS;AAAS;AAC1C;AACA;AACE;AACAxE;;AAEE;AACA;;AAKoBiF;AAAgB;;;AAGpC;AACEjF;;AACsBH;AAAS;AACjC;AACF;AACF;;AAEE;;AAEEiF;;AAEF;AACA9E;;AAEE;;AAEA;AACEA;;AACsBH;AAAS;AACjC;AACF;AACF;AACF;AAEJ;AAEO;AAIL;;AAEEiF;;AAEF;AACA9E;;AAEE;AACA;AAKA;;AAEA;AACEA;;AACsBH;AAAS;AACjC;AACF;AACA;AACF;AAEO;AAIL;AAAyC2E;;AACzC;AACAxE;;AAEE;;AAKoBkF;AAAe;AACnC;;AAEA;AACElF;;AACsBH;AAAS;AACjC;AACF;AACA;AACF;AAEO;AAIL;AACA;AACA;;AAEF;AAEO;AAIL;;AAEEiF;;AAEF;AACA9E;;AAEE;AACA;;AAEAA;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AAGL;AAAyC2E;;;AAEzCxE;;AAEE;;AAKoBmF;AAAc;AAClC;AACA;;AAEE3F;;;;AAIFQ;;AACsBH;AAAS;;AAE7BL;AACAS;AACAC;;AAEJ;AACF;AAEA;AAEO;;AAEL;;;;AAEA;AACA;AACA;AACA;AACA;AACEuB;AAAW2D;;;AACb;AACE;;AAEE;AACA;AACA;AACA;;AAEA;AACA;;AAEE3D;;AAAkB4D;;AACpB;;AAEJ;AACAC;AACA;;AAA4B;AAC9B;;AC5eO;AAGL;AAGF;;ACdO;AAGL;;;AACcC;AAAU;AACxB;AAGF;AAMO;AAGL;AACA;;AAEF;AAEO;;;AASP;;AClCA;AACA;AACA;AACA;AACO;AAGL;AAMF;;ACfO;AAIL;;;AAGE;;AACUC;AAAsBpB;;AAChC;AACF;AACF;;ACKA;AAIA;AACE;AACA;AACA;AAAQ;AACR;AAAQ;AACR;AAAe;AACf;AAAe;AACf;AAAS;AACT;AAAoB;AACpB;AAAY;AACZ;AAAgB;AAChB;AACA;AACA;AAGF;AAEA;AAIE;;;AAMI;AACA;AACA;;AAEIqB;;AAEF;AACE;AACF;AACF;AACF;AACF;AACEA;AAAkDC;;AAGpD;AACA;AAKF;AAEA;;;AAOE;AAAkB5D;;;AAEhB;;AAQA;AACF;AACA;AACF;AAEA;AAKE;AACF;;AAEA;AACA;AACA;AACA;AACA;AACE;AACA;AACA;AACA;AACA;AAME;AACF;AACA;AACA;AAIA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AAQF;AAEA;;AACUA;AAAO;;AAEb;AACF;AACA;;;AAGA;AACA;AACA;;AAMA;AACA;;AAEF;AAEO;AAIL;AACA;AACF;AAEO;;;AAKH;AACA;;AAEA;AACF;AACA;AACF;AAMO;;AAKH0C;;;AAGF;AAAM9D;;;AAEN;AAEA;AACA;AACE;AAKA;AACEiF;AACF;AACF;AAEA;AACEC;;AAEAC;AACF;;AASIC;AAAe;AAEjB;AACEH;AACF;AACF;;AAGA;;AAEII;AACA;AACF;AACF;AAEA;AACErF;AACAkF;;AAEAI;;;;;AAMA;AACF;;AAEA;AACA;;AAEA;AACA;AAIA;AACE;AACA;AACA;AACA;AACEC;AACF;AACF;AACA;AACF;AAEO;;AAIHL;;AAEAM;AACF;AACF;AAEO;;;AAOCN;;AAEAC;;AAGR;AAEO;AAIL;AACA;AACF;AAEO;AAGL;AACA;AACF;;ACpSO;AAKL;AAEIM;AACAC;;;AAEIC;AAAc;;AAEtB;AACA;AAAkBvE;;AAChB;AACA;;AAEEwE;AACF;AACEC;AACA;AACF;AACF;;AACSvF;AAAerB;;;AAC1B;AAEO;;AACG6G;AAAM;;AAEd;;AAEE;AACA;AACA;AACA;AACA;AACE;AACA;AACA;;AAGAC;AACF;AACA;AACA;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;;AAEF;AACC9G;AACC;AACC6G;;AAGL;AACA;;AAEE;AACF;AACAC;AACF;AACF;AAOO;;AAKGC;AAAsBlC;AAAoB;AAChD9D;;;;;AAMAiG;AACF;AAEA;AACF;;ACzGA;AACEvH;AAGA;AACA;AACA;AACAwE;AACF;AAEA;AACO;;AAEHgD;;;AAGA;AACF;AACA;AACF;AAEA;AACA;;AAEIC;AACF;AACA;AACF;AAEA;AACO;;AAEH;;;AAGEC;AACF;;;AAGE;;AAEA;AACA7G;AAEAA;AACAb;AACA;AACA;AACA;AACAwE;AACF;AACF;AACA;AACF;AAEA;AACO;;AAEH;;;AAQF;AACA;AACF;AAEA;AACO;;AAEHmD;;;AAGA;AACF;AACA;AACF;AAEA;AACA;;AAEIC;AACF;AACA;AACF;AAEO;AACL;AACF;AAEO;AACL;AACF;;ACxGA;AAEO;;AAEL;AAAkBlF;;;AAEd;AACA;AACEL;AACAwF;AACF;AACExF;AACF;AACF;AACF;AACA;AACF;AAEO;AACL;AACE;AACF;AACA;AACE;AACF;AACA;AACF;AAEO;;AAEL;AACF;AAEO;AACL;AACF;;ACxBO;AAGL;AAA6CiE;AAAc;AAC3D;;AAMF;AAEO;;AAOHwB;;;AAGF;AACExG;;;AAGF;AACEyG;;AAEF;AACA;AACEC;AACF;AAEA;AACEA;AACF;AACE;;AAEEA;AACF;AACF;;;AAGUC;;AAQJ;AACAH;AACE;;;AAGFI;AACE;AACA;AAEA;;AAEF;;;;AAKK9H;;;;AAET;;;AAESA;AAAWC;AAASQ;;AAC/B;AACF;;AChFO;;AAIP;AAEO;;AAEP;AAEO;;AAEP;;AC2CO;AACL;AACA;AAEI;AACAsH;AACN;AAEO;;AAEH;AAEA;AAEA;AAEA;AAEAtG;;AAEJ;AAEO;;AAKL;AACEjB;;AACSR;;;AACX;;;;;AAKE;AACEJ;;AACsBS;AAAS;;AACtBL;;;AACX;AACA;;AACsBK;AAAS;;AAE7BL;AACAS;;;AAGJ;AAEA;;AAEEuH;;AAEAxH;;AACsBH;AAAU4H;AAAK;AACrC;AACErI;;AACSI;;;AACX;;AAEEA;AACAS;AACAC;;AAEJ;;AAGEd;;AACSI;;;AACX;;AAEA;AACA;;AACSA;AAAUC;;AACrB;AAEO;;;;;AAQHO;;AACsBH;AAAU6H;AAAS;;AAEvClI;AACAS;AACAC;;AAGJ;;AAGA;;AAESV;AAAUC;;AACrB;;AC9GO;;AAGGgD;AAAK;;AAOf;;AC1DO;;AAGkB/B;;AAAwB;AACjD;;ACFO;AACLiH;AACAC;AACAC;AACF;;ACAO;AACLC;AACAC;AACAC;AACAC;AACF;;ACNO;;AAIH;AACF;;AAGE;AACF;AAEAC;AACE;AAGF;AAEAC;AAIIC;AACAC;;AAMF;AACE;;AAII;AACN;AACA;AACF;;AAKE;AACF;;AAGE;AACF;;;AAMA;;AAGE;;AAIF;AACF;;AC3DO;AACL;AAAqB3H;;AACrB;AACA;AACE;;AAEE4H;AACF;AACF;AACA;AACF;;;ACWO;AAKL;AACE;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;AAAiB;;;AAEPC;AAAI;;;AAEJzG;AAAO;;;AAGb;AACE;AACF;AACF;;AAEE;AACE;AACF;AACF;AACA;AACF;AACA;;AAEA;AACA;AACE;AACJ;AACF;AAEO;;AAEH;;;AAGF;AACF;AAEO;;AAEH;;AAEF;AACF;;AC7EA;AAEA;AAIO;;AAEH0G;AAGF;AACA;AACF;;ACcO;AACLV;AACAC;AACAC;AACAC;AACF;AAEO;AACLH;AACAC;AACAC;AACAC;AACAQ;AACF;AAiBA;AAEA;AAEA;AASA;AACE;AACEX;AACAC;AACAC;AACAC;;AAEF;;;;AAIM;;;AAGA;;;AAGA;;;AAGA;AACJ;AACF;AACA;AACF;AAEA;;;AAGIS;AACF;;AAEEA;AACF;;AAEEA;AACF;;AAEEA;AACF;AACA;AACF;AAoBO;AAKL;AACA;AACE;AACF;;AAEQjG;AAAiBxB;AAAQ;;AAG/B0H;;AAEAC;AACF;AACElI;;;AAIF;;AAQEmI;AACAf;AACAH;AACA;AACF;AAEA;AACEjH;AACA;;;AAIF;AACE;AACA;;AAKE;AACF;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;;;;;;;;;;AAgBI6H;AACAO;AACF;AACF;AACF;AACA;AACE;AACF;;AAEA;AACA;AAKE;AACA;;AAEA;AACE;;AAEA;AACE;AACA;AACA;AACA;;AAKA;AACE;AACA;;;AAGEC;AACEC;AACA/H;AACF;AACF;AACF;AACEgI;AACF;AACF;AACE;AACA;;;AAGEC;AAA+BF;AAAqB/H;AAAQ;AAC9D;AACF;AACEgI;AACF;AACF;AACAE;AACE;;AAOJ;AACEA;AACF;;AAEEC;AACF;AACA;AACF;AAEO;;AAEP;AAEO;AAIL;AACF;AAEO;AAIL;;AAEF;AAEO;AACL;;AACQC;;AACR;AASF;AAEO;;AAWP;AAoBO;;;AAOL;;AAC4B;AAAS;;AACnC;;AAMA;AACAC;AACE;;AAME;AACF;;AAEEC;AACF;AACA;;AAEEC;AACAD;AACF;;AACQE;AAAI;AACZ;AACE;AACA;AACA;AACA;AAEA;AAEA;;;AAGID;;AAEEE;AACE;AACA;AACAA;AAIJ;AACA;;AAEA7J;AACF;AACF;AACAG;;;AAC6BH;AAAM;AACrC;AACF;AACF;AACA;AACF;AAEO;AAGL;AACF;AAOO;;AAIG8J;;AAA2C;AACjDjJ;;;AAIF;AACA;AAIA;AACA;AACA;AAEA;AAAkBoB;;;AACR;AAAS;AAAU;;AAE3B;AACE;;AAGE8H;AACF;AACA;AACF;;;AAGA;AACA;AACE;AACF;AACAC;AACAC;AACA;AAMEC;AACF;AACF;;AAEA;AACA;AAAa;AAAQ;AACnB;AACE;AACF;AACAA;AACF;AACA;AACA;AAAa;AAAS;AAAgB;AACpC;AACE;AACF;AACAA;;AAEA;AACE;AACA;AACA;;AAKA;AACEC;AACAC;AACF;AACAH;AAIF;AACF;AAEA;;AAKMhI;;;AAII;AAAS;AAAU;AAC3B;AACA;;AACUW;AAAK;;;;AAYb;AACA;AACA;AACA;AACA;;AAEA;AACAb;AACF;AACA;AACA;;AAKA;AACA;AACEmI;AACAG;AACF;AACEA;AACF;AACA;AACEA;AACF;;;AAEQpI;AAA0B;AAClC;AACEqI;;;AAKA;;AAIA;AACF;AACAC;AACF;;AAIA;AACE;AACEtC;AACAC;AACAC;AACAC;;AAEF;AAAa;AAAS;AAAU;AAC9B;AACE;AACF;AACA;AACAoC;AACAA;AACAA;AACAA;AACF;AACAH;AAGF;AACAA;AACF;;AC5kBO;;AAEP;AAEO;AACL;AACF;;ACEO;AACL;AACA;;AAEA;;AAEF;AAEO;AAGL;AACA;;AAEE;AACE;AACF;AACA;;AAEE;AACF;AACAI;AACA;AACE5J;;;AAGA;;AAEF;AACE;AACA;;AAEF;;;;AAIF;;AAIF;AAEO;AACL;AACF;AAEO;AAGL;AACA;;;;AAIA;AACA;AACF;AAEO;;AAEH;;AAEF;AACF;AAQO;AACL;AACF;AAEO;AACL;AACA;AACA;AACF;;ACnEO;AAIL;AACA;;;AAGA;AACF;AAYO;AAIL;;AACoB6J;AAAiB;;AAE/BzI;AAAkB;AACxB;;AAGE;AACF;AAEA;AACEpB;AACAiI;AACAvC;AACA;;;;AAKAoE;AACF;;;AAEwCrJ;AAAQ;AAEhD;AAEAA;AAEA;AAAwCsJ;AAAS;AACjD;;AAEE;AACF;AACA;AACA;AAEA;;;;;AAKEtJ;;AAGF;AAEIuJ;AAAqCC;AAAK;AAC5C;AAEEC;AACEC;AACAC;;AACyBC;;;;;AAI3B;AACF;;AAGE;AACA;AACF;;AAEE;AACE;AACF;AACA;AACA;AAGF;;;;AAOwBC;AAAY;AAClC;AACF;AACAC;;AAEE9J;AACF;AACF;;AAIA;AACF;;ACjIO;;;;AAML;AACF;;ACVO;AACL;;AAC4C+J;AAAa3L;AAAgB;AACvEuG;AACF;AACF;AAOO;AAEHtB;;AAAmE;AAErE;;AACQ2G;AAAK;;AAEb;AACE;;AAEI;AACF;;;AAGE;;AAEA;AACE;AACF;;AAEJ;AACAC;AACF;AACA;AACF;;ACnBO;;;AAKHC;;;;AAIF;AAAM3K;;;AACN;AACA;AACA;;AAIA;;;;AAIA;;AAEA;AACA;AACA;AACA;AACA;AACEoE;;;AAGEA;AACF;AACF;AACEA;AACF;;AAgBI;AACA;AAEA;;AAEA;AACA;;AAMA;AACAoC;;;AAGE;;;AAGFpC;AACF;AAGF;AACEuC;;AAEI;AACA;;AAEF;AACF;AACF;AAEA;AACF;;AC3FO;;;AAIUiE;AAAc;AAC7B;AACA;AACA;AACA;AACE;;;AAGA;AACF;;AAEED;;;AAGF;AAAM3K;;;;;;;AAKJoE;AACA;AACAoC;;;AAGEqE;AAIA;AACF;AACF;AACF;;AC9BO;;AAIH/G;;;;AAIAgH;;;;AAIF;AAAM9K;;;AACN;AACEkC;;AAEA6I;;AAEAvE;;;AACAwE;;;AAGAC;AACF;AACA;AACA;AAAqBjL;AAAiB;;AAEtC;;AAEA;AACA;;AAEA;AACA;AACEkL;AACF;AACA;AACF;;ACzDO;;AAIP;;ACiBA;;;;;;;;;;;;AAYEC;AACF;AAEO;AAEP;AA+EA;;;;AAMQ;;AAEF;;AAEJ;AAEA;AAEA;;AAaQ;;AAEE;AACF;;AAEE;AACA;;;;AAIA;AACA;AACA;AACA;;;;;AAOF;AACA;AACF;AASR;;AAEA;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAEA;AACE;;;AAGA;AACA;AAA+BzF;;AACjC;AAEA;AAKE;AACA;AACApG;;;AAGI;AACA;AACA;AACA8L;AACE;;;;AAKE;;AAIN9L;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AACL2E;AACAuH;AACa;;AACqCvH;AAAI;;AAEtD;;AAMiCA;AAAI;AACrC;;AAKqCwH;;AAErC;AACA;AACA;AAIA;AACA;AACE;AACA;AACA;AACA;;;;AAIIpL;AACF;AACF;AACF;AACA;AAMEA;AACF;;AAEEA;;AAEF;AACA;;AAEA;AACEA;AACF;;AACQqL;AAAuB;;;AAG/B;AACA;;AAEA;AACA;AACA;;;;;AAIUC;;AACR;AACA;AACA;AACEC;AACA;AACA;AACA;;;AAGA;AACF;AACA;AACEC;AACA;AACA;AACA;;;AAGA;AACF;AACA;AAGA;AACE;AACA;;AAKE;;;;AAIA;AACF;AACF;;AAKF;AACEC;AACAC;AACF;;AAEA;AACA;AAGA;;AAGA;AAEA;AAKA;;;;;;;;AASEC;AAAYC;;;;;;;;;AAQZC;;;;AAIAC;AACE;;AAEA;AACAC;AAGF;;AAEJ;AAEO;;AAKHC;;AAEAC;AACF;AACEnM;;;AAGF;;;AAGItB;AAMF;AACF;;;;AAC4BqN;AAAgB;AAC5C;AACA;;;AAGIjN;AACAS;;;AAMJ;AACA;AACE;;AAEET;AACAS;;;AAMJ;AACA;;AAEIT;AACAS;AACAC;;AAKJ;AACA;;AAEIV;AACAS;;;AAMJ;AACA;;;AAGIT;AACAS;AACAC;;AAEJ;;;AAGIV;AACAS;AACAC;;AAEJ;AACA;;AAEIV;AACAS;AACAC;;AAEJ;;;AAGIV;AACAS;AACAC;;AAKJ;AACA;AAIE;;AAOF;;AACSV;AAAUC;;AACrB;;AC1fO;AAEA;AACL;;AAMA;;AAEID;AACAS;;;AAGJ;;AAEST;;;AACX;AAEO;AAOL;AACA;AACE;AACF;;AAEQL;AAAkB;;;AAGtBK;AACAS;AACAC;;AAEJ;;AAEA;AACA;;AAEA;AACA;;AAOF;AACA;AACA;AACA;AACA;AACA;AACA;;AAGIV;AACAC;;;AAGEqN;AACAC;AACAC;AACF;;AAEJ;;ACvEA;;AAEE;AAA+DC;AAAU;AAC3E;AAIO;AAEA;AAGLC;;;AAGA;AACF;AAEO;AAIL;AACA;;AAEA;AACF;AAEO;AAIL;;AAEE;AACF;AACAlN;AACA;AACF;;AC7BO;AA0CiC;AAEjC;;AAEP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","debugId":"7bc6e694-34e1-474f-8bfe-df9d9abb3db6"}
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../src/utils/config.mts","../src/utils/sdk.mts","../src/utils/api.mts","../src/utils/fail-msg-with-badge.mts","../src/utils/markdown.mts","../src/utils/serialize-result-json.mts","../src/utils/check-input.mts","../src/utils/get-output-kind.mts","../src/utils/requirements.mts","../src/utils/output-formatting.mts","../src/utils/tildify.mts","../src/utils/meow-with-subcommands.mts","../src/utils/ms-at-home.mts","../src/commands/organization/fetch-organization-list.mts","../src/commands/scan/suggest-org-slug.mts","../src/commands/scan/suggest-to-persist-orgslug.mts","../src/utils/determine-org-slug.mts","../src/commands/ci/fetch-default-org-slug.mts","../src/utils/git.mts","../src/utils/purl.mts","../src/utils/socket-url.mts","../src/utils/map-to-object.mts","../src/utils/walk-nested-map.mts","../src/utils/glob.mts","../src/utils/path-resolve.mts","../src/utils/npm-paths.mts","../src/utils/cmd.mts","../src/utils/fs.mts","../src/utils/coana.mts","../src/utils/organization.mts","../src/utils/socket-json.mts","../src/utils/semver.mts","../src/utils/completion.mts","../src/utils/errors.mts","../src/utils/npm-package-arg.mts","../src/shadow/npm/install.mts","../src/utils/agent.mts","../src/utils/package-environment.mts","../src/utils/ecosystem.mts","../src/utils/alert/artifact.mts","../src/utils/objects.mts","../src/utils/alert/fix.mts","../src/utils/alert/severity.mts","../src/utils/color-or-markdown.mts","../src/utils/filter-config.mts","../src/utils/translations.mts","../src/utils/socket-package-alert.mts","../src/utils/spec.mts","../src/utils/alerts-map.mts"],"sourcesContent":["import { mkdirSync, writeFileSync } from 'node:fs'\nimport path from 'node:path'\n\nimport config from '@socketsecurity/config'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { safeReadFileSync } from '@socketsecurity/registry/lib/fs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\n\nimport constants from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { SocketYml } from '@socketsecurity/config'\n\nexport interface LocalConfig {\n apiBaseUrl?: string | null | undefined\n // @deprecated ; use apiToken. when loading a config, if this prop exists it\n // is deleted and set to apiToken instead, and then persisted.\n // should only happen once for legacy users.\n apiKey?: string | null | undefined\n apiProxy?: string | null | undefined\n apiToken?: string | null | undefined\n defaultOrg?: string\n enforcedOrgs?: string[] | readonly string[] | null | undefined\n skipAskToPersistDefaultOrg?: boolean\n org?: string // convenience alias for defaultOrg\n}\n\nconst sensitiveConfigKeyLookup: Set<keyof LocalConfig> = new Set(['apiToken'])\n\nconst supportedConfig: Map<keyof LocalConfig, string> = new Map([\n ['apiBaseUrl', 'Base URL of the Socket API endpoint'],\n ['apiProxy', 'A proxy through which to access the Socket API'],\n [\n 'apiToken',\n 'The Socket API token required to access most Socket API endpoints',\n ],\n [\n 'defaultOrg',\n 'The default org slug to use; usually the org your Socket API token has access to. When set, all orgSlug arguments are implied to be this value.',\n ],\n [\n 'enforcedOrgs',\n 'Orgs in this list have their security policies enforced on this machine',\n ],\n [\n 'skipAskToPersistDefaultOrg',\n 'This flag prevents the Socket CLI from asking you to persist the org slug when you selected one interactively',\n ],\n ['org', 'Alias for defaultOrg'],\n])\n\nconst supportedConfigEntries = [...supportedConfig.entries()].sort((a, b) =>\n naturalCompare(a[0], b[0]),\n)\nconst supportedConfigKeys = supportedConfigEntries.map(p => p[0])\n\nfunction getConfigValues(): LocalConfig {\n if (_cachedConfig === undefined) {\n // Order: env var > --config flag > file\n _cachedConfig = {} as LocalConfig\n const { socketAppDataPath } = constants\n if (socketAppDataPath) {\n const raw = safeReadFileSync(socketAppDataPath)\n if (raw) {\n try {\n Object.assign(\n _cachedConfig,\n JSON.parse(Buffer.from(raw, 'base64').toString()),\n )\n } catch {\n logger.warn(`Failed to parse config at ${socketAppDataPath}`)\n }\n // Normalize apiKey to apiToken and persist it.\n // This is a one time migration per user.\n if (_cachedConfig['apiKey']) {\n const token = _cachedConfig['apiKey']\n delete _cachedConfig['apiKey']\n updateConfigValue('apiToken', token)\n }\n } else {\n mkdirSync(path.dirname(socketAppDataPath), { recursive: true })\n }\n }\n }\n return _cachedConfig\n}\n\nfunction normalizeConfigKey(\n key: keyof LocalConfig,\n): CResult<keyof LocalConfig> {\n // Note: apiKey was the old name of the token. When we load a config with\n // property apiKey, we'll copy that to apiToken and delete the old property.\n // We added `org` as a convenience alias for `defaultOrg`\n const normalizedKey =\n key === 'apiKey' ? 'apiToken' : key === 'org' ? 'defaultOrg' : key\n if (!isSupportedConfigKey(normalizedKey)) {\n return {\n ok: false,\n message: `Invalid config key: ${normalizedKey}`,\n data: undefined,\n }\n }\n return { ok: true, data: normalizedKey }\n}\n\nexport type FoundSocketYml = {\n path: string\n parsed: SocketYml\n}\n\nexport function findSocketYmlSync(\n dir = process.cwd(),\n): FoundSocketYml | undefined {\n let prevDir = null\n while (dir !== prevDir) {\n let ymlPath = path.join(dir, 'socket.yml')\n let yml = safeReadFileSync(ymlPath)\n if (yml === undefined) {\n ymlPath = path.join(dir, 'socket.yaml')\n yml = safeReadFileSync(ymlPath)\n }\n if (typeof yml === 'string') {\n try {\n return {\n path: ymlPath,\n parsed: config.parseSocketConfig(yml),\n }\n } catch (e) {\n debugDir('inspect', { error: e })\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 undefined\n}\n\nexport function getConfigValue<Key extends keyof LocalConfig>(\n key: Key,\n): CResult<LocalConfig[Key]> {\n const localConfig = getConfigValues()\n const keyResult = normalizeConfigKey(key)\n if (!keyResult.ok) {\n return keyResult\n }\n return { ok: true, data: localConfig[keyResult.data as Key] }\n}\n\n// This version squashes errors, returning undefined instead.\n// Should be used when we can reasonably predict the call can't fail.\nexport function getConfigValueOrUndef<Key extends keyof LocalConfig>(\n key: Key,\n): LocalConfig[Key] | undefined {\n const localConfig = getConfigValues()\n const keyResult = normalizeConfigKey(key)\n if (!keyResult.ok) {\n return undefined\n }\n return localConfig[keyResult.data as Key]\n}\n\n// Ensure export because dist/utils.js is required in src/constants.mts.\n// eslint-disable-next-line n/exports-style\nexports.getConfigValueOrUndef = getConfigValueOrUndef\n\nexport function getSupportedConfigEntries() {\n return [...supportedConfigEntries]\n}\n\nexport function getSupportedConfigKeys() {\n return [...supportedConfigKeys]\n}\n\nexport function isReadOnlyConfig() {\n return _readOnlyConfig\n}\n\nexport function isSensitiveConfigKey(key: string): key is keyof LocalConfig {\n return sensitiveConfigKeyLookup.has(key as keyof LocalConfig)\n}\n\nexport function isSupportedConfigKey(key: string): key is keyof LocalConfig {\n return supportedConfig.has(key as keyof LocalConfig)\n}\n\nlet _cachedConfig: LocalConfig | undefined\n// When using --config or SOCKET_CLI_CONFIG, do not persist the config.\nlet _readOnlyConfig = false\n\nexport function overrideCachedConfig(jsonConfig: unknown): CResult<undefined> {\n debugFn('notice', 'override: full config (not stored)')\n\n let config\n try {\n config = JSON.parse(String(jsonConfig))\n if (!config || typeof config !== 'object') {\n // `null` is valid json, so are primitive values.\n // They're not valid config objects :)\n return {\n ok: false,\n message: 'Could not parse Config as JSON',\n cause:\n \"Could not JSON parse the config override. Make sure it's a proper JSON object (double-quoted keys and strings, no unquoted `undefined`) and try again.\",\n }\n }\n } catch {\n // Force set an empty config to prevent accidentally using system settings.\n _cachedConfig = {} as LocalConfig\n _readOnlyConfig = true\n\n return {\n ok: false,\n message: 'Could not parse Config as JSON',\n cause:\n \"Could not JSON parse the config override. Make sure it's a proper JSON object (double-quoted keys and strings, no unquoted `undefined`) and try again.\",\n }\n }\n\n // @ts-ignore Override an illegal object.\n _cachedConfig = config as LocalConfig\n _readOnlyConfig = true\n\n // Normalize apiKey to apiToken.\n if (_cachedConfig['apiKey']) {\n if (_cachedConfig['apiToken']) {\n logger.warn(\n 'Note: The config override had both apiToken and apiKey. Using the apiToken value. Remove the apiKey to get rid of this message.',\n )\n }\n _cachedConfig['apiToken'] = _cachedConfig['apiKey']\n delete _cachedConfig['apiKey']\n }\n\n return { ok: true, data: undefined }\n}\n\nexport function overrideConfigApiToken(apiToken: unknown) {\n debugFn('notice', 'override: Socket API token (not stored)')\n // Set token to the local cached config and mark it read-only so it doesn't persist.\n _cachedConfig = {\n ...config,\n ...(apiToken === undefined ? {} : { apiToken: String(apiToken) }),\n } as LocalConfig\n _readOnlyConfig = true\n}\n\nlet _pendingSave = false\nexport function updateConfigValue<Key extends keyof LocalConfig>(\n configKey: keyof LocalConfig,\n value: LocalConfig[Key],\n): CResult<undefined | string> {\n const localConfig = getConfigValues()\n const keyResult = normalizeConfigKey(configKey)\n if (!keyResult.ok) {\n return keyResult\n }\n const key: Key = keyResult.data as Key\n // Implicitly deleting when serializing.\n let wasDeleted = value === undefined\n if (key === 'skipAskToPersistDefaultOrg') {\n if (value === 'true' || value === 'false') {\n localConfig['skipAskToPersistDefaultOrg'] = value === 'true'\n } else {\n delete localConfig['skipAskToPersistDefaultOrg']\n wasDeleted = true\n }\n } else {\n if (value === 'undefined' || value === 'true' || value === 'false') {\n logger.warn(\n `Note: The value is set to \"${value}\", as a string (!). Use \\`socket config unset\\` to reset a key.`,\n )\n }\n localConfig[key] = value\n }\n if (_readOnlyConfig) {\n return {\n ok: true,\n message: `Config key '${key}' was ${wasDeleted ? 'deleted' : `updated`}`,\n data: 'Change applied but not persisted; current config is overridden through env var or flag',\n }\n }\n\n if (!_pendingSave) {\n _pendingSave = true\n process.nextTick(() => {\n _pendingSave = false\n const { socketAppDataPath } = constants\n if (socketAppDataPath) {\n writeFileSync(\n socketAppDataPath,\n Buffer.from(JSON.stringify(localConfig)).toString('base64'),\n )\n }\n })\n }\n\n return {\n ok: true,\n message: `Config key '${key}' was ${wasDeleted ? 'deleted' : `updated`}`,\n data: undefined,\n }\n}\n","import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'\n\nimport isInteractive from '@socketregistry/is-interactive/index.cjs'\nimport { password } from '@socketsecurity/registry/lib/prompts'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\nimport { SocketSdk, createUserAgentFromPkgJson } from '@socketsecurity/sdk'\n\nimport { getConfigValueOrUndef } from './config.mts'\nimport constants from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\n\nconst TOKEN_PREFIX = 'sktsec_'\n\nconst TOKEN_PREFIX_LENGTH = TOKEN_PREFIX.length\n\nconst TOKEN_VISIBLE_LENGTH = 5\n\n// The Socket API server that should be used for operations.\nfunction getDefaultApiBaseUrl(): string | undefined {\n const baseUrl =\n constants.ENV.SOCKET_CLI_API_BASE_URL || getConfigValueOrUndef('apiBaseUrl')\n return isUrl(baseUrl) ? baseUrl : undefined\n}\n\n// The Socket API server that should be used for operations.\nfunction getDefaultProxyUrl(): string | undefined {\n const apiProxy =\n constants.ENV.SOCKET_CLI_API_PROXY || getConfigValueOrUndef('apiProxy')\n return isUrl(apiProxy) ? apiProxy : undefined\n}\n\nfunction isUrl(value: any): value is string {\n if (isNonEmptyString(value)) {\n try {\n // eslint-disable-next-line no-new\n new URL(value)\n return true\n } catch {}\n }\n return false\n}\n\n// This Socket API token should be stored globally for the duration of the CLI execution.\nlet _defaultToken: string | undefined\nexport function getDefaultApiToken(): string | undefined {\n if (constants.ENV.SOCKET_CLI_NO_API_TOKEN) {\n _defaultToken = undefined\n return _defaultToken\n }\n\n const key =\n constants.ENV.SOCKET_CLI_API_TOKEN ||\n getConfigValueOrUndef('apiToken') ||\n _defaultToken\n\n _defaultToken = isNonEmptyString(key) ? key : undefined\n return _defaultToken\n}\n\nexport function getPublicApiToken(): string {\n return (\n getDefaultApiToken() ||\n constants.ENV.SOCKET_CLI_API_TOKEN ||\n constants.SOCKET_PUBLIC_API_TOKEN\n )\n}\n\nexport function getVisibleTokenPrefix(): string {\n const apiToken = getDefaultApiToken()\n return apiToken\n ? apiToken.slice(\n TOKEN_PREFIX_LENGTH,\n TOKEN_PREFIX_LENGTH + TOKEN_VISIBLE_LENGTH,\n )\n : ''\n}\n\nexport function hasDefaultApiToken(): boolean {\n return !!getDefaultApiToken()\n}\n\nexport type SetupSdkOptions = {\n apiBaseUrl?: string | undefined\n apiProxy?: string | undefined\n apiToken?: string | undefined\n}\n\nexport async function setupSdk(\n options?: SetupSdkOptions | undefined,\n): Promise<CResult<SocketSdk>> {\n const opts = { __proto__: null, ...options } as SetupSdkOptions\n let { apiToken = getDefaultApiToken() } = opts\n\n if (typeof apiToken !== 'string' && isInteractive()) {\n apiToken = await password({\n message:\n 'Enter your Socket.dev API token (not saved, use socket login to persist)',\n })\n _defaultToken = apiToken\n }\n\n if (!apiToken) {\n return {\n ok: false,\n message: 'Auth Error',\n cause: 'You need to provide an API token. Run `socket login` first.',\n }\n }\n\n let { apiProxy } = opts\n if (!isUrl(apiProxy)) {\n apiProxy = getDefaultProxyUrl()\n }\n\n const { apiBaseUrl = getDefaultApiBaseUrl() } = opts\n const ProxyAgent = apiProxy?.startsWith('http:')\n ? HttpProxyAgent\n : HttpsProxyAgent\n\n return {\n ok: true,\n data: new SocketSdk(apiToken, {\n agent: apiProxy ? new ProxyAgent({ proxy: apiProxy }) : undefined,\n baseUrl: apiBaseUrl,\n timeout: constants.ENV.SOCKET_CLI_API_TIMEOUT,\n userAgent: createUserAgentFromPkgJson({\n name: constants.ENV.INLINED_SOCKET_CLI_NAME,\n version: constants.ENV.INLINED_SOCKET_CLI_VERSION,\n homepage: constants.ENV.INLINED_SOCKET_CLI_HOMEPAGE,\n }),\n }),\n }\n}\n","import { messageWithCauses } from 'pony-cause'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { getConfigValueOrUndef } from './config.mts'\nimport constants from '../constants.mts'\nimport { getDefaultApiToken } from './sdk.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\nimport type {\n SocketSdkErrorResult,\n SocketSdkOperations,\n SocketSdkResult,\n SocketSdkSuccessResult,\n} from '@socketsecurity/sdk'\n\nconst NO_ERROR_MESSAGE = 'No error message returned'\n\n// The Socket API server that should be used for operations.\nexport function getDefaultApiBaseUrl(): string | undefined {\n const baseUrl =\n constants.ENV.SOCKET_CLI_API_BASE_URL || getConfigValueOrUndef('apiBaseUrl')\n if (isNonEmptyString(baseUrl)) {\n return baseUrl\n }\n const API_V0_URL = constants.API_V0_URL\n return API_V0_URL\n}\n\nexport async function getErrorMessageForHttpStatusCode(code: number) {\n if (code === 400) {\n return 'One of the options passed might be incorrect'\n }\n if (code === 403 || code === 401) {\n return 'Your Socket API token may not have the required permissions for this command or you might be trying to access (data from) an organization that is not linked to the API token you are logged in with'\n }\n if (code === 404) {\n return 'The requested Socket API endpoint was not found (404) or there was no result for the requested parameters. If unexpected, this could be a temporary problem caused by an incident or a bug in the CLI. If the problem persists please let us know.'\n }\n if (code === 500) {\n return 'There was an unknown server side problem with your request. This ought to be temporary. Please let us know if this problem persists.'\n }\n return `Server responded with status code ${code}`\n}\n\nexport type HandleApiCallOptions = {\n desc?: string | undefined\n spinner?: Spinner | undefined\n}\n\nexport type ApiCallResult<T extends SocketSdkOperations> = CResult<\n SocketSdkSuccessResult<T>['data']\n>\n\nexport async function handleApiCall<T extends SocketSdkOperations>(\n value: Promise<SocketSdkResult<T>>,\n options?: HandleApiCallOptions | undefined,\n): Promise<ApiCallResult<T>> {\n const { desc, spinner } = {\n __proto__: null,\n ...options,\n } as HandleApiCallOptions\n\n if (desc) {\n spinner?.start(`Requesting ${desc} from API...`)\n } else {\n spinner?.start()\n }\n\n let sdkResult: SocketSdkResult<T>\n try {\n sdkResult = await value\n spinner?.stop()\n if (desc) {\n const message = `Received Socket API response (after requesting ${desc}).`\n if (sdkResult.success) {\n logger.success(message)\n } else {\n logger.info(message)\n }\n }\n } catch (e) {\n spinner?.stop()\n const socketSdkErrorResult: ApiCallResult<T> = {\n ok: false,\n message: 'Socket API returned an error',\n cause: messageWithCauses(e as Error),\n }\n if (desc) {\n logger.fail(`An error was thrown while requesting ${desc}`)\n debugFn('error', `caught: ${desc} error`)\n } else {\n debugFn('error', `caught: Socket API request error`)\n }\n debugDir('inspect', { error: e, socketSdkErrorResult })\n return socketSdkErrorResult\n }\n\n // Note: TS can't narrow down the type of result due to generics.\n if (sdkResult.success === false) {\n const errorResult = sdkResult as SocketSdkErrorResult<T>\n const message = `${errorResult.error || NO_ERROR_MESSAGE}`\n const { cause: reason } = errorResult\n const socketSdkErrorResult: ApiCallResult<T> = {\n ok: false,\n message: 'Socket API returned an error',\n cause: `${message}${reason ? ` ( Reason: ${reason} )` : ''}`,\n data: {\n code: sdkResult.status,\n },\n }\n debugFn('error', `fail:${desc ? ` ${desc}` : ''} bad response`)\n debugDir('inspect', { sdkResult })\n return socketSdkErrorResult\n }\n const socketSdkSuccessResult: ApiCallResult<T> = {\n ok: true,\n data: (sdkResult as SocketSdkSuccessResult<T>).data,\n }\n return socketSdkSuccessResult\n}\n\nexport async function handleApiCallNoSpinner<T extends SocketSdkOperations>(\n value: Promise<SocketSdkResult<T>>,\n description: string,\n): Promise<CResult<SocketSdkSuccessResult<T>['data']>> {\n let result: SocketSdkResult<T>\n try {\n result = await value\n } catch (e) {\n const message = `${e || NO_ERROR_MESSAGE}`\n const reason = `${e || NO_ERROR_MESSAGE}`\n\n debugFn('error', `caught: ${description} error`)\n debugDir('inspect', { error: e })\n\n return {\n ok: false,\n message: 'Socket API returned an error',\n cause: `${message}${reason ? ` ( Reason: ${reason} )` : ''}`,\n }\n }\n\n // Note: TS can't narrow down the type of result due to generics\n if (result.success === false) {\n const error = result as SocketSdkErrorResult<T>\n const message = `${error.error || NO_ERROR_MESSAGE}`\n\n debugFn('error', `fail: ${description} bad response`)\n debugDir('inspect', { error })\n\n return {\n ok: false,\n message: 'Socket API returned an error',\n cause: `${message}${error.cause ? ` ( Reason: ${error.cause} )` : ''}`,\n data: {\n code: result.status,\n },\n }\n } else {\n const ok = result as SocketSdkSuccessResult<T>\n return {\n ok: true,\n data: ok.data,\n }\n }\n}\n\nexport async function queryApi(path: string, apiToken: string) {\n const baseUrl = getDefaultApiBaseUrl() || ''\n if (!baseUrl) {\n logger.warn(\n 'API endpoint is not set and default was empty. Request is likely to fail.',\n )\n }\n\n return await fetch(`${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}${path}`, {\n method: 'GET',\n headers: {\n Authorization: `Basic ${btoa(`${apiToken}:`)}`,\n },\n })\n}\n\nexport async function queryApiSafeText(\n path: string,\n desc?: string | undefined,\n): Promise<CResult<string>> {\n const apiToken = getDefaultApiToken()\n if (!apiToken) {\n return {\n ok: false,\n message: 'Authentication Error',\n cause:\n 'User must be authenticated to run this command. Run `socket login` and enter your Socket API token.',\n }\n }\n\n const { spinner } = constants\n\n if (desc) {\n spinner.start(`Requesting ${desc} from API...`)\n }\n\n let result\n try {\n result = await queryApi(path, apiToken)\n if (desc) {\n spinner.successAndStop(\n `Received Socket API response (after requesting ${desc}).`,\n )\n }\n } catch (e) {\n if (desc) {\n spinner.failAndStop(`An error was thrown while requesting ${desc}.`)\n }\n\n const cause = (e as undefined | { message: string })?.message\n\n debugFn('error', 'caught: await queryApi() error')\n debugDir('inspect', { error: e })\n\n return {\n ok: false,\n message: 'API Request failed to complete',\n ...(cause ? { cause } : {}),\n }\n }\n\n if (!result.ok) {\n const cause = await getErrorMessageForHttpStatusCode(result.status)\n return {\n ok: false,\n message: 'Socket API returned an error',\n cause: `${result.statusText}${cause ? ` (cause: ${cause})` : ''}`,\n }\n }\n\n try {\n const data = await result.text()\n return {\n ok: true,\n data,\n }\n } catch (e) {\n debugFn('error', 'caught: await result.text() error')\n debugDir('inspect', { error: e })\n return {\n ok: false,\n message: 'API Request failed to complete',\n cause: 'There was an unexpected error trying to read the response text',\n }\n }\n}\n\nexport async function queryApiSafeJson<T>(\n path: string,\n desc = '',\n): Promise<CResult<T>> {\n const result = await queryApiSafeText(path, desc)\n\n if (!result.ok) {\n return result\n }\n\n try {\n return {\n ok: true,\n data: JSON.parse(result.data) as T,\n }\n } catch (e) {\n return {\n ok: false,\n message: 'Server returned invalid JSON',\n cause: `Please report this. JSON.parse threw an error over the following response: \\`${(result.data?.slice?.(0, 100) || '<empty>').trim() + (result.data?.length > 100 ? '...' : '')}\\``,\n }\n }\n}\n\nexport async function sendApiRequest<T>(\n path: string,\n options: {\n method: 'POST' | 'PUT'\n body?: unknown\n desc?: string\n },\n): Promise<CResult<T>> {\n const apiToken = getDefaultApiToken()\n if (!apiToken) {\n return {\n ok: false,\n message: 'Authentication Error',\n cause:\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your Socket API token.',\n }\n }\n\n const baseUrl = getDefaultApiBaseUrl() || ''\n if (!baseUrl) {\n logger.warn(\n 'API endpoint is not set and default was empty. Request is likely to fail.',\n )\n }\n\n const { spinner } = constants\n\n if (options.desc) {\n spinner.start(`Requesting ${options.desc} from API...`)\n }\n\n let result\n try {\n const fetchOptions = {\n method: options.method,\n headers: {\n Authorization: `Basic ${btoa(`${apiToken}:`)}`,\n 'Content-Type': 'application/json',\n },\n ...(options.body ? { body: JSON.stringify(options.body) } : {}),\n }\n\n result = await fetch(\n `${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}${path}`,\n fetchOptions,\n )\n if (options.desc) {\n spinner.successAndStop(\n `Received Socket API response (after requesting ${options.desc}).`,\n )\n }\n } catch (e) {\n if (options.desc) {\n spinner.failAndStop(\n `An error was thrown while requesting ${options.desc}.`,\n )\n }\n\n const cause = (e as undefined | { message: string })?.message\n\n debugFn('error', `caught: await fetch() ${options.method} error`)\n debugDir('inspect', { error: e })\n\n return {\n ok: false,\n message: 'API Request failed to complete',\n ...(cause ? { cause } : {}),\n }\n }\n\n if (!result.ok) {\n const cause = await getErrorMessageForHttpStatusCode(result.status)\n return {\n ok: false,\n message: 'Socket API returned an error',\n cause: `${result.statusText}${cause ? ` (cause: ${cause})` : ''}`,\n data: {\n code: result.status,\n },\n }\n }\n\n try {\n const data = await result.json()\n return {\n ok: true,\n data: data as T,\n }\n } catch (e) {\n debugFn('error', 'caught: await result.json() error')\n debugDir('inspect', { error: e })\n return {\n ok: false,\n message: 'API Request failed to complete',\n cause: 'There was an unexpected error trying to parse the response JSON',\n }\n }\n}\n","import colors from 'yoctocolors-cjs'\n\nexport function failMsgWithBadge(\n badge: string,\n message: string | undefined,\n): string {\n const prefix = colors.bgRed(\n colors.bold(colors.white(` ${badge}${message ? ': ' : ''}`)),\n )\n const postfix = message ? ` ${colors.bold(message)}` : ''\n return `${prefix}${postfix}`\n}\n","export function mdTableStringNumber(\n title1: string,\n title2: string,\n obj: Record<string, number | string>,\n): string {\n // | Date | Counts |\n // | ----------- | ------ |\n // | Header | 201464 |\n // | Paragraph | 18 |\n let mw1 = title1.length\n let mw2 = title2.length\n for (const [key, value] of Object.entries(obj)) {\n mw1 = Math.max(mw1, key.length)\n mw2 = Math.max(mw2, String(value ?? '').length)\n }\n\n const lines = []\n lines.push(`| ${title1.padEnd(mw1, ' ')} | ${title2.padEnd(mw2)} |`)\n lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)\n for (const [key, value] of Object.entries(obj)) {\n lines.push(\n `| ${key.padEnd(mw1, ' ')} | ${String(value ?? '').padStart(mw2, ' ')} |`,\n )\n }\n lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)\n\n return lines.join('\\n')\n}\n\nexport function mdTable<T extends Array<Record<string, string>>>(\n logs: T,\n // This is saying \"an array of strings and the strings are a valid key of elements of T\"\n // In turn, T is defined above as the audit log event type from our OpenAPI docs.\n cols: Array<string & keyof T[number]>,\n titles: string[] = cols,\n): string {\n // Max col width required to fit all data in that column\n const cws = cols.map(col => col.length)\n\n for (const log of logs) {\n for (let i = 0, { length } = cols; i < length; i += 1) {\n // @ts-ignore\n const val: unknown = log[cols[i] ?? ''] ?? ''\n cws[i] = Math.max(\n cws[i] ?? 0,\n String(val).length,\n (titles[i] || '').length,\n )\n }\n }\n\n let div = '|'\n for (const cw of cws) {\n div += ' ' + '-'.repeat(cw) + ' |'\n }\n\n let header = '|'\n for (let i = 0, { length } = titles; i < length; i += 1) {\n header += ' ' + String(titles[i]).padEnd(cws[i] ?? 0, ' ') + ' |'\n }\n\n let body = ''\n for (const log of logs) {\n body += '|'\n for (let i = 0, { length } = cols; i < length; i += 1) {\n // @ts-ignore\n const val: unknown = log[cols[i] ?? ''] ?? ''\n body += ' ' + String(val).padEnd(cws[i] ?? 0, ' ') + ' |'\n }\n body += '\\n'\n }\n\n return [div, header, div, body.trim(), div].filter(s => s.trim()).join('\\n')\n}\n\nexport function mdTableOfPairs(\n arr: Array<[string, string]>,\n // This is saying \"an array of strings and the strings are a valid key of elements of T\"\n // In turn, T is defined above as the audit log event type from our OpenAPI docs.\n cols: string[],\n): string {\n // Max col width required to fit all data in that column\n const cws = cols.map(col => col.length)\n\n for (const [key, val] of arr) {\n cws[0] = Math.max(cws[0] ?? 0, String(key).length)\n cws[1] = Math.max(cws[1] ?? 0, String(val ?? '').length)\n }\n\n let div = '|'\n for (const cw of cws) {\n div += ' ' + '-'.repeat(cw) + ' |'\n }\n\n let header = '|'\n for (let i = 0, { length } = cols; i < length; i += 1) {\n header += ' ' + String(cols[i]).padEnd(cws[i] ?? 0, ' ') + ' |'\n }\n\n let body = ''\n for (const [key, val] of arr) {\n body += '|'\n body += ' ' + String(key).padEnd(cws[0] ?? 0, ' ') + ' |'\n body += ' ' + String(val ?? '').padEnd(cws[1] ?? 0, ' ') + ' |'\n body += '\\n'\n }\n\n return [div, header, div, body.trim(), div].filter(s => s.trim()).join('\\n')\n}\n","import { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { isObject } from '@socketsecurity/registry/lib/objects'\n\nimport type { CResult } from '../types.mts'\n\n// Serialize the final result object before printing it\n// All commands that support the --json flag should call this before printing\nexport function serializeResultJson(data: CResult<unknown>): string {\n if (!isObject(data)) {\n process.exitCode = 1\n\n debugFn('inspect', { data })\n\n // We should not allow the JSON value to be \"null\", or a boolean/number/string,\n // even if they are valid \"json\".\n return `${JSON.stringify({\n ok: false,\n message: 'Unable to serialize JSON',\n cause:\n 'There was a problem converting the data set to JSON. The JSON was not an object. Please try again without --json',\n }).trim()}\\n`\n }\n\n try {\n return `${JSON.stringify(data, null, 2).trim()}\\n`\n } catch (e) {\n process.exitCode = 1\n\n const message =\n 'There was a problem converting the data set to JSON. Please try again without --json'\n\n logger.fail(message)\n debugDir('inspect', { error: e })\n\n // This could be caused by circular references, which is an \"us\" problem.\n return `${JSON.stringify({\n ok: false,\n message: 'Unable to serialize JSON',\n cause: message,\n }).trim()}\\n`\n }\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { LOG_SYMBOLS, logger } from '@socketsecurity/registry/lib/logger'\nimport { stripAnsi } from '@socketsecurity/registry/lib/strings'\n\nimport { failMsgWithBadge } from './fail-msg-with-badge.mts'\nimport { serializeResultJson } from './serialize-result-json.mts'\n\nimport type { OutputKind } from '../types.mts'\n\nexport function checkCommandInput(\n outputKind: OutputKind,\n ...checks: Array<{\n fail: string\n message: string\n test: boolean\n nook?: boolean | undefined\n pass?: string | undefined\n }>\n): boolean {\n if (checks.every(d => d.test)) {\n return true\n }\n\n const msg = ['Please review the input requirements and try again', '']\n for (const d of checks) {\n // If nook, then ignore when test is ok\n if (d.nook && d.test) {\n continue\n }\n const lines = d.message.split('\\n')\n const { length: lineCount } = lines\n if (!lineCount) {\n continue\n }\n // If the message has newlines then format the first line with the input\n // expectation and the rest indented below it.\n const logSymbol = d.test ? LOG_SYMBOLS.success : LOG_SYMBOLS.fail\n const reason = d.test ? d.pass : d.fail\n let listItem = ` ${logSymbol} ${lines[0]}`\n if (reason) {\n const styledReason = d.test ? colors.green(reason) : colors.red(reason)\n listItem += ` (${styledReason})`\n }\n msg.push(listItem)\n if (lineCount > 1) {\n msg.push(...lines.slice(1).map(str => ` ${str}`))\n }\n }\n\n // Use exit status of 2 to indicate incorrect usage, generally invalid\n // options or missing arguments.\n // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html\n process.exitCode = 2\n\n if (outputKind === 'json') {\n logger.log(\n serializeResultJson({\n ok: false,\n message: 'Input error',\n data: stripAnsi(msg.join('\\n')),\n }),\n )\n } else {\n logger.fail(failMsgWithBadge('Input error', msg.join('\\n')))\n }\n\n return false\n}\n","import type { OutputKind } from '../types.mts'\n\nexport function getOutputKind(json: unknown, markdown: unknown): OutputKind {\n if (json) {\n return 'json'\n }\n if (markdown) {\n return 'markdown'\n }\n return 'text'\n}\n","import { createRequire } from 'node:module'\nimport path from 'node:path'\n\nimport constants from '../constants.mts'\n\nconst require = createRequire(import.meta.url)\n\nlet _requirements:\n | Readonly<typeof import('../../requirements.json')>\n | undefined\n\nexport function getRequirements() {\n if (_requirements === undefined) {\n _requirements = /*@__PURE__*/ require(\n path.join(constants.rootPath, 'requirements.json'),\n )\n }\n return _requirements!\n}\n","import { isObject } from '@socketsecurity/registry/lib/objects'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport { indentString } from '@socketsecurity/registry/lib/strings'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { getRequirements } from './requirements.mts'\n\nimport type { MeowFlags } from '../flags.mts'\n\ntype ApiRequirementsOptions = {\n indent?: number | undefined\n}\n\ntype HelpListOptions = {\n indent?: number | undefined\n keyPrefix?: string | undefined\n padName?: number | undefined\n}\n\ntype ListDescription =\n | { description: string }\n | { description: string; hidden: boolean }\n\nfunction camelToKebab(string: string): string {\n return string.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()\n}\n\nexport function getFlagApiRequirementsOutput(\n cmdPath: string,\n options?: ApiRequirementsOptions | undefined,\n): string {\n const { indent = 6 } = {\n __proto__: null,\n ...options,\n } as ApiRequirementsOptions\n const key = cmdPath.replace(/^socket[: ]/, '').replace(/ +/g, ':')\n const requirements = getRequirements()\n const data = (requirements.api as any)[key]\n let result = ''\n if (data) {\n const quota: number = data?.quota\n const perms: string[] = data?.permissions\n const padding = ''.padEnd(indent)\n const lines = []\n if (typeof quota === 'number') {\n lines.push(`${padding}- Quota: ${quota} ${pluralize('unit', quota)}`)\n }\n if (Array.isArray(perms) && perms.length) {\n lines.push(`${padding}- Permissions: ${perms.join(' ')}`)\n }\n result += lines.join('\\n')\n }\n return result.trim() || '(none)'\n}\n\nexport function getFlagListOutput(\n list: MeowFlags,\n options?: HelpListOptions | undefined,\n): string {\n const { keyPrefix = '--' } = {\n __proto__: null,\n ...options,\n } as HelpListOptions\n return getHelpListOutput(\n {\n ...list,\n },\n { ...options, keyPrefix },\n )\n}\n\nexport function getHelpListOutput(\n list: Record<string, ListDescription>,\n options?: HelpListOptions | undefined,\n): string {\n const {\n indent = 6,\n keyPrefix = '',\n padName = 20,\n } = {\n __proto__: null,\n ...options,\n } as HelpListOptions\n let result = ''\n const names = Object.keys(list).sort(naturalCompare)\n for (const name of names) {\n const entry = list[name]\n const entryIsObj = isObject(entry)\n if (entryIsObj && 'hidden' in entry && entry?.hidden) {\n continue\n }\n const printedName = `${keyPrefix}${camelToKebab(name)}`\n const preDescription = `${''.padEnd(indent)}${printedName.padEnd(Math.max(printedName.length + 2, padName))}`\n\n result += preDescription\n\n const description = entryIsObj ? entry.description : String(entry)\n if (description) {\n result += indentString(description, preDescription.length).trimStart()\n }\n result += '\\n'\n }\n return result.trim() || '(none)'\n}\n","import path from 'node:path'\n\nimport { escapeRegExp } from '@socketsecurity/registry/lib/regexps'\n\nimport constants from '../constants.mts'\n\n// Replace the start of a path with ~/ when it starts with your home dir.\n// A common way to abbreviate the user home dir (though not strictly posix).\nexport function tildify(cwd: string) {\n return cwd.replace(\n new RegExp(`^${escapeRegExp(constants.homePath)}(?:${path.sep}|$)`, 'i'),\n '~/',\n )\n}\n","import meow from 'meow'\nimport terminalLink from 'terminal-link'\nimport colors from 'yoctocolors-cjs'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { hasOwn, toSortedObject } from '@socketsecurity/registry/lib/objects'\nimport { normalizePath } from '@socketsecurity/registry/lib/path'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport {\n indentString,\n trimNewlines,\n} from '@socketsecurity/registry/lib/strings'\n\nimport {\n getConfigValueOrUndef,\n isReadOnlyConfig,\n overrideCachedConfig,\n overrideConfigApiToken,\n} from './config.mts'\nimport { getFlagListOutput, getHelpListOutput } from './output-formatting.mts'\nimport constants from '../constants.mts'\nimport { commonFlags } from '../flags.mts'\nimport { getVisibleTokenPrefix } from './sdk.mts'\nimport { tildify } from './tildify.mts'\n\nimport type { MeowFlags } from '../flags.mts'\nimport type { Options, Result } from 'meow'\n\nexport interface CliAlias {\n description: string\n argv: readonly string[]\n hidden?: boolean | undefined\n}\n\nexport type CliAliases = Record<string, CliAlias>\n\nexport type CliSubcommandRun = (\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n context: { parentName: string },\n) => Promise<void> | void\n\nexport interface CliSubcommand {\n description: string\n hidden?: boolean | undefined\n run: CliSubcommandRun\n}\n\n// Property names are picked such that the name is at the top when the props\n// get ordered by alphabet while flags is near the bottom and the help text\n// at the bottom, because they tend ot occupy the most lines of code.\nexport interface CliCommandConfig {\n commandName: string\n description: string\n hidden: boolean\n flags: MeowFlags\n help: (command: string, config: CliCommandConfig) => string\n}\n\nexport interface MeowOptions extends Options<any> {\n aliases?: CliAliases | undefined\n argv: readonly string[]\n name: string\n // When no sub-command is given, default to this sub-command.\n defaultSub?: string\n}\n\nconst HELP_INDENT = 2\n\nconst HELP_PAD_NAME = 28\n\nfunction description(command: CliSubcommand | undefined): string {\n const description = command?.description\n const str =\n typeof description === 'string' ? description : String(description)\n return indentString(str, HELP_PAD_NAME).trimStart()\n}\n\n/**\n * Find the best matching command name for a typo.\n */\nfunction findBestCommandMatch(\n input: string,\n subcommands: Record<string, unknown>,\n aliases: Record<string, unknown>,\n): string | null {\n let bestMatch = null\n let bestScore = Infinity\n const allCommands = [...Object.keys(subcommands), ...Object.keys(aliases)]\n for (const command of allCommands) {\n const distance = levenshteinDistance(\n input.toLowerCase(),\n command.toLowerCase(),\n )\n const maxLength = Math.max(input.length, command.length)\n // Only suggest if the similarity is reasonable (more than 50% similar).\n if (distance < maxLength * 0.5 && distance < bestScore) {\n bestScore = distance\n bestMatch = command\n }\n }\n return bestMatch\n}\n\n/**\n * Calculate Levenshtein distance between two strings for fuzzy matching.\n */\nfunction levenshteinDistance(a: string, b: string): number {\n const matrix = Array.from({ length: a.length + 1 }, () =>\n Array(b.length + 1).fill(0),\n )\n for (let i = 0; i <= a.length; i++) {\n matrix[i]![0] = i\n }\n for (let j = 0; j <= b.length; j++) {\n matrix[0]![j] = j\n }\n for (let i = 1; i <= a.length; i++) {\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n matrix[i]![j] = Math.min(\n matrix[i - 1]![j]! + 1, // Deletion.\n matrix[i]![j - 1]! + 1, // Insertion.\n matrix[i - 1]![j - 1]! + cost, // Substitution.\n )\n }\n }\n return matrix[a.length]![b.length]!\n}\n\nfunction shouldSuppressBanner(flags: Record<string, unknown>): boolean {\n return Boolean(flags['json'] || flags['markdown'] || flags['nobanner'])\n}\n\n// For debugging. Whenever you call meowOrExit it will store the command here\n// This module exports a getter that returns the current value.\nlet lastSeenCommand = ''\n\nexport function getLastSeenCommand(): string {\n return lastSeenCommand\n}\n\nexport async function meowWithSubcommands(\n subcommands: Record<string, CliSubcommand>,\n options: MeowOptions,\n): Promise<void> {\n const {\n aliases = {},\n argv,\n defaultSub,\n importMeta,\n name,\n ...additionalOptions\n } = { __proto__: null, ...options }\n const [commandOrAliasName_, ...rawCommandArgv] = argv\n let commandOrAliasName = commandOrAliasName_\n if (!commandOrAliasName && defaultSub) {\n commandOrAliasName = defaultSub\n }\n\n const flags: MeowFlags = {\n ...commonFlags,\n version: {\n type: 'boolean',\n hidden: true,\n description: 'Print the app version',\n },\n ...additionalOptions.flags,\n }\n\n // No further args or first arg is a flag (shrug)\n const isRootCommand =\n name === 'socket' &&\n (!commandOrAliasName || commandOrAliasName?.startsWith('-'))\n\n // Try to support `socket <purl>` as a shorthand for `socket package score <purl>`\n if (!isRootCommand) {\n if (commandOrAliasName?.startsWith('pkg:')) {\n logger.info('Invoking `socket package score`.')\n return await meowWithSubcommands(subcommands, {\n ...options,\n argv: ['package', 'deep', ...argv],\n })\n }\n // Support `socket npm/lodash` or whatever as a shorthand, too.\n // Accept any ecosystem and let the remote sort it out.\n if (/^[a-z]+\\//.test(commandOrAliasName || '')) {\n logger.info('Invoking `socket package score`.')\n return await meowWithSubcommands(subcommands, {\n ...options,\n argv: [\n 'package',\n 'deep',\n `pkg:${commandOrAliasName}`,\n ...rawCommandArgv,\n ],\n })\n }\n }\n\n if (isRootCommand) {\n flags['help'] = {\n ...flags['help'],\n hidden: false,\n } as (typeof flags)['help']\n\n flags['config'] = {\n ...flags['config'],\n hidden: false,\n } as (typeof flags)['config']\n\n flags['dryRun'] = {\n ...flags['dryRun'],\n hidden: false,\n } as (typeof flags)['dryRun']\n\n flags['maxOldSpaceSize'] = {\n ...flags['maxOldSpaceSize'],\n hidden: false,\n } as (typeof flags)['maxOldSpaceSize']\n\n flags['maxSemiSpaceSize'] = {\n ...flags['maxSemiSpaceSize'],\n hidden: false,\n } as (typeof flags)['maxSemiSpaceSize']\n\n flags['version'] = {\n ...flags['version'],\n hidden: false,\n } as (typeof flags)['version']\n\n delete flags['json']\n delete flags['markdown']\n } else {\n delete flags['help']\n delete flags['version']\n }\n\n // This is basically a dry-run parse of cli args and flags. We use this to\n // determine config overrides and expected output mode.\n const cli1 = meow({\n argv,\n importMeta,\n ...additionalOptions,\n flags,\n // Ensure we don't check unknown flags.\n allowUnknownFlags: true,\n // Prevent meow from potentially exiting early.\n autoHelp: false,\n autoVersion: false,\n // We want to detect whether a bool flag is given at all.\n booleanDefault: undefined,\n })\n\n const orgFlag = String(cli1.flags['org'] || '') || undefined\n\n // Hard override the config if instructed to do so.\n // The env var overrides the --flag, which overrides the persisted config\n // Also, when either of these are used, config updates won't persist.\n let configOverrideResult\n if (constants.ENV.SOCKET_CLI_CONFIG) {\n configOverrideResult = overrideCachedConfig(constants.ENV.SOCKET_CLI_CONFIG)\n } else if (cli1.flags['config']) {\n configOverrideResult = overrideCachedConfig(\n String(cli1.flags['config'] || ''),\n )\n }\n\n if (constants.ENV.SOCKET_CLI_NO_API_TOKEN) {\n // This overrides the config override and even the explicit token env var.\n // The config will be marked as readOnly to prevent persisting it.\n overrideConfigApiToken(undefined)\n } else {\n const tokenOverride = constants.ENV.SOCKET_CLI_API_TOKEN\n if (tokenOverride) {\n // This will set the token (even if there was a config override) and\n // set it to readOnly, making sure the temp token won't be persisted.\n overrideConfigApiToken(tokenOverride)\n }\n }\n\n if (configOverrideResult?.ok === false) {\n if (!shouldSuppressBanner(cli1.flags)) {\n emitBanner(name, orgFlag)\n // Add newline in stderr.\n logger.error('')\n }\n logger.fail(configOverrideResult.message)\n process.exitCode = 2\n return\n }\n\n // If we have got some args, then lets find out if we can find a command.\n if (commandOrAliasName) {\n const alias = aliases[commandOrAliasName]\n // First: Resolve argv data from alias if its an alias that's been given.\n const [commandName, ...commandArgv] = alias\n ? [...alias.argv, ...rawCommandArgv]\n : [commandOrAliasName, ...rawCommandArgv]\n // Second: Find a command definition using that data.\n const commandDefinition = commandName ? subcommands[commandName] : undefined\n // Third: If a valid command has been found, then we run it...\n if (commandDefinition) {\n return await commandDefinition.run(commandArgv, importMeta, {\n parentName: name,\n })\n }\n\n // Suggest similar commands for typos.\n if (commandName && !commandDefinition) {\n const suggestion = findBestCommandMatch(commandName, subcommands, aliases)\n if (suggestion) {\n process.exitCode = 2\n logger.fail(\n `Unknown command \"${commandName}\". Did you mean \"${suggestion}\"?`,\n )\n return\n }\n }\n }\n\n const lines = ['', 'Usage', ` $ ${name} <command>`]\n if (isRootCommand) {\n lines.push(\n ` $ ${name} scan create --json`,\n ` $ ${name} package score npm lodash --markdown`,\n )\n }\n lines.push('')\n if (isRootCommand) {\n // \"Bucket\" some commands for easier usage.\n const commands = new Set([\n 'analytics',\n 'audit-log',\n 'ci',\n 'cdxgen',\n 'config',\n 'dependencies',\n 'fix',\n 'install',\n //'json',\n 'license',\n 'login',\n 'logout',\n 'manifest',\n 'npm',\n 'npx',\n 'optimize',\n 'organization',\n 'package',\n //'patch',\n 'raw-npm',\n 'raw-npx',\n 'repository',\n 'scan',\n //'security',\n 'threat-feed',\n 'uninstall',\n 'wrapper',\n ])\n Object.entries(subcommands)\n .filter(([_name, subcommand]) => !subcommand.hidden)\n .map(([name]) => name)\n .forEach(name => {\n if (commands.has(name)) {\n commands.delete(name)\n } else {\n logger.fail('Received an unknown command:', name)\n }\n })\n if (commands.size) {\n logger.fail(\n 'Found commands in the list that were not marked as public or not defined at all:',\n // Node < 22 will print 'Object (n)' before the array. So to have consistent\n // test snapshots we use joinAnd.\n joinAnd(\n Array.from(commands)\n .sort(naturalCompare)\n .map(c => `'${c}'`),\n ),\n )\n }\n lines.push(\n 'Note: All commands have their own --help',\n '',\n 'Main commands',\n ` socket login ${description(subcommands['login'])}`,\n ` socket scan create Create a new Socket scan and report`,\n ` socket npm/lodash@4.17.21 Request the Socket score of a package`,\n ` socket ci ${description(subcommands['ci'])}`,\n ``,\n 'Socket API',\n ` analytics ${description(subcommands['analytics'])}`,\n ` audit-log ${description(subcommands['audit-log'])}`,\n ` organization ${description(subcommands['organization'])}`,\n ` package ${description(subcommands['package'])}`,\n ` repository ${description(subcommands['repository'])}`,\n ` scan ${description(subcommands['scan'])}`,\n ` threat-feed ${description(subcommands['threat-feed'])}`,\n ``,\n 'Local tools',\n ` fix ${description(subcommands['fix'])}`,\n ` manifest ${description(subcommands['manifest'])}`,\n ` npm ${description(subcommands['npm'])}`,\n ` npx ${description(subcommands['npx'])}`,\n ` optimize ${description(subcommands['optimize'])}`,\n ` raw-npm ${description(subcommands['raw-npm'])}`,\n ` raw-npx ${description(subcommands['raw-npx'])}`,\n '',\n 'CLI configuration',\n ` config ${description(subcommands['config'])}`,\n ` install ${description(subcommands['install'])}`,\n ` login Socket API login and CLI setup`,\n ` logout ${description(subcommands['logout'])}`,\n ` uninstall ${description(subcommands['uninstall'])}`,\n ` wrapper ${description(subcommands['wrapper'])}`,\n )\n } else {\n lines.push('Commands')\n lines.push(\n ` ${getHelpListOutput(\n {\n ...toSortedObject(\n Object.fromEntries(\n Object.entries(subcommands).filter(\n ({ 1: subcommand }) => !subcommand.hidden,\n ),\n ),\n ),\n ...toSortedObject(\n Object.fromEntries(\n Object.entries(aliases).filter(({ 1: alias }) => {\n const { hidden } = alias\n const cmdName = hidden ? '' : alias.argv[0]\n const subcommand = cmdName ? subcommands[cmdName] : undefined\n return subcommand && !subcommand.hidden\n }),\n ),\n ),\n },\n { indent: HELP_INDENT, padName: HELP_PAD_NAME },\n )}`,\n )\n }\n\n lines.push('', 'Options')\n if (isRootCommand) {\n lines.push(\n ' Note: All commands have these flags even when not displayed in their help',\n '',\n )\n } else {\n lines.push('')\n }\n lines.push(\n ` ${getFlagListOutput(flags, { indent: HELP_INDENT, padName: HELP_PAD_NAME })}`,\n )\n if (isRootCommand) {\n lines.push(\n '',\n 'Environment variables',\n ' SOCKET_CLI_API_TOKEN Set the Socket API token',\n ' SOCKET_CLI_CONFIG A JSON stringified Socket configuration object',\n ' SOCKET_CLI_GITHUB_API_URL Change the base URL for GitHub REST API calls',\n ' SOCKET_CLI_GIT_USER_EMAIL The git config `user.email` used by Socket CLI',\n ` ${colors.italic('Defaults:')} github-actions[bot]@users.noreply.github.com`,\n ' SOCKET_CLI_GIT_USER_NAME The git config `user.name` used by Socket CLI',\n ` ${colors.italic('Defaults:')} github-actions[bot]`,\n ` SOCKET_CLI_GITHUB_TOKEN A classic or fine-grained ${terminalLink('GitHub personal access token', 'https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens')}`,\n ` ${colors.italic('Aliases:')} GITHUB_TOKEN`,\n ' SOCKET_CLI_NO_API_TOKEN Make the default API token `undefined`',\n ' SOCKET_CLI_NPM_PATH The absolute location of the npm directory',\n ' SOCKET_CLI_ORG_SLUG Specify the Socket organization slug',\n '',\n ' SOCKET_CLI_ACCEPT_RISKS Accept risks of a Socket wrapped npm/npx run',\n ' SOCKET_CLI_VIEW_ALL_RISKS View all risks of a Socket wrapped npm/npx run',\n '',\n 'Environment variables for development',\n ' SOCKET_CLI_API_BASE_URL Change the base URL for Socket API calls',\n ` ${colors.italic('Defaults:')} The \"apiBaseUrl\" value of socket/settings local app data`,\n ' if present, else https://api.socket.dev/v0/',\n ' SOCKET_CLI_API_PROXY Set the proxy Socket API requests are routed through, e.g. if set to',\n ` ${terminalLink('http://127.0.0.1:9090', 'https://docs.proxyman.io/troubleshooting/couldnt-see-any-requests-from-3rd-party-network-libraries')} then all request are passed through that proxy`,\n ` ${colors.italic('Aliases:')} HTTPS_PROXY, https_proxy, HTTP_PROXY, and http_proxy`,\n ' SOCKET_CLI_API_TIMEOUT Set the timeout in milliseconds for Socket API requests',\n ' SOCKET_CLI_DEBUG Enable debug logging in Socket CLI',\n ` DEBUG Enable debug logging based on the ${terminalLink('debug', 'https://socket.dev/npm/package/debug')} package`,\n )\n }\n\n // Parse it again. Config overrides should now be applied (may affect help).\n // Note: this is displayed as help screen if the command does not override it\n // (which is the case for most sub-commands with sub-commands)\n const cli2 = meow({\n argv,\n importMeta,\n ...additionalOptions,\n flags,\n // Do not strictly check for flags here.\n allowUnknownFlags: true,\n // We will emit help when we're ready.\n // Plus, if we allow this then meow may exit here.\n autoHelp: false,\n autoVersion: false,\n // We want to detect whether a bool flag is given at all.\n booleanDefault: undefined,\n help: lines.map(l => indentString(l, HELP_INDENT)).join('\\n'),\n })\n\n // ...else we provide basic instructions and help.\n if (!shouldSuppressBanner(cli2.flags)) {\n emitBanner(name, orgFlag)\n // meow will add newline so don't add stderr spacing here\n }\n if (!cli2.flags['help'] && cli2.flags['dryRun']) {\n process.exitCode = 0\n logger.log(`${constants.DRY_RUN_LABEL}: No-op, call a sub-command; ok`)\n } else {\n // When you explicitly request --help, the command should be successful\n // so we exit(0). If we do it because we need more input, we exit(2).\n cli2.showHelp(cli2.flags['help'] ? 0 : 2)\n }\n}\n\n/**\n * Note: meow will exit immediately if it calls its .showHelp()\n */\nexport function meowOrExit({\n allowUnknownFlags = true,\n argv,\n config,\n importMeta,\n parentName,\n}: {\n allowUnknownFlags?: boolean | undefined\n argv: readonly string[]\n config: CliCommandConfig\n parentName: string\n importMeta: ImportMeta\n}): Result<MeowFlags> {\n const command = `${parentName} ${config.commandName}`\n lastSeenCommand = command\n\n // This exits if .printHelp() is called either by meow itself or by us.\n const cli = meow({\n argv,\n // Prevent meow from potentially exiting early.\n autoHelp: false,\n autoVersion: false,\n // We want to detect whether a bool flag is given at all.\n booleanDefault: undefined,\n collectUnknownFlags: true,\n description: config.description,\n flags: config.flags,\n help: trimNewlines(config.help(command, config)),\n importMeta,\n })\n\n if (!shouldSuppressBanner(cli.flags)) {\n emitBanner(command, String(cli.flags['org'] || '') || undefined)\n // Add newline in stderr.\n // Meow help adds a newline too so we do it here.\n logger.error('')\n }\n\n // As per https://github.com/sindresorhus/meow/issues/178\n // Setting `allowUnknownFlags: false` makes it reject camel cased flags.\n // if (!allowUnknownFlags) {\n // // Run meow specifically with the flag setting. It will exit(2) if an\n // // invalid flag is set and print a message.\n // meow({\n // argv,\n // allowUnknownFlags: false,\n // // Prevent meow from potentially exiting early.\n // autoHelp: false,\n // autoVersion: false,\n // description: config.description,\n // flags: config.flags,\n // help: trimNewlines(config.help(command, config)),\n // importMeta,\n // })\n // }\n\n if (cli.flags['help']) {\n cli.showHelp(0)\n }\n\n // meow doesn't detect 'version' as an unknown flag, so we do the leg work here.\n if (!hasOwn(config.flags, 'version') && cli.flags['version']) {\n // Use `console.error` here instead of `logger.error` to match meow behavior.\n console.error('Unknown flag\\n--version')\n // eslint-disable-next-line n/no-process-exit\n process.exit(2)\n }\n\n // Now test for help state. Run meow again. If it exits now, it must be due\n // to wanting to print the help screen. But it would exit(0) and we want a\n // consistent exit(2) for that case (missing input).\n // TODO: Move away from meow.\n process.exitCode = 2\n meow({\n argv,\n // As per https://github.com/sindresorhus/meow/issues/178\n // Setting `allowUnknownFlags: false` makes it reject camel cased flags.\n allowUnknownFlags: Boolean(allowUnknownFlags),\n // Prevent meow from potentially exiting early.\n autoHelp: false,\n autoVersion: false,\n description: config.description,\n help: trimNewlines(config.help(command, config)),\n importMeta,\n flags: config.flags,\n })\n // Ok, no help, reset to default.\n process.exitCode = 0\n\n return cli\n}\n\nexport function emitBanner(name: string, orgFlag: string | undefined) {\n // Print a banner at the top of each command.\n // This helps with brand recognition and marketing.\n // It also helps with debugging since it contains version and command details.\n // Note: print over stderr to preserve stdout for flags like --json and\n // --markdown. If we don't do this, you can't use --json in particular\n // and pipe the result to other tools. By emitting the banner over stderr\n // you can do something like `socket scan view xyz | jq | process`.\n // The spinner also emits over stderr for example.\n logger.error(getAsciiHeader(name, orgFlag))\n}\n\nfunction getAsciiHeader(command: string, orgFlag: string | undefined) {\n // Note: In tests we return <redacted> because otherwise snapshots will fail.\n const { REDACTED } = constants\n const redacting = constants.ENV.VITEST\n const cliVersion = redacting\n ? REDACTED\n : constants.ENV.INLINED_SOCKET_CLI_VERSION_HASH\n const nodeVersion = redacting ? REDACTED : process.version\n const defaultOrg = getConfigValueOrUndef('defaultOrg')\n const readOnlyConfig = isReadOnlyConfig() ? '*' : '.'\n const shownToken = redacting\n ? REDACTED\n : getVisibleTokenPrefix() || '(not set)'\n const relCwd = redacting ? REDACTED : normalizePath(tildify(process.cwd()))\n // Note: we must redact org when creating snapshots because dev machine probably\n // has a default org set but CI won't. Showing --org is fine either way.\n const orgPart = orgFlag\n ? `--org: ${orgFlag}`\n : redacting\n ? 'org: <redacted>'\n : defaultOrg\n ? `default org: ${defaultOrg}`\n : '(org not set)'\n // Note: We could draw these with ascii box art instead but I worry about\n // portability and paste-ability. \"simple\" ascii chars just work.\n const body = `\n _____ _ _ /---------------\n | __|___ ___| |_ ___| |_ | Socket.dev CLI ver ${cliVersion}\n |__ | ${readOnlyConfig} | _| '_| -_| _| | Node: ${nodeVersion}, API token: ${shownToken}, ${orgPart}\n |_____|___|___|_,_|___|_|.dev | Command: \\`${command}\\`, cwd: ${relCwd}\n `.trim()\n // Note: logger will auto-append a newline.\n return ` ${body}`\n}\n","export function msAtHome(isoTimeStamp: string): string {\n const timeStart = Date.parse(isoTimeStamp)\n const timeEnd = Date.now()\n\n const rtf = new Intl.RelativeTimeFormat('en', {\n numeric: 'always',\n style: 'short',\n })\n\n const delta = timeEnd - timeStart\n if (delta < 60 * 60 * 1000) {\n return rtf.format(-Math.round(delta / (60 * 1000)), 'minute')\n // return Math.round(delta / (60 * 1000)) + ' min ago'\n } else if (delta < 24 * 60 * 60 * 1000) {\n return rtf.format(-(delta / (60 * 60 * 1000)).toFixed(1), 'hour')\n // return (delta / (60 * 60 * 1000)).toFixed(1) + ' hr ago'\n } else if (delta < 7 * 24 * 60 * 60 * 1000) {\n return rtf.format(-(delta / (24 * 60 * 60 * 1000)).toFixed(1), 'day')\n // return (delta / (24 * 60 * 60 * 1000)).toFixed(1) + ' day ago'\n } else {\n return isoTimeStamp.slice(0, 10)\n }\n}\n","import { handleApiCall } from '../../utils/api.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { SetupSdkOptions } from '../../utils/sdk.mts'\nimport type { SocketSdk, SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchOrganizationOptions = {\n desc?: string | undefined\n sdk?: SocketSdk | undefined\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport type EnterpriseOrganization = Omit<Organization, 'plan'> & {\n plan: 'enterprise'\n}\n\nexport type EnterpriseOrganizations = EnterpriseOrganization[]\n\nexport type Organization =\n SocketSdkSuccessResult<'getOrganizations'>['data']['organizations'][string]\n\nexport type Organizations = Organization[]\n\nexport type OrganizationsData = { organizations: Organizations }\n\nexport type OrganizationsCResult = CResult<OrganizationsData>\n\nexport async function fetchOrganization(\n options?: FetchOrganizationOptions | undefined,\n): Promise<OrganizationsCResult> {\n const {\n desc = 'organization list',\n sdk,\n sdkOpts,\n } = {\n __proto__: null,\n ...options,\n } as FetchOrganizationOptions\n\n let sockSdk = sdk\n if (!sockSdk) {\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n sockSdk = sockSdkCResult.data\n }\n\n const orgsCResult = await handleApiCall(sockSdk.getOrganizations(), { desc })\n if (!orgsCResult.ok) {\n return orgsCResult\n }\n\n return {\n ...orgsCResult,\n data: {\n organizations: Object.values(orgsCResult.data.organizations),\n },\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\nimport { select } from '@socketsecurity/registry/lib/prompts'\n\nimport { fetchOrganization } from '../organization/fetch-organization-list.mts'\n\nexport async function suggestOrgSlug(): Promise<string | void> {\n const orgsCResult = await fetchOrganization()\n if (!orgsCResult.ok) {\n logger.fail(\n 'Failed to lookup organization list from API, unable to suggest',\n )\n return undefined\n }\n\n // Ignore a failed request here. It was not the primary goal of\n // running this command and reporting it only leads to end-user confusion.\n const { organizations } = orgsCResult.data\n const proceed = await select<string>({\n message:\n 'Missing org name; do you want to use any of these orgs for this scan?',\n choices: [\n ...organizations.map(o => {\n const name = o.name ?? o.slug\n return {\n name: `Yes [${name}]`,\n value: name,\n description: `Use \"${name}\" as the organization`,\n }\n }),\n {\n name: 'No',\n value: '',\n description:\n 'Do not use any of these organizations (will end in a no-op)',\n },\n ],\n })\n\n if (proceed) {\n return proceed\n }\n return undefined\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\nimport { select } from '@socketsecurity/registry/lib/prompts'\n\nimport { getConfigValue, updateConfigValue } from '../../utils/config.mts'\n\nexport async function suggestToPersistOrgSlug(orgSlug: string): Promise<void> {\n const skipAsk = getConfigValue('skipAskToPersistDefaultOrg')\n if (!skipAsk.ok || skipAsk.data) {\n // Don't ask to store it when disabled before, or when reading config fails.\n return\n }\n\n const result = await select<string>({\n message: `Would you like to use this org (${orgSlug}) as the default org for future calls?`,\n choices: [\n {\n name: 'Yes',\n value: 'yes',\n description: 'Stores it in your config',\n },\n {\n name: 'No',\n value: 'no',\n description: 'Do not persist this org as default org',\n },\n {\n name: \"No and don't ask again\",\n value: 'sush',\n description:\n 'Do not store as default org and do not ask again to persist it',\n },\n ],\n })\n if (result === 'yes') {\n const updateResult = updateConfigValue('defaultOrg', orgSlug)\n if (updateResult.ok) {\n logger.success('Updated default org config to:', orgSlug)\n } else {\n logger.fail(\n '(Non blocking) Failed to update default org in config:',\n updateResult.cause,\n )\n }\n } else if (result === 'sush') {\n const updateResult = updateConfigValue('skipAskToPersistDefaultOrg', true)\n if (updateResult.ok) {\n logger.info('Default org not changed. Will not ask to persist again.')\n } else {\n logger.fail(\n `(Non blocking) Failed to store preference; will ask to persist again next time. Reason: ${updateResult.cause}`,\n )\n }\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getConfigValueOrUndef } from './config.mts'\nimport { suggestOrgSlug } from '../commands/scan/suggest-org-slug.mts'\nimport { suggestToPersistOrgSlug } from '../commands/scan/suggest-to-persist-orgslug.mts'\n\nexport async function determineOrgSlug(\n orgFlag: string,\n interactive: boolean,\n dryRun: boolean,\n): Promise<[string, string | undefined]> {\n const defaultOrgSlug = getConfigValueOrUndef('defaultOrg')\n let orgSlug = String(orgFlag || defaultOrgSlug || '')\n if (!orgSlug) {\n if (!interactive) {\n logger.warn(\n 'Note: This command requires an org slug because the Socket API endpoint does.',\n )\n logger.warn('')\n logger.warn(\n 'It seems no default org was setup and the `--org` flag was not used.',\n )\n logger.warn(\n \"Additionally, `--no-interactive` was set so we can't ask for it.\",\n )\n logger.warn(\n 'Since v1.0.0 the org _argument_ for all commands was dropped in favor of an',\n )\n logger.warn(\n 'implicit default org setting, which will be setup when you run `socket login`.',\n )\n logger.warn('')\n logger.warn(\n 'Note: When running in CI, you probably want to set the `--org` flag.',\n )\n logger.warn('')\n logger.warn(\n 'For details, see: https://docs.socket.dev/docs/v1-migration-guide',\n )\n logger.warn('')\n logger.warn(\n 'This command will exit now because the org slug is required to proceed.',\n )\n return ['', undefined]\n }\n\n logger.warn(\n 'Unable to determine the target org. Trying to auto-discover it now...',\n )\n logger.info('Note: Run `socket login` to set a default org.')\n logger.error(' Use the --org flag to override the default org.')\n logger.error('')\n if (dryRun) {\n logger.fail('Skipping auto-discovery of org in dry-run mode')\n } else {\n orgSlug = (await suggestOrgSlug()) || ''\n if (orgSlug) {\n await suggestToPersistOrgSlug(orgSlug)\n }\n }\n }\n\n return [orgSlug, defaultOrgSlug]\n}\n","import { debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport constants from '../../constants.mts'\nimport { getConfigValueOrUndef } from '../../utils/config.mts'\nimport { fetchOrganization } from '../organization/fetch-organization-list.mts'\n\nimport type { CResult } from '../../types.mts'\n\n// Use the config defaultOrg when set, otherwise discover from remote.\nexport async function getDefaultOrgSlug(): Promise<CResult<string>> {\n const defaultOrgResult = getConfigValueOrUndef('defaultOrg')\n if (defaultOrgResult) {\n debugFn(\n 'notice',\n 'use: org from \"defaultOrg\" value of socket/settings local app data',\n defaultOrgResult,\n )\n return { ok: true, data: defaultOrgResult }\n }\n\n const envOrgSlug = constants.ENV.SOCKET_CLI_ORG_SLUG\n if (envOrgSlug) {\n debugFn(\n 'notice',\n 'use: org from SOCKET_CLI_ORG_SLUG environment variable',\n envOrgSlug,\n )\n return { ok: true, data: envOrgSlug }\n }\n\n const orgsCResult = await fetchOrganization()\n if (!orgsCResult.ok) {\n return orgsCResult\n }\n\n const { organizations } = orgsCResult.data\n const keys = Object.keys(organizations)\n if (!keys.length) {\n return {\n ok: false,\n message: 'Failed to establish identity',\n data: `No organization associated with the Socket API token. Unable to continue.`,\n }\n }\n\n const slug = (organizations as any)[keys[0]!]?.name ?? undefined\n if (!slug) {\n return {\n ok: false,\n message: 'Failed to establish identity',\n data: `Cannot determine the default organization for the API token. Unable to continue.`,\n }\n }\n\n debugFn('notice', 'resolve: org from Socket API', slug)\n\n return {\n ok: true,\n message: 'Retrieved default org from server',\n data: slug,\n }\n}\n","import { debugDir, debugFn, isDebug } from '@socketsecurity/registry/lib/debug'\nimport { normalizePath } from '@socketsecurity/registry/lib/path'\nimport { isSpawnError, spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { SpawnOptions } from '@socketsecurity/registry/lib/spawn'\n\n// Listed in order of check preference.\nconst COMMON_DEFAULT_BRANCH_NAMES = [\n // Modern default (GitHub, GitLab, Bitbucket have switched to this).\n 'main',\n // Historic default in Git (pre-2020, still used in many repos).\n 'master',\n // Common in Git Flow workflows (main for stable, develop for ongoing work).\n 'develop',\n // Used by teams adopting trunk-based development practices.\n 'trunk',\n // Used in some older enterprise setups and tools.\n 'default',\n]\n\nexport async function getBaseBranch(cwd = process.cwd()): Promise<string> {\n const { GITHUB_BASE_REF, GITHUB_REF_NAME, GITHUB_REF_TYPE } = constants.ENV\n // 1. In a pull request, this is always the base branch.\n if (GITHUB_BASE_REF) {\n return GITHUB_BASE_REF\n }\n // 2. If it's a branch (not a tag), GITHUB_REF_TYPE should be 'branch'.\n if (GITHUB_REF_TYPE === 'branch' && GITHUB_REF_NAME) {\n return GITHUB_REF_NAME\n }\n // 3. Try to resolve the default remote branch using 'git remote show origin'.\n // This handles detached HEADs or workflows triggered by tags/releases.\n try {\n const originDetails = (\n await spawn('git', ['remote', 'show', 'origin'], { cwd })\n ).stdout\n\n const match = /(?<=HEAD branch: ).+/.exec(originDetails)\n if (match?.[0]) {\n return match[0].trim()\n }\n } catch {}\n // GitHub and GitLab default to branch name \"main\"\n // https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches#about-the-default-branch\n return 'main'\n}\n\nexport type RepoInfo = {\n owner: string\n repo: string\n}\n\nexport async function getRepoInfo(\n cwd = process.cwd(),\n): Promise<RepoInfo | null> {\n let info = null\n const quotedCmd = '`git remote get-url origin`'\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n const remoteUrl = (\n await spawn('git', ['remote', 'get-url', 'origin'], { cwd })\n ).stdout\n info = parseGitRemoteUrl(remoteUrl)\n if (!info) {\n debugFn('error', 'git: unmatched git remote URL format')\n debugDir('inspect', { remoteUrl })\n }\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n return info\n}\n\nexport async function getRepoName(cwd = process.cwd()): Promise<string> {\n const repoInfo = await getRepoInfo(cwd)\n return repoInfo?.repo ?? constants.SOCKET_DEFAULT_REPOSITORY\n}\n\nexport async function getRepoOwner(\n cwd = process.cwd(),\n): Promise<string | null> {\n const repoInfo = await getRepoInfo(cwd)\n return repoInfo?.owner ?? null\n}\n\nexport async function gitBranch(cwd = process.cwd()): Promise<string | null> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n let quotedCmd = '`git symbolic-ref --short HEAD`'\n debugFn('stdio', `spawn: ${quotedCmd}`)\n // Try symbolic-ref first which returns the branch name or fails in a\n // detached HEAD state.\n try {\n const gitSymbolicRefResult = await spawn(\n 'git',\n ['symbolic-ref', '--short', 'HEAD'],\n stdioPipeOptions,\n )\n debugDir('stdio', { gitSymbolicRefResult })\n return gitSymbolicRefResult.stdout\n } catch (e) {\n if (isDebug('stdio')) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n }\n // Fallback to using rev-parse to get the short commit hash in a\n // detached HEAD state.\n quotedCmd = '`git rev-parse --short HEAD`'\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n const gitRevParseResult = await spawn(\n 'git',\n ['rev-parse', '--short', 'HEAD'],\n stdioPipeOptions,\n )\n debugDir('stdio', { gitRevParseResult })\n return gitRevParseResult.stdout\n } catch (e) {\n if (isDebug('stdio')) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n }\n return null\n}\n\n/**\n * Try to detect the default branch name by checking common patterns.\n * Returns the first branch that exists in the repository.\n */\nexport async function detectDefaultBranch(\n cwd = process.cwd(),\n): Promise<string> {\n // First pass: check all local branches\n for (const branch of COMMON_DEFAULT_BRANCH_NAMES) {\n // eslint-disable-next-line no-await-in-loop\n if (await gitLocalBranchExists(branch, cwd)) {\n return branch\n }\n }\n // Second pass: check remote branches only if no local branch found\n for (const branch of COMMON_DEFAULT_BRANCH_NAMES) {\n // eslint-disable-next-line no-await-in-loop\n if (await gitRemoteBranchExists(branch, cwd)) {\n return branch\n }\n }\n return constants.SOCKET_DEFAULT_BRANCH\n}\n\nexport type GitCreateAndPushBranchOptions = {\n cwd?: string | undefined\n email?: string | undefined\n user?: string | undefined\n}\n\nexport async function gitCleanFdx(cwd = process.cwd()): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = '`git clean -fdx`'\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n await spawn('git', ['clean', '-fdx'], stdioIgnoreOptions)\n return true\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n return false\n}\n\nexport async function gitCheckoutBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git checkout ${branch}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n await spawn('git', ['checkout', branch], stdioIgnoreOptions)\n return true\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n return false\n}\n\nexport async function gitCreateBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n if (await gitLocalBranchExists(branch)) {\n return true\n }\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git branch ${branch}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n await spawn('git', ['branch', branch], stdioIgnoreOptions)\n return true\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n return false\n}\n\nexport async function gitPushBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git push --force --set-upstream origin ${branch}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n await spawn(\n 'git',\n ['push', '--force', '--set-upstream', 'origin', branch],\n stdioIgnoreOptions,\n )\n return true\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n if (isSpawnError(e) && e.code === 128) {\n debugFn(\n 'error',\n \"denied: token requires write permissions for 'contents' and 'pull-requests'\",\n )\n }\n debugDir('inspect', { error: e })\n }\n return false\n}\n\nexport async function gitCommit(\n commitMsg: string,\n filepaths: string[],\n options?: GitCreateAndPushBranchOptions | undefined,\n): Promise<boolean> {\n if (!filepaths.length) {\n debugFn('notice', `miss: no filepaths to add`)\n return false\n }\n const {\n cwd = process.cwd(),\n email = constants.ENV.SOCKET_CLI_GIT_USER_EMAIL,\n user = constants.ENV.SOCKET_CLI_GIT_USER_NAME,\n } = { __proto__: null, ...options } as GitCreateAndPushBranchOptions\n\n await gitEnsureIdentity(user, email, cwd)\n\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedAddCmd = `\\`git add ${filepaths.join(' ')}\\``\n debugFn('stdio', `spawn: ${quotedAddCmd}`)\n try {\n await spawn('git', ['add', ...filepaths], stdioIgnoreOptions)\n } catch (e) {\n debugFn('error', `caught: ${quotedAddCmd} failed`)\n debugDir('inspect', { error: e })\n }\n\n const quotedCommitCmd = `\\`git commit -m ${commitMsg}\\``\n debugFn('stdio', `spawn: ${quotedCommitCmd}`)\n try {\n await spawn('git', ['commit', '-m', commitMsg], stdioIgnoreOptions)\n return true\n } catch (e) {\n debugFn('error', `caught: ${quotedCommitCmd} failed`)\n debugDir('inspect', { error: e })\n }\n return false\n}\n\nexport async function gitDeleteBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git branch -D ${branch}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n // Will throw with exit code 1 if branch does not exist.\n await spawn('git', ['branch', '-D', branch], stdioIgnoreOptions)\n return true\n } catch (e) {\n if (isDebug('stdio')) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n }\n return false\n}\n\nexport async function gitEnsureIdentity(\n name: string,\n email: string,\n cwd = process.cwd(),\n): Promise<void> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n const identEntries: Array<[string, string]> = [\n ['user.email', email],\n ['user.name', name],\n ]\n await Promise.all(\n identEntries.map(async ({ 0: prop, 1: value }) => {\n let configValue\n {\n const quotedCmd = `\\`git config --get ${prop}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n // Will throw with exit code 1 if the config property is not set.\n const gitConfigResult = await spawn(\n 'git',\n ['config', '--get', prop],\n stdioPipeOptions,\n )\n debugDir('stdio', { gitConfigResult })\n configValue = gitConfigResult.stdout\n } catch (e) {\n if (isDebug('stdio')) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n }\n }\n if (configValue !== value) {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git config ${prop} ${value}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n await spawn('git', ['config', prop, value], stdioIgnoreOptions)\n } catch (e) {\n if (isDebug('stdio')) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n }\n }\n }),\n )\n}\n\nexport async function gitLocalBranchExists(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git show-ref --quiet refs/heads/${branch}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n // Will throw with exit code 1 if the branch does not exist.\n await spawn(\n 'git',\n ['show-ref', '--quiet', `refs/heads/${branch}`],\n stdioIgnoreOptions,\n )\n return true\n } catch (e) {\n if (isDebug('stdio')) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n }\n return false\n}\n\nexport async function gitRemoteBranchExists(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n const quotedCmd = `\\`git ls-remote --heads origin ${branch}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n const lsRemoteResult = await spawn(\n 'git',\n ['ls-remote', '--heads', 'origin', branch],\n stdioPipeOptions,\n )\n debugDir('stdio', { lsRemoteResult })\n return lsRemoteResult.stdout.length > 0\n } catch (e) {\n if (isDebug('stdio')) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n }\n return false\n}\n\nexport async function gitResetAndClean(\n branch = 'HEAD',\n cwd = process.cwd(),\n): Promise<void> {\n // Discards tracked changes.\n await gitResetHard(branch, cwd)\n // Deletes all untracked files and directories.\n await gitCleanFdx(cwd)\n}\n\nexport async function gitResetHard(\n branch = 'HEAD',\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git reset --hard ${branch}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n await spawn('git', ['reset', '--hard', branch], stdioIgnoreOptions)\n return true\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n return false\n}\n\nexport async function gitUnstagedModifiedFiles(\n cwd = process.cwd(),\n): Promise<CResult<string[]>> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n const quotedCmd = `\\`git diff --name-only\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n const gitDiffResult = await spawn(\n 'git',\n ['diff', '--name-only'],\n stdioPipeOptions,\n )\n debugDir('stdio', { gitDiffResult })\n const changedFilesDetails = gitDiffResult.stdout\n const relPaths = changedFilesDetails.split('\\n')\n return {\n ok: true,\n data: relPaths.map(p => normalizePath(p)),\n }\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n return {\n ok: false,\n message: 'Git Error',\n cause: 'Unexpected error while trying to ask git whether repo is dirty',\n }\n }\n}\n\nconst parsedGitRemoteUrlCache = new Map<string, RepoInfo | null>()\n\nexport function parseGitRemoteUrl(remoteUrl: string): RepoInfo | null {\n let result = parsedGitRemoteUrlCache.get(remoteUrl) ?? null\n if (result) {\n return { ...result }\n }\n // Handle SSH-style\n const sshMatch = /^git@[^:]+:([^/]+)\\/(.+?)(?:\\.git)?$/.exec(remoteUrl)\n // 1. Handle SSH-style, e.g. git@github.com:owner/repo.git\n if (sshMatch) {\n result = { owner: sshMatch[1]!, repo: sshMatch[2]! }\n } else {\n // 2. Handle HTTPS/URL-style, e.g. https://github.com/owner/repo.git\n try {\n const parsed = new URL(remoteUrl)\n // Remove leading slashes from pathname and split by \"/\" to extract segments.\n const segments = parsed.pathname.replace(/^\\/+/, '').split('/')\n // The second-to-last segment is expected to be the owner (e.g., \"owner\" in /owner/repo.git).\n const owner = segments.at(-2)\n // The last segment is expected to be the repo name, so we remove the \".git\" suffix if present.\n const repo = segments.at(-1)?.replace(/\\.git$/, '')\n if (owner && repo) {\n result = { owner, repo }\n }\n } catch {}\n }\n parsedGitRemoteUrlCache.set(remoteUrl, result)\n return result ? { ...result } : result\n}\n","import { PackageURL } from '@socketregistry/packageurl-js'\n\nimport type { SocketArtifact } from './alert/artifact.mts'\nimport type { PURL_Type } from './ecosystem.mts'\n\nexport function getPurlObject(purl: string): PackageURL & { type: PURL_Type }\nexport function getPurlObject(\n purl: PackageURL,\n): PackageURL & { type: PURL_Type }\nexport function getPurlObject(\n purl: SocketArtifact,\n): SocketArtifact & { type: PURL_Type }\nexport function getPurlObject(\n purl: string | PackageURL | SocketArtifact,\n): (PackageURL | SocketArtifact) & { type: PURL_Type }\nexport function getPurlObject(\n purl: string | PackageURL | SocketArtifact,\n): (PackageURL | SocketArtifact) & { type: PURL_Type } {\n return typeof purl === 'string'\n ? (PackageURL.fromString(purl) as PackageURL & { type: PURL_Type })\n : (purl as (PackageURL | SocketArtifact) & { type: PURL_Type })\n}\n","import constants from '../constants.mts'\nimport { getPurlObject } from './purl.mts'\n\nimport type { SocketArtifact } from './alert/artifact.mts'\nimport type { PURL_Type } from './ecosystem.mts'\nimport type { PackageURL } from '@socketregistry/packageurl-js'\n\nexport function getPkgFullNameFromPurl(\n purl: string | PackageURL | SocketArtifact,\n): string {\n const purlObj = getPurlObject(purl)\n const { name, namespace } = purlObj\n return namespace\n ? `${namespace}${purlObj.type === 'maven' ? ':' : '/'}${name}`\n : name!\n}\n\nexport function getSocketDevAlertUrl(alertType: string): string {\n return `${constants.SOCKET_WEBSITE_URL}/alerts/${alertType}`\n}\n\nexport function getSocketDevPackageOverviewUrlFromPurl(\n purl: string | PackageURL | SocketArtifact,\n): string {\n const purlObj = getPurlObject(purl)\n const fullName = getPkgFullNameFromPurl(purlObj)\n return getSocketDevPackageOverviewUrl(purlObj.type, fullName, purlObj.version)\n}\n\nexport function getSocketDevPackageOverviewUrl(\n ecosystem: PURL_Type,\n fullName: string,\n version?: string | undefined,\n): string {\n const url = `${constants.SOCKET_WEBSITE_URL}/${ecosystem}/package/${fullName}`\n return ecosystem === 'golang'\n ? `${url}${version ? `?section=overview&version=${version}` : ''}`\n : `${url}${version ? `/overview/${version}` : ''}`\n}\n","interface NestedRecord<T> {\n [key: string]: T | NestedRecord<T>\n}\n\n/**\n * Convert a Map<string, Map|string> to a nested object of similar shape.\n * The goal is to serialize it with JSON.stringify, which Map can't do.\n */\nexport function mapToObject<T>(\n map: Map<string, T | Map<string, T | Map<string, T>>>,\n): NestedRecord<T> {\n return Object.fromEntries(\n Array.from(map.entries()).map(([k, v]) => [\n k,\n v instanceof Map ? mapToObject(v) : v,\n ]),\n )\n}\n","type NestedMap<T> = Map<string, T | NestedMap<T>>\n\nexport function* walkNestedMap<T>(\n map: NestedMap<T>,\n keys: string[] = [],\n): Generator<{ keys: string[]; value: T }> {\n for (const [key, value] of map.entries()) {\n if (value instanceof Map) {\n yield* walkNestedMap(value as NestedMap<T>, [...keys, key])\n } else {\n yield { keys: [...keys, key], value: value }\n }\n }\n}\n","import path from 'node:path'\n\nimport { glob, globStream } from 'fast-glob'\nimport ignore from 'ignore'\nimport micromatch from 'micromatch'\nimport { parse as yamlParse } from 'yaml'\n\nimport { safeReadFile } from '@socketsecurity/registry/lib/fs'\nimport { defaultIgnore } from '@socketsecurity/registry/lib/globs'\nimport { readPackageJson } from '@socketsecurity/registry/lib/packages'\nimport { transform } from '@socketsecurity/registry/lib/streams'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport type { Agent } from './package-environment.mts'\nimport type { SocketYml } from '@socketsecurity/config'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\nimport type { Options as GlobOptions } from 'fast-glob'\n\nconst DEFAULT_IGNORE_FOR_GIT_IGNORE = defaultIgnore.filter(\n p => !p.endsWith('.gitignore'),\n)\n\nconst IGNORED_DIRS = [\n // Taken from ignore-by-default:\n // https://github.com/novemberborn/ignore-by-default/blob/v2.1.0/index.js\n '.git', // Git repository files, see <https://git-scm.com/>\n '.log', // Log files emitted by tools such as `tsserver`, see <https://github.com/Microsoft/TypeScript/wiki/Standalone-Server-%28tsserver%29>\n '.nyc_output', // Temporary directory where nyc stores coverage data, see <https://github.com/bcoe/nyc>\n '.sass-cache', // Cache folder for node-sass, see <https://github.com/sass/node-sass>\n '.yarn', // Where node modules are installed when using Yarn, see <https://yarnpkg.com/>\n 'bower_components', // Where Bower packages are installed, see <http://bower.io/>\n 'coverage', // Standard output directory for code coverage reports, see <https://github.com/gotwarlost/istanbul>\n 'node_modules', // Where Node modules are installed, see <https://nodejs.org/>\n // Taken from globby:\n // https://github.com/sindresorhus/globby/blob/v14.0.2/ignore.js#L11-L16\n 'flow-typed',\n] as const\n\nconst IGNORED_DIR_PATTERNS = IGNORED_DIRS.map(i => `**/${i}`)\n\nasync function getWorkspaceGlobs(\n agent: Agent,\n cwd = process.cwd(),\n): Promise<string[]> {\n let workspacePatterns\n if (agent === 'pnpm') {\n for (const workspacePath of [\n path.join(cwd, 'pnpm-workspace.yaml'),\n path.join(cwd, 'pnpm-workspace.yml'),\n ]) {\n // eslint-disable-next-line no-await-in-loop\n const yml = await safeReadFile(workspacePath)\n if (yml) {\n try {\n workspacePatterns = yamlParse(yml)?.packages\n } catch {}\n if (workspacePatterns) {\n break\n }\n }\n }\n } else {\n workspacePatterns = (await readPackageJson(cwd, { throws: false }))?.[\n 'workspaces'\n ]\n }\n return Array.isArray(workspacePatterns)\n ? workspacePatterns\n .filter(isNonEmptyString)\n .map(workspacePatternToGlobPattern)\n : []\n}\n\nfunction ignoreFileLinesToGlobPatterns(\n lines: string[] | readonly string[],\n filepath: string,\n cwd: string,\n): string[] {\n const base = path.relative(cwd, path.dirname(filepath)).replace(/\\\\/g, '/')\n const patterns = []\n for (let i = 0, { length } = lines; i < length; i += 1) {\n const pattern = lines[i]!.trim()\n if (pattern.length > 0 && pattern.charCodeAt(0) !== 35 /*'#'*/) {\n patterns.push(\n ignorePatternToMinimatch(\n pattern.length && pattern.charCodeAt(0) === 33 /*'!'*/\n ? `!${path.posix.join(base, pattern.slice(1))}`\n : path.posix.join(base, pattern),\n ),\n )\n }\n }\n return patterns\n}\n\nfunction ignoreFileToGlobPatterns(\n content: string,\n filepath: string,\n cwd: string,\n): string[] {\n return ignoreFileLinesToGlobPatterns(content.split(/\\r?\\n/), filepath, cwd)\n}\n\n// Based on `@eslint/compat` convertIgnorePatternToMinimatch.\n// Apache v2.0 licensed\n// Copyright Nicholas C. Zakas\n// https://github.com/eslint/rewrite/blob/compat-v1.2.1/packages/compat/src/ignore-file.js#L28\nfunction ignorePatternToMinimatch(pattern: string): string {\n const isNegated = pattern.startsWith('!')\n const negatedPrefix = isNegated ? '!' : ''\n const patternToTest = (isNegated ? pattern.slice(1) : pattern).trimEnd()\n // Special cases.\n if (\n patternToTest === '' ||\n patternToTest === '**' ||\n patternToTest === '/**' ||\n patternToTest === '**'\n ) {\n return `${negatedPrefix}${patternToTest}`\n }\n const firstIndexOfSlash = patternToTest.indexOf('/')\n const matchEverywherePrefix =\n firstIndexOfSlash === -1 || firstIndexOfSlash === patternToTest.length - 1\n ? '**/'\n : ''\n const patternWithoutLeadingSlash =\n firstIndexOfSlash === 0 ? patternToTest.slice(1) : patternToTest\n // Escape `{` and `(` because in gitignore patterns they are just\n // literal characters without any specific syntactic meaning,\n // while in minimatch patterns they can form brace expansion or extglob syntax.\n //\n // For example, gitignore pattern `src/{a,b}.js` ignores file `src/{a,b}.js`.\n // But, the same minimatch pattern `src/{a,b}.js` ignores files `src/a.js` and `src/b.js`.\n // Minimatch pattern `src/\\{a,b}.js` is equivalent to gitignore pattern `src/{a,b}.js`.\n const escapedPatternWithoutLeadingSlash =\n patternWithoutLeadingSlash.replaceAll(\n /(?=((?:\\\\.|[^{(])*))\\1([{(])/guy,\n '$1\\\\$2',\n )\n const matchInsideSuffix = patternToTest.endsWith('/**') ? '/*' : ''\n return `${negatedPrefix}${matchEverywherePrefix}${escapedPatternWithoutLeadingSlash}${matchInsideSuffix}`\n}\n\nfunction workspacePatternToGlobPattern(workspace: string): string {\n const { length } = workspace\n if (!length) {\n return ''\n }\n // If the workspace ends with \"/\"\n if (workspace.charCodeAt(length - 1) === 47 /*'/'*/) {\n return `${workspace}/*/package.json`\n }\n // If the workspace ends with \"/**\"\n if (\n workspace.charCodeAt(length - 1) === 42 /*'*'*/ &&\n workspace.charCodeAt(length - 2) === 42 /*'*'*/ &&\n workspace.charCodeAt(length - 3) === 47 /*'/'*/\n ) {\n return `${workspace}/*/**/package.json`\n }\n // Things like \"packages/a\" or \"packages/*\"\n return `${workspace}/package.json`\n}\n\nexport function filterBySupportedScanFiles(\n filepaths: string[] | readonly string[],\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n): string[] {\n const patterns = getSupportedFilePatterns(supportedFiles)\n return filepaths.filter(p => micromatch.some(p, patterns))\n}\n\nexport function getSupportedFilePatterns(\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n): string[] {\n const patterns: string[] = []\n for (const key of Object.keys(supportedFiles)) {\n const supported = supportedFiles[key]\n if (supported) {\n patterns.push(...Object.values(supported).map(p => `**/${p.pattern}`))\n }\n }\n return patterns\n}\n\ntype GlobWithGitIgnoreOptions = GlobOptions & {\n socketConfig?: SocketYml | undefined\n}\n\nexport async function globWithGitIgnore(\n patterns: string[] | readonly string[],\n options: GlobWithGitIgnoreOptions,\n): Promise<string[]> {\n const {\n cwd = process.cwd(),\n socketConfig,\n ...additionalOptions\n } = { __proto__: null, ...options } as GlobWithGitIgnoreOptions\n\n const ignores = new Set<string>(IGNORED_DIR_PATTERNS)\n\n const projectIgnorePaths = socketConfig?.projectIgnorePaths\n if (Array.isArray(projectIgnorePaths)) {\n const ignorePatterns = ignoreFileLinesToGlobPatterns(\n projectIgnorePaths,\n path.join(cwd, '.gitignore'),\n cwd,\n )\n for (const pattern of ignorePatterns) {\n ignores.add(pattern)\n }\n }\n\n const gitIgnoreStream = globStream(['**/.gitignore'], {\n absolute: true,\n cwd,\n ignore: DEFAULT_IGNORE_FOR_GIT_IGNORE,\n })\n for await (const ignorePatterns of transform(\n gitIgnoreStream,\n async (filepath: string) =>\n ignoreFileToGlobPatterns(\n (await safeReadFile(filepath)) ?? '',\n filepath,\n cwd,\n ),\n { concurrency: 8 },\n )) {\n for (const p of ignorePatterns) {\n ignores.add(p)\n }\n }\n\n let hasNegatedPattern = false\n for (const p of ignores) {\n if (p.charCodeAt(0) === 33 /*'!'*/) {\n hasNegatedPattern = true\n break\n }\n }\n\n const globOptions = {\n __proto__: null,\n absolute: true,\n cwd,\n dot: true,\n ignore: hasNegatedPattern ? defaultIgnore : [...ignores],\n ...additionalOptions,\n } as GlobOptions\n\n if (!hasNegatedPattern) {\n return await glob(patterns as string[], globOptions)\n }\n\n // Add support for negated \"ignore\" patterns which many globbing libraries,\n // including 'fast-glob', 'globby', and 'tinyglobby', lack support for.\n const filtered: string[] = []\n const ig = ignore().add([...ignores])\n const stream = globStream(\n patterns as string[],\n globOptions,\n ) as AsyncIterable<string>\n for await (const p of stream) {\n // Note: the input files must be INSIDE the cwd. If you get strange looking\n // relative path errors here, most likely your path is outside the given cwd.\n const relPath = globOptions.absolute ? path.relative(cwd, p) : p\n if (!ig.ignores(relPath)) {\n filtered.push(p)\n }\n }\n return filtered\n}\n\nexport async function globStreamNodeModules(\n cwd = process.cwd(),\n): Promise<NodeJS.ReadableStream> {\n return globStream('**/node_modules', {\n absolute: true,\n cwd,\n onlyDirectories: true,\n })\n}\n\nexport async function globWorkspace(\n agent: Agent,\n cwd = process.cwd(),\n): Promise<string[]> {\n const workspaceGlobs = await getWorkspaceGlobs(agent, cwd)\n return workspaceGlobs.length\n ? await glob(workspaceGlobs, {\n absolute: true,\n cwd,\n ignore: defaultIgnore,\n })\n : []\n}\n\nexport function isReportSupportedFile(\n filepath: string,\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n) {\n const patterns = getSupportedFilePatterns(supportedFiles)\n return micromatch.some(filepath, patterns)\n}\n\nexport function pathsToGlobPatterns(\n paths: string[] | readonly string[],\n): string[] {\n // TODO: Does not support `~/` paths.\n return paths.map(p => (p === '.' || p === './' ? '**/*' : p))\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport which from 'which'\n\nimport { isDirSync } from '@socketsecurity/registry/lib/fs'\nimport { resolveBinPathSync } from '@socketsecurity/registry/lib/npm'\n\nimport constants from '../constants.mts'\nimport {\n filterBySupportedScanFiles,\n globWithGitIgnore,\n pathsToGlobPatterns,\n} from './glob.mts'\n\nimport type { SocketYml } from '@socketsecurity/config'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport function findBinPathDetailsSync(binName: string): {\n name: string\n path: string | undefined\n shadowed: boolean\n} {\n const binPaths =\n which.sync(binName, {\n all: true,\n nothrow: true,\n }) ?? []\n const { shadowBinPath } = constants\n let shadowIndex = -1\n let theBinPath: string | undefined\n for (let i = 0, { length } = binPaths; i < length; i += 1) {\n const binPath = binPaths[i]!\n // Skip our bin directory if it's in the front.\n if (path.dirname(binPath) === shadowBinPath) {\n shadowIndex = i\n } else {\n theBinPath = resolveBinPathSync(binPath)\n break\n }\n }\n return { name: binName, path: theBinPath, shadowed: shadowIndex !== -1 }\n}\n\nexport function findNpmDirPathSync(npmBinPath: string): string | undefined {\n const { WIN32 } = constants\n let thePath = npmBinPath\n while (true) {\n const libNmNpmPath = path.join(thePath, 'lib/node_modules/npm')\n // mise, which uses opaque binaries, puts its npm bin in a path like:\n // /Users/SomeUsername/.local/share/mise/installs/node/vX.X.X/bin/npm.\n // HOWEVER, the location of the npm install is:\n // /Users/SomeUsername/.local/share/mise/installs/node/vX.X.X/lib/node_modules/npm.\n if (\n // Use existsSync here because statsSync, even with { throwIfNoEntry: false },\n // will throw an ENOTDIR error for paths like ./a-file-that-exists/a-directory-that-does-not.\n // See https://github.com/nodejs/node/issues/56993.\n isDirSync(libNmNpmPath)\n ) {\n thePath = libNmNpmPath\n }\n const hasNmInCurrPath = isDirSync(path.join(thePath, 'node_modules'))\n const hasNmInParentPath =\n !hasNmInCurrPath && isDirSync(path.join(thePath, '../node_modules'))\n if (\n // npm bin paths may look like:\n // /usr/local/share/npm/bin/npm\n // /Users/SomeUsername/.nvm/versions/node/vX.X.X/bin/npm\n // C:\\Users\\SomeUsername\\AppData\\Roaming\\npm\\bin\\npm.cmd\n // OR\n // C:\\Program Files\\nodejs\\npm.cmd\n //\n // In practically all cases the npm path contains a node_modules folder:\n // /usr/local/share/npm/bin/npm/node_modules\n // C:\\Program Files\\nodejs\\node_modules\n (hasNmInCurrPath ||\n // In some bespoke cases the node_modules folder is in the parent directory.\n hasNmInParentPath) &&\n // Optimistically look for the default location.\n (path.basename(thePath) === 'npm' ||\n // Chocolatey installs npm bins in the same directory as node bins.\n (WIN32 && existsSync(path.join(thePath, 'npm.cmd'))))\n ) {\n return hasNmInParentPath ? path.dirname(thePath) : thePath\n }\n const parent = path.dirname(thePath)\n if (parent === thePath) {\n return undefined\n }\n thePath = parent\n }\n}\n\nexport type PackageFilesForScanOptions = {\n cwd?: string | undefined\n config?: SocketYml | undefined\n}\n\nexport async function getPackageFilesForScan(\n inputPaths: string[],\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n options?: PackageFilesForScanOptions | undefined,\n): Promise<string[]> {\n const { config: socketConfig, cwd = process.cwd() } = {\n __proto__: null,\n ...options,\n } as PackageFilesForScanOptions\n\n const filepaths = await globWithGitIgnore(pathsToGlobPatterns(inputPaths), {\n cwd,\n socketConfig,\n })\n\n return filterBySupportedScanFiles(filepaths!, supportedFiles)\n}\n","import { existsSync } from 'node:fs'\nimport Module from 'node:module'\nimport path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../constants.mts'\nimport { findBinPathDetailsSync, findNpmDirPathSync } from './path-resolve.mts'\n\nfunction exitWithBinPathError(binName: string): never {\n logger.fail(\n `Socket unable to locate ${binName}; ensure it is available in the PATH environment variable`,\n )\n // The exit code 127 indicates that the command or binary being executed\n // could not be found.\n // eslint-disable-next-line n/no-process-exit\n process.exit(127)\n}\n\nlet _npmBinPath: string | undefined\nexport function getNpmBinPath(): string {\n if (_npmBinPath === undefined) {\n _npmBinPath = getNpmBinPathDetails().path\n if (!_npmBinPath) {\n exitWithBinPathError('npm')\n }\n }\n return _npmBinPath\n}\n\nlet _npmBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined\nfunction getNpmBinPathDetails(): ReturnType<typeof findBinPathDetailsSync> {\n if (_npmBinPathDetails === undefined) {\n _npmBinPathDetails = findBinPathDetailsSync('npm')\n }\n return _npmBinPathDetails\n}\n\nlet _npmDirPath: string | undefined\nexport function getNpmDirPath() {\n if (_npmDirPath === undefined) {\n const npmBinPath = getNpmBinPath()\n _npmDirPath = npmBinPath ? findNpmDirPathSync(npmBinPath) : undefined\n if (!_npmDirPath) {\n _npmDirPath = constants.ENV.SOCKET_CLI_NPM_PATH || undefined\n }\n if (!_npmDirPath) {\n let message = 'Unable to find npm CLI install directory.'\n if (npmBinPath) {\n message += `\\nSearched parent directories of ${path.dirname(npmBinPath)}.`\n }\n message +=\n '\\n\\nThis is may be a bug with socket-npm related to changes to the npm CLI.'\n message += `\\nPlease report to ${constants.SOCKET_CLI_ISSUES_URL}.`\n logger.fail(message)\n // The exit code 127 indicates that the command or binary being executed\n // could not be found.\n // eslint-disable-next-line n/no-process-exit\n process.exit(127)\n }\n }\n return _npmDirPath\n}\n\nlet _npmRequire: NodeJS.Require | undefined\nexport function getNpmRequire(): NodeJS.Require {\n if (_npmRequire === undefined) {\n const npmDirPath = getNpmDirPath()\n const npmNmPath = path.join(npmDirPath, 'node_modules/npm')\n _npmRequire = Module.createRequire(\n path.join(\n existsSync(npmNmPath) ? npmNmPath : npmDirPath,\n '<dummy-basename>',\n ),\n )\n }\n return _npmRequire\n}\n\nlet _npxBinPath: string | undefined\nexport function getNpxBinPath(): string {\n if (_npxBinPath === undefined) {\n _npxBinPath = getNpxBinPathDetails().path\n if (!_npxBinPath) {\n exitWithBinPathError('npx')\n }\n }\n return _npxBinPath\n}\n\nlet _npxBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined\nfunction getNpxBinPathDetails(): ReturnType<typeof findBinPathDetailsSync> {\n if (_npxBinPathDetails === undefined) {\n _npxBinPathDetails = findBinPathDetailsSync('npx')\n }\n return _npxBinPathDetails\n}\n\nexport function isNpmBinPathShadowed() {\n return getNpmBinPathDetails().shadowed\n}\n\nexport function isNpxBinPathShadowed() {\n return getNpxBinPathDetails().shadowed\n}\n","const helpFlags = new Set(['--help', '-h'])\n\nexport function cmdFlagsToString(args: string[]) {\n const result = []\n for (let i = 0, { length } = args; i < length; i += 1) {\n if (args[i]!.startsWith('--')) {\n // Check if the next item exists and is NOT another flag.\n if (i + 1 < length && !args[i + 1]!.startsWith('--')) {\n result.push(`${args[i]}=${args[i + 1]}`)\n i += 1\n } else {\n result.push(args[i])\n }\n }\n }\n return result.join(' ')\n}\n\nexport function cmdFlagValueToArray(value: any): string[] {\n if (typeof value === 'string') {\n return value.trim().split(/, */).filter(Boolean)\n }\n if (Array.isArray(value)) {\n return value.flatMap(cmdFlagValueToArray)\n }\n return []\n}\n\nexport function cmdPrefixMessage(cmdName: string, text: string): string {\n const cmdPrefix = cmdName ? `${cmdName}: ` : ''\n return `${cmdPrefix}${text}`\n}\n\nexport function isHelpFlag(cmdArg: string) {\n return helpFlags.has(cmdArg)\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nimport { remove } from '@socketsecurity/registry/lib/fs'\nimport { parallelEach } from '@socketsecurity/registry/lib/streams'\n\nimport constants from '../constants.mts'\nimport { globStreamNodeModules } from './glob.mts'\n\nexport async function removeNodeModules(cwd = process.cwd()) {\n const stream = await globStreamNodeModules(cwd)\n await parallelEach(stream, p => remove(p, { force: true, recursive: true }), {\n concurrency: 8,\n })\n}\n\nexport type FindUpOptions = {\n cwd?: string | undefined\n onlyDirectories?: boolean | undefined\n onlyFiles?: boolean | undefined\n signal?: AbortSignal | undefined\n}\n\nexport async function findUp(\n name: string | string[],\n options?: FindUpOptions | undefined,\n): Promise<string | undefined> {\n const opts = { __proto__: null, ...options }\n const { cwd = process.cwd(), signal = constants.abortSignal } = opts\n let { onlyDirectories = false, onlyFiles = true } = opts\n if (onlyDirectories) {\n onlyFiles = false\n }\n if (onlyFiles) {\n onlyDirectories = false\n }\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 if (signal?.aborted) {\n return undefined\n }\n const thePath = path.join(dir, name)\n try {\n // eslint-disable-next-line no-await-in-loop\n const stats = await fs.stat(thePath)\n if (!onlyDirectories && (stats.isFile() || stats.isSymbolicLink())) {\n return thePath\n }\n if (!onlyFiles && stats.isDirectory()) {\n return thePath\n }\n } catch {}\n }\n dir = path.dirname(dir)\n }\n return undefined\n}\n","import { readJsonSync } from '@socketsecurity/registry/lib/fs'\n\nimport { getDefaultOrgSlug } from '../commands/ci/fetch-default-org-slug.mts'\nimport constants from '../constants.mts'\nimport { getDefaultApiToken } from './sdk.mts'\nimport shadowBin from '../shadow/npm/bin.mts'\n\nimport type { ShadowBinOptions } from '../shadow/npm/bin.mts'\nimport type { CResult } from '../types.mts'\nimport type { SpawnExtra } from '@socketsecurity/registry/lib/spawn'\n\nexport function extractTier1ReachabilityScanId(\n socketFactsFile: string,\n): string | undefined {\n const json = readJsonSync(socketFactsFile, { throws: false })\n const tier1ReachabilityScanId = String(\n json?.['tier1ReachabilityScanId'] ?? '',\n ).trim()\n return tier1ReachabilityScanId.length > 0\n ? tier1ReachabilityScanId\n : undefined\n}\n\nexport async function spawnCoana(\n args: string[] | readonly string[],\n orgSlug?: string,\n options?: ShadowBinOptions | undefined,\n extra?: SpawnExtra | undefined,\n): Promise<CResult<string>> {\n const {\n env: spawnEnv,\n ipc,\n ...spawnOpts\n } = {\n __proto__: null,\n ...options,\n } as ShadowBinOptions\n const mixinsEnv: Record<string, string> = {\n SOCKET_CLI_VERSION: constants.ENV.INLINED_SOCKET_CLI_VERSION,\n }\n const defaultApiToken = getDefaultApiToken()\n if (defaultApiToken) {\n mixinsEnv['SOCKET_CLI_API_TOKEN'] = defaultApiToken\n }\n\n if (orgSlug) {\n mixinsEnv['SOCKET_ORG_SLUG'] = orgSlug\n } else {\n const orgSlugCResult = await getDefaultOrgSlug()\n if (orgSlugCResult.ok) {\n mixinsEnv['SOCKET_ORG_SLUG'] = orgSlugCResult.data\n }\n }\n\n try {\n const { spawnPromise } = await shadowBin(\n 'npx',\n [\n '--yes',\n `@coana-tech/cli@~${constants.ENV.INLINED_SOCKET_CLI_COANA_TECH_CLI_VERSION}`,\n ...args,\n ],\n {\n ...spawnOpts,\n env: {\n ...mixinsEnv,\n ...spawnEnv,\n },\n ipc: {\n [constants.SOCKET_CLI_SHADOW_ACCEPT_RISKS]: true,\n [constants.SOCKET_CLI_SHADOW_API_TOKEN]:\n constants.SOCKET_PUBLIC_API_TOKEN,\n [constants.SOCKET_CLI_SHADOW_SILENT]: true,\n ...ipc,\n },\n },\n extra,\n )\n const output = await spawnPromise\n return { ok: true, data: output.stdout }\n } catch (e) {\n const stderr = (e as any)?.stderr\n const message = stderr ? stderr : (e as Error)?.message\n return { ok: false, data: e, message }\n }\n}\n","import type {\n EnterpriseOrganizations,\n Organizations,\n} from '../commands/organization/fetch-organization-list.mts'\n\nexport function getEnterpriseOrgs(\n orgs: Organizations,\n): EnterpriseOrganizations {\n return orgs.filter(o => o.plan === 'enterprise') as EnterpriseOrganizations\n}\n\nexport function getOrgSlugs(orgs: Organizations): string[] {\n return orgs.map(o => o.slug)\n}\n\nexport function hasEnterpriseOrgPlan(orgs: Organizations): boolean {\n return orgs.some(o => o.plan === 'enterprise')\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport type { CResult } from '../types.mts'\n\nexport interface SocketJson {\n ' _____ _ _ ': string\n '| __|___ ___| |_ ___| |_ ': string\n \"|__ | . | _| '_| -_| _| \": string\n '|_____|___|___|_,_|___|_|.dev': string\n version: number\n\n defaults?: {\n manifest?: {\n conda?: {\n disabled?: boolean\n infile?: string\n outfile?: string\n stdin?: boolean\n stdout?: boolean\n target?: string\n verbose?: boolean\n }\n gradle?: {\n disabled?: boolean\n bin?: string\n gradleOpts?: string\n verbose?: boolean\n }\n sbt?: {\n disabled?: boolean\n infile?: string\n stdin?: boolean\n bin?: string\n outfile?: string\n sbtOpts?: string\n stdout?: boolean\n verbose?: boolean\n }\n }\n scan?: {\n create?: {\n autoManifest?: boolean\n repo?: string\n report?: boolean\n branch?: string\n }\n github?: {\n all?: boolean\n githubApiUrl?: string\n orgGithub?: string\n repos?: string\n }\n }\n }\n}\n\nexport function readOrDefaultSocketJson(cwd: string): SocketJson {\n const jsonCResult = readSocketJsonSync(cwd, true)\n return jsonCResult.ok\n ? jsonCResult.data\n : // This should be unreachable but it makes TS happy.\n getDefaultSocketJson()\n}\n\nexport function getDefaultSocketJson(): SocketJson {\n return {\n ' _____ _ _ ':\n 'Local config file for Socket CLI tool ( https://npmjs.org/socket ), to work with https://socket.dev',\n '| __|___ ___| |_ ___| |_ ':\n ' The config in this file is used to set as defaults for flags or command args when using the CLI',\n \"|__ | . | _| '_| -_| _| \":\n ' in this dir, often a repo root. You can choose commit or .ignore this file, both works.',\n '|_____|___|___|_,_|___|_|.dev':\n 'Warning: This file may be overwritten without warning by `socket manifest setup` or other commands',\n version: 1,\n }\n}\n\nexport function readSocketJsonSync(\n cwd: string,\n defaultOnError = false,\n): CResult<SocketJson> {\n const sockJsonPath = path.join(cwd, 'socket.json')\n if (!fs.existsSync(sockJsonPath)) {\n debugFn('notice', `miss: socket.json not found at ${cwd}`)\n return { ok: true, data: getDefaultSocketJson() }\n }\n let json = null\n try {\n json = fs.readFileSync(sockJsonPath, 'utf8')\n } catch (e) {\n if (defaultOnError) {\n logger.warn('Failed to read socket.json, using default')\n debugDir('inspect', { error: e })\n return { ok: true, data: getDefaultSocketJson() }\n }\n const msg = (e as Error)?.message\n debugDir('inspect', { error: e })\n return {\n ok: false,\n message: 'Failed to read socket.json',\n cause: `An error occurred while trying to read socket.json${msg ? `: ${msg}` : ''}`,\n }\n }\n\n let obj\n try {\n obj = JSON.parse(json)\n } catch (e) {\n debugFn('error', 'caught: JSON.parse error')\n debugDir('inspect', { error: e, json })\n if (defaultOnError) {\n logger.warn('Failed to parse socket.json, using default')\n return { ok: true, data: getDefaultSocketJson() }\n }\n return {\n ok: false,\n message: 'Failed to parse socket.json',\n cause: 'socket.json does not contain valid JSON, please verify',\n }\n }\n\n if (!obj) {\n logger.warn('Warning: file contents was empty, using default')\n return { ok: true, data: getDefaultSocketJson() }\n }\n\n // Do we really care to validate? All properties are optional so code will have\n // to check every step of the way regardless. Who cares about validation here...?\n return { ok: true, data: obj }\n}\n\nexport async function writeSocketJson(\n cwd: string,\n sockJson: SocketJson,\n): Promise<CResult<undefined>> {\n let json = ''\n try {\n json = JSON.stringify(sockJson, null, 2)\n } catch (e) {\n debugFn('error', 'caught: JSON.stringify error')\n debugDir('inspect', { error: e, sockJson })\n return {\n ok: false,\n message: 'Failed to serialize to JSON',\n cause:\n 'There was an unexpected problem converting the socket json object to a JSON string. Unable to store it.',\n }\n }\n\n const filepath = path.join(cwd, 'socket.json')\n await fs.promises.writeFile(filepath, json + '\\n', 'utf8')\n\n return { ok: true, data: undefined }\n}\n","import semver from 'semver'\n\nimport type { SemVer } from 'semver'\n\nexport const RangeStyles = [\n 'caret',\n 'gt',\n 'gte',\n 'lt',\n 'lte',\n 'pin',\n 'preserve',\n 'tilde',\n]\n\nexport type RangeStyle =\n | 'caret'\n | 'gt'\n | 'gte'\n | 'lt'\n | 'lte'\n | 'pin'\n | 'preserve'\n | 'tilde'\n\nexport type { SemVer }\n\nexport function applyRange(\n refRange: string,\n version: string,\n style: RangeStyle = 'preserve',\n): string {\n switch (style) {\n case 'caret':\n return `^${version}`\n case 'gt':\n return `>${version}`\n case 'gte':\n return `>=${version}`\n case 'lt':\n return `<${version}`\n case 'lte':\n return `<=${version}`\n case 'preserve': {\n const range = new semver.Range(refRange)\n const { raw } = range\n const comparators = range.set.flat()\n const { length } = comparators\n if (length === 1) {\n const char = /^[<>]=?/.exec(raw)?.[0]\n if (char) {\n return `${char}${version}`\n }\n } else if (length === 2) {\n const char = /^[~^]/.exec(raw)?.[0]\n if (char) {\n return `${char}${version}`\n }\n }\n return version\n }\n case 'tilde':\n return `~${version}`\n case 'pin':\n default:\n return version\n }\n}\n\nexport function getMajor(version: unknown): number | null {\n try {\n const coerced = semver.coerce(version as string)\n return coerced ? semver.major(coerced) : null\n } catch {}\n return null\n}\n\nexport function getMinVersion(range: unknown): SemVer | null {\n try {\n return semver.minVersion(range as string)\n } catch {}\n return null\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nimport constants from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\n\nexport const COMPLETION_CMD_PREFIX = 'complete -F _socket_completion'\n\nexport function getCompletionSourcingCommand(): CResult<string> {\n // Note: this is exported to distPath in .config/rollup.dist.config.mjs\n const completionScriptExportPath = path.join(\n constants.distPath,\n 'socket-completion.bash',\n )\n\n if (!fs.existsSync(completionScriptExportPath)) {\n return {\n ok: false,\n message: 'Tab Completion script not found',\n cause: `Expected to find completion script at \\`${completionScriptExportPath}\\` but it was not there`,\n }\n }\n\n return { ok: true, data: `source ${completionScriptExportPath}` }\n}\n\nexport function getBashrcDetails(targetCommandName: string): CResult<{\n completionCommand: string\n sourcingCommand: string\n toAddToBashrc: string\n targetName: string\n targetPath: string\n}> {\n const sourcingCommand = getCompletionSourcingCommand()\n if (!sourcingCommand.ok) {\n return sourcingCommand\n }\n\n const { socketAppDataPath } = constants\n if (!socketAppDataPath) {\n return {\n ok: false,\n message: 'Could not determine config directory',\n cause: 'Failed to get config path',\n }\n }\n\n // _socket_completion is the function defined in our completion bash script\n const completionCommand = `${COMPLETION_CMD_PREFIX} ${targetCommandName}`\n\n // Location of completion script in config after installing\n const completionScriptPath = path.join(\n path.dirname(socketAppDataPath),\n 'completion',\n 'socket-completion.bash',\n )\n\n const bashrcContent = `# Socket CLI completion for \"${targetCommandName}\"\nif [ -f \"${completionScriptPath}\" ]; then\n # Load the tab completion script\n source \"${completionScriptPath}\"\n # Tell bash to use this function for tab completion of this function\n ${completionCommand}\nfi\n`\n\n return {\n ok: true,\n data: {\n sourcingCommand: sourcingCommand.data,\n completionCommand,\n toAddToBashrc: bashrcContent,\n targetName: targetCommandName,\n targetPath: completionScriptPath,\n },\n }\n}\n","import { setTimeout as wait } from 'node:timers/promises'\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport constants from '../constants.mts'\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 debugFn('notice', 'send: exception to Sentry')\n return Sentry.captureException(exception, hint) as string\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 npmPackageArg from 'npm-package-arg'\n\nexport type {\n AliasResult,\n FileResult,\n HostedGit,\n HostedGitResult,\n RegistryResult,\n Result,\n URLResult,\n} from 'npm-package-arg'\n\nexport function npa(\n ...args: Parameters<typeof npmPackageArg>\n): ReturnType<typeof npmPackageArg> | null {\n try {\n return Reflect.apply(npmPackageArg, undefined, args)\n } catch {}\n return null\n}\n","import { isDebug } from '@socketsecurity/registry/lib/debug'\nimport {\n isNpmAuditFlag,\n isNpmFundFlag,\n isNpmLoglevelFlag,\n isNpmProgressFlag,\n resolveBinPathSync,\n} from '@socketsecurity/registry/lib/npm'\nimport { getOwn, isObject } from '@socketsecurity/registry/lib/objects'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants.mts'\nimport { getNpmBinPath } from '../../utils/npm-paths.mts'\n\nimport type { SpawnResult } from '@socketsecurity/registry/lib/spawn'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\ntype SpawnOption = Exclude<Parameters<typeof spawn>[2], undefined>\n\nexport type ShadowNpmInstallOptions = SpawnOption & {\n agentExecPath?: string | undefined\n args?: string[] | readonly string[] | undefined\n ipc?: object | undefined\n spinner?: Spinner | undefined\n}\n\nexport function shadowNpmInstall(\n options?: ShadowNpmInstallOptions,\n): SpawnResult<string, Record<any, any> | undefined> {\n const {\n agentExecPath = getNpmBinPath(),\n args = [],\n ipc,\n spinner,\n ...spawnOpts\n } = { __proto__: null, ...options } as ShadowNpmInstallOptions\n const useDebug = isDebug('stdio')\n const terminatorPos = args.indexOf('--')\n const rawBinArgs = terminatorPos === -1 ? args : args.slice(0, terminatorPos)\n const binArgs = rawBinArgs.filter(\n a => !isNpmAuditFlag(a) && !isNpmFundFlag(a) && !isNpmProgressFlag(a),\n )\n const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos)\n const progressArg = rawBinArgs.findLast(isNpmProgressFlag) !== '--no-progress'\n const isSilent = !useDebug && !binArgs.some(isNpmLoglevelFlag)\n const logLevelArgs = isSilent ? ['--loglevel', 'silent'] : []\n const useIpc = isObject(ipc)\n\n // Include 'ipc' in the spawnOpts.stdio when an options.ipc object is provided.\n // See https://github.com/nodejs/node/blob/v23.6.0/lib/child_process.js#L161-L166\n // and https://github.com/nodejs/node/blob/v23.6.0/lib/internal/child_process.js#L238.\n let stdio = getOwn(spawnOpts, 'stdio')\n if (typeof stdio === 'string') {\n stdio = useIpc ? [stdio, stdio, stdio, 'ipc'] : [stdio, stdio, stdio]\n } else if (Array.isArray(stdio)) {\n if (useIpc && !stdio.includes('ipc')) {\n stdio = stdio.concat('ipc')\n }\n } else {\n stdio = useIpc ? ['pipe', 'pipe', 'pipe', 'ipc'] : 'pipe'\n }\n\n const spawnPromise = spawn(\n constants.execPath,\n [\n ...constants.nodeNoWarningsFlags,\n ...constants.nodeDebugFlags,\n ...constants.nodeHardenFlags,\n ...constants.nodeMemoryFlags,\n ...(constants.ENV.INLINED_SOCKET_CLI_SENTRY_BUILD\n ? ['--require', constants.instrumentWithSentryPath]\n : []),\n '--require',\n constants.shadowNpmInjectPath,\n resolveBinPathSync(agentExecPath),\n 'install',\n // Avoid code paths for 'audit' and 'fund'.\n '--no-audit',\n '--no-fund',\n // Add '--no-progress' to fix input being swallowed by the npm spinner.\n '--no-progress',\n // Add '--loglevel=silent' if a loglevel flag is not provided and the\n // SOCKET_CLI_DEBUG environment variable is not truthy.\n ...logLevelArgs,\n ...binArgs,\n ...otherArgs,\n ],\n {\n ...spawnOpts,\n env: {\n ...process.env,\n ...constants.processEnv,\n ...getOwn(spawnOpts, 'env'),\n },\n spinner,\n stdio,\n },\n )\n\n if (useIpc) {\n spawnPromise.process.send({\n [constants.SOCKET_IPC_HANDSHAKE]: {\n [constants.SOCKET_CLI_SHADOW_BIN]: 'npm',\n [constants.SOCKET_CLI_SHADOW_PROGRESS]: progressArg,\n ...ipc,\n },\n })\n }\n\n return spawnPromise\n}\n","import { getOwn } from '@socketsecurity/registry/lib/objects'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\nimport { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nimport constants from '../constants.mts'\nimport { cmdFlagsToString } from './cmd.mts'\nimport { shadowNpmInstall } from '../shadow/npm/install.mts'\n\nimport type { EnvDetails } from './package-environment.mts'\n\ntype SpawnOption = Exclude<Parameters<typeof spawn>[2], undefined>\n\nexport type AgentInstallOptions = SpawnOption & {\n args?: string[] | readonly string[] | undefined\n spinner?: Spinner | undefined\n}\n\nexport type AgentSpawnResult = ReturnType<typeof spawn>\n\nexport function runAgentInstall(\n pkgEnvDetails: EnvDetails,\n options?: AgentInstallOptions | undefined,\n): AgentSpawnResult {\n const { agent, agentExecPath } = pkgEnvDetails\n const isNpm = agent === 'npm'\n const isPnpm = agent === 'pnpm'\n // All package managers support the \"install\" command.\n if (isNpm) {\n return shadowNpmInstall({\n agentExecPath,\n ...options,\n })\n }\n const {\n args = [],\n spinner,\n ...spawnOpts\n } = { __proto__: null, ...options } as AgentInstallOptions\n const skipNodeHardenFlags = isPnpm && pkgEnvDetails.agentVersion.major < 11\n return spawn(agentExecPath, ['install', ...args], {\n shell: constants.WIN32,\n spinner,\n stdio: 'inherit',\n ...spawnOpts,\n env: {\n ...process.env,\n ...constants.processEnv,\n NODE_OPTIONS: cmdFlagsToString([\n ...(skipNodeHardenFlags ? [] : constants.nodeHardenFlags),\n ...constants.nodeNoWarningsFlags,\n ]),\n ...getOwn(spawnOpts, 'env'),\n },\n })\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport browserslist from 'browserslist'\nimport semver from 'semver'\nimport which from 'which'\n\nimport { parse as parseBunLockb } from '@socketregistry/hyrious__bun.lockb/index.cjs'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { readFileBinary, readFileUtf8 } from '@socketsecurity/registry/lib/fs'\nimport { Logger } from '@socketsecurity/registry/lib/logger'\nimport { readPackageJson } from '@socketsecurity/registry/lib/packages'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { cmdPrefixMessage } from './cmd.mts'\nimport { findUp } from './fs.mts'\nimport constants from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\nimport type { EditablePackageJson } from '@socketsecurity/registry/lib/packages'\nimport type { SemVer } from 'semver'\n\nconst {\n BINARY_LOCK_EXT,\n BUN,\n HIDDEN_PACKAGE_LOCK_JSON,\n LOCK_EXT,\n NPM,\n NPM_BUGGY_OVERRIDES_PATCHED_VERSION,\n PACKAGE_JSON,\n PNPM,\n VLT,\n YARN,\n YARN_BERRY,\n YARN_CLASSIC,\n} = constants\n\nexport const AGENTS = [BUN, NPM, PNPM, YARN_BERRY, YARN_CLASSIC, VLT] as const\n\nconst binByAgent = new Map<Agent, string>([\n [BUN, BUN],\n [NPM, NPM],\n [PNPM, PNPM],\n [YARN_BERRY, YARN],\n [YARN_CLASSIC, YARN],\n [VLT, VLT],\n])\n\nexport type Agent = (typeof AGENTS)[number]\n\nexport type EnvBase = {\n agent: Agent\n agentExecPath: string\n agentSupported: boolean\n features: {\n // Fixed by https://github.com/npm/cli/pull/8089.\n // Landed in npm v11.2.0.\n npmBuggyOverrides: boolean\n }\n nodeSupported: boolean\n nodeVersion: SemVer\n npmExecPath: string\n pkgRequirements: {\n agent: string\n node: string\n }\n pkgSupports: {\n agent: boolean\n node: boolean\n }\n}\n\nexport type EnvDetails = Readonly<\n Remap<\n EnvBase & {\n agentVersion: SemVer\n editablePkgJson: EditablePackageJson\n lockName: string\n lockPath: string\n lockSrc: string\n pkgPath: string\n }\n >\n>\n\nexport type DetectAndValidateOptions = {\n cmdName?: string | undefined\n logger?: Logger | undefined\n prod?: boolean | undefined\n}\n\nexport type DetectOptions = {\n cwd?: string | undefined\n onUnknown?: (pkgManager: string | undefined) => void\n}\n\nexport type PartialEnvDetails = Readonly<\n Remap<\n EnvBase & {\n agentVersion: SemVer | undefined\n editablePkgJson: EditablePackageJson | undefined\n lockName: string | undefined\n lockPath: string | undefined\n lockSrc: string | undefined\n pkgPath: string | undefined\n }\n >\n>\n\nexport type ReadLockFile =\n | ((lockPath: string) => Promise<string | undefined>)\n | ((lockPath: string, agentExecPath: string) => Promise<string | undefined>)\n | ((\n lockPath: string,\n agentExecPath: string,\n cwd: string,\n ) => Promise<string | undefined>)\n\nconst readLockFileByAgent: Map<Agent, ReadLockFile> = (() => {\n function wrapReader<T extends (...args: any[]) => Promise<any>>(\n reader: T,\n ): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>> | undefined> {\n return async (...args: any[]): Promise<any> => {\n try {\n return await reader(...args)\n } catch {}\n return undefined\n }\n }\n\n const binaryReader = wrapReader(readFileBinary)\n\n const defaultReader = wrapReader(\n async (lockPath: string) => await readFileUtf8(lockPath),\n )\n\n return new Map([\n [\n BUN,\n wrapReader(\n async (\n lockPath: string,\n agentExecPath: string,\n cwd = process.cwd(),\n ) => {\n const ext = path.extname(lockPath)\n if (ext === LOCK_EXT) {\n return await defaultReader(lockPath)\n }\n if (ext === BINARY_LOCK_EXT) {\n const lockBuffer = await binaryReader(lockPath)\n if (lockBuffer) {\n try {\n return parseBunLockb(lockBuffer)\n } catch {}\n }\n // To print a Yarn lockfile to your console without writing it to disk\n // use `bun bun.lockb`.\n // https://bun.sh/guides/install/yarnlock\n return (\n await spawn(agentExecPath, [lockPath], {\n cwd,\n shell: constants.WIN32,\n })\n ).stdout\n }\n return undefined\n },\n ),\n ],\n [NPM, defaultReader],\n [PNPM, defaultReader],\n [VLT, defaultReader],\n [YARN_BERRY, defaultReader],\n [YARN_CLASSIC, defaultReader],\n ])\n})()\n\n// The order of LOCKS properties IS significant as it affects iteration order.\nconst LOCKS: Record<string, Agent> = {\n [`bun${LOCK_EXT}`]: BUN,\n [`bun${BINARY_LOCK_EXT}`]: BUN,\n // If both package-lock.json and npm-shrinkwrap.json are present in the root\n // of a project, npm-shrinkwrap.json will take precedence and package-lock.json\n // will be ignored.\n // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#package-lockjson-vs-npm-shrinkwrapjson\n 'npm-shrinkwrap.json': NPM,\n 'package-lock.json': NPM,\n 'pnpm-lock.yaml': PNPM,\n 'pnpm-lock.yml': PNPM,\n [`yarn${LOCK_EXT}`]: YARN_CLASSIC,\n 'vlt-lock.json': VLT,\n // Lastly, look for a hidden lock file which is present if .npmrc has package-lock=false:\n // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#hidden-lockfiles\n //\n // Unlike the other LOCKS keys this key contains a directory AND filename so\n // it has to be handled differently.\n 'node_modules/.package-lock.json': NPM,\n}\n\nasync function getAgentExecPath(agent: Agent): Promise<string> {\n const binName = binByAgent.get(agent)!\n if (binName === NPM) {\n return constants.npmExecPath\n }\n return (await which(binName, { nothrow: true })) ?? binName\n}\n\nasync function getAgentVersion(\n agent: Agent,\n agentExecPath: string,\n cwd: string,\n): Promise<SemVer | undefined> {\n let result\n const quotedCmd = `\\`${agent} --version\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n result =\n // Coerce version output into a valid semver version by passing it through\n // semver.coerce which strips leading v's, carets (^), comparators (<,<=,>,>=,=),\n // and tildes (~).\n semver.coerce(\n // All package managers support the \"--version\" flag.\n (\n await spawn(agentExecPath, ['--version'], {\n cwd,\n shell: constants.WIN32,\n })\n ).stdout,\n ) ?? undefined\n } catch (e) {\n debugFn('error', `caught: ${quotedCmd} failed`)\n debugDir('inspect', { error: e })\n }\n return result\n}\n\nexport async function detectPackageEnvironment({\n cwd = process.cwd(),\n onUnknown,\n}: DetectOptions = {}): Promise<EnvDetails | PartialEnvDetails> {\n let lockPath = await findUp(Object.keys(LOCKS), { cwd })\n let lockName = lockPath ? path.basename(lockPath) : undefined\n const isHiddenLockFile = lockName === HIDDEN_PACKAGE_LOCK_JSON\n const pkgJsonPath = lockPath\n ? path.resolve(\n lockPath,\n `${isHiddenLockFile ? '../' : ''}../${PACKAGE_JSON}`,\n )\n : await findUp(PACKAGE_JSON, { cwd })\n const pkgPath =\n pkgJsonPath && existsSync(pkgJsonPath)\n ? path.dirname(pkgJsonPath)\n : undefined\n const editablePkgJson = pkgPath\n ? await readPackageJson(pkgPath, { editable: true })\n : undefined\n // Read Corepack `packageManager` field in package.json:\n // https://nodejs.org/api/packages.html#packagemanager\n const pkgManager = isNonEmptyString(editablePkgJson?.content?.packageManager)\n ? editablePkgJson.content.packageManager\n : undefined\n\n let agent: Agent | undefined\n if (pkgManager) {\n // A valid \"packageManager\" field value is \"<package manager name>@<version>\".\n // https://nodejs.org/api/packages.html#packagemanager\n const atSignIndex = pkgManager.lastIndexOf('@')\n if (atSignIndex !== -1) {\n const name = pkgManager.slice(0, atSignIndex) as Agent\n const version = pkgManager.slice(atSignIndex + 1)\n if (version && AGENTS.includes(name)) {\n agent = name\n }\n }\n }\n if (\n agent === undefined &&\n !isHiddenLockFile &&\n typeof pkgJsonPath === 'string' &&\n typeof lockName === 'string'\n ) {\n agent = LOCKS[lockName] as Agent\n }\n if (agent === undefined) {\n agent = NPM\n onUnknown?.(pkgManager)\n }\n const agentExecPath = await getAgentExecPath(agent)\n const agentVersion = await getAgentVersion(agent, agentExecPath, cwd)\n if (agent === YARN_CLASSIC && (agentVersion?.major ?? 0) > 1) {\n agent = YARN_BERRY\n }\n const { maintainedNodeVersions } = constants\n const minSupportedAgentVersion = constants.minimumVersionByAgent.get(agent)!\n const minSupportedNodeMajor = semver.major(maintainedNodeVersions.last)\n const minSupportedNodeVersion = `${minSupportedNodeMajor}.0.0`\n const minSupportedNodeRange = `>=${minSupportedNodeMajor}`\n const nodeVersion = semver.coerce(process.version)!\n let lockSrc: string | undefined\n let pkgAgentRange: string | undefined\n let pkgNodeRange: string | undefined\n let pkgMinAgentVersion = minSupportedAgentVersion\n let pkgMinNodeVersion = minSupportedNodeVersion\n if (editablePkgJson?.content) {\n const { engines } = editablePkgJson.content\n const engineAgentRange = engines?.[agent]\n const engineNodeRange = engines?.['node']\n if (isNonEmptyString(engineAgentRange)) {\n pkgAgentRange = engineAgentRange\n // Roughly check agent range as semver.coerce will strip leading\n // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).\n const coerced = semver.coerce(pkgAgentRange)\n if (coerced && semver.lt(coerced, pkgMinAgentVersion)) {\n pkgMinAgentVersion = coerced.version\n }\n }\n if (isNonEmptyString(engineNodeRange)) {\n pkgNodeRange = engineNodeRange\n // Roughly check Node range as semver.coerce will strip leading\n // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).\n const coerced = semver.coerce(pkgNodeRange)\n if (coerced && semver.lt(coerced, pkgMinNodeVersion)) {\n pkgMinNodeVersion = coerced.version\n }\n }\n const browserslistQuery = editablePkgJson.content['browserslist'] as\n | string[]\n | undefined\n if (Array.isArray(browserslistQuery)) {\n // List Node targets in ascending version order.\n const browserslistNodeTargets = browserslist(browserslistQuery)\n .filter(v => /^node /i.test(v))\n .map(v => v.slice(5 /*'node '.length*/))\n .sort(naturalCompare)\n if (browserslistNodeTargets.length) {\n // browserslistNodeTargets[0] is the lowest Node target version.\n const coerced = semver.coerce(browserslistNodeTargets[0])\n if (coerced && semver.lt(coerced, pkgMinNodeVersion)) {\n pkgMinNodeVersion = coerced.version\n }\n }\n }\n lockSrc =\n typeof lockPath === 'string'\n ? await readLockFileByAgent.get(agent)!(lockPath, agentExecPath, cwd)\n : undefined\n } else {\n lockName = undefined\n lockPath = undefined\n }\n\n // Does the system agent version meet our minimum supported agent version?\n const agentSupported =\n !!agentVersion &&\n semver.satisfies(agentVersion, `>=${minSupportedAgentVersion}`)\n // Does the system Node version meet our minimum supported Node version?\n const nodeSupported = semver.satisfies(nodeVersion, minSupportedNodeRange)\n\n const npmExecPath =\n agent === NPM ? agentExecPath : await getAgentExecPath(NPM)\n const npmBuggyOverrides =\n agent === NPM &&\n !!agentVersion &&\n semver.lt(agentVersion, NPM_BUGGY_OVERRIDES_PATCHED_VERSION)\n\n const pkgMinAgentRange = `>=${pkgMinAgentVersion}`\n const pkgMinNodeRange = `>=${semver.major(pkgMinNodeVersion)}`\n\n return {\n agent,\n agentExecPath,\n agentSupported,\n agentVersion,\n editablePkgJson,\n features: { npmBuggyOverrides },\n lockName,\n lockPath,\n lockSrc,\n nodeSupported,\n nodeVersion,\n npmExecPath,\n pkgPath,\n pkgRequirements: {\n agent: pkgAgentRange ?? pkgMinAgentRange,\n node: pkgNodeRange ?? pkgMinNodeRange,\n },\n pkgSupports: {\n // Does our minimum supported agent version meet the package's requirements?\n agent: semver.satisfies(minSupportedAgentVersion, pkgMinAgentRange),\n // Does our supported Node versions meet the package's requirements?\n node: maintainedNodeVersions.some(v =>\n semver.satisfies(v, pkgMinNodeRange),\n ),\n },\n }\n}\n\nexport async function detectAndValidatePackageEnvironment(\n cwd: string,\n options?: DetectAndValidateOptions | undefined,\n): Promise<CResult<EnvDetails>> {\n const {\n cmdName = '',\n logger,\n prod,\n } = {\n __proto__: null,\n ...options,\n } as DetectAndValidateOptions\n const details = await detectPackageEnvironment({\n cwd,\n onUnknown(pkgManager: string | undefined) {\n logger?.warn(\n cmdPrefixMessage(\n cmdName,\n `Unknown package manager${pkgManager ? ` ${pkgManager}` : ''}, defaulting to npm`,\n ),\n )\n },\n })\n const { agent, nodeVersion, pkgRequirements } = details\n const agentVersion = details.agentVersion ?? 'unknown'\n if (!details.agentSupported) {\n const minVersion = constants.minimumVersionByAgent.get(agent)!\n return {\n ok: false,\n message: 'Version mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Requires ${agent} >=${minVersion}. Current version: ${agentVersion}.`,\n ),\n }\n }\n if (!details.nodeSupported) {\n const minVersion = constants.maintainedNodeVersions.last\n return {\n ok: false,\n message: 'Version mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Requires Node >=${minVersion}. Current version: ${nodeVersion}.`,\n ),\n }\n }\n if (!details.pkgSupports.agent) {\n return {\n ok: false,\n message: 'Engine mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Package engine \"${agent}\" requires ${pkgRequirements.agent}. Current version: ${agentVersion}`,\n ),\n }\n }\n if (!details.pkgSupports.node) {\n return {\n ok: false,\n message: 'Version mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Package engine \"node\" requires ${pkgRequirements.node}. Current version: ${nodeVersion}`,\n ),\n }\n }\n const lockName = details.lockName ?? 'lock file'\n if (details.lockName === undefined || details.lockSrc === undefined) {\n return {\n ok: false,\n message: 'Missing lockfile',\n cause: cmdPrefixMessage(cmdName, `No ${lockName} found`),\n }\n }\n if (details.lockSrc.trim() === '') {\n return {\n ok: false,\n message: 'Empty lockfile',\n cause: cmdPrefixMessage(cmdName, `${lockName} is empty`),\n }\n }\n if (details.pkgPath === undefined) {\n return {\n ok: false,\n message: 'Missing package.json',\n cause: cmdPrefixMessage(cmdName, `No ${PACKAGE_JSON} found`),\n }\n }\n if (prod && (agent === BUN || agent === YARN_BERRY)) {\n return {\n ok: false,\n message: 'Bad input',\n cause: cmdPrefixMessage(\n cmdName,\n `--prod not supported for ${agent}${agentVersion ? `@${agentVersion}` : ''}`,\n ),\n }\n }\n if (\n details.lockPath &&\n path.relative(cwd, details.lockPath).startsWith('.')\n ) {\n // Note: In tests we return <redacted> because otherwise snapshots will fail.\n logger?.warn(\n cmdPrefixMessage(\n cmdName,\n `Package ${lockName} found at ${constants.ENV.VITEST ? constants.REDACTED : details.lockPath}`,\n ),\n )\n }\n return { ok: true, data: details as EnvDetails }\n}\n","import type { EcosystemString } from '@socketsecurity/registry'\nimport type { components } from '@socketsecurity/sdk/types/api'\n\nexport type PURL_Type = components['schemas']['SocketPURL_Type']\n\ntype ExpectNever<T extends never> = T\n\ntype MissingInEcosystemString = Exclude<PURL_Type, EcosystemString>\ntype ExtraInEcosystemString = Exclude<EcosystemString, PURL_Type>\n\nexport type _Check_EcosystemString_has_all_purl_types =\n ExpectNever<MissingInEcosystemString>\nexport type _Check_EcosystemString_has_no_extras =\n ExpectNever<ExtraInEcosystemString>\n\nexport const ALL_ECOSYSTEMS = [\n 'apk',\n 'bitbucket',\n 'cargo',\n 'chrome',\n 'cocoapods',\n 'composer',\n 'conan',\n 'conda',\n 'cran',\n 'deb',\n 'docker',\n 'gem',\n 'generic',\n 'github',\n 'golang',\n 'hackage',\n 'hex',\n 'huggingface',\n 'maven',\n 'mlflow',\n 'npm',\n 'nuget',\n 'oci',\n 'pub',\n 'pypi',\n 'qpkg',\n 'rpm',\n 'swift',\n 'swid',\n 'unknown',\n] as const satisfies readonly PURL_Type[]\n\ntype AllEcosystemsUnion = (typeof ALL_ECOSYSTEMS)[number]\ntype MissingInAllEcosystems = Exclude<PURL_Type, AllEcosystemsUnion>\ntype ExtraInAllEcosystems = Exclude<AllEcosystemsUnion, PURL_Type>\n\nexport type _Check_ALL_ECOSYSTEMS_has_all_purl_types =\n ExpectNever<MissingInAllEcosystems>\nexport type _Check_ALL_ECOSYSTEMS_has_no_extras =\n ExpectNever<ExtraInAllEcosystems>\n\nexport const ALL_SUPPORTED_ECOSYSTEMS = new Set<string>(ALL_ECOSYSTEMS)\n\nexport function getEcosystemChoicesForMeow(): string[] {\n return [...ALL_ECOSYSTEMS]\n}\n\nexport function isValidEcosystem(value: string): value is PURL_Type {\n return ALL_SUPPORTED_ECOSYSTEMS.has(value)\n}\n\nexport function parseEcosystems(\n value: string | string[] | undefined,\n): PURL_Type[] {\n if (!value) {\n return []\n }\n const values =\n typeof value === 'string'\n ? value.split(',').map(v => v.trim().toLowerCase())\n : value.map(v => String(v).toLowerCase())\n\n return values.filter(isValidEcosystem)\n}\n","import constants from '../../constants.mts'\n\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\nimport type {\n ALERT_ACTION,\n ALERT_TYPE,\n CompactSocketArtifact,\n CompactSocketArtifactAlert,\n SocketArtifact,\n SocketArtifactAlert,\n} from '@socketsecurity/sdk'\n\nexport type {\n ALERT_ACTION,\n ALERT_TYPE,\n CompactSocketArtifact,\n CompactSocketArtifactAlert,\n SocketArtifact,\n SocketArtifactAlert,\n}\n\nexport type CVE_ALERT_TYPE = 'cve' | 'mediumCVE' | 'mildCVE' | 'criticalCVE'\n\nexport type ArtifactAlertCve = Remap<\n Omit<CompactSocketArtifactAlert, 'type'> & {\n type: CVE_ALERT_TYPE\n }\n>\n\nexport type ArtifactAlertCveFixable = Remap<\n Omit<CompactSocketArtifactAlert, 'props' | 'type'> & {\n type: CVE_ALERT_TYPE\n props: CveProps\n }\n>\n\nexport type ArtifactAlertUpgrade = Remap<\n Omit<CompactSocketArtifactAlert, 'type'> & {\n type: 'socketUpgradeAvailable'\n }\n>\n\nexport type CveProps = {\n firstPatchedVersionIdentifier?: string\n vulnerableVersionRange: string\n [key: string]: any\n}\n\nexport function isArtifactAlertCve(\n alert: CompactSocketArtifactAlert,\n): alert is ArtifactAlertCve {\n const { type } = alert\n return (\n type === constants.ALERT_TYPE_CVE ||\n type === constants.ALERT_TYPE_MEDIUM_CVE ||\n type === constants.ALERT_TYPE_MILD_CVE ||\n type === constants.ALERT_TYPE_CRITICAL_CVE\n )\n}\n","export function createEnum<const T extends Record<string, any>>(\n obj: T,\n): Readonly<T> {\n return Object.freeze({ __proto__: null, ...obj }) as any\n}\n\nexport function pick<T extends Record<string, any>, K extends keyof T>(\n input: T,\n keys: K[] | readonly K[],\n): Pick<T, K> {\n const result: Partial<Pick<T, K>> = {}\n for (const key of keys) {\n result[key] = input[key]\n }\n return result as Pick<T, K>\n}\n","import { createEnum } from '../objects.mts'\n\nexport const ALERT_FIX_TYPE = createEnum({\n cve: 'cve',\n remove: 'remove',\n upgrade: 'upgrade',\n})\n","import { joinAnd } from '@socketsecurity/registry/lib/arrays'\n\nimport { createEnum, pick } from '../objects.mts'\n\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport const ALERT_SEVERITY = createEnum({\n critical: 'critical',\n high: 'high',\n middle: 'middle',\n low: 'low',\n})\n\nexport type SocketSdkAlertList =\n SocketSdkSuccessResult<'getIssuesByNPMPackage'>['data']\n\nexport type SocketSdkAlert = SocketSdkAlertList[number]['value'] extends\n | infer U\n | undefined\n ? U\n : never\n\n// Ordered from most severe to least.\nexport const ALERT_SEVERITIES_SORTED: ReadonlyArray<\n SocketSdkAlert['severity']\n> = Object.freeze(['critical', 'high', 'middle', 'low'])\n\nfunction getDesiredSeverities(\n lowestToInclude: SocketSdkAlert['severity'] | undefined,\n): Array<SocketSdkAlert['severity']> {\n const result: Array<SocketSdkAlert['severity']> = []\n for (const severity of ALERT_SEVERITIES_SORTED) {\n result.push(severity)\n if (severity === lowestToInclude) {\n break\n }\n }\n return result\n}\n\nexport function formatSeverityCount(\n severityCount: Record<SocketSdkAlert['severity'], number>,\n): string {\n const summary: string[] = []\n for (const severity of ALERT_SEVERITIES_SORTED) {\n if (severityCount[severity]) {\n summary.push(`${severityCount[severity]} ${severity}`)\n }\n }\n return joinAnd(summary)\n}\n\nexport function getSeverityCount(\n issues: SocketSdkAlertList,\n lowestToInclude: SocketSdkAlert['severity'] | undefined,\n): Record<SocketSdkAlert['severity'], number> {\n const severityCount = pick(\n { low: 0, middle: 0, high: 0, critical: 0 },\n getDesiredSeverities(lowestToInclude),\n ) as Record<SocketSdkAlert['severity'], number>\n\n for (const issue of issues) {\n const { value } = issue\n if (!value) {\n continue\n }\n const { severity } = value\n if (severityCount[severity] !== undefined) {\n severityCount[severity] += 1\n }\n }\n return severityCount\n}\n","import terminalLink from 'terminal-link'\nimport colors from 'yoctocolors-cjs'\n\nimport indentString from '@socketregistry/indent-string/index.cjs'\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","import { isObject } from '@socketsecurity/registry/lib/objects'\n\nexport type FilterConfig = {\n [key: string]: boolean | string[]\n}\n\nexport function toFilterConfig(obj: any): FilterConfig {\n const normalized = { __proto__: null } as unknown as FilterConfig\n const keys = isObject(obj) ? Object.keys(obj) : []\n for (const key of keys) {\n const value = obj[key]\n if (typeof value === 'boolean' || Array.isArray(value)) {\n normalized[key] = value\n }\n }\n return normalized\n}\n","import { createRequire } from 'node:module'\nimport path from 'node:path'\n\nimport constants from '../constants.mts'\n\nconst require = createRequire(import.meta.url)\n\nlet _translations:\n | Readonly<typeof import('../../translations.json')>\n | undefined\n\nexport function getTranslations() {\n if (_translations === undefined) {\n _translations = /*@__PURE__*/ require(\n path.join(constants.rootPath, 'translations.json'),\n )\n }\n return _translations!\n}\n","import semver from 'semver'\nimport colors from 'yoctocolors-cjs'\n\nimport { PackageURL } from '@socketregistry/packageurl-js'\nimport { getManifestData } from '@socketsecurity/registry'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { getOwn, hasOwn } from '@socketsecurity/registry/lib/objects'\nimport { resolvePackageName } from '@socketsecurity/registry/lib/packages'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\n\nimport { isArtifactAlertCve } from './alert/artifact.mts'\nimport { ALERT_FIX_TYPE } from './alert/fix.mts'\nimport { ALERT_SEVERITY } from './alert/severity.mts'\nimport { ColorOrMarkdown } from './color-or-markdown.mts'\nimport { toFilterConfig } from './filter-config.mts'\nimport { createEnum } from './objects.mts'\nimport { getPurlObject } from './purl.mts'\nimport { getMajor } from './semver.mts'\nimport { getSocketDevPackageOverviewUrl } from './socket-url.mts'\nimport { getTranslations } from './translations.mts'\n\nimport type {\n ALERT_ACTION,\n ALERT_TYPE,\n CompactSocketArtifact,\n CompactSocketArtifactAlert,\n CveProps,\n} from './alert/artifact.mts'\nimport type { PURL_Type } from './ecosystem.mts'\nimport type { SocketYml } from '@socketsecurity/config'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nexport const ALERT_SEVERITY_COLOR = createEnum({\n critical: 'magenta',\n high: 'red',\n middle: 'yellow',\n low: 'white',\n})\n\nexport const ALERT_SEVERITY_ORDER = createEnum({\n critical: 0,\n high: 1,\n middle: 2,\n low: 3,\n none: 4,\n})\n\nexport type SocketPackageAlert = {\n name: string\n version: string\n key: string\n type: string\n blocked: boolean\n critical: boolean\n ecosystem: PURL_Type\n fixable: boolean\n raw: CompactSocketArtifactAlert\n upgradable: boolean\n}\n\nexport type AlertsByPurl = Map<string, SocketPackageAlert[]>\n\nconst MIN_ABOVE_THE_FOLD_COUNT = 3\n\nconst MIN_ABOVE_THE_FOLD_ALERT_COUNT = 1\n\nconst format = new ColorOrMarkdown(false)\n\nexport type RiskCounts = {\n critical: number\n high: number\n middle: number\n low: number\n}\n\nfunction getHiddenRiskCounts(hiddenAlerts: SocketPackageAlert[]): RiskCounts {\n const riskCounts = {\n critical: 0,\n high: 0,\n middle: 0,\n low: 0,\n }\n for (const alert of hiddenAlerts) {\n switch (getAlertSeverityOrder(alert)) {\n case ALERT_SEVERITY_ORDER.critical:\n riskCounts.critical += 1\n break\n case ALERT_SEVERITY_ORDER.high:\n riskCounts.high += 1\n break\n case ALERT_SEVERITY_ORDER.middle:\n riskCounts.middle += 1\n break\n case ALERT_SEVERITY_ORDER.low:\n riskCounts.low += 1\n break\n }\n }\n return riskCounts\n}\n\nfunction getHiddenRisksDescription(riskCounts: RiskCounts): string {\n const descriptions: string[] = []\n if (riskCounts.critical) {\n descriptions.push(`${riskCounts.critical} ${getSeverityLabel('critical')}`)\n }\n if (riskCounts.high) {\n descriptions.push(`${riskCounts.high} ${getSeverityLabel('high')}`)\n }\n if (riskCounts.middle) {\n descriptions.push(`${riskCounts.middle} ${getSeverityLabel('middle')}`)\n }\n if (riskCounts.low) {\n descriptions.push(`${riskCounts.low} ${getSeverityLabel('low')}`)\n }\n return `(${descriptions.join('; ')})`\n}\n\nexport type AlertFilter = {\n actions?: ALERT_ACTION[] | undefined\n blocked?: boolean | undefined\n critical?: boolean | undefined\n cve?: boolean | undefined\n existing?: boolean | undefined\n fixable?: boolean | undefined\n upgradable?: boolean | undefined\n}\n\nexport type AddArtifactToAlertsMapOptions = {\n consolidate?: boolean | undefined\n filter?: AlertFilter | undefined\n overrides?: { [key: string]: string } | undefined\n socketYml?: SocketYml | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function addArtifactToAlertsMap<T extends AlertsByPurl>(\n artifact: CompactSocketArtifact,\n alertsByPurl: T,\n options?: AddArtifactToAlertsMapOptions | undefined,\n): Promise<T> {\n // Make TypeScript happy.\n if (!artifact.name || !artifact.version || !artifact.alerts?.length) {\n return alertsByPurl\n }\n\n const { type: ecosystem, version } = artifact\n\n const {\n consolidate = false,\n overrides,\n socketYml,\n } = {\n __proto__: null,\n ...options,\n } as AddArtifactToAlertsMapOptions\n\n const name = resolvePackageName(\n artifact as {\n name: string\n namespace?: string | undefined\n },\n )\n\n const filterConfig = toFilterConfig({\n blocked: true,\n critical: true,\n cve: true,\n ...getOwn(options, 'filter'),\n }) as AlertFilter\n\n const enabledState = {\n __proto__: null,\n ...socketYml?.issueRules,\n } as Partial<Record<ALERT_TYPE, boolean>>\n\n let sockPkgAlerts: SocketPackageAlert[] = []\n for (const alert of artifact.alerts) {\n const action = alert.action ?? ''\n const enabledFlag = enabledState[alert.type]\n if (\n (action === 'ignore' && enabledFlag !== true) ||\n enabledFlag === false\n ) {\n continue\n }\n const blocked = action === 'error'\n const critical = alert.severity === ALERT_SEVERITY.critical\n const cve = isArtifactAlertCve(alert)\n const fixType = alert.fix?.type ?? ''\n const fixableCve = fixType === ALERT_FIX_TYPE.cve\n const fixableUpgrade = fixType === ALERT_FIX_TYPE.upgrade\n const fixable = fixableCve || fixableUpgrade\n const upgradable = fixableUpgrade && !hasOwn(overrides, name)\n if (\n (filterConfig.blocked && blocked) ||\n (filterConfig.critical && critical) ||\n (filterConfig.cve && cve) ||\n (filterConfig.fixable && fixable) ||\n (filterConfig.upgradable && upgradable)\n ) {\n sockPkgAlerts.push({\n name,\n version,\n key: alert.key,\n type: alert.type,\n blocked,\n critical,\n ecosystem,\n fixable,\n raw: alert,\n upgradable,\n })\n }\n }\n if (!sockPkgAlerts.length) {\n return alertsByPurl\n }\n const purl = `pkg:${ecosystem}/${name}@${version}`\n const major = getMajor(version)!\n if (consolidate) {\n type HighestVersionByMajor = Map<\n number,\n { alert: SocketPackageAlert; version: string }\n >\n const highestForCve: HighestVersionByMajor = new Map()\n const highestForUpgrade: HighestVersionByMajor = new Map()\n const unfixableAlerts: SocketPackageAlert[] = []\n for (const sockPkgAlert of sockPkgAlerts) {\n const alert = sockPkgAlert.raw\n const fixType = alert.fix?.type ?? ''\n if (fixType === ALERT_FIX_TYPE.cve) {\n // An alert with alert.fix.type of 'cve' should have a\n // alert.props.firstPatchedVersionIdentifier property value.\n // We're just being cautious.\n const firstPatchedVersionIdentifier = (alert.props as CveProps)\n ?.firstPatchedVersionIdentifier\n const patchedMajor = firstPatchedVersionIdentifier\n ? getMajor(firstPatchedVersionIdentifier)\n : null\n if (typeof patchedMajor === 'number') {\n // Consolidate to the highest \"first patched version\" by each major\n // version number.\n const highest = highestForCve.get(patchedMajor)?.version ?? '0.0.0'\n if (semver.gt(firstPatchedVersionIdentifier!, highest)) {\n highestForCve.set(patchedMajor, {\n alert: sockPkgAlert,\n version: firstPatchedVersionIdentifier!,\n })\n }\n } else {\n unfixableAlerts.push(sockPkgAlert)\n }\n } else if (fixType === ALERT_FIX_TYPE.upgrade) {\n // For Socket Optimize upgrades we assume the highest version available\n // is compatible. This may change in the future.\n const highest = highestForUpgrade.get(major)?.version ?? '0.0.0'\n if (semver.gt(version, highest)) {\n highestForUpgrade.set(major, { alert: sockPkgAlert, version })\n }\n } else {\n unfixableAlerts.push(sockPkgAlert)\n }\n }\n sockPkgAlerts = [\n // Sort CVE alerts by severity: critical, high, middle, then low.\n ...Array.from(highestForCve.values())\n .map(d => d.alert)\n .sort(alertSeverityComparator),\n ...Array.from(highestForUpgrade.values()).map(d => d.alert),\n ...unfixableAlerts,\n ]\n } else {\n sockPkgAlerts.sort((a, b) => naturalCompare(a.type, b.type))\n }\n if (sockPkgAlerts.length) {\n alertsByPurl.set(purl, sockPkgAlerts)\n }\n return alertsByPurl\n}\n\nexport function alertsHaveBlocked(alerts: SocketPackageAlert[]): boolean {\n return alerts.find(a => a.blocked) !== undefined\n}\n\nexport function alertsHaveSeverity(\n alerts: SocketPackageAlert[],\n severity: `${keyof typeof ALERT_SEVERITY}`,\n): boolean {\n return alerts.find(a => a.raw.severity === severity) !== undefined\n}\n\nexport function alertSeverityComparator(\n a: SocketPackageAlert,\n b: SocketPackageAlert,\n): number {\n // Put the most severe first.\n return getAlertSeverityOrder(a) - getAlertSeverityOrder(b)\n}\n\nexport function getAlertSeverityOrder(alert: SocketPackageAlert): number {\n // The more severe, the lower the sort number.\n const { severity } = alert.raw\n return severity === ALERT_SEVERITY.critical\n ? 0\n : severity === ALERT_SEVERITY.high\n ? 1\n : severity === ALERT_SEVERITY.middle\n ? 2\n : severity === ALERT_SEVERITY.low\n ? 3\n : 4\n}\n\nexport function getAlertsSeverityOrder(alerts: SocketPackageAlert[]): number {\n return alertsHaveBlocked(alerts) ||\n alertsHaveSeverity(alerts, ALERT_SEVERITY.critical)\n ? 0\n : alertsHaveSeverity(alerts, ALERT_SEVERITY.high)\n ? 1\n : alertsHaveSeverity(alerts, ALERT_SEVERITY.middle)\n ? 2\n : alertsHaveSeverity(alerts, ALERT_SEVERITY.low)\n ? 3\n : 4\n}\n\nexport type CveFilter = {\n upgradable?: boolean | undefined\n}\n\nexport type CveInfoByAlertKey = Map<\n string,\n {\n firstPatchedVersionIdentifier: string\n vulnerableVersionRange: string\n }\n>\n\nexport type CveInfoByPartialPurl = Map<string, CveInfoByAlertKey>\n\nexport type GetCveInfoByPackageOptions = {\n filter?: CveFilter | undefined\n}\n\nexport function getCveInfoFromAlertsMap(\n alertsMap: AlertsByPurl,\n options?: GetCveInfoByPackageOptions | undefined,\n): CveInfoByPartialPurl | null {\n const filterConfig = toFilterConfig(getOwn(options, 'filter')) as CveFilter\n\n let infoByPartialPurl: CveInfoByPartialPurl | null = null\n // eslint-disable-next-line no-unused-labels\n alertsMapLoop: for (const { 0: purl, 1: sockPkgAlerts } of alertsMap) {\n const purlObj = getPurlObject(purl)\n const partialPurl = new PackageURL(\n purlObj.type,\n purlObj.namespace,\n purlObj.name,\n ).toString()\n const name = resolvePackageName(purlObj)\n sockPkgAlertsLoop: for (const sockPkgAlert of sockPkgAlerts) {\n const alert = sockPkgAlert.raw\n if (\n alert.fix?.type !== ALERT_FIX_TYPE.cve ||\n (filterConfig.upgradable === false &&\n getManifestData(sockPkgAlert.ecosystem as any, name))\n ) {\n continue sockPkgAlertsLoop\n }\n if (!infoByPartialPurl) {\n infoByPartialPurl = new Map()\n }\n let infos = infoByPartialPurl.get(partialPurl)\n if (!infos) {\n infos = new Map()\n infoByPartialPurl.set(partialPurl, infos)\n }\n const { key } = alert\n if (!infos.has(key)) {\n // An alert with alert.fix.type of 'cve' should have a\n // alert.props.firstPatchedVersionIdentifier property value.\n // We're just being cautious.\n const firstPatchedVersionIdentifier = (alert.props as CveProps)\n ?.firstPatchedVersionIdentifier\n const vulnerableVersionRange = (alert.props as CveProps)\n ?.vulnerableVersionRange\n let error: unknown\n if (firstPatchedVersionIdentifier && vulnerableVersionRange) {\n try {\n infos.set(key, {\n firstPatchedVersionIdentifier,\n vulnerableVersionRange: new semver.Range(\n // Replace ', ' in a range like '>= 1.0.0, < 1.8.2' with ' ' so that\n // semver.Range will parse it without erroring.\n vulnerableVersionRange\n .replace(/, +/g, ' ')\n .replace(/; +/g, ' || '),\n ).format(),\n })\n continue sockPkgAlertsLoop\n } catch (e) {\n error = e\n }\n }\n debugFn('error', 'fail: invalid SocketPackageAlert')\n debugDir('inspect', { alert, error })\n }\n }\n }\n return infoByPartialPurl\n}\n\nexport function getSeverityLabel(\n severity: `${keyof typeof ALERT_SEVERITY}`,\n): string {\n return severity === 'middle' ? 'moderate' : severity\n}\n\nexport type LogAlertsMapOptions = {\n hideAt?: `${keyof typeof ALERT_SEVERITY}` | 'none' | undefined\n output?: NodeJS.WriteStream | undefined\n}\n\nexport function logAlertsMap(\n alertsMap: AlertsByPurl,\n options: LogAlertsMapOptions,\n) {\n const { hideAt = 'middle', output = process.stderr } = {\n __proto__: null,\n ...options,\n } as LogAlertsMapOptions\n\n const translations = getTranslations()\n const sortedEntries = Array.from(alertsMap.entries()).sort(\n (a, b) => getAlertsSeverityOrder(a[1]) - getAlertsSeverityOrder(b[1]),\n )\n\n const aboveTheFoldPurls = new Set<string>()\n const viewableAlertsByPurl = new Map<string, SocketPackageAlert[]>()\n const hiddenAlertsByPurl = new Map<string, SocketPackageAlert[]>()\n\n for (let i = 0, { length } = sortedEntries; i < length; i += 1) {\n const { 0: purl, 1: alerts } = sortedEntries[i]!\n const hiddenAlerts: typeof alerts = []\n const viewableAlerts = alerts.filter(a => {\n const keep =\n a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER[hideAt]\n if (!keep) {\n hiddenAlerts.push(a)\n }\n return keep\n })\n if (hiddenAlerts.length) {\n hiddenAlertsByPurl.set(purl, hiddenAlerts.sort(alertSeverityComparator))\n }\n if (!viewableAlerts.length) {\n continue\n }\n viewableAlerts.sort(alertSeverityComparator)\n viewableAlertsByPurl.set(purl, viewableAlerts)\n if (\n viewableAlerts.find(\n (a: SocketPackageAlert) =>\n a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER.middle,\n )\n ) {\n aboveTheFoldPurls.add(purl)\n }\n }\n\n // If MIN_ABOVE_THE_FOLD_COUNT is NOT met add more from viewable pkg ids.\n for (const { 0: purl } of viewableAlertsByPurl.entries()) {\n if (aboveTheFoldPurls.size >= MIN_ABOVE_THE_FOLD_COUNT) {\n break\n }\n aboveTheFoldPurls.add(purl)\n }\n // If MIN_ABOVE_THE_FOLD_COUNT is STILL NOT met add more from hidden pkg ids.\n for (const { 0: purl, 1: hiddenAlerts } of hiddenAlertsByPurl.entries()) {\n if (aboveTheFoldPurls.size >= MIN_ABOVE_THE_FOLD_COUNT) {\n break\n }\n aboveTheFoldPurls.add(purl)\n const viewableAlerts = viewableAlertsByPurl.get(purl) ?? []\n if (viewableAlerts.length < MIN_ABOVE_THE_FOLD_ALERT_COUNT) {\n const neededCount = MIN_ABOVE_THE_FOLD_ALERT_COUNT - viewableAlerts.length\n let removedHiddenAlerts: SocketPackageAlert[] | undefined\n if (hiddenAlerts.length - neededCount > 0) {\n removedHiddenAlerts = hiddenAlerts.splice(\n 0,\n MIN_ABOVE_THE_FOLD_ALERT_COUNT,\n )\n } else {\n removedHiddenAlerts = hiddenAlerts\n hiddenAlertsByPurl.delete(purl)\n }\n viewableAlertsByPurl.set(purl, [\n ...viewableAlerts,\n ...removedHiddenAlerts,\n ])\n }\n }\n\n const mentionedPurlsWithHiddenAlerts = new Set<string>()\n for (\n let i = 0,\n prevAboveTheFold = true,\n entries = Array.from(viewableAlertsByPurl.entries()),\n { length } = entries;\n i < length;\n i += 1\n ) {\n const { 0: purl, 1: alerts } = entries[i]!\n const lines = new Set<string>()\n for (const alert of alerts) {\n const { type } = alert\n const severity = alert.raw.severity ?? ''\n const attributes = [\n ...(severity\n ? [colors[ALERT_SEVERITY_COLOR[severity]](getSeverityLabel(severity))]\n : []),\n ...(alert.blocked ? [colors.bold(colors.red('blocked'))] : []),\n ...(alert.fixable ? ['fixable'] : []),\n ]\n const maybeAttributes = attributes.length\n ? ` ${colors.italic(`(${attributes.join('; ')})`)}`\n : ''\n // Based data from { pageProps: { alertTypes } } of:\n // https://socket.dev/_next/data/9a6db8224b68b6da0eb9f7dbb17aff7e51568ac2/en-US.json\n const info = (translations.alerts as any)[type]\n const title = info?.title ?? type\n const maybeDesc = info?.description ? ` - ${info.description}` : ''\n const content = `${title}${maybeAttributes}${maybeDesc}`\n // TODO: An added emoji seems to mis-align terminals sometimes.\n lines.add(` ${content}`)\n }\n const purlObj = getPurlObject(purl)\n const pkgName = resolvePackageName(purlObj)\n const hyperlink = format.hyperlink(\n `${pkgName}@${purlObj.version}`,\n getSocketDevPackageOverviewUrl(purlObj.type, pkgName, purlObj.version),\n )\n const isAboveTheFold = aboveTheFoldPurls.has(purl)\n if (isAboveTheFold) {\n aboveTheFoldPurls.add(purl)\n output.write(`${i ? '\\n' : ''}${hyperlink}:\\n`)\n } else {\n output.write(`${prevAboveTheFold ? '\\n' : ''}${hyperlink}:\\n`)\n }\n for (const line of lines) {\n output.write(`${line}\\n`)\n }\n const hiddenAlerts = hiddenAlertsByPurl.get(purl) ?? []\n const { length: hiddenAlertsCount } = hiddenAlerts\n if (hiddenAlertsCount) {\n mentionedPurlsWithHiddenAlerts.add(purl)\n if (hiddenAlertsCount === 1) {\n output.write(\n ` ${colors.dim(`+1 Hidden ${getSeverityLabel(hiddenAlerts[0]!.raw.severity ?? 'low')} risk alert`)}\\n`,\n )\n } else {\n output.write(\n ` ${colors.dim(`+${hiddenAlertsCount} Hidden alerts ${colors.italic(getHiddenRisksDescription(getHiddenRiskCounts(hiddenAlerts)))}`)}\\n`,\n )\n }\n }\n prevAboveTheFold = isAboveTheFold\n }\n\n const additionalHiddenCount =\n hiddenAlertsByPurl.size - mentionedPurlsWithHiddenAlerts.size\n if (additionalHiddenCount) {\n const totalRiskCounts = {\n critical: 0,\n high: 0,\n middle: 0,\n low: 0,\n }\n for (const { 0: purl, 1: alerts } of hiddenAlertsByPurl.entries()) {\n if (mentionedPurlsWithHiddenAlerts.has(purl)) {\n continue\n }\n const riskCounts = getHiddenRiskCounts(alerts)\n totalRiskCounts.critical += riskCounts.critical\n totalRiskCounts.high += riskCounts.high\n totalRiskCounts.middle += riskCounts.middle\n totalRiskCounts.low += riskCounts.low\n }\n output.write(\n `${aboveTheFoldPurls.size ? '\\n' : ''}${colors.dim(`${aboveTheFoldPurls.size ? '+' : ''}${additionalHiddenCount} Packages with hidden alerts ${colors.italic(getHiddenRisksDescription(totalRiskCounts))}`)}\\n`,\n )\n }\n output.write('\\n')\n}\n","import semver from 'semver'\n\nimport { PackageURL } from '@socketregistry/packageurl-js'\n\nimport { stripPnpmPeerSuffix } from './pnpm.mts'\n\nexport function idToNpmPurl(id: string): string {\n return `pkg:npm/${id}`\n}\n\nexport function idToPurl(id: string, type: string): string {\n return `pkg:${type}/${id}`\n}\n\nexport function resolvePackageVersion(purlObj: PackageURL): string {\n const { version } = purlObj\n if (!version) {\n return ''\n }\n const { type } = purlObj\n return (\n semver.coerce(type === 'npm' ? stripPnpmPeerSuffix(version) : version)\n ?.version ?? ''\n )\n}\n","import { arrayUnique } from '@socketsecurity/registry/lib/arrays'\nimport { debugDir } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { getOwn } from '@socketsecurity/registry/lib/objects'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { findSocketYmlSync } from './config.mts'\nimport { toFilterConfig } from './filter-config.mts'\nimport { extractPurlsFromPnpmLockfile } from './pnpm.mts'\nimport { getPublicApiToken, setupSdk } from './sdk.mts'\nimport { addArtifactToAlertsMap } from './socket-package-alert.mts'\n\nimport type { CompactSocketArtifact } from './alert/artifact.mts'\nimport type { AlertFilter, AlertsByPurl } from './socket-package-alert.mts'\nimport type { LockfileObject } from '@pnpm/lockfile.fs'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nexport type GetAlertsMapFromPnpmLockfileOptions = {\n apiToken?: string | undefined\n consolidate?: boolean | undefined\n filter?: AlertFilter | undefined\n overrides?: { [key: string]: string } | undefined\n nothrow?: boolean | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function getAlertsMapFromPnpmLockfile(\n lockfile: LockfileObject,\n options?: GetAlertsMapFromPnpmLockfileOptions | undefined,\n): Promise<AlertsByPurl> {\n const purls = await extractPurlsFromPnpmLockfile(lockfile)\n return await getAlertsMapFromPurls(purls, {\n overrides: lockfile.overrides,\n ...options,\n })\n}\n\nexport type GetAlertsMapFromPurlsOptions = {\n apiToken?: string | undefined\n consolidate?: boolean | undefined\n filter?: AlertFilter | undefined\n onlyFixable?: boolean | undefined\n overrides?: { [key: string]: string } | undefined\n nothrow?: boolean | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function getAlertsMapFromPurls(\n purls: string[] | readonly string[],\n options?: GetAlertsMapFromPurlsOptions | undefined,\n): Promise<AlertsByPurl> {\n const uniqPurls = arrayUnique(purls)\n debugDir('silly', { purls: uniqPurls })\n\n let { length: remaining } = uniqPurls\n const alertsByPurl: AlertsByPurl = new Map()\n\n if (!remaining) {\n return alertsByPurl\n }\n\n const opts = {\n __proto__: null,\n consolidate: false,\n nothrow: false,\n ...options,\n filter: toFilterConfig(getOwn(options, 'filter')),\n } as GetAlertsMapFromPurlsOptions & { filter: AlertFilter }\n\n if (opts.onlyFixable) {\n opts.filter.fixable = true\n }\n\n const { apiToken = getPublicApiToken(), spinner } = opts\n\n const getText = () => `Looking up data for ${remaining} packages`\n\n spinner?.start(getText())\n\n const sockSdkCResult = await setupSdk({ apiToken })\n if (!sockSdkCResult.ok) {\n spinner?.stop()\n throw new Error('Auth error: Run `socket login` first')\n }\n const sockSdk = sockSdkCResult.data\n const socketYml = findSocketYmlSync()?.parsed\n\n const alertsMapOptions = {\n consolidate: opts.consolidate,\n filter: opts.filter,\n overrides: opts.overrides,\n socketYml,\n spinner,\n }\n\n try {\n for await (const batchResult of sockSdk.batchPackageStream(\n {\n components: uniqPurls.map(purl => ({ purl })),\n },\n {\n queryParams: {\n alerts: 'true',\n compact: 'true',\n ...(opts.onlyFixable ? { fixable: 'true ' } : {}),\n ...(Array.isArray(opts.filter.actions)\n ? { actions: opts.filter.actions.join(',') }\n : {}),\n },\n },\n )) {\n if (batchResult.success) {\n const artifact = batchResult.data as CompactSocketArtifact\n await addArtifactToAlertsMap(artifact, alertsByPurl, alertsMapOptions)\n } else if (!opts.nothrow) {\n spinner?.stop()\n if (isNonEmptyString(batchResult.error)) {\n throw new Error(batchResult.error)\n }\n const statusCode = batchResult.status ?? 'unknown'\n throw new Error(\n `Socket API server error (${statusCode}): No status message`,\n )\n } else {\n spinner?.stop()\n logger.fail(\n `Received a ${batchResult.status} response from Socket API which we consider a permanent failure:`,\n batchResult.error,\n batchResult.cause ? `( ${batchResult.cause} )` : '',\n )\n debugDir('inspect', { batchResult })\n break\n }\n remaining -= 1\n if (remaining > 0) {\n spinner?.start(getText())\n }\n }\n } catch (e) {\n spinner?.stop()\n throw e\n }\n\n spinner?.stop()\n\n return alertsByPurl\n}\n"],"names":["socketAppDataPath","logger","updateConfigValue","mkdirSync","recursive","ok","data","yml","path","parsed","error","prevDir","exports","debugFn","message","cause","_readOnlyConfig","_cachedConfig","localConfig","wasDeleted","_pendingSave","writeFileSync","_defaultToken","__proto__","apiProxy","agent","proxy","baseUrl","timeout","name","version","homepage","spinner","socketSdkErrorResult","sdkResult","method","headers","Authorization","result","body","mw2","lines","cols","length","cws","msg","_requirements","indent","keyPrefix","padName","bestScore","bestMatch","matrix","commandOrAliasName","type","hidden","description","argv","allowUnknownFlags","autoHelp","autoVersion","booleanDefault","configOverrideResult","emitBanner","parentName","Object","commands","help","cli2","collectUnknownFlags","importMeta","cli","console","process","meow","REDACTED","numeric","style","desc","sdkOpts","organizations","value","choices","orgSlug","GITHUB_REF_TYPE","cwd","info","remoteUrl","gitSymbolicRefResult","quotedCmd","gitRevParseResult","stdio","email","user","gitConfigResult","lsRemoteResult","gitDiffResult","owner","repo","parsedGitRemoteUrlCache","namespace","keys","workspacePatterns","throws","ignores","absolute","ignore","concurrency","hasNegatedPattern","dot","filtered","all","nothrow","shadowBinPath","shadowIndex","theBinPath","WIN32","thePath","config","socketConfig","_npmBinPath","_npmBinPathDetails","_npmDirPath","_npxBinPath","_npxBinPathDetails","i","onlyDirectories","onlyFiles","root","dir","env","SOCKET_CLI_VERSION","mixinsEnv","spawnPromise","ipc","getDefaultSocketJson","obj","json","sockJson","toAddToBashrc","targetName","targetPath","getSentry","constructor","args","agentExecPath","NODE_OPTIONS","YARN_CLASSIC","semver","onUnknown","editable","maintainedNodeVersions","engines","pkgAgentRange","pkgNodeRange","lockName","lockPath","features","npmBuggyOverrides","pkgRequirements","pkgSupports","node","cmdName","prod","cve","remove","upgrade","critical","high","middle","low","header","hyperlink","fallback","fallbackToUrl","normalized","_translations","none","descriptions","consolidate","socketYml","blocked","raw","upgradable","highestForCve","alert","unfixableAlerts","highestForUpgrade","sockPkgAlerts","alertsByPurl","severity","hideAt","hiddenAlerts","viewableAlerts","viewableAlertsByPurl","aboveTheFoldPurls","removedHiddenAlerts","hiddenAlertsByPurl","output","mentionedPurlsWithHiddenAlerts","prevAboveTheFold","totalRiskCounts","purls","opts","apiToken","components","purl","queryParams","alerts","compact","fixable","batchResult","remaining"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA;AAEA;AAsBA;AAGA;AAEA;;AAEI;;;AAEQA;AAAkB;AAC1B;AACE;AACA;;;AAME;AACEC;AACF;AACA;AACA;AACA;AACE;;AAEAC;AACF;AACF;AACEC;AAA6CC;AAAgB;AAC/D;AACF;AACF;AACA;AACF;AAEA;AAGE;AACA;AACA;AACA;AAEA;;AAEIC;;AAEAC;;AAEJ;;AACSD;AAAUC;;AACrB;AAOO;;;;AAMH;;;AAGEC;AACF;AACA;;;AAGMC;AACAC;;;;AAGoBC;AAAS;AAC/B;AACF;AACF;AACAC;;AAEF;AACA;AACF;AAEO;AAGL;AACA;AACA;AACE;AACF;;AACSN;AAAUC;;AACrB;;AAEA;AACA;AACO;AAGL;AACA;AACA;AACE;AACF;AACA;AACF;;AAEA;AACA;AACAM;AAEO;;AAEP;AAEO;;AAEP;AAEO;AACL;AACF;AAEO;AACL;AACF;AAEO;AACL;AACF;AAEA;AACA;AACA;AAEO;AACLC;AAEA;;;AAGE;AACE;AACA;;AAEER;AACAS;AACAC;;AAGJ;AACF;AACE;;AAEAC;;AAGEX;AACAS;AACAC;;AAGJ;;AAEA;AACAE;AACAD;;AAEA;AACA;AACE;AACEf;AAGF;AACAgB;;AAEF;;AAESZ;AAAUC;;AACrB;AAEO;AACLO;AACA;AACAI;AACE;AACA;;;;AAEFD;AACF;AAEA;AACO;AAIL;AACA;AACA;AACE;AACF;AACA;AACA;AACA;;AAEE;AACEE;AACF;;AAEEC;AACF;AACF;;AAEIlB;AAGF;AACAiB;AACF;AACA;;AAEIb;;AAEAC;;AAEJ;;AAGEc;;AAEEA;;AACQpB;AAAkB;AAC1B;AACEqB;AAIF;AACF;AACF;;AAGEhB;;AAEAC;;AAEJ;;ACnSA;AAEA;AAEA;;AAEA;AACA;;AAGE;AACF;;AAEA;AACA;;AAGE;AACF;AAEA;AACE;;AAEI;;AAEA;;AAEJ;AACA;AACF;;AAEA;AACA;AACO;AACL;AACEgB;AACA;AACF;AAEA;;AAMA;AACF;AAEO;AACL;AAKF;AAEO;AACL;AACA;AAMF;AAEO;AACL;AACF;AAQO;AAGL;AAAeC;;;;;AACuB;;;AAIlCT;AAEF;AACAQ;AACF;;;AAIIjB;AACAS;AACAC;;AAEJ;;AAEMS;AAAS;AACf;;AAEA;;;AAE4C;;;AAM1CnB;AACAC;AACEmB;AAAmCC;;AACnCC;AACAC;;AAEEC;AACAC;AACAC;;;;AAIR;;AClHA;;AAEA;AACO;;AAGL;AACE;AACF;AACA;AACA;AACF;AAEO;;AAEH;AACF;AACA;AACE;AACF;;AAEE;AACF;;AAEE;AACF;;AAEF;AAWO;;;AAISC;AAAQ;AACpBT;;;AAIF;AACES;AACF;;AAEA;AAEA;;;;AAIE;AACE;;AAEE/B;AACF;AACEA;AACF;AACF;;;AAGA;AACEI;AACAS;;;AAGF;AACEb;AACAY;AACF;AACEA;AACF;;AACsBH;AAAUuB;AAAqB;AACrD;AACF;;AAEA;AACA;;;;AAGUlB;AAAc;AACtB;AACEV;AACAS;;AAEAR;;AAEA;;AAEFO;;AACsBqB;AAAU;AAChC;AACF;AACA;AACE7B;;;AAGF;AACF;AAEO;AAIL;;;;AAIE;AACA;AAEAQ;;AACsBH;AAAS;;AAG7BL;AACAS;;;AAGJ;;AAEA;AACA;;;AAIED;;AACsBH;AAAM;;AAG1BL;AACAS;AACAC;AACAT;;AAEA;;AAEJ;;;AAGID;;;AAGJ;AACF;AAEO;AACL;;AAEEJ;AAGF;AAEA;AACEkC;AACAC;AACEC;AACF;AACF;AACF;AAEO;AAIL;;;AAGIhC;AACAS;AACAC;;AAGJ;;AAEQiB;AAAQ;AAEhB;AACEA;AACF;AAEA;;AAEEM;AACA;AACEN;AAGF;;AAEA;AACEA;AACF;AAEA;AAEAnB;;AACsBH;AAAS;;AAG7BL;AACAS;AACA;AAAcC;;;AAElB;AAEA;;;AAGIV;AACAS;AACAC;;AAEJ;;AAGE;;AAEEV;AACAC;;;AAGFO;;AACsBH;AAAS;;AAE7BL;AACAS;AACAC;;AAEJ;AACF;AAEO;;AAML;AACE;AACF;;;AAIIV;AACAC;;;;AAIAD;AACAS;AACAC;;AAEJ;AACF;AAEO;AAQL;;;AAGIV;AACAS;AACAC;;AAGJ;AAEA;;AAEEd;AAGF;;AAEQ+B;AAAQ;;;AAIhB;AAEA;;AAEE;;AAEEI;;AAEE;;;AAEmBG;;;;;;AAWvB;;;;AAMA;AAEA;;;AAGsB7B;AAAS;;AAG7BL;AACAS;AACA;AAAcC;;;AAElB;AAEA;;;AAGIV;AACAS;AACAC;AACAT;;AAEA;;AAEJ;;AAGE;;AAEED;AACAC;;;AAGFO;;AACsBH;AAAS;;AAE7BL;AACAS;AACAC;;AAEJ;AACF;;ACzXO;;AAOL;AACA;AACF;;ACXO;AAKL;AACA;AACA;AACA;AACA;AACA;AACA;;AAEEyB;AACF;;;AAIAC;AACA;;AAIA;AACAA;AAEA;AACF;AAEO;AAEL;AACA;AACAC;AAGA;;AAGA;AACE;AAAkBC;;AAChB;AACA;AACAC;AAKF;AACF;;AAGA;;AAEA;;AAGA;AAAkBD;;;AAElB;;AAGA;AACEJ;AACA;AAAkBI;;AAChB;AACA;;AAEF;AACAJ;AACF;AAEA;AACF;AAEO;AAEL;AACA;AACAG;AAEA;;;;;AAMA;;AAGA;;AAEA;;AAGA;AAAkBC;;;AAElB;;;AAIEJ;;;AAGAA;AACF;AAEA;AACF;;ACtGA;AACA;AACO;AACL;;;AAGuBjC;AAAK;;AAE1B;AACA;AACA;AACED;AACAS;AACAC;AAEF;AACF;;AAGE;;;;AAOAd;;AACsBS;AAAS;;AAE/B;AACA;AACEL;AACAS;AACAC;AACF;AACF;AACF;;AChCO;;AAWH;AACF;AAEA;AACA;AACE;AACA;AACE;AACF;;;AAEQ4B;AAAkB;;AAExB;AACF;AACA;AACA;AACA;AACA;;AAEA;AACE;;AAEF;AACAE;;AAEEA;AACF;AACF;;AAEA;AACA;AACA;;;AAIE5C;AAEII;AACAS;;AAEF;AAEJ;AACEb;AACF;AAEA;AACF;;AClEO;AACL;AACE;AACF;AACA;AACE;AACF;AACA;AACF;;ACLA;AAEA;AAIO;;AAEH6C;AAGF;AACA;AACF;;ACKA;;AAEA;AAEO;;AAIGC;AAAW;AACjBxB;;;AAGF;AACA;AACA;;AAEA;AACE;AACA;AACA;;AAEA;AACEkB;AACF;;AAEEA;AACF;AACAH;AACF;AACA;AACF;AAEO;;AAIGU;AAAiB;AACvBzB;;;AAGF;;AAGE;AACE;AAAYyB;AAAU;AAE5B;AAEO;;AAKHD;AACAC;AACAC;AACF;AACE1B;;;;AAIF;AACA;AACE;AACA;;AAEE;AACF;;;AAIAe;;AAGA;AACEA;AACF;AACAA;AACF;AACA;AACF;;ACjGA;AACA;AACO;;AAKP;;ACoCA;AACA;AACA;;AAiBA;AAEA;AAEA;AACE;AACA;;AAGF;;AAEA;AACA;AACA;AACA;;;AAOE;AACA;AACE;AAIA;AACA;;AAEEY;AACAC;AACF;AACF;AACA;AACF;;AAEA;AACA;AACA;AACA;AACE;AAA4BR;AAAqB;AAGjD;AACES;AACF;AACA;AACEA;AACF;AACA;AACE;AACE;;AAE0B;;AACA;AACxBA;;AAEJ;AACF;;AAEF;AAEA;AACE;AACF;AAUO;;;;;;;;AAWL;AAAM7B;;;AACN;;AAEA;AACE8B;AACF;AAEA;AACE;AACAvB;AACEwB;AACAC;AACAC;;AAEF;;;AAGF;AACA;;AAIA;;AAEE;AACEvD;AACA;AACE;AACAwD;AACF;AACF;AACA;AACA;;AAEExD;AACA;AACE;;AAOF;AACF;AACF;AAEA;;;AAGIsD;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKJ;;;AAGA;;AAEA;AACA;;;;AAIE;;AAEA;AACAG;AACA;AACAC;AACAC;AACA;AACAC;AACF;AAEA;;AAEA;AACA;AACA;AACA;AACA;;;AAGEC;AAGF;AAEA;AACE;AACA;;AAEF;AACE;AACA;AACE;AACA;;AAEF;AACF;AAEA;AACE;AACEC;AACA;AACA9D;AACF;AACAA;;AAEA;AACF;;AAEA;AACA;AACE;AACA;;AAIA;;AAEA;AACA;;AAEI+D;AACF;AACF;;AAEA;AACA;;AAEE;;;AAKE;AACF;AACF;AACF;;AAGA;;AAKA;AACAvB;AACA;AACE;;AAUE;AACA;AASA;AACA;AAIA;AACA;AAIFwB;AAII;AACEC;AACF;AACEjE;AACF;AACF;;;AAIE;AACA;;AAOJ;;AAoCF;AACEwC;AACAA;AAGM;AAGS;AAAc;AAIvB;AAEsC;AAAS;;AACjCc;AAAO;;;AAGf;AACF;AAGN;AACER;AAAqBE;;AAG7B;AAEAR;AACA;AACEA;AAIF;AACEA;AACF;AACAA;AACkCM;AAAqBE;;AAEvD;;AA+BA;;AAEA;AACA;AACA;;;;AAIE;;AAEA;AACAS;AACA;AACA;AACAC;AACAC;AACA;AACAC;AACAM;AACF;;AAEA;AACA;AACEJ;AACA;AACF;AACA;;;AAGA;AACE;AACA;AACAK;AACF;AACF;;AAEA;AACA;AACA;AACO;AACLV;;;;AAIAM;AAOF;;;AAIE;;;AAGE;AACAL;AACAC;AACA;AACAC;AACAQ;;;;AAIAC;AACF;AAEA;AACEP;AACA;AACA;AACA9D;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACEsE;AACF;;AAEA;AACA;AACE;AACAC;AACA;AACAC;AACF;;AAEA;AACA;AACA;AACA;;AAEAC;;AAEE;AACA;AACAhB;AACA;AACAC;AACAC;;;;;AAKF;AACA;;AAGA;AACF;AAEO;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEF;AAEA;AACE;;AACQe;AAAS;AACjB;;;AAKA;;;AAKA;AACA;AACA;AACA;AAOA;AACA;AACA;AACF;AACA;AACA;AACA;AACA;AACE;;AAEF;;ACzpBO;AACL;AACA;;AAGEC;AACAC;AACF;AAEA;AACA;AACE;AACA;;;AAGA;AACF;;AAEE;AACF;AACE;AACF;AACF;;ACMO;;AAIHC;;AAEAC;AACF;AACExD;;;;;AAMA;AACA;AACE;AACF;;AAEF;;AAEsEuD;AAAK;AAC3E;AACE;AACF;;AAGE;AACAxE;;AAEA;;AAEJ;;ACvDO;AACL;AACA;AACEL;AAGA;AACF;;AAEA;AACA;;AACQ+E;;AACR;AACElE;;;;;AAOMmE;;;AAGJ;AAEEpD;AACAoD;AACAzB;;AAIN;AAEA;AACE;AACF;AACA;AACF;;ACrCO;AACL;;AAEE;AACA;AACF;AAEA;;AAEE0B;AAEIrD;AACAoD;AACAzB;AACF;AAEE3B;AACAoD;AACAzB;AACF;AAEE3B;AACAoD;AACAzB;;AAIN;;AAEE;;AAEEvD;AACF;;AAKA;AACF;AACE;;AAEEA;AACF;;AAIA;AACF;AACF;;AC/CO;AAKL;;;;AAIIA;AAGAA;AACAA;AAGAA;AAGAA;AAGAA;AAGAA;AACAA;AAGAA;AACAA;AAGAA;AACAA;AAGA;AACF;AAEAA;AAGAA;AACAA;AACAA;AACA;AACEA;AACF;AACEkF;AACA;;AAEA;AACF;AACF;AAEA;AACF;;ACvDA;AACO;AACL;AACA;AACEtE;;AAKSR;AAAUC;;AACrB;AAEA;AACA;AACEO;;AAKSR;AAAUC;;AACrB;AAEA;AACA;AACE;AACF;;AAEQ0E;;AACR;AACA;;AAEI3E;AACAS;AACAR;;AAEJ;AAEA;;;AAGID;AACAS;AACAR;;AAEJ;AAEAO;;AAGER;AACAS;AACAR;;AAEJ;;ACpDA;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGK;;;;AACqC8E;;AAC1C;AACA;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;;AAEE;AACqDC;;AAGrD;AACA;AACE;AACF;;AAEF;AACA;AACA;AACF;AAOO;;;AAKLxE;;AAEE;AACwDwE;;AAExDC;;AAEEzE;;AACsB0E;AAAU;AAClC;;AAEA1E;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AACL;AACA;AACF;AASO;AACL;AAAyC2E;;;AAEzCxE;AACA;AACA;;AAEE;;AAKoB2E;AAAqB;;;AAGzC;AACE3E;;AACsBH;AAAS;AACjC;AACF;AACA;AACA;AACA+E;AACA5E;;AAEE;;AAKoB6E;AAAkB;;;AAGtC;AACE7E;;AACsBH;AAAS;AACjC;AACF;AACA;AACF;;AAEA;AACA;AACA;AACA;AACO;AAGL;AACA;AACE;AACA;AACE;AACF;AACF;AACA;AACA;AACE;AACA;AACE;AACF;AACF;;AAEF;AAQO;AACL;;AAEEiF;;;AAGF9E;;;AAGE;;AAEAA;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AAIL;;AAEEiF;;AAEF;AACA9E;;;AAGE;;AAEAA;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AAIL;AACE;AACF;AACA;;AAEEiF;;AAEF;AACA9E;;;AAGE;;AAEAA;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AAIL;;AAEEiF;;AAEF;AACA9E;;AAEE;AAKA;;AAEAA;;AAEEA;AAIF;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AAKL;AACEG;AACA;AACF;;AAEEwE;AACAO;AACAC;AACF;AAAMtE;;;AAEN;AAEA;;AAEEoE;;;AAGF9E;;AAEE;;AAEAA;;AACsBH;AAAS;AACjC;AAEA;AACAG;;AAEE;AACA;;AAEAA;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AAIL;;AAEEiF;;AAEF;AACA9E;;AAEE;AACA;AACA;;AAEA;AACEA;;AACsBH;AAAS;AACjC;AACF;AACA;AACF;AAEO;AAKL;AAAyC2E;;AACzC;;AAK4B;AAAS;AAAS;AAC1C;AACA;AACE;AACAxE;;AAEE;AACA;;AAKoBiF;AAAgB;;;AAGpC;AACEjF;;AACsBH;AAAS;AACjC;AACF;AACF;;AAEE;;AAEEiF;;AAEF;AACA9E;;AAEE;;AAEA;AACEA;;AACsBH;AAAS;AACjC;AACF;AACF;AACF;AAEJ;AAEO;AAIL;;AAEEiF;;AAEF;AACA9E;;AAEE;AACA;AAKA;;AAEA;AACEA;;AACsBH;AAAS;AACjC;AACF;AACA;AACF;AAEO;AAIL;AAAyC2E;;AACzC;AACAxE;;AAEE;;AAKoBkF;AAAe;AACnC;;AAEA;AACElF;;AACsBH;AAAS;AACjC;AACF;AACA;AACF;AAEO;AAIL;AACA;AACA;;AAEF;AAEO;AAIL;;AAEEiF;;AAEF;AACA9E;;AAEE;AACA;;AAEAA;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AAGL;AAAyC2E;;;AAEzCxE;;AAEE;;AAKoBmF;AAAc;AAClC;AACA;;AAEE3F;;;;AAIFQ;;AACsBH;AAAS;;AAE7BL;AACAS;AACAC;;AAEJ;AACF;AAEA;AAEO;;AAEL;;;;AAEA;AACA;AACA;AACA;AACA;AACEuB;AAAW2D;;;AACb;AACE;;AAEE;AACA;AACA;AACA;;AAEA;AACA;;AAEE3D;;AAAkB4D;;AACpB;;AAEJ;AACAC;AACA;;AAA4B;AAC9B;;AC5eO;AAGL;AAGF;;ACdO;AAGL;;;AACcC;AAAU;AACxB;AAGF;AAMO;AAGL;AACA;;AAEF;AAEO;;;AASP;;AClCA;AACA;AACA;AACA;AACO;AAGL;AAMF;;ACfO;AAIL;;;AAGE;;AACUC;AAAsBpB;;AAChC;AACF;AACF;;ACKA;AAIA;AACE;AACA;AACA;AAAQ;AACR;AAAQ;AACR;AAAe;AACf;AAAe;AACf;AAAS;AACT;AAAoB;AACpB;AAAY;AACZ;AAAgB;AAChB;AACA;AACA;AAGF;AAEA;AAIE;;;AAMI;AACA;AACA;;AAEIqB;;AAEF;AACE;AACF;AACF;AACF;AACF;AACEA;AAAkDC;;AAGpD;AACA;AAKF;AAEA;;;AAOE;AAAkB5D;;;AAEhB;;AAQA;AACF;AACA;AACF;AAEA;AAKE;AACF;;AAEA;AACA;AACA;AACA;AACA;AACE;AACA;AACA;AACA;AACA;AAME;AACF;AACA;AACA;AAIA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AAQF;AAEA;;AACUA;AAAO;;AAEb;AACF;AACA;;;AAGA;AACA;AACA;;AAMA;AACA;;AAEF;AAEO;AAIL;AACA;AACF;AAEO;;;AAKH;AACA;;AAEA;AACF;AACA;AACF;AAMO;;AAKH0C;;;AAGF;AAAM9D;;;AAEN;AAEA;AACA;AACE;AAKA;AACEiF;AACF;AACF;AAEA;AACEC;;AAEAC;AACF;;AASIC;AAAe;AAEjB;AACEH;AACF;AACF;;AAGA;;AAEII;AACA;AACF;AACF;AAEA;AACErF;AACAkF;;AAEAI;;;;;AAMA;AACF;;AAEA;AACA;;AAEA;AACA;AAIA;AACE;AACA;AACA;AACA;AACEC;AACF;AACF;AACA;AACF;AAYO;;;AAOCL;;AAEAC;;AAGR;AAEO;AAIL;AACA;AACF;AAEO;AAGL;AACA;AACF;;ACpSO;AAKL;AAEIK;AACAC;;;AAEIC;AAAc;;AAEtB;AACA;AAAkBtE;;AAChB;AACA;;AAEEuE;AACF;AACEC;AACA;AACF;AACF;;AACStF;AAAerB;;;AAC1B;AAEO;;AACG4G;AAAM;;AAEd;;AAEE;AACA;AACA;AACA;AACA;AACE;AACA;AACA;;AAGAC;AACF;AACA;AACA;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;;AAEF;AACC7G;AACC;AACC4G;;AAGL;AACA;;AAEE;AACF;AACAC;AACF;AACF;AAOO;;AAKGC;AAAsBjC;AAAoB;AAChD9D;;;;;AAMAgG;AACF;AAEA;AACF;;ACzGA;AACEtH;AAGA;AACA;AACA;AACAwE;AACF;AAEA;AACO;;AAEH+C;;;AAGA;AACF;AACA;AACF;AAEA;AACA;;AAEIC;AACF;AACA;AACF;AAEA;AACO;;AAEH;;;AAGEC;AACF;;;AAGE;;AAEA;AACA5G;AAEAA;AACAb;AACA;AACA;AACA;AACAwE;AACF;AACF;AACA;AACF;AAEA;AACO;;AAEH;;;AAQF;AACA;AACF;AAEA;AACO;;AAEHkD;;;AAGA;AACF;AACA;AACF;AAEA;AACA;;AAEIC;AACF;AACA;AACF;AAEO;AACL;AACF;AAEO;AACL;AACF;;ACxGA;AAEO;;AAEL;AAAkBjF;;;AAEd;AACA;AACEL;AACAuF;AACF;AACEvF;AACF;AACF;AACF;AACA;AACF;AAEO;AACL;AACE;AACF;AACA;AACE;AACF;AACA;AACF;AAEO;;AAEL;AACF;AAEO;AACL;AACF;;ACZO;AAIL;AAAef;;;;AACP8D;;AAAoD;;AACtDyC;AAAyBC;AAAiB;AAChD;AACEA;AACF;AACA;AACED;AACF;AACA;;AACQE;AAAK;;AAEb;AACE;;AAEI;AACF;;;AAGE;;AAEA;AACE;AACF;;AAEE;AACF;;AAEJ;AACAC;AACF;AACA;AACF;;AChDO;AAGL;AAA6C1B;AAAc;AAC3D;;AAMF;AAEO;;AAOH2B;;;AAGF;AACE3G;;;AAGF;AACE4G;;AAEF;AACA;AACEC;AACF;AAEA;AACEA;AACF;AACE;;AAEEA;AACF;AACF;;;AAGUC;;AAQJ;AACAH;AACE;;;AAGFI;AACE;AACA;AAEA;;AAEF;;;;AAKKjI;;;;AAET;;;AAESA;AAAWC;AAASQ;;AAC/B;AACF;;AChFO;;AAIP;AAEO;;AAEP;AAEO;;AAEP;;AC2CO;AACL;AACA;AAEI;AACAyH;AACN;AAEO;;AAEH;AAEA;AAEA;AAEA;AAEAzG;;AAEJ;AAEO;;AAKL;AACEjB;;AACSR;;;AACX;;;;;AAKE;AACEJ;;AACsBS;AAAS;;AACtBL;;;AACX;AACA;;AACsBK;AAAS;;AAE7BL;AACAS;;;AAGJ;AAEA;;AAEE0H;;AAEA3H;;AACsBH;AAAU+H;AAAK;AACrC;AACExI;;AACSI;;;AACX;;AAEEA;AACAS;AACAC;;AAEJ;;AAGEd;;AACSI;;;AACX;;AAEA;AACA;;AACSA;AAAUC;;AACrB;AAEO;;;;;AAQHO;;AACsBH;AAAUgI;AAAS;;AAEvCrI;AACAS;AACAC;;AAGJ;;AAGA;;AAESV;AAAUC;;AACrB;;;ACzFO;;AAEH;;;AAGF;AACF;;ACpEO;AAEA;AACL;;AAMA;;AAEID;AACAS;;;AAGJ;;AAEST;;;AACX;AAEO;AAOL;AACA;AACE;AACF;;AAEQL;AAAkB;;;AAGtBK;AACAS;AACAC;;AAEJ;;AAEA;AACA;;AAEA;AACA;;AAOF;AACA;AACA;AACA;AACA;AACA;AACA;;AAGIV;AACAC;;;AAGEqI;AACAC;AACAC;AACF;;AAEJ;;ACvEA;;AAEE;AAA+DC;AAAU;AAC3E;AAIO;AAEA;AAGLC;;;AAGA;AACF;AAEO;AAIL;AACA;;AAEA;AACF;AAEO;AAIL;;AAEE;AACF;AACAlI;AACA;AACF;;AChCO;;;;AAML;AACF;;ACOO;;;AAKHmI;;;;AAIF;AAAMzH;;;AACN;AACA;AACA;;AAIA;;;;AAIA;;AAEA;AACA;AACA;AACA;AACA;AACEoE;;;AAGEA;AACF;AACF;AACEA;AACF;;AAgBI;AACA;AAEA;;AAEA;AACA;;AAMA;AACAuC;;;AAGE;;;AAGFvC;AACF;AAGF;AACE0C;;AAEI;AACA;;AAEF;AACF;AACF;AAEA;AACF;;AC3FO;;;AAIUY;AAAc;AAC7B;AACA;AACA;AACA;AACE;;;AAGA;AACF;;AAEED;;;AAGF;AAAMzH;;;;;;;AAKJoE;AACA;AACAuC;;;AAGEgB;AAIA;AACF;AACF;AACF;;AC7BA;;;;;;;;;;;;AAYEC;AACF;AAEO;AAEP;AA+EA;;;;AAMQ;;AAEF;;AAEJ;AAEA;AAEA;;AAaQ;;AAEE;AACF;;AAEE;AACA;;;;AAIA;AACA;AACA;AACA;;;;;AAOF;AACA;AACF;AASR;;AAEA;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAEA;AACE;;;AAGA;AACA;AAA+BnC;;AACjC;AAEA;AAKE;AACA;AACAnG;;;AAGI;AACA;AACA;AACAuI;AACE;;;;AAKE;;AAINvI;;AACsBH;AAAS;AACjC;AACA;AACF;AAEO;AACL2E;AACAgE;AACa;;AACqChE;AAAI;;AAEtD;;AAMiCA;AAAI;AACrC;;AAKqCiE;;AAErC;AACA;AACA;AAIA;AACA;AACE;AACA;AACA;AACA;;;;AAII7H;AACF;AACF;AACF;AACA;AAMEA;AACF;;AAEEA;;AAEF;AACA;;AAEA;AACEA;AACF;;AACQ8H;AAAuB;;;AAG/B;AACA;;AAEA;AACA;AACA;;;;;AAIUC;;AACR;AACA;AACA;AACEC;AACA;AACA;AACA;;;AAGA;AACF;AACA;AACEC;AACA;AACA;AACA;;;AAGA;AACF;AACA;AAGA;AACE;AACA;;AAKE;;;;AAIA;AACF;AACF;;AAKF;AACEC;AACAC;AACF;;AAEA;AACA;AAGA;;AAGA;AAEA;AAKA;;;;;;;;AASEC;AAAYC;;;;;;;;;AAQZC;;;;AAIAC;AACE;;AAEA;AACAC;AAGF;;AAEJ;AAEO;;AAKHC;;AAEAC;AACF;AACE5I;;;AAGF;;;AAGItB;AAMF;AACF;;;;AAC4B8J;AAAgB;AAC5C;AACA;;;AAGI1J;AACAS;;;AAMJ;AACA;AACE;;AAEET;AACAS;;;AAMJ;AACA;;AAEIT;AACAS;AACAC;;AAKJ;AACA;;AAEIV;AACAS;;;AAMJ;AACA;;;AAGIT;AACAS;AACAC;;AAEJ;;;AAGIV;AACAS;AACAC;;AAEJ;AACA;;AAEIV;AACAS;AACAC;;AAEJ;;;AAGIV;AACAS;AACAC;;AAKJ;AACA;AAIE;;AAOF;;AACSV;AAAUC;;AACrB;;AClfO;AA0CiC;AAEjC;;AAEP;;ACbO;;AAGGgD;AAAK;;AAOf;;AC1DO;;AAGkB/B;;AAAwB;AACjD;;ACFO;AACL6I;AACAC;AACAC;AACF;;ACAO;AACLC;AACAC;AACAC;AACAC;AACF;;ACNO;;AAIH;AACF;;AAGE;AACF;AAEAC;AACE;AAGF;AAEAC;AAIIC;AACAC;;AAMF;AACE;;AAII;AACN;AACA;AACF;;AAKE;AACF;;AAGE;AACF;;;AAMA;;AAGE;;AAIF;AACF;;AC3DO;AACL;AAAqBvJ;;AACrB;AACA;AACE;;AAEEwJ;AACF;AACF;AACA;AACF;;ACXA;AAEA;AAIO;;AAEHC;AAGF;AACA;AACF;;ACcO;AACLT;AACAC;AACAC;AACAC;AACF;AAEO;AACLH;AACAC;AACAC;AACAC;AACAO;AACF;AAiBA;AAEA;AAEA;AASA;AACE;AACEV;AACAC;AACAC;AACAC;;AAEF;;;;AAIM;;;AAGA;;;AAGA;;;AAGA;AACJ;AACF;AACA;AACF;AAEA;;;AAGIQ;AACF;;AAEEA;AACF;;AAEEA;AACF;;AAEEA;AACF;AACA;AACF;AAoBO;AAKL;AACA;AACE;AACF;;AAEQ5H;AAAiBxB;AAAQ;;AAG/BqJ;;AAEAC;AACF;AACE7J;;;AAIF;;AAQE8J;AACAd;AACAH;AACA;AACF;AAEA;AACE7I;AACA;;;AAIF;AACE;AACA;;AAKE;AACF;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;;;;;;;;;;AAgBI+J;AACAC;AACF;AACF;AACF;AACA;AACE;AACF;;AAEA;AACA;AAKE;AACA;;AAEA;AACE;;AAEA;AACE;AACA;AACA;AACA;;AAKA;AACE;AACA;;;AAGEC;AACEC;AACA3J;AACF;AACF;AACF;AACE4J;AACF;AACF;AACE;AACA;;;AAGEC;AAA+BF;AAAqB3J;AAAQ;AAC9D;AACF;AACE4J;AACF;AACF;AACAE;AACE;;AAOJ;AACEA;AACF;;AAEEC;AACF;AACA;AACF;AAEO;;AAEP;AAEO;AAIL;AACF;AAEO;AAIL;;AAEF;AAEO;AACL;;AACQC;;AACR;AASF;AAEO;;AAWP;AAwFO;AAGL;AACF;AAOO;;AAIGC;;AAA2C;AACjDxK;;;AAIF;AACA;AAIA;AACA;AACA;AAEA;AAAkBoB;;;AACR;AAAS;AAAU;;AAE3B;AACE;;AAGEqJ;AACF;AACA;AACF;;;AAGA;AACA;AACE;AACF;AACAC;AACAC;AACA;AAMEC;AACF;AACF;;AAEA;AACA;AAAa;AAAQ;AACnB;AACE;AACF;AACAA;AACF;AACA;AACA;AAAa;AAAS;AAAgB;AACpC;AACE;AACF;AACAA;;AAEA;AACE;AACA;AACA;;AAKA;AACEC;AACAC;AACF;AACAH;AAIF;AACF;AAEA;;AAKMvJ;;;AAII;AAAS;AAAU;AAC3B;AACA;;AACUW;AAAK;;;;AAYb;AACA;AACA;AACA;AACA;;AAEA;AACAb;AACF;AACA;AACA;;AAKA;AACA;AACE0J;AACAG;AACF;AACEA;AACF;AACA;AACEA;AACF;;;AAEQ3J;AAA0B;AAClC;AACE4J;;;AAKA;;AAIA;AACF;AACAC;AACF;;AAIA;AACE;AACEjC;AACAC;AACAC;AACAC;;AAEF;AAAa;AAAS;AAAU;AAC9B;AACE;AACF;AACA;AACA+B;AACAA;AACAA;AACAA;AACF;AACAH;AAGF;AACAA;AACF;;AC5kBO;;AAEP;;ACuCO;AAIL;;AACoBI;AAAiB;;AAE/B/J;AAAkB;AACxB;;AAGE;AACF;AAEA;AACEpB;AACA4J;AACAnE;AACA;;;;AAKA2F;AACF;;;AAEwC3K;AAAQ;AAEhD;AAEAA;AAEA;AAAwC4K;AAAS;AACjD;;AAEE;AACF;AACA;AACA;AAEA;;;;;AAKE5K;;;AAIA;AAEI6K;AAAqCC;AAAK;AAC5C;AAEEC;AACEC;AACAC;;AACyBC;;;;;AAI3B;AACF;;AAGE;AACA;AACF;;AAEE;AACE;AACF;AACA;AACA;AAGF;;;;AAOwBC;AAAY;AAClC;AACF;AACAC;;AAEEpL;AACF;AACF;;;AAGA;AACF;;AAIA;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","debugId":"6bbbb6b9-ace3-439a-9c19-0674a8ba872d"}
|