@socketsecurity/cli-with-sentry 1.1.3 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +426 -0
- package/bin/cli.js +3 -1
- package/dist/cli.js +362 -400
- package/dist/cli.js.map +1 -1
- package/dist/constants.js +211 -19
- package/dist/constants.js.map +1 -1
- package/dist/flags.js +3 -3
- package/dist/flags.js.map +1 -1
- package/dist/instrument-with-sentry.js +8 -8
- package/dist/instrument-with-sentry.js.map +1 -1
- package/dist/shadow-npm-bin.js +14 -14
- package/dist/shadow-npm-bin.js.map +1 -1
- package/dist/shadow-npm-inject.js +16 -16
- package/dist/shadow-npm-inject.js.map +1 -1
- package/dist/tsconfig.dts.tsbuildinfo +1 -1
- package/dist/types/commands/ci/handle-ci.d.mts.map +1 -1
- package/dist/types/commands/fix/cmd-fix.d.mts.map +1 -1
- package/dist/types/commands/npm/cmd-npm.d.mts +1 -1
- package/dist/types/commands/npm/cmd-npm.d.mts.map +1 -1
- package/dist/types/commands/optimize/add-overrides.d.mts.map +1 -1
- package/dist/types/commands/patch/cmd-patch.d.mts.map +1 -1
- package/dist/types/commands/patch/handle-patch.d.mts +9 -2
- package/dist/types/commands/patch/handle-patch.d.mts.map +1 -1
- package/dist/types/commands/patch/output-patch-result.d.mts +1 -1
- package/dist/types/commands/patch/output-patch-result.d.mts.map +1 -1
- package/dist/types/commands/scan/cmd-scan-create.d.mts.map +1 -1
- package/dist/types/commands/scan/cmd-scan-github.d.mts.map +1 -1
- package/dist/types/commands/scan/cmd-scan-report.d.mts.map +1 -1
- package/dist/types/commands/scan/create-scan-from-github.d.mts.map +1 -1
- package/dist/types/commands/scan/generate-report.d.mts +9 -8
- package/dist/types/commands/scan/generate-report.d.mts.map +1 -1
- package/dist/types/commands/scan/handle-create-new-scan.d.mts +5 -2
- package/dist/types/commands/scan/handle-create-new-scan.d.mts.map +1 -1
- package/dist/types/commands/scan/handle-scan-report.d.mts +7 -5
- package/dist/types/commands/scan/handle-scan-report.d.mts.map +1 -1
- package/dist/types/commands/scan/output-scan-report.d.mts +10 -8
- package/dist/types/commands/scan/output-scan-report.d.mts.map +1 -1
- package/dist/types/commands/scan/perform-reachability-analysis.d.mts.map +1 -1
- package/dist/types/commands/scan/types.d.mts +3 -0
- package/dist/types/commands/scan/types.d.mts.map +1 -0
- package/dist/types/constants.d.mts +99 -46
- package/dist/types/constants.d.mts.map +1 -1
- package/dist/types/shadow/npm/arborist-helpers.d.mts +1 -17
- package/dist/types/shadow/npm/arborist-helpers.d.mts.map +1 -1
- package/dist/types/shadow/npm/bin.d.mts +4 -3
- package/dist/types/shadow/npm/bin.d.mts.map +1 -1
- package/dist/types/utils/coana.d.mts.map +1 -1
- package/dist/types/utils/ecosystem.d.mts.map +1 -1
- package/dist/types/utils/get-output-kind.d.mts.map +1 -1
- package/dist/types/utils/glob.d.mts.map +1 -1
- package/dist/types/utils/package-environment.d.mts.map +1 -1
- package/dist/types/utils/purl.d.mts +25 -9
- package/dist/types/utils/purl.d.mts.map +1 -1
- package/dist/types/utils/spec.d.mts.map +1 -1
- package/dist/utils.js +120 -102
- package/dist/utils.js.map +1 -1
- package/dist/vendor.js +222 -4598
- package/external/@socketsecurity/registry/lib/constants/env.js +0 -3
- package/external/@socketsecurity/registry/lib/constants/ext-cjs.js +3 -0
- package/external/@socketsecurity/registry/lib/constants/ext-cts.js +3 -0
- package/external/@socketsecurity/registry/lib/constants/ext-dts.js +3 -0
- package/external/@socketsecurity/registry/lib/constants/ext-js.js +3 -0
- package/external/@socketsecurity/registry/lib/constants/ext-json.js +3 -0
- package/external/@socketsecurity/registry/lib/constants/ext-lock.js +3 -0
- package/external/@socketsecurity/registry/lib/constants/ext-lockb.js +3 -0
- package/external/@socketsecurity/registry/lib/constants/ext-md.js +3 -0
- package/external/@socketsecurity/registry/lib/constants/ext-mjs.js +3 -0
- package/external/@socketsecurity/registry/lib/constants/ext-mts.js +3 -0
- package/external/@socketsecurity/registry/lib/constants/index.js +82 -83
- package/external/@socketsecurity/registry/lib/constants/ipc-promise.js +4 -5
- package/external/@socketsecurity/registry/lib/constants/node-debug-flags.js +9 -0
- package/external/@socketsecurity/registry/lib/constants/pnpm.js +3 -0
- package/external/@socketsecurity/registry/lib/constants/yarn-lock.js +3 -0
- package/external/@socketsecurity/registry/lib/json.js +11 -0
- package/external/@socketsecurity/registry/lib/strings.js +16 -0
- package/package.json +13 -12
- package/external/@socketsecurity/registry/lib/constants/socket-public-api-key.js +0 -3
- package/external/@socketsecurity/registry/lib/constants/tap.js +0 -3
- /package/external/@socketsecurity/registry/lib/constants/{ipc.js → ipc-object.js} +0 -0
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sources":["../src/commands/analytics/fetch-org-analytics.mts","../src/commands/analytics/fetch-repo-analytics.mts","../src/commands/analytics/output-analytics.mts","../src/commands/analytics/handle-analytics.mts","../src/commands/analytics/cmd-analytics.mts","../src/commands/audit-log/fetch-audit-log.mts","../src/commands/audit-log/output-audit-log.mts","../src/commands/audit-log/handle-audit-log.mts","../src/commands/audit-log/cmd-audit-log.mts","../src/commands/scan/fetch-create-org-full-scan.mts","../src/commands/scan/fetch-supported-scan-file-names.mts","../src/commands/scan/finalize-tier1-scan.mts","../src/commands/scan/fetch-report-data.mts","../src/commands/scan/generate-report.mts","../src/commands/scan/output-scan-report.mts","../src/commands/scan/handle-scan-report.mts","../src/commands/scan/output-create-new-scan.mts","../src/commands/scan/perform-reachability-analysis.mts","../src/commands/manifest/detect-manifest-actions.mts","../src/commands/manifest/convert_gradle_to_maven.mts","../src/commands/manifest/convert_sbt_to_maven.mts","../src/commands/manifest/convert-conda-to-requirements.mts","../src/commands/manifest/output-requirements.mts","../src/commands/manifest/handle-manifest-conda.mts","../src/commands/manifest/generate_auto_manifest.mts","../src/commands/scan/handle-create-new-scan.mts","../src/commands/ci/handle-ci.mts","../src/commands/ci/cmd-ci.mts","../src/commands/config/discover-config-value.mts","../src/commands/config/output-config-auto.mts","../src/commands/config/handle-config-auto.mts","../src/commands/config/cmd-config-auto.mts","../src/commands/config/output-config-get.mts","../src/commands/config/handle-config-get.mts","../src/commands/config/cmd-config-get.mts","../src/commands/config/output-config-list.mts","../src/commands/config/cmd-config-list.mts","../src/commands/config/output-config-set.mts","../src/commands/config/handle-config-set.mts","../src/commands/config/cmd-config-set.mts","../src/commands/config/output-config-unset.mts","../src/commands/config/handle-config-unset.mts","../src/commands/config/cmd-config-unset.mts","../src/commands/config/cmd-config.mts","../src/commands/fix/git.mts","../src/commands/fix/pull-request.mts","../src/commands/fix/env-helpers.mts","../src/commands/fix/coana-fix.mts","../src/commands/fix/output-fix-result.mts","../src/commands/fix/handle-fix.mts","../src/commands/fix/cmd-fix.mts","../src/commands/install/output-install-completion.mts","../src/commands/install/setup-tab-completion.mts","../src/commands/install/handle-install-completion.mts","../src/commands/install/cmd-install-completion.mts","../src/commands/install/cmd-install.mts","../src/commands/json/output-cmd-json.mts","../src/commands/json/handle-cmd-json.mts","../src/commands/json/cmd-json.mts","../src/commands/login/apply-login.mts","../src/commands/login/attempt-login.mts","../src/commands/login/cmd-login.mts","../src/commands/logout/apply-logout.mts","../src/commands/logout/attempt-logout.mts","../src/commands/logout/cmd-logout.mts","../src/commands/manifest/run-cdxgen.mts","../src/commands/manifest/cmd-manifest-cdxgen.mts","../src/commands/manifest/cmd-manifest-auto.mts","../src/commands/manifest/cmd-manifest-conda.mts","../src/commands/manifest/cmd-manifest-gradle.mts","../src/commands/manifest/cmd-manifest-kotlin.mts","../src/commands/manifest/cmd-manifest-scala.mts","../src/commands/manifest/output-manifest-setup.mts","../src/commands/manifest/setup-manifest-config.mts","../src/commands/manifest/handle-manifest-setup.mts","../src/commands/manifest/cmd-manifest-setup.mts","../src/commands/manifest/cmd-manifest.mts","../src/commands/npm/cmd-npm.mts","../src/commands/npx/cmd-npx.mts","../src/commands/oops/cmd-oops.mts","../src/commands/optimize/deps-includes-by-agent.mts","../src/commands/optimize/get-dependency-entries.mts","../src/commands/optimize/get-overrides-by-agent.mts","../src/commands/optimize/lockfile-includes-by-agent.mts","../src/commands/optimize/ls-by-agent.mts","../src/commands/optimize/shared.mts","../src/commands/optimize/update-manifest-by-agent.mts","../src/commands/optimize/add-overrides.mts","../src/commands/optimize/update-lockfile.mts","../src/commands/optimize/apply-optimization.mts","../src/commands/optimize/output-optimize-result.mts","../src/commands/optimize/handle-optimize.mts","../src/commands/optimize/cmd-optimize.mts","../src/commands/organization/fetch-dependencies.mts","../src/commands/organization/output-dependencies.mts","../src/commands/organization/handle-dependencies.mts","../src/commands/organization/cmd-organization-dependencies.mts","../src/commands/organization/fetch-license-policy.mts","../src/commands/organization/output-license-policy.mts","../src/commands/organization/handle-license-policy.mts","../src/commands/organization/cmd-organization-policy-license.mts","../src/commands/organization/fetch-security-policy.mts","../src/commands/organization/output-security-policy.mts","../src/commands/organization/handle-security-policy.mts","../src/commands/organization/cmd-organization-policy-security.mts","../src/commands/organization/output-organization-list.mts","../src/commands/organization/handle-organization-list.mts","../src/commands/organization/cmd-organization-list.mts","../src/commands/organization/cmd-organization-policy.mts","../src/commands/organization/fetch-quota.mts","../src/commands/organization/output-quota.mts","../src/commands/organization/handle-quota.mts","../src/commands/organization/cmd-organization-quota.mts","../src/commands/organization/cmd-organization.mts","../src/commands/package/fetch-purl-deep-score.mts","../src/commands/package/output-purls-deep-score.mts","../src/commands/package/handle-purl-deep-score.mts","../src/commands/package/parse-package-specifiers.mts","../src/commands/package/cmd-package-score.mts","../src/commands/package/fetch-purls-shallow-score.mts","../src/commands/package/output-purls-shallow-score.mts","../src/commands/package/handle-purls-shallow-score.mts","../src/commands/package/cmd-package-shallow.mts","../src/commands/package/cmd-package.mts","../src/commands/patch/manifest-schema.mts","../src/commands/patch/output-patch-result.mts","../src/commands/patch/handle-patch.mts","../src/commands/patch/cmd-patch.mts","../src/commands/raw-npm/run-raw-npm.mts","../src/commands/raw-npm/cmd-raw-npm.mts","../src/commands/raw-npx/run-raw-npx.mts","../src/commands/raw-npx/cmd-raw-npx.mts","../src/commands/repository/fetch-create-repo.mts","../src/commands/repository/output-create-repo.mts","../src/commands/repository/handle-create-repo.mts","../src/commands/repository/cmd-repository-create.mts","../src/commands/repository/fetch-delete-repo.mts","../src/commands/repository/output-delete-repo.mts","../src/commands/repository/handle-delete-repo.mts","../src/commands/repository/cmd-repository-del.mts","../src/commands/repository/fetch-list-all-repos.mts","../src/commands/repository/fetch-list-repos.mts","../src/commands/repository/output-list-repos.mts","../src/commands/repository/handle-list-repos.mts","../src/commands/repository/cmd-repository-list.mts","../src/commands/repository/fetch-update-repo.mts","../src/commands/repository/output-update-repo.mts","../src/commands/repository/handle-update-repo.mts","../src/commands/repository/cmd-repository-update.mts","../src/commands/repository/fetch-view-repo.mts","../src/commands/repository/output-view-repo.mts","../src/commands/repository/handle-view-repo.mts","../src/commands/repository/cmd-repository-view.mts","../src/commands/repository/cmd-repository.mts","../src/commands/scan/reachability-flags.mts","../src/commands/scan/suggest_target.mts","../src/commands/scan/cmd-scan-create.mts","../src/commands/scan/fetch-delete-org-full-scan.mts","../src/commands/scan/output-delete-scan.mts","../src/commands/scan/handle-delete-scan.mts","../src/commands/scan/cmd-scan-del.mts","../src/commands/scan/fetch-diff-scan.mts","../src/commands/scan/output-diff-scan.mts","../src/commands/scan/handle-diff-scan.mts","../src/commands/scan/cmd-scan-diff.mts","../src/commands/scan/create-scan-from-github.mts","../src/commands/scan/output-scan-github.mts","../src/commands/scan/handle-create-github-scan.mts","../src/commands/scan/cmd-scan-github.mts","../src/commands/scan/fetch-list-scans.mts","../src/commands/scan/output-list-scans.mts","../src/commands/scan/handle-list-scans.mts","../src/commands/scan/cmd-scan-list.mts","../src/commands/scan/fetch-scan-metadata.mts","../src/commands/scan/output-scan-metadata.mts","../src/commands/scan/handle-scan-metadata.mts","../src/commands/scan/cmd-scan-metadata.mts","../src/commands/scan/output-scan-reach.mts","../src/commands/scan/handle-scan-reach.mts","../src/commands/scan/cmd-scan-reach.mts","../src/commands/scan/cmd-scan-report.mts","../src/commands/scan/output-scan-config-result.mts","../src/commands/scan/setup-scan-config.mts","../src/commands/scan/handle-scan-config.mts","../src/commands/scan/cmd-scan-setup.mts","../src/commands/scan/fetch-scan.mts","../src/commands/scan/output-scan-view.mts","../src/commands/scan/handle-scan-view.mts","../src/commands/scan/stream-scan.mts","../src/commands/scan/cmd-scan-view.mts","../src/commands/scan/cmd-scan.mts","../src/commands/threat-feed/fetch-threat-feed.mts","../src/commands/threat-feed/output-threat-feed.mts","../src/commands/threat-feed/handle-threat-feed.mts","../src/commands/threat-feed/cmd-threat-feed.mts","../src/commands/uninstall/output-uninstall-completion.mts","../src/commands/uninstall/teardown-tab-completion.mts","../src/commands/uninstall/handle-uninstall-completion.mts","../src/commands/uninstall/cmd-uninstall-completion.mts","../src/commands/uninstall/cmd-uninstall.mts","../src/commands/wrapper/add-socket-wrapper.mts","../src/commands/wrapper/check-socket-wrapper-setup.mts","../src/commands/wrapper/postinstall-wrapper.mts","../src/commands/wrapper/remove-socket-wrapper.mts","../src/commands/wrapper/cmd-wrapper.mts","../src/commands.mts","../src/cli.mts"],"sourcesContent":["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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchOrgAnalyticsDataOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchOrgAnalyticsData(\n time: number,\n options?: FetchOrgAnalyticsDataOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgAnalytics'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchOrgAnalyticsDataOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getOrgAnalytics(time.toString()), {\n desc: 'analytics data',\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type RepoAnalyticsDataOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchRepoAnalyticsData(\n repo: string,\n time: number,\n options?: RepoAnalyticsDataOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getRepoAnalytics'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as RepoAnalyticsDataOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getRepoAnalytics(repo, time.toString()), {\n desc: 'analytics data',\n })\n}\n","import fs from 'node:fs/promises'\nimport { createRequire } from 'node:module'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mdTableStringNumber } from '../../utils/markdown.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\nimport type { Widgets } from 'blessed' // Note: Widgets does not seem to actually work as code :'(\nimport type { grid as ContribGrid } from 'blessed-contrib'\n\nconst require = createRequire(import.meta.url)\n\nconst METRICS = [\n 'total_critical_alerts',\n 'total_high_alerts',\n 'total_medium_alerts',\n 'total_low_alerts',\n 'total_critical_added',\n 'total_medium_added',\n 'total_low_added',\n 'total_high_added',\n 'total_critical_prevented',\n 'total_high_prevented',\n 'total_medium_prevented',\n 'total_low_prevented',\n] as const\n\n// Note: This maps `new Date(date).getMonth()` to English three letters\nconst Months = [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec',\n] as const\n\nexport async function outputAnalytics(\n result: CResult<\n | SocketSdkSuccessResult<'getOrgAnalytics'>['data']\n | SocketSdkSuccessResult<'getRepoAnalytics'>['data']\n >,\n {\n filePath,\n outputKind,\n repo,\n scope,\n time,\n }: {\n scope: string\n time: number\n repo: string\n outputKind: OutputKind\n filePath: string\n },\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (!result.ok) {\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'json') {\n const serialized = serializeResultJson(result)\n\n if (filePath) {\n try {\n await fs.writeFile(filePath, serialized, 'utf8')\n logger.success(`Data successfully written to ${filePath}`)\n } catch (e) {\n process.exitCode = 1\n logger.log(\n serializeResultJson({\n ok: false,\n message: 'File Write Failure',\n cause: 'There was an error trying to write the json to disk',\n }),\n )\n }\n } else {\n logger.log(serialized)\n }\n\n return\n }\n\n const fdata =\n scope === 'org' ? formatDataOrg(result.data) : formatDataRepo(result.data)\n\n if (outputKind === 'markdown') {\n const serialized = renderMarkdown(fdata, time, repo)\n\n // TODO: Do we want to write to file even if there was an error...?\n if (filePath) {\n try {\n await fs.writeFile(filePath, serialized, 'utf8')\n logger.success(`Data successfully written to ${filePath}`)\n } catch (e) {\n logger.error(e)\n }\n } else {\n logger.log(serialized)\n }\n } else {\n displayAnalyticsScreen(fdata)\n }\n}\n\nexport interface FormattedData {\n top_five_alert_types: Record<string, number>\n total_critical_alerts: Record<string, number>\n total_high_alerts: Record<string, number>\n total_medium_alerts: Record<string, number>\n total_low_alerts: Record<string, number>\n total_critical_added: Record<string, number>\n total_medium_added: Record<string, number>\n total_low_added: Record<string, number>\n total_high_added: Record<string, number>\n total_critical_prevented: Record<string, number>\n total_high_prevented: Record<string, number>\n total_medium_prevented: Record<string, number>\n total_low_prevented: Record<string, number>\n}\n\nexport function renderMarkdown(\n data: FormattedData,\n days: number,\n repoSlug: string,\n): string {\n return (\n `\n# Socket Alert Analytics\n\nThese are the Socket.dev analytics for the ${repoSlug ? `${repoSlug} repo` : 'org'} of the past ${days} days\n\n${[\n [\n 'Total critical alerts',\n mdTableStringNumber('Date', 'Counts', data['total_critical_alerts']),\n ],\n [\n 'Total high alerts',\n mdTableStringNumber('Date', 'Counts', data['total_high_alerts']),\n ],\n [\n 'Total critical alerts added to the main branch',\n mdTableStringNumber('Date', 'Counts', data['total_critical_added']),\n ],\n [\n 'Total high alerts added to the main branch',\n mdTableStringNumber('Date', 'Counts', data['total_high_added']),\n ],\n [\n 'Total critical alerts prevented from the main branch',\n mdTableStringNumber('Date', 'Counts', data['total_critical_prevented']),\n ],\n [\n 'Total high alerts prevented from the main branch',\n mdTableStringNumber('Date', 'Counts', data['total_high_prevented']),\n ],\n [\n 'Total medium alerts prevented from the main branch',\n mdTableStringNumber('Date', 'Counts', data['total_medium_prevented']),\n ],\n [\n 'Total low alerts prevented from the main branch',\n mdTableStringNumber('Date', 'Counts', data['total_low_prevented']),\n ],\n]\n .map(([title, table]) =>\n `\n## ${title}\n\n${table}\n`.trim(),\n )\n .join('\\n\\n')}\n\n## Top 5 alert types\n\n${mdTableStringNumber('Name', 'Counts', data['top_five_alert_types'])}\n`.trim() + '\\n'\n )\n}\n\nfunction displayAnalyticsScreen(data: FormattedData): void {\n const ScreenWidget = /*@__PURE__*/ require('blessed/lib/widgets/screen.js')\n const screen: Widgets.Screen = new ScreenWidget({\n ...constants.blessedOptions,\n })\n const GridLayout = /*@__PURE__*/ require('blessed-contrib/lib/layout/grid.js')\n const grid = new GridLayout({ rows: 5, cols: 4, screen })\n\n renderLineCharts(\n grid,\n screen,\n 'Total critical alerts',\n [0, 0, 1, 2],\n data['total_critical_alerts'],\n )\n renderLineCharts(\n grid,\n screen,\n 'Total high alerts',\n [0, 2, 1, 2],\n data['total_high_alerts'],\n )\n renderLineCharts(\n grid,\n screen,\n 'Total critical alerts added to the main branch',\n [1, 0, 1, 2],\n data['total_critical_added'],\n )\n renderLineCharts(\n grid,\n screen,\n 'Total high alerts added to the main branch',\n [1, 2, 1, 2],\n data['total_high_added'],\n )\n renderLineCharts(\n grid,\n screen,\n 'Total critical alerts prevented from the main branch',\n [2, 0, 1, 2],\n data['total_critical_prevented'],\n )\n renderLineCharts(\n grid,\n screen,\n 'Total high alerts prevented from the main branch',\n [2, 2, 1, 2],\n data['total_high_prevented'],\n )\n renderLineCharts(\n grid,\n screen,\n 'Total medium alerts prevented from the main branch',\n [3, 0, 1, 2],\n data['total_medium_prevented'],\n )\n renderLineCharts(\n grid,\n screen,\n 'Total low alerts prevented from the main branch',\n [3, 2, 1, 2],\n data['total_low_prevented'],\n )\n\n const BarChart = /*@__PURE__*/ require('blessed-contrib/lib/widget/charts/bar.js')\n const bar = grid.set(4, 0, 1, 2, BarChart, {\n label: 'Top 5 alert types',\n barWidth: 10,\n barSpacing: 17,\n xOffset: 0,\n maxHeight: 9,\n barBgColor: 'magenta',\n })\n\n // Must append before setting data.\n screen.append(bar)\n\n bar.setData({\n titles: Object.keys(data.top_five_alert_types),\n data: Object.values(data.top_five_alert_types),\n })\n\n screen.render()\n // eslint-disable-next-line n/no-process-exit\n screen.key(['escape', 'q', 'C-c'], () => process.exit(0))\n}\n\nexport function formatDataRepo(\n data: SocketSdkSuccessResult<'getRepoAnalytics'>['data'],\n): FormattedData {\n const sortedTopFiveAlerts: Record<string, number> = {}\n const totalTopAlerts: Record<string, number> = {}\n\n const formattedData = {} as Omit<FormattedData, 'top_five_alert_types'>\n for (const metric of METRICS) {\n formattedData[metric] = {}\n }\n\n for (const entry of data) {\n const topFiveAlertTypes = entry['top_five_alert_types']\n for (const type of Object.keys(topFiveAlertTypes)) {\n const count = topFiveAlertTypes[type] ?? 0\n if (!totalTopAlerts[type]) {\n totalTopAlerts[type] = count\n } else if (count > (totalTopAlerts[type] ?? 0)) {\n totalTopAlerts[type] = count\n }\n }\n }\n for (const entry of data) {\n for (const metric of METRICS) {\n formattedData[metric]![formatDate(entry['created_at'])] = entry[metric]\n }\n }\n\n const topFiveAlertEntries = Object.entries(totalTopAlerts)\n .sort(([_keya, a], [_keyb, b]) => b - a)\n .slice(0, 5)\n for (const [key, value] of topFiveAlertEntries) {\n sortedTopFiveAlerts[key] = value\n }\n\n return {\n ...formattedData,\n top_five_alert_types: sortedTopFiveAlerts,\n }\n}\n\nexport function formatDataOrg(\n data: SocketSdkSuccessResult<'getOrgAnalytics'>['data'],\n): FormattedData {\n const sortedTopFiveAlerts: Record<string, number> = {}\n const totalTopAlerts: Record<string, number> = {}\n\n const formattedData = {} as Omit<FormattedData, 'top_five_alert_types'>\n for (const metric of METRICS) {\n formattedData[metric] = {}\n }\n\n for (const entry of data) {\n const topFiveAlertTypes = entry['top_five_alert_types']\n for (const type of Object.keys(topFiveAlertTypes)) {\n const count = topFiveAlertTypes[type] ?? 0\n if (!totalTopAlerts[type]) {\n totalTopAlerts[type] = count\n } else {\n totalTopAlerts[type] += count\n }\n }\n }\n\n for (const metric of METRICS) {\n const formatted = formattedData[metric]\n for (const entry of data) {\n const date = formatDate(entry['created_at'])\n if (!formatted[date]) {\n formatted[date] = entry[metric]!\n } else {\n formatted[date] += entry[metric]!\n }\n }\n }\n\n const topFiveAlertEntries = Object.entries(totalTopAlerts)\n .sort(([_keya, a], [_keyb, b]) => b - a)\n .slice(0, 5)\n for (const [key, value] of topFiveAlertEntries) {\n sortedTopFiveAlerts[key] = value\n }\n\n return {\n ...formattedData,\n top_five_alert_types: sortedTopFiveAlerts,\n }\n}\n\nfunction formatDate(date: string): string {\n return `${Months[new Date(date).getMonth()]} ${new Date(date).getDate()}`\n}\n\nfunction renderLineCharts(\n grid: ContribGrid,\n screen: Widgets.Screen,\n title: string,\n coords: number[],\n data: Record<string, number>,\n): void {\n const LineChart = /*@__PURE__*/ require('blessed-contrib/lib/widget/charts/line.js')\n const line = grid.set(...coords, LineChart, {\n style: { line: 'cyan', text: 'cyan', baseline: 'black' },\n xLabelPadding: 0,\n xPadding: 0,\n xOffset: 0,\n wholeNumbersOnly: true,\n legend: {\n width: 1,\n },\n label: title,\n })\n\n screen.append(line)\n\n const lineData = {\n x: Object.keys(data),\n y: Object.values(data),\n }\n\n line.setData([lineData])\n}\n","import { fetchOrgAnalyticsData } from './fetch-org-analytics.mts'\nimport { fetchRepoAnalyticsData } from './fetch-repo-analytics.mts'\nimport { outputAnalytics } from './output-analytics.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function handleAnalytics({\n filePath,\n outputKind,\n repo,\n scope,\n time,\n}: {\n scope: string\n time: number\n repo: string\n outputKind: OutputKind\n filePath: string\n}) {\n let result: CResult<\n | SocketSdkSuccessResult<'getOrgAnalytics'>['data']\n | SocketSdkSuccessResult<'getRepoAnalytics'>['data']\n >\n if (scope === 'org') {\n result = await fetchOrgAnalyticsData(time)\n } else if (repo) {\n result = await fetchRepoAnalyticsData(repo, time)\n } else {\n result = {\n ok: false,\n message: 'Missing repository name in command',\n }\n }\n if (result.ok && !result.data.length) {\n result = {\n ok: true,\n message: `The analytics data for this ${scope === 'org' ? 'organization' : 'repository'} is not yet available.`,\n data: [],\n }\n }\n\n await outputAnalytics(result, {\n filePath,\n outputKind,\n repo,\n scope,\n time,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleAnalytics } from './handle-analytics.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'analytics'\n\nconst description = 'Look up analytics data'\n\nconst hidden = false\n\nexport const cmdAnalytics = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n file: {\n type: 'string',\n description: 'Path to store result, only valid with --json/--markdown',\n },\n },\n help: (command, { flags }) =>\n `\n Usage\n $ ${command} [options] [ \"org\" | \"repo\" <reponame>] [TIME]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n The scope is either org or repo level, defaults to org.\n\n When scope is repo, a repo slug must be given as well.\n\n The TIME argument must be number 7, 30, or 90 and defaults to 30.\n\n Options\n ${getFlagListOutput(flags)}\n\n Examples\n $ ${command} org 7\n $ ${command} repo test-repo 30\n $ ${command} 90\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n // Supported inputs:\n // - [] (no args)\n // - ['org']\n // - ['org', '30']\n // - ['repo', 'name']\n // - ['repo', 'name', '30']\n // - ['30']\n // Validate final values in the next step\n let scope = 'org'\n let time = '30'\n let repoName = ''\n\n if (cli.input[0] === 'org') {\n if (cli.input[1]) {\n time = cli.input[1]\n }\n } else if (cli.input[0] === 'repo') {\n scope = 'repo'\n if (cli.input[1]) {\n repoName = cli.input[1]\n }\n if (cli.input[2]) {\n time = cli.input[2]\n }\n } else if (cli.input[0]) {\n time = cli.input[0]\n }\n\n const { file, json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const noLegacy =\n !cli.flags['scope'] && !cli.flags['repo'] && !cli.flags['time']\n\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: noLegacy,\n message: 'Legacy flags are no longer supported. See v1 migration guide.',\n fail: `received legacy flags`,\n },\n {\n nook: true,\n test: scope === 'org' || !!repoName,\n message: 'When scope=repo, repo name should be the second argument',\n fail: 'missing',\n },\n {\n nook: true,\n test:\n scope === 'org' ||\n (repoName !== '7' && repoName !== '30' && repoName !== '90'),\n message: 'When scope is repo, the second arg should be repo, not time',\n fail: 'missing',\n },\n {\n test: time === '7' || time === '30' || time === '90',\n message: 'The time filter must either be 7, 30 or 90',\n fail: 'invalid range set, see --help for command arg details.',\n },\n {\n nook: true,\n test: !file || !!json || !!markdown,\n message:\n 'The `--file` flag is only valid when using `--json` or `--markdown`',\n fail: 'bad',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n return await handleAnalytics({\n scope,\n time: time === '90' ? 90 : time === '30' ? 30 : 7,\n repo: repoName,\n outputKind,\n filePath: String(file || ''),\n })\n}\n","import { handleApiCall } from '../../utils/api.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SetupSdkOptions } from '../../utils/sdk.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchAuditLogsConfig = {\n logType: string\n orgSlug: string\n outputKind: OutputKind\n page: number\n perPage: number\n}\n\nexport type FetchAuditLogOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchAuditLog(\n config: FetchAuditLogsConfig,\n options?: FetchAuditLogOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getAuditLogEvents'>['data']>> {\n const { sdkOpts } = { __proto__: null, ...options } as FetchAuditLogOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n const { logType, orgSlug, outputKind, page, perPage } = {\n __proto__: null,\n ...config,\n } as FetchAuditLogsConfig\n\n return await handleApiCall(\n sockSdk.getAuditLogEvents(orgSlug, {\n // I'm not sure this is used at all.\n outputJson: String(outputKind === 'json'),\n // I'm not sure this is used at all.\n outputMarkdown: String(outputKind === 'markdown'),\n orgSlug,\n type: logType,\n page: String(page),\n per_page: String(perPage),\n }),\n { desc: `audit log for ${orgSlug}` },\n )\n}\n","import { createRequire } from 'node:module'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mdTable } from '../../utils/markdown.mts'\nimport { msAtHome } from '../../utils/ms-at-home.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\nimport type { Widgets } from 'blessed'\n\nconst require = createRequire(import.meta.url)\n\nexport async function outputAuditLog(\n result: CResult<SocketSdkSuccessResult<'getAuditLogEvents'>['data']>,\n {\n logType,\n orgSlug,\n outputKind,\n page,\n perPage,\n }: {\n logType: string\n outputKind: OutputKind\n orgSlug: string\n page: number\n perPage: number\n },\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(\n await outputAsJson(result, {\n logType,\n orgSlug,\n page,\n perPage,\n }),\n )\n }\n\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'markdown') {\n logger.log(\n await outputAsMarkdown(result.data, {\n logType,\n orgSlug,\n page,\n perPage,\n }),\n )\n return\n }\n\n await outputWithBlessed(result.data, orgSlug)\n}\n\nfunction formatResult(\n selectedRow?: SocketSdkSuccessResult<'getAuditLogEvents'>['data']['results'][number],\n keepQuotes = false,\n): string {\n if (!selectedRow) {\n return '(none)'\n }\n // Format the object with spacing but keep the payload compact because\n // that can contain just about anything and spread many lines.\n const obj = { ...selectedRow, payload: 'REPLACEME' }\n const json = JSON.stringify(obj, null, 2).replace(\n /\"payload\": \"REPLACEME\"/,\n `\"payload\": ${JSON.stringify(selectedRow.payload ?? {})}`,\n )\n if (keepQuotes) {\n return json\n }\n return json.replace(/^\\s*\"([^\"]+)?\"/gm, ' $1')\n}\n\nexport async function outputAsJson(\n auditLogs: CResult<SocketSdkSuccessResult<'getAuditLogEvents'>['data']>,\n {\n logType,\n orgSlug,\n page,\n perPage,\n }: {\n logType: string\n orgSlug: string\n page: number\n perPage: number\n },\n): Promise<string> {\n if (!auditLogs.ok) {\n return serializeResultJson(auditLogs)\n }\n\n return serializeResultJson({\n ok: true,\n data: {\n desc: 'Audit logs for given query',\n generated: constants.ENV.VITEST\n ? constants.REDACTED\n : new Date().toISOString(),\n logType,\n nextPage: auditLogs.data.nextPage,\n org: orgSlug,\n page,\n perPage,\n logs: auditLogs.data.results.map(log => {\n // Note: The subset is pretty arbitrary\n const {\n created_at,\n event_id,\n ip_address,\n type,\n user_agent,\n user_email,\n } = log\n return {\n event_id,\n created_at,\n ip_address,\n type,\n user_agent,\n user_email,\n }\n }),\n },\n })\n}\n\nexport async function outputAsMarkdown(\n auditLogs: SocketSdkSuccessResult<'getAuditLogEvents'>['data'],\n {\n logType,\n orgSlug,\n page,\n perPage,\n }: {\n orgSlug: string\n page: number\n perPage: number\n logType: string\n },\n): Promise<string> {\n try {\n const table = mdTable<any>(auditLogs.results, [\n 'event_id',\n 'created_at',\n 'type',\n 'user_email',\n 'ip_address',\n 'user_agent',\n ])\n\n return `\n# Socket Audit Logs\n\nThese are the Socket.dev audit logs as per requested query.\n- org: ${orgSlug}\n- type filter: ${logType || '(none)'}\n- page: ${page}\n- next page: ${auditLogs.nextPage}\n- per page: ${perPage}\n- generated: ${constants.ENV.VITEST ? constants.REDACTED : new Date().toISOString()}\n\n${table}\n`\n } catch (e) {\n process.exitCode = 1\n logger.fail(\n 'There was a problem converting the logs to Markdown, please try the `--json` flag',\n )\n debugFn('error', 'caught: markdown conversion error')\n debugDir('inspect', { error: e })\n return 'Failed to generate the markdown report'\n }\n}\n\nasync function outputWithBlessed(\n data: SocketSdkSuccessResult<'getAuditLogEvents'>['data'],\n orgSlug: string,\n) {\n const filteredLogs = data.results\n const formattedOutput = filteredLogs.map(logs => [\n logs.event_id ?? '',\n msAtHome(logs.created_at ?? ''),\n logs.type ?? '',\n logs.user_email ?? '',\n logs.ip_address ?? '',\n logs.user_agent ?? '',\n ])\n const headers = [\n ' Event id',\n ' Created at',\n ' Event type',\n ' User email',\n ' IP address',\n ' User agent',\n ]\n\n // Note: this temporarily takes over the terminal (just like `man` does).\n const ScreenWidget = /*@__PURE__*/ require('blessed/lib/widgets/screen.js')\n const screen: Widgets.Screen = new ScreenWidget({\n ...constants.blessedOptions,\n })\n // Register these keys first so you can always exit, even when it gets stuck\n // If we don't do this and the code crashes, the user must hard-kill the\n // node process just to exit it. That's very bad UX.\n // eslint-disable-next-line n/no-process-exit\n screen.key(['escape', 'q', 'C-c'], () => process.exit(0))\n\n const TableWidget = /*@__PURE__*/ require('blessed-contrib/lib/widget/table.js')\n const tipsBoxHeight = 1 // 1 row for tips box\n const detailsBoxHeight = 20 // bottom N rows for details box. 20 gives 4 lines for condensed payload before it scrolls out of view\n\n const maxWidths = headers.map(s => s.length + 1)\n formattedOutput.forEach(row => {\n row.forEach((str, i) => {\n maxWidths[i] = Math.max(str.length, maxWidths[i] ?? str.length)\n })\n })\n\n const table: any = new TableWidget({\n keys: 'true',\n fg: 'white',\n selectedFg: 'white',\n selectedBg: 'magenta',\n interactive: 'true',\n label: `Audit Logs for ${orgSlug}`,\n width: '100%',\n top: 0,\n bottom: detailsBoxHeight + tipsBoxHeight,\n border: {\n type: 'line',\n fg: 'cyan',\n },\n columnWidth: maxWidths, //[10, 30, 40, 25, 15, 200],\n // Note: spacing works as long as you don't reserve more than total width\n columnSpacing: 4,\n truncate: '_',\n })\n\n const BoxWidget = /*@__PURE__*/ require('blessed/lib/widgets/box.js')\n const tipsBox: Widgets.BoxElement = new BoxWidget({\n bottom: detailsBoxHeight, // sits just above the details box\n height: tipsBoxHeight,\n width: '100%',\n style: {\n fg: 'yellow',\n bg: 'black',\n },\n tags: true,\n content: `↑/↓: Move Enter: Select q/ESC: Quit`,\n })\n const detailsBox: Widgets.BoxElement = new BoxWidget({\n bottom: 0,\n height: detailsBoxHeight,\n width: '100%',\n border: {\n type: 'line',\n fg: 'cyan',\n },\n label: 'Details',\n content: formatResult(filteredLogs[0], true),\n style: {\n fg: 'white',\n },\n })\n\n table.setData({\n headers: headers,\n data: formattedOutput,\n })\n\n // allow control the table with the keyboard\n table.focus()\n\n // Stacking order: table (top), tipsBox (middle), detailsBox (bottom)\n screen.append(table)\n screen.append(tipsBox)\n screen.append(detailsBox)\n\n // Update details box when selection changes\n table.rows.on('select item', () => {\n const selectedIndex = table.rows.selected\n if (selectedIndex !== undefined && selectedIndex >= 0) {\n const selectedRow = filteredLogs[selectedIndex]\n detailsBox.setContent(formatResult(selectedRow))\n screen.render()\n }\n })\n\n screen.render()\n\n screen.key(['return'], () => {\n const selectedIndex = table.rows.selected\n screen.destroy()\n const selectedRow = formattedOutput[selectedIndex]\n ? formatResult(filteredLogs[selectedIndex], true)\n : '(none)'\n logger.log(`Last selection:\\n${selectedRow.trim()}`)\n })\n}\n","import { fetchAuditLog } from './fetch-audit-log.mts'\nimport { outputAuditLog } from './output-audit-log.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleAuditLog({\n logType,\n orgSlug,\n outputKind,\n page,\n perPage,\n}: {\n logType: string\n outputKind: OutputKind\n orgSlug: string\n page: number\n perPage: number\n}): Promise<void> {\n const auditLogs = await fetchAuditLog({\n logType,\n orgSlug,\n outputKind,\n page,\n perPage,\n })\n\n await outputAuditLog(auditLogs, {\n logType,\n orgSlug,\n outputKind,\n page,\n perPage,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleAuditLog } from './handle-audit-log.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'audit-log'\n\nconst description = 'Look up the audit log for an organization'\n\nconst hidden = false\n\nexport const cmdAuditLog = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input.\\nUse --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n page: {\n type: 'number',\n description: 'Result page to fetch',\n },\n perPage: {\n type: 'number',\n default: 30,\n description: 'Results per page - default is 30',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [FILTER]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n This feature requires an Enterprise Plan. To learn more about getting access\n to this feature and many more, please visit ${constants.SOCKET_WEBSITE_URL}/pricing\n\n The type FILTER arg is an enum. Defaults to any. It should be one of these:\n associateLabel, cancelInvitation, changeMemberRole, changePlanSubscriptionSeats,\n createApiToken, createLabel, deleteLabel, deleteLabelSetting, deleteReport,\n deleteRepository, disassociateLabel, joinOrganization, removeMember,\n resetInvitationLink, resetOrganizationSettingToDefault, rotateApiToken,\n sendInvitation, setLabelSettingToDefault, syncOrganization, transferOwnership,\n updateAlertTriage, updateApiTokenCommitter, updateApiTokenMaxQuota,\n updateApiTokenName', updateApiTokenScopes, updateApiTokenVisibility,\n updateLabelSetting, updateOrganizationSetting, upgradeOrganizationPlan\n\n The page arg should be a positive integer, offset 1. Defaults to 1.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} deleteReport --page 2 --per-page 10\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag, page, perPage } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const noLegacy = !cli.flags['type']\n\n let [typeFilter = ''] = cli.input\n\n typeFilter = String(typeFilter)\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: noLegacy,\n message: 'Legacy flags are no longer supported. See v1 migration guide.',\n fail: `received legacy flags`,\n },\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: /^[a-zA-Z]*$/.test(typeFilter),\n message: 'The filter must be an a-zA-Z string, it is an enum',\n fail: 'it was given but not a-zA-Z',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleAuditLog({\n orgSlug,\n outputKind,\n page: Number(page || 0),\n perPage: Number(perPage || 0),\n logType: typeFilter.charAt(0).toUpperCase() + typeFilter.slice(1),\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchCreateOrgFullScanConfigs = {\n branchName: string\n commitHash: string\n commitMessage: string\n committers: string\n pullRequest: number\n repoName: string\n}\n\nexport type FetchCreateOrgFullScanOptions = {\n cwd?: string | undefined\n defaultBranch?: boolean | undefined\n pendingHead?: boolean | undefined\n sdkOpts?: SetupSdkOptions | undefined\n tmp?: boolean | undefined\n}\n\nexport async function fetchCreateOrgFullScan(\n packagePaths: string[],\n orgSlug: string,\n config: FetchCreateOrgFullScanConfigs,\n options?: FetchCreateOrgFullScanOptions,\n): Promise<CResult<SocketSdkSuccessResult<'CreateOrgFullScan'>['data']>> {\n const {\n branchName,\n commitHash,\n commitMessage,\n committers,\n pullRequest,\n repoName,\n } = { __proto__: null, ...config } as FetchCreateOrgFullScanConfigs\n\n const {\n cwd = process.cwd(),\n defaultBranch,\n pendingHead,\n sdkOpts,\n tmp,\n } = { __proto__: null, ...options } as FetchCreateOrgFullScanOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(\n sockSdk.createOrgFullScan(orgSlug, packagePaths, cwd, {\n ...(branchName ? { branch: branchName } : {}),\n ...(commitHash ? { commit_hash: commitHash } : {}),\n ...(commitMessage ? { commit_message: commitMessage } : {}),\n ...(committers ? { committers } : {}),\n make_default_branch: String(defaultBranch),\n ...(pullRequest ? { pull_request: String(pullRequest) } : {}),\n repo: repoName,\n set_as_pending_head: String(pendingHead),\n tmp: String(tmp),\n }),\n { desc: 'to create a scan' },\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 { Spinner } from '@socketsecurity/registry/lib/spinner'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchSupportedScanFileNamesOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function fetchSupportedScanFileNames(\n options?: FetchSupportedScanFileNamesOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getReportSupportedFiles'>['data']>> {\n const { sdkOpts, spinner } = {\n __proto__: null,\n ...options,\n } as FetchSupportedScanFileNamesOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getSupportedScanFiles(), {\n desc: 'supported scan file types',\n spinner,\n })\n}\n","import { sendApiRequest } from '../../utils/api.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport type FinalizeTier1ScanOptions = {\n tier1_reachability_scan_id: string\n report_run_id: string\n}\n\n/**\n * Finalize a tier1 reachability scan.\n * - Associates the tier1 reachability scan metadata with the full scan.\n * - Sets the tier1 reachability scan to \"finalized\" state.\n */\nexport async function finalizeTier1Scan(\n tier1ReachabilityScanId: string,\n scanId: string,\n): Promise<CResult<unknown>> {\n // we do not use the SDK here because the tier1-reachability-scan/finalize is a hidden\n // endpoint that is not part of the OpenAPI specification.\n return await sendApiRequest('tier1-reachability-scan/finalize', {\n method: 'POST',\n body: {\n tier1_reachability_scan_id: tier1ReachabilityScanId,\n report_run_id: scanId,\n },\n })\n}\n","import { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { handleApiCallNoSpinner, queryApiSafeText } from '../../utils/api.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { SocketArtifact } from '../../utils/alert/artifact.mts'\nimport type { SetupSdkOptions } from '../../utils/sdk.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchScanData = {\n includeLicensePolicy?: boolean | undefined\n sdkOpts?: SetupSdkOptions | undefined\n}\n\n/**\n * This fetches all the relevant pieces of data to generate a report, given a\n * full scan ID.\n */\nexport async function fetchScanData(\n orgSlug: string,\n scanId: string,\n options?: FetchScanData | undefined,\n): Promise<\n CResult<{\n scan: SocketArtifact[]\n securityPolicy: SocketSdkSuccessResult<'getOrgSecurityPolicy'>['data']\n }>\n> {\n const { includeLicensePolicy, sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchScanData\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n let policyStatus = 'requested...'\n let scanStatus = 'requested...'\n let finishedFetching = false\n\n const { spinner } = constants\n\n function updateScan(desc: string) {\n scanStatus = desc\n updateProgress()\n }\n\n function updatePolicy(desc: string) {\n policyStatus = desc\n updateProgress()\n }\n\n function updateProgress() {\n if (finishedFetching) {\n spinner.stop()\n logger.info(\n `Scan result: ${scanStatus}. Security policy: ${policyStatus}.`,\n )\n } else {\n spinner.start(\n `Scan result: ${scanStatus}. Security policy: ${policyStatus}.`,\n )\n }\n }\n\n async function fetchScanResult(): Promise<CResult<SocketArtifact[]>> {\n const result = await queryApiSafeText(\n `orgs/${orgSlug}/full-scans/${encodeURIComponent(scanId)}${includeLicensePolicy ? '?include_license_details=true' : ''}`,\n )\n\n updateScan(`response received`)\n\n if (!result.ok) {\n return result\n }\n\n const ndJsonString = result.data\n\n // This is nd-json; each line is a json object.\n const lines = ndJsonString.split('\\n').filter(Boolean)\n let ok = true\n const data = lines.map(line => {\n try {\n return JSON.parse(line)\n } catch (e) {\n ok = false\n debugFn('error', 'caught: JSON.parse error')\n debugDir('inspect', { error: e, line })\n return\n }\n }) as unknown as SocketArtifact[]\n\n if (ok) {\n updateScan('success')\n return { ok: true, data }\n }\n\n updateScan('received invalid JSON response')\n\n return {\n ok: false,\n message: 'Invalid Socket API response',\n cause:\n 'The Socket API responded with at least one line that was not valid JSON. Please report if this persists.',\n }\n }\n\n async function fetchSecurityPolicy(): Promise<\n CResult<SocketSdkSuccessResult<'getOrgSecurityPolicy'>['data']>\n > {\n const result = await handleApiCallNoSpinner(\n sockSdk.getOrgSecurityPolicy(orgSlug),\n 'GetOrgSecurityPolicy',\n )\n\n updatePolicy('received policy')\n\n return result\n }\n\n updateProgress()\n\n const [scan, securityPolicy]: [\n CResult<SocketArtifact[]>,\n CResult<SocketSdkSuccessResult<'getOrgSecurityPolicy'>['data']>,\n ] = await Promise.all([\n fetchScanResult().catch(e => {\n updateScan('failure; unknown blocking error occurred')\n return {\n ok: false as const,\n message: 'Socket API error',\n cause: `Error requesting scan: ${e?.message || '(no error message found)'}${e?.cause ? ` (cause: ${e.cause})` : ''}`,\n }\n }),\n fetchSecurityPolicy().catch(e => {\n updatePolicy('failure; unknown blocking error occurred')\n return {\n ok: false as const,\n message: 'Socket API error',\n cause: `Error requesting policy: ${e?.message || '(no error message found)'}${e?.cause ? ` (cause: ${e.cause})` : ''}`,\n }\n }),\n ]).finally(() => {\n finishedFetching = true\n updateProgress()\n })\n\n if (!scan.ok) {\n return scan\n }\n if (!securityPolicy.ok) {\n return securityPolicy\n }\n\n if (!Array.isArray(scan.data)) {\n return {\n ok: false,\n message: 'Failed to fetch',\n cause: 'Was unable to fetch scan result, bailing',\n }\n }\n\n return {\n ok: true,\n data: {\n scan: scan.data satisfies SocketArtifact[],\n securityPolicy: securityPolicy.data,\n },\n }\n}\n","import { getSocketDevPackageOverviewUrlFromPurl } from '../../utils/socket-url.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { SocketArtifact } from '../../utils/alert/artifact.mts'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\ntype AlertAction = 'defer' | 'ignore' | 'monitor' | 'error' | 'warn'\ntype AlertKey = string\n\ntype FileMap = Map<string, ReportLeafNode | Map<AlertKey, ReportLeafNode>>\ntype VersionMap = Map<string, ReportLeafNode | FileMap>\ntype PackageMap = Map<string, ReportLeafNode | VersionMap>\ntype EcoMap = Map<string, ReportLeafNode | PackageMap>\nexport type ViolationsMap = Map<string, EcoMap>\n\nexport interface ShortScanReport {\n healthy: boolean\n}\nexport interface ScanReport {\n orgSlug: string\n scanId: string\n options: { fold: string; reportLevel: string }\n healthy: boolean\n alerts: ViolationsMap\n}\n\nexport type ReportLeafNode = {\n type: string\n policy: 'defer' | 'ignore' | 'monitor' | 'warn' | 'error'\n url: string\n manifest: string[]\n}\n\n// Note: The returned cresult will only be ok:false when the generation\n// failed. It won't reflect the healthy state.\nexport function generateReport(\n scan: SocketArtifact[],\n securityPolicy: SocketSdkSuccessResult<'getOrgSecurityPolicy'>['data'],\n {\n fold,\n orgSlug,\n reportLevel,\n scanId,\n short,\n spinner,\n }: {\n fold: 'pkg' | 'version' | 'file' | 'none'\n orgSlug: string\n reportLevel: 'defer' | 'ignore' | 'monitor' | 'warn' | 'error'\n scanId: string\n short?: boolean | undefined\n spinner?: Spinner | undefined\n },\n): CResult<ScanReport | { healthy: boolean }> {\n const now = Date.now()\n\n spinner?.start('Generating report...')\n\n // Create an object that includes:\n // healthy: boolean\n // worst violation level;\n // per eco\n // per package\n // per version\n // per offending file\n // reported issue -> policy action\n\n // In the context of a report;\n // - the alert.severity is irrelevant\n // - the securityPolicyDefault is irrelevant\n // - the report defaults to healthy:true with no alerts\n // - the appearance of an alert will trigger the policy action;\n // - error: healthy will end up as false, add alerts to report\n // - warn: healthy unchanged, add alerts to report\n // - monitor/ignore: no action\n // - defer: unknown (no action)\n\n // Note: the server will emit alerts for license policy violations but\n // those are only included if you set the flag when requesting the scan\n // data. The alerts map to a single security policy key that determines\n // what to do with any violation, regardless of the concrete license.\n // That rule is called \"License Policy Violation\".\n // The license policy part is implicitly handled here. Either they are\n // included and may show up, or they are not and won't show up.\n\n const violations = new Map()\n\n let healthy = true\n\n const securityRules = securityPolicy.securityPolicyRules\n if (securityRules) {\n // Note: reportLevel: error > warn > monitor > ignore > defer\n scan.forEach(artifact => {\n const {\n alerts,\n name: pkgName = '<unknown>',\n type: ecosystem,\n version = '<unknown>',\n } = artifact\n\n alerts?.forEach(\n (alert: NonNullable<SocketArtifact['alerts']>[number]) => {\n const alertName = alert.type as keyof typeof securityRules // => policy[type]\n const action = securityRules[alertName]?.action || ''\n switch (action) {\n case 'error': {\n healthy = false\n if (!short) {\n addAlert(\n artifact,\n violations,\n fold,\n ecosystem,\n pkgName,\n version,\n alert,\n action,\n )\n }\n break\n }\n case 'warn': {\n if (!short && reportLevel !== 'error') {\n addAlert(\n artifact,\n violations,\n fold,\n ecosystem,\n pkgName,\n version,\n alert,\n action,\n )\n }\n break\n }\n case 'monitor': {\n if (!short && reportLevel !== 'warn' && reportLevel !== 'error') {\n addAlert(\n artifact,\n violations,\n fold,\n ecosystem,\n pkgName,\n version,\n alert,\n action,\n )\n }\n break\n }\n\n case 'ignore': {\n if (\n !short &&\n reportLevel !== 'warn' &&\n reportLevel !== 'error' &&\n reportLevel !== 'monitor'\n ) {\n addAlert(\n artifact,\n violations,\n fold,\n ecosystem,\n pkgName,\n version,\n alert,\n action,\n )\n }\n break\n }\n\n case 'defer': {\n // Not sure but ignore for now. Defer to later ;)\n if (!short && reportLevel === 'defer') {\n addAlert(\n artifact,\n violations,\n fold,\n ecosystem,\n pkgName,\n version,\n alert,\n action,\n )\n }\n break\n }\n\n default: {\n // This value was not emitted from the Socket API at the time of writing.\n }\n }\n },\n )\n })\n }\n\n spinner?.successAndStop(`Generated reported in ${Date.now() - now} ms`)\n\n if (short) {\n return {\n ok: true,\n data: { healthy },\n }\n }\n\n const report = {\n healthy,\n orgSlug,\n scanId,\n options: { fold, reportLevel },\n alerts: violations,\n }\n\n if (!healthy) {\n return {\n ok: true,\n message:\n 'The report contains at least one alert that violates the policies set by your organization',\n data: report,\n }\n }\n\n return {\n ok: true,\n data: report,\n }\n}\n\nfunction createLeaf(\n art: SocketArtifact,\n alert: NonNullable<SocketArtifact['alerts']>[number],\n policyAction: AlertAction,\n): ReportLeafNode {\n const leaf: ReportLeafNode = {\n type: alert.type,\n policy: policyAction,\n url: getSocketDevPackageOverviewUrlFromPurl(art),\n manifest: art.manifestFiles?.map(o => o.file) ?? [],\n }\n return leaf\n}\n\nfunction addAlert(\n art: SocketArtifact,\n violations: ViolationsMap,\n foldSetting: 'pkg' | 'version' | 'file' | 'none',\n ecosystem: string,\n pkgName: string,\n version: string,\n alert: NonNullable<SocketArtifact['alerts']>[number],\n policyAction: AlertAction,\n): void {\n if (!violations.has(ecosystem)) {\n violations.set(ecosystem, new Map())\n }\n const ecomap: EcoMap = violations.get(ecosystem)!\n if (foldSetting === 'pkg') {\n const existing = ecomap.get(pkgName) as ReportLeafNode | undefined\n if (!existing || isStricterPolicy(existing.policy, policyAction)) {\n ecomap.set(pkgName, createLeaf(art, alert, policyAction))\n }\n } else {\n if (!ecomap.has(pkgName)) {\n ecomap.set(pkgName, new Map())\n }\n const pkgmap = ecomap.get(pkgName) as PackageMap\n if (foldSetting === 'version') {\n const existing = pkgmap.get(version) as ReportLeafNode | undefined\n if (!existing || isStricterPolicy(existing.policy, policyAction)) {\n pkgmap.set(version, createLeaf(art, alert, policyAction))\n }\n } else {\n if (!pkgmap.has(version)) {\n pkgmap.set(version, new Map())\n }\n const file = alert.file || '<unknown>'\n const vermap = pkgmap.get(version) as VersionMap\n\n if (foldSetting === 'file') {\n const existing = vermap.get(file) as ReportLeafNode | undefined\n if (!existing || isStricterPolicy(existing.policy, policyAction)) {\n vermap.set(file, createLeaf(art, alert, policyAction))\n }\n } else {\n if (!vermap.has(file)) {\n vermap.set(file, new Map())\n }\n const key = `${alert.type} at ${alert.start}:${alert.end}`\n const filemap: FileMap = vermap.get(file) as FileMap\n const existing = filemap.get(key) as ReportLeafNode | undefined\n if (!existing || isStricterPolicy(existing.policy, policyAction)) {\n filemap.set(key, createLeaf(art, alert, policyAction))\n }\n }\n }\n }\n}\n\nfunction isStricterPolicy(\n was: 'error' | 'warn' | 'monitor' | 'ignore' | 'defer',\n is: 'error' | 'warn' | 'monitor' | 'ignore' | 'defer',\n): boolean {\n // error > warn > monitor > ignore > defer > {unknown}\n if (was === 'error') {\n return false\n }\n if (is === 'error') {\n return true\n }\n if (was === 'warn') {\n return false\n }\n if (is === 'warn') {\n return false\n }\n if (was === 'monitor') {\n return false\n }\n if (is === 'monitor') {\n return false\n }\n if (was === 'ignore') {\n return false\n }\n if (is === 'ignore') {\n return false\n }\n if (was === 'defer') {\n return false\n }\n if (is === 'defer') {\n return false\n }\n // unreachable?\n return false\n}\n","import fs from 'node:fs/promises'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { generateReport } from './generate-report.mts'\nimport constants from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mapToObject } from '../../utils/map-to-object.mts'\nimport { mdTable } from '../../utils/markdown.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\nimport { walkNestedMap } from '../../utils/walk-nested-map.mts'\n\nimport type { ReportLeafNode, ScanReport } from './generate-report.mts'\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketArtifact } from '../../utils/alert/artifact.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputScanReport(\n result: CResult<{\n scan: SocketArtifact[]\n securityPolicy: SocketSdkSuccessResult<'getOrgSecurityPolicy'>['data']\n }>,\n {\n filePath,\n fold,\n includeLicensePolicy,\n orgSlug,\n outputKind,\n reportLevel,\n scanId,\n short,\n }: {\n orgSlug: string\n scanId: string\n includeLicensePolicy: boolean\n outputKind: OutputKind\n filePath: string\n fold: 'pkg' | 'version' | 'file' | 'none'\n reportLevel: 'defer' | 'ignore' | 'monitor' | 'warn' | 'error'\n short: boolean\n },\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (!result.ok) {\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const scanReport = generateReport(\n result.data.scan,\n result.data.securityPolicy,\n {\n orgSlug,\n scanId,\n fold,\n reportLevel,\n short,\n spinner: constants.spinner,\n },\n )\n\n if (!scanReport.ok) {\n // Note: this means generation failed, it does not reflect the healthy state\n process.exitCode = scanReport.code ?? 1\n\n // If report generation somehow failed then .data should not be set.\n if (outputKind === 'json') {\n logger.log(serializeResultJson(scanReport))\n return\n }\n logger.fail(failMsgWithBadge(scanReport.message, scanReport.cause))\n return\n }\n\n // I don't think we emit the default error message with banner for an unhealhty report, do we?\n // if (!scanReport.data.healhty) {\n // logger.fail(failMsgWithBadge(scanReport.message, scanReport.cause))\n // return\n // }\n\n if (\n outputKind === 'json' ||\n (outputKind === 'text' && filePath && filePath.endsWith('.json'))\n ) {\n const json = short\n ? serializeResultJson(scanReport)\n : toJsonReport(scanReport.data as ScanReport, includeLicensePolicy)\n\n if (filePath && filePath !== '-') {\n logger.log('Writing json report to', filePath)\n return await fs.writeFile(filePath, json)\n }\n\n logger.log(json)\n return\n }\n\n if (outputKind === 'markdown' || (filePath && filePath.endsWith('.md'))) {\n const md = short\n ? `healthy = ${scanReport.data.healthy}`\n : toMarkdownReport(\n scanReport.data as ScanReport, // not short so must be regular report\n includeLicensePolicy,\n )\n\n if (filePath && filePath !== '-') {\n logger.log('Writing markdown report to', filePath)\n return await fs.writeFile(filePath, md)\n }\n\n logger.log(md)\n logger.log('')\n return\n }\n\n if (short) {\n logger.log(scanReport.data.healthy ? 'OK' : 'ERR')\n } else {\n logger.dir(scanReport.data, { depth: null })\n }\n}\n\nexport function toJsonReport(\n report: ScanReport,\n includeLicensePolicy?: boolean | undefined,\n): string {\n const obj = mapToObject(report.alerts)\n\n const newReport = {\n includeLicensePolicy,\n ...report,\n alerts: obj,\n }\n\n return serializeResultJson({\n ok: true,\n data: newReport,\n })\n}\n\nexport function toMarkdownReport(\n report: ScanReport,\n includeLicensePolicy?: boolean | undefined,\n): string {\n const flatData = Array.from(walkNestedMap(report.alerts)).map(\n ({ keys, value }: { keys: string[]; value: ReportLeafNode }) => {\n const { manifest, policy, type, url } = value\n return {\n 'Alert Type': type,\n Package: keys[1] || '<unknown>',\n 'Introduced by': keys[2] || '<unknown>',\n url,\n 'Manifest file': manifest.join(', '),\n Policy: policy,\n }\n },\n )\n\n const md =\n `\n# Scan Policy Report\n\nThis report tells you whether the results of a Socket scan results violate the\nsecurity${includeLicensePolicy ? ' or license' : ''} policy set by your organization.\n\n## Health status\n\n${\n report.healthy\n ? `The scan *PASSES* all requirements set by your security${includeLicensePolicy ? ' and license' : ''} policy.`\n : 'The scan *VIOLATES* one or more policies set to the \"error\" level.'\n}\n\n## Settings\n\nConfiguration used to generate this report:\n\n- Organization: ${report.orgSlug}\n- Scan ID: ${report.scanId}\n- Alert folding: ${report.options.fold === 'none' ? 'none' : `up to ${report.options.fold}`}\n- Minimal policy level for alert to be included in report: ${report.options.reportLevel === 'defer' ? 'everything' : report.options.reportLevel}\n- Include license alerts: ${includeLicensePolicy ? 'yes' : 'no'}\n\n## Alerts\n\n${\n report.alerts.size\n ? `All the alerts from the scan with a policy set to at least \"${report.options.reportLevel}\".`\n : `The scan contained no alerts with a policy set to at least \"${report.options.reportLevel}\".`\n}\n\n${\n !report.alerts.size\n ? ''\n : mdTable(flatData, [\n 'Policy',\n 'Alert Type',\n 'Package',\n 'Introduced by',\n 'url',\n 'Manifest file',\n ])\n}\n `.trim() + '\\n'\n\n return md\n}\n","import { fetchScanData } from './fetch-report-data.mts'\nimport { outputScanReport } from './output-scan-report.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleScanReport({\n filePath,\n fold,\n includeLicensePolicy,\n orgSlug,\n outputKind,\n reportLevel,\n scanId,\n short,\n}: {\n orgSlug: string\n scanId: string\n includeLicensePolicy: boolean\n outputKind: OutputKind\n filePath: string\n fold: 'pkg' | 'version' | 'file' | 'none'\n reportLevel: 'defer' | 'ignore' | 'monitor' | 'warn' | 'error'\n short: boolean\n}): Promise<void> {\n const scanDataCResult = await fetchScanData(orgSlug, scanId, {\n includeLicensePolicy,\n })\n\n await outputScanReport(scanDataCResult, {\n filePath,\n fold,\n scanId: scanId,\n includeLicensePolicy,\n orgSlug,\n outputKind,\n reportLevel,\n short,\n })\n}\n","import open from 'open'\nimport terminalLink from 'terminal-link'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { confirm } from '@socketsecurity/registry/lib/prompts'\n\nimport constants from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type CreateNewScanOptions = {\n interactive?: boolean | undefined\n outputKind?: OutputKind | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function outputCreateNewScan(\n result: CResult<SocketSdkSuccessResult<'CreateOrgFullScan'>['data']>,\n options?: CreateNewScanOptions | undefined,\n) {\n const {\n interactive = false,\n outputKind = 'text',\n spinner = constants.spinner,\n } = { __proto__: null, ...options } as CreateNewScanOptions\n\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n const wasSpinning = !!spinner?.isSpinning\n\n spinner?.stop()\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n if (wasSpinning) {\n spinner.start()\n }\n return\n }\n\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n if (wasSpinning) {\n spinner.start()\n }\n return\n }\n\n if (!result.data.id) {\n logger.fail('Did not receive a scan ID from the API.')\n process.exitCode = 1\n }\n\n if (outputKind === 'markdown') {\n logger.log('# Create New Scan')\n logger.log('')\n if (result.data.id) {\n logger.log(\n `A [new Scan](${result.data.html_report_url}) was created with ID: ${result.data.id}`,\n )\n logger.log('')\n } else {\n logger.log(\n `The server did not return a Scan ID while trying to create a new Scan. This could be an indication something went wrong.`,\n )\n }\n logger.log('')\n if (wasSpinning) {\n spinner.start()\n }\n return\n }\n\n logger.log('')\n logger.success('Scan completed successfully!')\n\n const htmlReportUrl = result.data.html_report_url\n if (htmlReportUrl) {\n logger.log(`View report at: ${terminalLink(htmlReportUrl, htmlReportUrl)}`)\n } else {\n logger.log('No report available.')\n }\n\n if (\n interactive &&\n (await confirm(\n {\n message: 'Would you like to open it in your browser?',\n default: false,\n },\n { spinner },\n ))\n ) {\n await open(`${result.data.html_report_url}`)\n }\n\n if (wasSpinning) {\n spinner.start()\n }\n}\n","import path from 'node:path'\n\nimport terminalLink from 'terminal-link'\n\nimport constants from '../../constants.mts'\nimport { handleApiCall } from '../../utils/api.mts'\nimport {\n extractTier1ReachabilityScanId,\n spawnCoana,\n} from '../../utils/coana.mts'\nimport { hasEnterpriseOrgPlan } from '../../utils/organization.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\nimport { fetchOrganization } from '../organization/fetch-organization-list.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { PURL_Type } from '../../utils/ecosystem.mts'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nexport type ReachabilityOptions = {\n reachAnalysisTimeout: number\n reachAnalysisMemoryLimit: number\n reachDisableAnalytics: boolean\n reachEcosystems: PURL_Type[]\n reachExcludePaths: string[]\n reachSkipCache: boolean\n}\n\nexport type ReachabilityAnalysisOptions = {\n branchName?: string | undefined\n cwd?: string | undefined\n orgSlug?: string | undefined\n packagePaths?: string[] | undefined\n reachabilityOptions: ReachabilityOptions\n repoName?: string | undefined\n spinner?: Spinner | undefined\n uploadManifests?: boolean | undefined\n}\n\nexport type ReachabilityAnalysisResult = {\n reachabilityReport: string\n tier1ReachabilityScanId: string | undefined\n}\n\nexport async function performReachabilityAnalysis(\n options?: ReachabilityAnalysisOptions | undefined,\n): Promise<CResult<ReachabilityAnalysisResult>> {\n const {\n branchName,\n cwd = process.cwd(),\n orgSlug,\n packagePaths,\n reachabilityOptions,\n repoName,\n spinner,\n uploadManifests = true,\n } = { __proto__: null, ...options } as ReachabilityAnalysisOptions\n\n // Check if user has enterprise plan for reachability analysis\n const orgsCResult = await fetchOrganization()\n if (!orgsCResult.ok) {\n return {\n ok: false,\n message: 'Unable to verify plan permissions',\n cause:\n 'Failed to fetch organization information to verify enterprise plan access',\n }\n }\n\n const { organizations } = orgsCResult.data\n\n if (!hasEnterpriseOrgPlan(organizations)) {\n return {\n ok: false,\n message: 'Tier 1 Reachability analysis requires an enterprise plan',\n cause: `Please ${terminalLink('upgrade your plan', 'https://socket.dev/pricing')}. This feature is only available for organizations with an enterprise plan.`,\n }\n }\n\n let tarHash: string | undefined\n\n if (uploadManifests && orgSlug && packagePaths) {\n // Setup SDK for uploading manifests\n const sockSdkCResult = await setupSdk()\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n\n const sockSdk = sockSdkCResult.data\n\n const wasSpinning = !!spinner?.isSpinning\n\n // Exclude any .socket.facts.json files that happen to be in the scan\n // folder before the analysis was run.\n const filepathsToUpload = packagePaths.filter(\n p =>\n path.basename(p).toLowerCase() !== constants.DOT_SOCKET_DOT_FACTS_JSON,\n )\n\n spinner?.start('Uploading manifests for reachability analysis...')\n\n const uploadCResult = await handleApiCall(\n sockSdk.uploadManifestFiles(orgSlug, filepathsToUpload),\n {\n desc: 'upload manifests',\n spinner,\n },\n )\n\n spinner?.stop()\n\n if (!uploadCResult.ok) {\n if (wasSpinning) {\n spinner.start()\n }\n return uploadCResult\n }\n\n tarHash = (uploadCResult.data as { tarHash?: string })?.tarHash\n if (!tarHash) {\n if (wasSpinning) {\n spinner.start()\n }\n return {\n ok: false,\n message: 'Failed to get manifest tar hash',\n cause: 'Server did not return a tar hash for the uploaded manifests',\n }\n }\n\n spinner?.start()\n spinner?.success(`Manifests uploaded successfully. Tar hash: ${tarHash}`)\n }\n\n spinner?.start()\n spinner?.infoAndStop('Running reachability analysis with Coana...')\n\n // Build Coana arguments.\n const coanaArgs = [\n 'run',\n cwd,\n '--output-dir',\n cwd,\n '--socket-mode',\n constants.DOT_SOCKET_DOT_FACTS_JSON,\n '--disable-report-submission',\n ...(reachabilityOptions.reachAnalysisTimeout\n ? ['--analysis-timeout', `${reachabilityOptions.reachAnalysisTimeout}`]\n : []),\n ...(reachabilityOptions.reachAnalysisMemoryLimit\n ? ['--memory-limit', `${reachabilityOptions.reachAnalysisMemoryLimit}`]\n : []),\n ...(reachabilityOptions.reachDisableAnalytics\n ? ['--disable-analytics-sharing']\n : []),\n ...(tarHash\n ? ['--run-without-docker', '--manifests-tar-hash', tarHash]\n : []),\n // Empty reachEcosystems implies scanning all ecosystems.\n ...(reachabilityOptions.reachEcosystems.length\n ? ['--purl-types', ...reachabilityOptions.reachEcosystems]\n : []),\n ...(reachabilityOptions.reachExcludePaths.length\n ? ['--exclude-dirs', ...reachabilityOptions.reachExcludePaths]\n : []),\n ...(reachabilityOptions.reachSkipCache ? ['--skip-cache-usage'] : []),\n ]\n\n // Build environment variables.\n const env: NodeJS.ProcessEnv = {\n ...process.env,\n }\n // do not pass default repo and branch name to coana to avoid mixing\n // buckets (cached configuration) from projects that are likely very different.\n if (repoName && repoName !== constants.SOCKET_DEFAULT_REPOSITORY) {\n env['SOCKET_REPO_NAME'] = repoName\n }\n if (branchName && branchName !== constants.SOCKET_DEFAULT_BRANCH) {\n env['SOCKET_BRANCH_NAME'] = branchName\n }\n\n // Run Coana with the manifests tar hash.\n const coanaResult = await spawnCoana(coanaArgs, orgSlug, {\n cwd,\n env,\n spinner,\n stdio: 'inherit',\n })\n\n const wasSpinning = !!spinner?.isSpinning\n if (wasSpinning) {\n spinner.start()\n }\n\n return coanaResult.ok\n ? {\n ok: true,\n data: {\n // Use the DOT_SOCKET_DOT_FACTS_JSON file for the scan.\n reachabilityReport: constants.DOT_SOCKET_DOT_FACTS_JSON,\n tier1ReachabilityScanId: extractTier1ReachabilityScanId(\n constants.DOT_SOCKET_DOT_FACTS_JSON,\n ),\n },\n }\n : coanaResult\n}\n","// The point here is to attempt to detect the various supported manifest files\n// the CLI can generate. This would be environments that we can't do server side\n\nimport { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport { debugLog } from '@socketsecurity/registry/lib/debug'\n\nimport type { SocketJson } from '../../utils/socket-json.mts'\n\nexport interface GeneratableManifests {\n cdxgen: boolean\n count: number\n conda: boolean\n gradle: boolean\n sbt: boolean\n}\n\nexport async function detectManifestActions(\n // Passing in null means we attempt detection for every supported language\n // regardless of local socket.json status. Sometimes we want that.\n sockJson: SocketJson | null,\n cwd = process.cwd(),\n): Promise<GeneratableManifests> {\n const output = {\n cdxgen: false, // TODO\n count: 0,\n conda: false,\n gradle: false,\n sbt: false,\n }\n\n if (sockJson?.defaults?.manifest?.sbt?.disabled) {\n debugLog(\n 'notice',\n '[DEBUG] - sbt auto-detection is disabled in socket.json',\n )\n } else if (existsSync(path.join(cwd, 'build.sbt'))) {\n debugLog('notice', '[DEBUG] - Detected a Scala sbt build file')\n\n output.sbt = true\n output.count += 1\n }\n\n if (sockJson?.defaults?.manifest?.gradle?.disabled) {\n debugLog(\n 'notice',\n '[DEBUG] - gradle auto-detection is disabled in socket.json',\n )\n } else if (existsSync(path.join(cwd, 'gradlew'))) {\n debugLog('notice', '[DEBUG] - Detected a gradle build file')\n output.gradle = true\n output.count += 1\n }\n\n if (sockJson?.defaults?.manifest?.conda?.disabled) {\n debugLog(\n 'notice',\n '[DEBUG] - conda auto-detection is disabled in socket.json',\n )\n } else {\n const envyml = path.join(cwd, 'environment.yml')\n const hasEnvyml = existsSync(envyml)\n const envyaml = path.join(cwd, 'environment.yaml')\n const hasEnvyaml = !hasEnvyml && existsSync(envyaml)\n if (hasEnvyml || hasEnvyaml) {\n debugLog('notice', '[DEBUG] - Detected an environment.yml Conda file')\n output.conda = true\n output.count += 1\n }\n }\n\n return output\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants.mts'\n\nexport async function convertGradleToMaven({\n bin,\n cwd,\n gradleOpts,\n verbose,\n}: {\n bin: string\n cwd: string\n verbose: boolean\n gradleOpts: string[]\n}) {\n // TODO: Implement json/md.\n\n // Note: use resolve because the bin could be an absolute path, away from cwd\n // TODO: what about $PATH resolved commands? (`gradlew` without dir prefix)\n const rBin = path.resolve(cwd, bin)\n const binExists = fs.existsSync(rBin)\n const cwdExists = fs.existsSync(cwd)\n\n logger.group('gradle2maven:')\n logger.info(`- executing: \\`${rBin}\\``)\n if (!binExists) {\n logger.warn(\n `Warning: It appears the executable could not be found. An error might be printed later because of that.`,\n )\n }\n logger.info(`- src dir: \\`${cwd}\\``)\n if (!cwdExists) {\n logger.warn(\n `Warning: It appears the src dir could not be found. An error might be printed later because of that.`,\n )\n }\n logger.groupEnd()\n\n try {\n // Run gradlew with the init script we provide which should yield zero or more\n // pom files. We have to figure out where to store those pom files such that\n // we can upload them and predict them through the GitHub API. We could do a\n // .socket folder. We could do a socket.pom.gz with all the poms, although\n // I'd prefer something plain-text if it is to be committed.\n // Note: init.gradle will be exported by .config/rollup.dist.config.mjs\n const initLocation = path.join(constants.distPath, 'init.gradle')\n const commandArgs = ['--init-script', initLocation, ...gradleOpts, 'pom']\n if (verbose) {\n logger.log('[VERBOSE] Executing:', [bin], ', args:', commandArgs)\n }\n logger.log(`Converting gradle to maven from \\`${bin}\\` on \\`${cwd}\\` ...`)\n const output = await execGradleWithSpinner(rBin, commandArgs, cwd)\n if (verbose) {\n logger.group('[VERBOSE] gradle stdout:')\n logger.log(output)\n logger.groupEnd()\n }\n if (output.code) {\n process.exitCode = 1\n logger.fail(`Gradle exited with exit code ${output.code}`)\n // (In verbose mode, stderr was printed above, no need to repeat it)\n if (!verbose) {\n logger.group('stderr:')\n logger.error(output.stderr)\n logger.groupEnd()\n }\n return\n }\n logger.success('Executed gradle successfully')\n logger.log('Reported exports:')\n output.stdout.replace(\n /^POM file copied to: (.*)/gm,\n (_all: string, fn: string) => {\n logger.log('- ', fn)\n return fn\n },\n )\n logger.log('')\n logger.log(\n 'Next step is to generate a Scan by running the `socket scan create` command on the same directory',\n )\n } catch (e) {\n process.exitCode = 1\n logger.fail(\n 'There was an unexpected error while generating manifests' +\n (verbose ? '' : ' (use --verbose for details)'),\n )\n if (verbose) {\n logger.group('[VERBOSE] error:')\n logger.log(e)\n logger.groupEnd()\n }\n }\n}\n\nasync function execGradleWithSpinner(\n bin: string,\n commandArgs: string[],\n cwd: string,\n): Promise<{ code: number; stdout: string; stderr: string }> {\n const { spinner } = constants\n\n let pass = false\n try {\n logger.info(\n '(Running gradle can take a while, it depends on how long gradlew has to run)',\n )\n logger.info(\n '(It will show no output, you can use --verbose to see its output)',\n )\n spinner.start(`Running gradlew...`)\n\n const output = await spawn(bin, commandArgs, {\n // We can pipe the output through to have the user see the result\n // of running gradlew, but then we can't (easily) gather the output\n // to discover the generated files... probably a flag we should allow?\n // stdio: isDebug() ? 'inherit' : undefined,\n cwd,\n })\n\n pass = true\n const { code, stderr, stdout } = output\n return { code, stdout, stderr }\n } finally {\n if (pass) {\n spinner.successAndStop('Gracefully completed gradlew execution.')\n } else {\n spinner.failAndStop('There was an error while trying to run gradlew.')\n }\n }\n}\n","import { safeReadFile } from '@socketsecurity/registry/lib/fs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants.mts'\n\nexport async function convertSbtToMaven({\n bin,\n cwd,\n out,\n sbtOpts,\n verbose,\n}: {\n bin: string\n cwd: string\n out: string\n sbtOpts: string[]\n verbose: boolean\n}) {\n // TODO: Implement json/md.\n\n const { spinner } = constants\n\n logger.group('sbt2maven:')\n logger.info(`- executing: \\`${bin}\\``)\n logger.info(`- src dir: \\`${cwd}\\``)\n logger.groupEnd()\n\n try {\n spinner.start(`Converting sbt to maven from \\`${bin}\\` on \\`${cwd}\\`...`)\n\n // Run sbt with the init script we provide which should yield zero or more\n // pom files. We have to figure out where to store those pom files such that\n // we can upload them and predict them through the GitHub API. We could do a\n // .socket folder. We could do a socket.pom.gz with all the poms, although\n // I'd prefer something plain-text if it is to be committed.\n const output = await spawn(bin, ['makePom', ...sbtOpts], { cwd })\n\n spinner.stop()\n\n if (verbose) {\n logger.group('[VERBOSE] sbt stdout:')\n logger.log(output)\n logger.groupEnd()\n }\n if (output.stderr) {\n process.exitCode = 1\n logger.fail('There were errors while running sbt')\n // (In verbose mode, stderr was printed above, no need to repeat it)\n if (!verbose) {\n logger.group('[VERBOSE] stderr:')\n logger.error(output.stderr)\n logger.groupEnd()\n }\n return\n }\n const poms: string[] = []\n output.stdout.replace(/Wrote (.*?.pom)\\n/g, (_all: string, fn: string) => {\n poms.push(fn)\n return fn\n })\n if (!poms.length) {\n process.exitCode = 1\n logger.fail(\n 'There were no errors from sbt but it seems to not have generated any poms either',\n )\n return\n }\n // Move the pom file to ...? initial cwd? loc will be an absolute path, or dump to stdout\n // TODO: What do we do with multiple output files? Do we want to dump them to stdout? Raw or with separators or ?\n // TODO: Maybe we can add an option to target a specific file to dump to stdout.\n if (out === '-' && poms.length === 1) {\n logger.log('Result:\\n```')\n logger.log(await safeReadFile(poms[0]!))\n logger.log('```')\n logger.success(`OK`)\n } else if (out === '-') {\n process.exitCode = 1\n logger.error('')\n logger.fail(\n 'Requested output target was stdout but there are multiple generated files',\n )\n logger.error('')\n poms.forEach(fn => logger.info('-', fn))\n if (poms.length > 10) {\n logger.error('')\n logger.fail(\n 'Requested output target was stdout but there are multiple generated files',\n )\n }\n logger.error('')\n logger.info('Exiting now...')\n return\n } else {\n // if (verbose) {\n // logger.log(\n // `Moving manifest file from \\`${loc.replace(/^\\/home\\/[^/]*?\\//, '~/')}\\` to \\`${out}\\``\n // )\n // } else {\n // logger.log('Moving output pom file')\n // }\n // TODO: Do we prefer fs-extra? Renaming can be gnarly on windows and fs-extra's version is better.\n // await renamep(loc, out)\n logger.success(`Generated ${poms.length} pom files`)\n poms.forEach(fn => logger.log('-', fn))\n logger.success(`OK`)\n }\n } catch (e) {\n process.exitCode = 1\n spinner.stop()\n logger.fail(\n 'There was an unexpected error while running this' +\n (verbose ? '' : ' (use --verbose for details)'),\n )\n if (verbose) {\n logger.group('[VERBOSE] error:')\n logger.log(e)\n logger.groupEnd()\n }\n }\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { stripAnsi } from '@socketsecurity/registry/lib/strings'\n\nimport type { CResult } from '../../types.mts'\n\nfunction prepareContent(content: string): string {\n return stripAnsi(content.trim())\n}\n\nexport async function convertCondaToRequirements(\n filename: string,\n cwd: string,\n verbose: boolean,\n): Promise<CResult<{ content: string; pip: string }>> {\n let content: string\n if (filename === '-') {\n if (verbose) {\n logger.info(`[VERBOSE] reading input from stdin`)\n }\n\n const strings: string[] = []\n content = await new Promise((resolve, reject) => {\n process.stdin.on('data', chunk => {\n const input = chunk.toString()\n strings.push(input)\n })\n process.stdin.on('end', () => {\n resolve(prepareContent(strings.join('')))\n })\n process.stdin.on('error', e => {\n if (verbose) {\n logger.error('Unexpected error while reading from stdin:', e)\n }\n reject(e)\n })\n process.stdin.on('close', () => {\n if (strings.length) {\n if (verbose) {\n logger.error(\n 'warning: stdin closed explicitly with some data received',\n )\n }\n resolve(prepareContent(strings.join('')))\n } else {\n if (verbose) {\n logger.error('stdin closed explicitly without data received')\n }\n reject(new Error('No data received from stdin'))\n }\n })\n })\n\n if (!content) {\n return {\n ok: false,\n message: 'Manifest Generation Failed',\n cause: 'No data received from stdin',\n }\n }\n } else {\n const filepath = path.join(cwd, filename)\n\n if (verbose) {\n logger.info(`[VERBOSE] target: ${filepath}`)\n }\n\n if (!existsSync(filepath)) {\n return {\n ok: false,\n message: 'Manifest Generation Failed',\n cause: `The file was not found at ${filepath}`,\n }\n }\n\n content = readFileSync(filepath, 'utf8')\n\n if (!content) {\n return {\n ok: false,\n message: 'Manifest Generation Failed',\n cause: `File at ${filepath} is empty`,\n }\n }\n }\n\n return {\n ok: true,\n data: {\n content,\n pip: convertCondaToRequirementsFromInput(content),\n },\n }\n}\n\n// Just extract the first pip block, if one exists at all.\nexport function convertCondaToRequirementsFromInput(input: string): string {\n let collecting = false\n let delim = '-'\n let indent = ''\n const keeping: string[] = []\n for (const line of input.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed) {\n // Ignore empty lines.\n continue\n }\n if (collecting) {\n if (line.startsWith('#')) {\n // Ignore comment lines (keep?).\n continue\n }\n if (line.startsWith(delim)) {\n // In this case we have a line with the same indentation as the\n // `- pip:` line, so we have reached the end of the pip block.\n break\n }\n if (!indent) {\n // Store the indentation of the block.\n if (trimmed.startsWith('-')) {\n indent = line.split('-')[0] + '-'\n if (indent.length <= delim.length) {\n // The first line after the `pip:` line does not indent further\n // than that so the block is empty?\n break\n }\n }\n }\n if (line.startsWith(indent)) {\n keeping.push(line.slice(indent.length).trim())\n } else {\n // Unexpected input. bail.\n break\n }\n }\n // Note: the line may end with a line comment so don't === it.\n else if (trimmed.startsWith('- pip:')) {\n delim = line.split('-')[0] + '-'\n collecting = true\n }\n }\n\n return prepareContent(keeping.join('\\n'))\n}\n","import fs from 'node:fs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputRequirements(\n result: CResult<{ content: string; pip: string }>,\n outputKind: OutputKind,\n out: string,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (!result.ok) {\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'json') {\n const json = serializeResultJson(result)\n\n if (out === '-') {\n logger.log(json)\n } else {\n fs.writeFileSync(out, json, 'utf8')\n }\n\n return\n }\n\n if (outputKind === 'markdown') {\n const arr = []\n arr.push('# Converted Conda file')\n arr.push('')\n arr.push(\n 'This is the Conda `environment.yml` file converted to python `requirements.txt`:',\n )\n arr.push('')\n arr.push('```file=requirements.txt')\n arr.push(result.data.pip)\n arr.push('```')\n arr.push('')\n const md = arr.join('\\n')\n\n if (out === '-') {\n logger.log(md)\n } else {\n fs.writeFileSync(out, md, 'utf8')\n }\n return\n }\n\n if (out === '-') {\n logger.log(result.data.pip)\n logger.log('')\n } else {\n fs.writeFileSync(out, result.data.pip, 'utf8')\n }\n}\n","import { convertCondaToRequirements } from './convert-conda-to-requirements.mts'\nimport { outputRequirements } from './output-requirements.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleManifestConda({\n cwd,\n filename,\n out,\n outputKind,\n verbose,\n}: {\n cwd: string\n filename: string\n out: string\n outputKind: OutputKind\n verbose: boolean\n}): Promise<void> {\n const data = await convertCondaToRequirements(filename, cwd, verbose)\n\n await outputRequirements(data, outputKind, out)\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { convertGradleToMaven } from './convert_gradle_to_maven.mts'\nimport { convertSbtToMaven } from './convert_sbt_to_maven.mts'\nimport { handleManifestConda } from './handle-manifest-conda.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\n\nimport type { GeneratableManifests } from './detect-manifest-actions.mts'\nimport type { OutputKind } from '../../types.mts'\n\nexport async function generateAutoManifest({\n cwd,\n detected,\n outputKind,\n verbose,\n}: {\n detected: GeneratableManifests\n cwd: string\n outputKind: OutputKind\n verbose: boolean\n}) {\n const sockJson = readOrDefaultSocketJson(cwd)\n\n if (verbose) {\n logger.info('Using this socket.json for defaults:', sockJson)\n }\n\n if (!sockJson?.defaults?.manifest?.sbt?.disabled && detected.sbt) {\n logger.log('Detected a Scala sbt build, generating pom files with sbt...')\n await convertSbtToMaven({\n // Note: `sbt` is more likely to be resolved against PATH env\n bin: sockJson.defaults?.manifest?.sbt?.bin ?? 'sbt',\n cwd,\n out: sockJson.defaults?.manifest?.sbt?.outfile ?? './socket.sbt.pom.xml',\n sbtOpts:\n sockJson.defaults?.manifest?.sbt?.sbtOpts\n ?.split(' ')\n .map(s => s.trim())\n .filter(Boolean) ?? [],\n verbose: Boolean(sockJson.defaults?.manifest?.sbt?.verbose),\n })\n }\n\n if (!sockJson?.defaults?.manifest?.gradle?.disabled && detected.gradle) {\n logger.log(\n 'Detected a gradle build (Gradle, Kotlin, Scala), running default gradle generator...',\n )\n await convertGradleToMaven({\n // Note: `gradlew` is more likely to be resolved against cwd.\n // Note: .resolve() won't butcher an absolute path.\n // TODO: `gradlew` (or anything else given) may want to resolve against PATH.\n bin: sockJson.defaults?.manifest?.gradle?.bin\n ? path.resolve(cwd, sockJson.defaults.manifest.gradle.bin)\n : path.join(cwd, 'gradlew'),\n cwd,\n verbose: Boolean(sockJson.defaults?.manifest?.gradle?.verbose),\n gradleOpts:\n sockJson.defaults?.manifest?.gradle?.gradleOpts\n ?.split(' ')\n .map(s => s.trim())\n .filter(Boolean) ?? [],\n })\n }\n\n if (!sockJson?.defaults?.manifest?.conda?.disabled && detected.conda) {\n logger.log(\n 'Detected an environment.yml file, running default Conda generator...',\n )\n await handleManifestConda({\n cwd,\n filename: sockJson.defaults?.manifest?.conda?.infile ?? 'environment.yml',\n outputKind,\n out: sockJson.defaults?.manifest?.conda?.outfile ?? 'requirements.txt',\n verbose: Boolean(sockJson.defaults?.manifest?.conda?.verbose),\n })\n }\n}\n","import path from 'node:path'\n\nimport terminalLink from 'terminal-link'\n\nimport { debugDir } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { fetchCreateOrgFullScan } from './fetch-create-org-full-scan.mts'\nimport { fetchSupportedScanFileNames } from './fetch-supported-scan-file-names.mts'\nimport { finalizeTier1Scan } from './finalize-tier1-scan.mts'\nimport { handleScanReport } from './handle-scan-report.mts'\nimport { outputCreateNewScan } from './output-create-new-scan.mts'\nimport { performReachabilityAnalysis } from './perform-reachability-analysis.mts'\nimport constants from '../../constants.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getPackageFilesForScan } from '../../utils/path-resolve.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\nimport { detectManifestActions } from '../manifest/detect-manifest-actions.mts'\nimport { generateAutoManifest } from '../manifest/generate_auto_manifest.mts'\n\nimport type { ReachabilityOptions } from './perform-reachability-analysis.mts'\nimport type { OutputKind } from '../../types.mts'\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\n\nexport async function handleCreateNewScan({\n autoManifest,\n branchName,\n commitHash,\n commitMessage,\n committers,\n cwd,\n defaultBranch,\n interactive,\n orgSlug,\n outputKind,\n pendingHead,\n pullRequest,\n reach,\n readOnly,\n repoName,\n report,\n targets,\n tmp,\n}: {\n autoManifest: boolean\n branchName: string\n commitHash: string\n commitMessage: string\n committers: string\n cwd: string\n defaultBranch: boolean\n interactive: boolean\n orgSlug: string\n pendingHead: boolean\n pullRequest: number\n outputKind: OutputKind\n reach: Remap<\n ReachabilityOptions & {\n runReachabilityAnalysis: boolean\n }\n >\n readOnly: boolean\n repoName: string\n report: boolean\n targets: string[]\n tmp: boolean\n}): Promise<void> {\n if (autoManifest) {\n logger.info('Auto-generating manifest files ...')\n const sockJson = readOrDefaultSocketJson(cwd)\n const detected = await detectManifestActions(sockJson, cwd)\n await generateAutoManifest({\n detected,\n cwd,\n outputKind,\n verbose: false,\n })\n logger.info('Auto-generation finished. Proceeding with Scan creation.')\n }\n\n const { spinner } = constants\n\n const supportedFilesCResult = await fetchSupportedScanFileNames({ spinner })\n if (!supportedFilesCResult.ok) {\n await outputCreateNewScan(supportedFilesCResult, {\n interactive,\n outputKind,\n })\n return\n }\n\n spinner.start('Searching for local files to include in scan...')\n\n const supportedFiles = supportedFilesCResult.data\n const packagePaths = await getPackageFilesForScan(targets, supportedFiles, {\n cwd,\n })\n\n spinner.successAndStop(\n `Found ${packagePaths.length} ${pluralize('file', packagePaths.length)} to include in scan.`,\n )\n\n const wasValidInput = checkCommandInput(outputKind, {\n nook: true,\n test: packagePaths.length > 0,\n fail: `found no eligible files to scan. See supported manifest files at ${terminalLink('docs.socket.dev', 'https://docs.socket.dev/docs/manifest-file-detection-in-socket')}`,\n message:\n 'TARGET (file/dir) must contain matching / supported file types for a scan',\n })\n if (!wasValidInput) {\n return\n }\n\n logger.success(\n `Found ${packagePaths.length} local ${pluralize('file', packagePaths.length)}`,\n )\n\n debugDir('inspect', { packagePaths })\n\n if (readOnly) {\n logger.log('[ReadOnly] Bailing now')\n return\n }\n\n let scanPaths: string[] = packagePaths\n let tier1ReachabilityScanId: string | undefined\n\n // If reachability is enabled, perform reachability analysis.\n if (reach.runReachabilityAnalysis) {\n logger.error('')\n logger.info('Starting reachability analysis...')\n\n spinner.start()\n\n const reachResult = await performReachabilityAnalysis({\n branchName,\n cwd,\n orgSlug,\n packagePaths,\n reachabilityOptions: reach,\n repoName,\n spinner,\n })\n\n spinner.stop()\n\n if (!reachResult.ok) {\n await outputCreateNewScan(reachResult, { interactive, outputKind })\n return\n }\n\n logger.success('Reachability analysis completed successfully')\n\n const reachabilityReport = reachResult.data?.reachabilityReport\n\n scanPaths = [\n ...packagePaths.filter(\n // Ensure the .socket.facts.json isn't duplicated in case it happened\n // to be in the scan folder before the analysis was run.\n p =>\n path.basename(p).toLowerCase() !==\n constants.DOT_SOCKET_DOT_FACTS_JSON,\n ),\n ...(reachabilityReport ? [reachabilityReport] : []),\n ]\n\n tier1ReachabilityScanId = reachResult.data?.tier1ReachabilityScanId\n }\n\n const fullScanCResult = await fetchCreateOrgFullScan(\n scanPaths,\n orgSlug,\n {\n commitHash,\n commitMessage,\n committers,\n pullRequest,\n repoName,\n branchName,\n },\n {\n cwd,\n defaultBranch,\n pendingHead,\n tmp,\n },\n )\n\n const scanId = fullScanCResult.ok ? fullScanCResult.data?.id : undefined\n\n if (reach && scanId && tier1ReachabilityScanId) {\n await finalizeTier1Scan(tier1ReachabilityScanId, scanId)\n }\n\n if (report && fullScanCResult.ok) {\n if (scanId) {\n await handleScanReport({\n filePath: '-',\n fold: 'version',\n includeLicensePolicy: true,\n orgSlug,\n outputKind,\n reportLevel: 'error',\n scanId,\n short: false,\n })\n } else {\n await outputCreateNewScan(\n {\n ok: false,\n message: 'Missing Scan ID',\n cause: 'Server did not respond with a scan ID',\n data: fullScanCResult.data,\n },\n {\n interactive,\n outputKind,\n },\n )\n }\n } else {\n spinner.stop()\n\n await outputCreateNewScan(fullScanCResult, { interactive, outputKind })\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getDefaultOrgSlug } from './fetch-default-org-slug.mts'\nimport {\n detectDefaultBranch,\n getRepoName,\n gitBranch,\n} from '../../utils/git.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\nimport { handleCreateNewScan } from '../scan/handle-create-new-scan.mts'\n\nexport async function handleCi(autoManifest: boolean): Promise<void> {\n const orgSlugCResult = await getDefaultOrgSlug()\n if (!orgSlugCResult.ok) {\n process.exitCode = orgSlugCResult.code ?? 1\n // Always assume json mode.\n logger.log(serializeResultJson(orgSlugCResult))\n return\n }\n\n const orgSlug = orgSlugCResult.data\n const cwd = process.cwd()\n const branchName = (await gitBranch(cwd)) || (await detectDefaultBranch(cwd))\n const repoName = await getRepoName(cwd)\n\n await handleCreateNewScan({\n autoManifest,\n branchName,\n commitMessage: '',\n commitHash: '',\n committers: '',\n cwd,\n defaultBranch: false,\n interactive: false,\n orgSlug,\n outputKind: 'json',\n // When 'pendingHead' is true, it requires 'branchName' set and 'tmp' false.\n pendingHead: true,\n pullRequest: 0,\n reach: {\n reachAnalysisTimeout: 0,\n reachAnalysisMemoryLimit: 0,\n reachDisableAnalytics: false,\n reachEcosystems: [],\n reachExcludePaths: [],\n reachSkipCache: false,\n runReachabilityAnalysis: false,\n },\n repoName,\n readOnly: false,\n report: true,\n targets: ['.'],\n // Don't set 'tmp' when 'pendingHead' is true.\n tmp: false,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleCi } from './handle-ci.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'ci',\n description: 'Shorthand for `socket scan create --report --no-interactive`',\n hidden: false,\n flags: {\n ...commonFlags,\n autoManifest: {\n type: 'boolean',\n // Dev tools in CI environments are not likely to be set up, so this is safer.\n default: false,\n description:\n 'Auto generate manifest files where detected? See autoManifest flag in `socket scan create`',\n },\n },\n help: (command, _config) => `\n Usage\n $ ${command} [options]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n This command is intended to use in CI runs to allow automated systems to\n accept or reject a current build. It will use the default org of the\n Socket API token. The exit code will be non-zero when the scan does not pass\n your security policy.\n\n The --auto-manifest flag does the same as the one from \\`socket scan create\\`\n but is not enabled by default since the CI is less likely to be set up with\n all the necessary dev tooling. Enable it if you want the scan to include\n locally generated manifests like for gradle and sbt.\n\n Examples\n $ ${command}\n $ ${command} --auto-manifest\n `,\n}\n\nexport const cmdCI = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleCi(Boolean(cli.flags['autoManifest']))\n}\n","import { isSupportedConfigKey } from '../../utils/config.mts'\nimport { getOrgSlugs } from '../../utils/organization.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\nimport { fetchOrganization } from '../organization/fetch-organization-list.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport async function discoverConfigValue(\n key: string,\n): Promise<CResult<unknown>> {\n // This will have to be a specific implementation per key because certain\n // keys should request information from particular API endpoints while\n // others should simply return their default value, like endpoint URL.\n\n if (key !== 'test' && !isSupportedConfigKey(key)) {\n return {\n ok: false,\n message: 'Auto discover failed',\n cause: 'Requested key is not a valid config key.',\n }\n }\n\n if (key === 'apiBaseUrl') {\n // Return the default value\n return {\n ok: false,\n message: 'Auto discover failed',\n cause:\n \"If you're unsure about the base endpoint URL then simply unset it.\",\n }\n }\n\n if (key === 'apiProxy') {\n // I don't think we can auto-discover this with any order of reliability..?\n return {\n ok: false,\n message: 'Auto discover failed',\n cause:\n 'When uncertain, unset this key. Otherwise ask your network administrator',\n }\n }\n\n if (key === 'apiToken') {\n return {\n ok: false,\n message: 'Auto discover failed',\n cause:\n 'You can find/create your API token in your Socket dashboard > settings > API tokens.\\nYou should then use `socket login` to login instead of this command.',\n }\n }\n\n if (key === 'defaultOrg') {\n const hasApiToken = hasDefaultApiToken()\n if (!hasApiToken) {\n return {\n ok: false,\n message: 'Auto discover failed',\n cause:\n 'No API token set, must have a token to resolve its default org.',\n }\n }\n\n const org = await getDefaultOrgFromToken()\n if (!org?.length) {\n return {\n ok: false,\n message: 'Auto discover failed',\n cause: 'Was unable to determine default org for the current API token.',\n }\n }\n\n if (Array.isArray(org)) {\n return {\n ok: true,\n data: org,\n message: 'These are the orgs that the current API token can access.',\n }\n }\n\n return {\n ok: true,\n data: org,\n message: 'This is the org that belongs to the current API token.',\n }\n }\n\n if (key === 'enforcedOrgs') {\n const hasApiToken = hasDefaultApiToken()\n if (!hasApiToken) {\n return {\n ok: false,\n message: 'Auto discover failed',\n cause:\n 'No API token set, must have a token to resolve orgs to enforce.',\n }\n }\n\n const orgs = await getEnforceableOrgsFromToken()\n if (!orgs?.length) {\n return {\n ok: false,\n message: 'Auto discover failed',\n cause:\n 'Was unable to determine any orgs to enforce for the current API token.',\n }\n }\n\n return {\n ok: true,\n data: orgs,\n message: 'These are the orgs whose security policy you can enforce.',\n }\n }\n\n if (key === 'test') {\n return {\n ok: false,\n message: 'Auto discover failed',\n cause: 'congrats, you found the test key',\n }\n }\n\n // Mostly to please TS, because we're not telling it `key` is keyof LocalConfig\n return {\n ok: false,\n message: 'Auto discover failed',\n cause: 'unreachable?',\n }\n}\n\nasync function getDefaultOrgFromToken(): Promise<\n string[] | string | undefined\n> {\n const orgsCResult = await fetchOrganization()\n if (!orgsCResult.ok) {\n return undefined\n }\n\n const { organizations } = orgsCResult.data\n if (organizations.length === 0) {\n return undefined\n }\n const slugs = getOrgSlugs(organizations)\n if (slugs.length === 1) {\n return slugs[0]\n }\n return slugs\n}\n\nasync function getEnforceableOrgsFromToken(): Promise<string[] | undefined> {\n const orgsCResult = await fetchOrganization()\n if (!orgsCResult.ok) {\n return undefined\n }\n\n const { organizations } = orgsCResult.data\n return organizations.length ? getOrgSlugs(organizations) : undefined\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\nimport { select } from '@socketsecurity/registry/lib/prompts'\n\nimport { isReadOnlyConfig, updateConfigValue } from '../../utils/config.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { LocalConfig } from '../../utils/config.mts'\n\nexport async function outputConfigAuto(\n key: keyof LocalConfig,\n result: CResult<unknown>,\n outputKind: OutputKind,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'markdown') {\n logger.log(`# Auto discover config value`)\n logger.log('')\n logger.log(\n `Attempted to automatically discover the value for config key: \"${key}\"`,\n )\n logger.log('')\n if (result.ok) {\n logger.log(`The discovered value is: \"${result.data}\"`)\n if (result.message) {\n logger.log('')\n logger.log(result.message)\n }\n }\n logger.log('')\n } else {\n if (result.message) {\n logger.log(result.message)\n logger.log('')\n }\n logger.log(`- ${key}: ${result.data}`)\n logger.log('')\n\n if (isReadOnlyConfig()) {\n logger.log(\n '(Unable to persist this value because the config is in read-only mode, meaning it was overridden through env or flag.)',\n )\n } else if (key === 'defaultOrg') {\n const proceed = await select<string>({\n message:\n 'Would you like to update the default org in local config to this value?',\n choices: (Array.isArray(result.data) ? result.data : [result.data])\n .map(slug => ({\n name: 'Yes [' + slug + ']',\n value: slug,\n description: `Use \"${slug}\" as the default organization`,\n }))\n .concat({\n name: 'No',\n value: '',\n description: 'Do not use any of these organizations',\n }),\n })\n if (proceed) {\n logger.log(`Setting defaultOrg to \"${proceed}\"...`)\n const updateResult = updateConfigValue('defaultOrg', proceed)\n if (updateResult.ok) {\n logger.log(\n `OK. Updated defaultOrg to \"${proceed}\".\\nYou should no longer need to add the org to commands that normally require it.`,\n )\n } else {\n logger.log(failMsgWithBadge(updateResult.message, updateResult.cause))\n }\n } else {\n logger.log('OK. No changes made.')\n }\n } else if (key === 'enforcedOrgs') {\n const proceed = await select<string>({\n message:\n 'Would you like to update the enforced orgs in local config to this value?',\n choices: (Array.isArray(result.data) ? result.data : [result.data])\n .map(slug => ({\n name: 'Yes [' + slug + ']',\n value: slug,\n description: `Enforce the security policy of \"${slug}\" on this machine`,\n }))\n .concat({\n name: 'No',\n value: '',\n description: 'Do not use any of these organizations',\n }),\n })\n if (proceed) {\n logger.log(`Setting enforcedOrgs key to \"${proceed}\"...`)\n const updateResult = updateConfigValue('defaultOrg', proceed)\n if (updateResult.ok) {\n logger.log(`OK. Updated enforcedOrgs to \"${proceed}\".`)\n } else {\n logger.log(failMsgWithBadge(updateResult.message, updateResult.cause))\n }\n } else {\n logger.log('OK. No changes made.')\n }\n }\n }\n}\n","import { discoverConfigValue } from './discover-config-value.mts'\nimport { outputConfigAuto } from './output-config-auto.mts'\n\nimport type { OutputKind } from '../../types.mts'\nimport type { LocalConfig } from '../../utils/config.mts'\n\nexport async function handleConfigAuto({\n key,\n outputKind,\n}: {\n key: keyof LocalConfig\n outputKind: OutputKind\n}) {\n const result = await discoverConfigValue(key)\n\n await outputConfigAuto(key, result, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleConfigAuto } from './handle-config-auto.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport {\n getSupportedConfigEntries,\n isSupportedConfigKey,\n} from '../../utils/config.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { LocalConfig } from '../../utils/config.mts'\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'auto'\n\nconst description =\n 'Automatically discover and set the correct value config item'\n\nconst hidden = false\n\nexport const cmdConfigAuto = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] KEY\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Attempt to automatically discover the correct value for a given config KEY.\n\n Examples\n $ ${command} defaultOrg\n\n Keys:\n${getSupportedConfigEntries()\n .map(([key, desc]) => ` - ${key} -- ${desc}`)\n .join('\\n')}\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const [key = ''] = cli.input\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: key !== 'test' && isSupportedConfigKey(key),\n message: 'Config key should be the first arg',\n fail: key ? 'invalid config key' : 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleConfigAuto({\n key: key as keyof LocalConfig,\n outputKind,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { isReadOnlyConfig } from '../../utils/config.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { LocalConfig } from '../../utils/config.mts'\n\nexport async function outputConfigGet(\n key: keyof LocalConfig,\n result: CResult<LocalConfig[keyof LocalConfig]>,\n outputKind: OutputKind,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const readOnly = isReadOnlyConfig()\n\n if (outputKind === 'markdown') {\n logger.log(`# Config Value`)\n logger.log('')\n logger.log(`Config key '${key}' has value '${result.data}`)\n if (readOnly) {\n logger.log('')\n logger.log(\n 'Note: the config is in read-only mode, meaning at least one key was temporarily\\n overridden from an env var or command flag.',\n )\n }\n } else {\n logger.log(`${key}: ${result.data}`)\n if (readOnly) {\n logger.log('')\n logger.log(\n 'Note: the config is in read-only mode, meaning at least one key was temporarily overridden from an env var or command flag.',\n )\n }\n }\n}\n","import { outputConfigGet } from './output-config-get.mts'\nimport { getConfigValue } from '../../utils/config.mts'\n\nimport type { OutputKind } from '../../types.mts'\nimport type { LocalConfig } from '../../utils/config.mts'\n\nexport async function handleConfigGet({\n key,\n outputKind,\n}: {\n key: keyof LocalConfig\n outputKind: OutputKind\n}) {\n const result = getConfigValue(key)\n\n await outputConfigGet(key, result, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleConfigGet } from './handle-config-get.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport {\n getSupportedConfigEntries,\n isSupportedConfigKey,\n} from '../../utils/config.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { LocalConfig } from '../../utils/config.mts'\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'get',\n description: 'Get the value of a local CLI config item',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] KEY\n\n Retrieve the value for given KEY at this time. If you have overridden the\n config then the value will come from that override.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n KEY is an enum. Valid keys:\n\n${getSupportedConfigEntries()\n .map(([key, desc]) => ` - ${key} -- ${desc}`)\n .join('\\n')}\n\n Examples\n $ ${command} defaultOrg\n `,\n}\n\nexport const cmdConfigGet = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const [key = ''] = cli.input\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: key === 'test' || isSupportedConfigKey(key),\n message: 'Config key should be the first arg',\n fail: key ? 'invalid config key' : 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleConfigGet({\n key: key as keyof LocalConfig,\n outputKind,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport {\n getConfigValue,\n getSupportedConfigKeys,\n isReadOnlyConfig,\n isSensitiveConfigKey,\n} from '../../utils/config.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function outputConfigList({\n full,\n outputKind,\n}: {\n full: boolean\n outputKind: OutputKind\n}) {\n const readOnly = isReadOnlyConfig()\n const supportedConfigKeys = getSupportedConfigKeys()\n if (outputKind === 'json') {\n let failed = false\n const obj: Record<string, unknown> = {}\n for (const key of supportedConfigKeys) {\n const result = getConfigValue(key)\n let value = result.data\n if (!result.ok) {\n value = `Failed to retrieve: ${result.message}`\n failed = true\n } else if (!full && isSensitiveConfigKey(key)) {\n value = '********'\n }\n if (full || value !== undefined) {\n obj[key as any] = value ?? '<none>'\n }\n }\n if (failed) {\n process.exitCode = 1\n }\n logger.log(\n serializeResultJson(\n failed\n ? {\n ok: false,\n message: 'At least one config key failed to be fetched...',\n data: JSON.stringify({\n full,\n config: obj,\n readOnly,\n }),\n }\n : {\n ok: true,\n data: {\n full,\n config: obj,\n readOnly,\n },\n },\n ),\n )\n } else {\n const maxWidth = supportedConfigKeys.reduce(\n (a, b) => Math.max(a, b.length),\n 0,\n )\n\n logger.log('# Local CLI Config')\n logger.log('')\n logger.log(`This is the local CLI config (full=${!!full}):`)\n logger.log('')\n for (const key of supportedConfigKeys) {\n const result = getConfigValue(key)\n if (!result.ok) {\n logger.log(`- ${key}: failed to read: ${result.message}`)\n } else {\n let value = result.data\n if (!full && isSensitiveConfigKey(key)) {\n value = '********'\n }\n if (full || value !== undefined) {\n logger.log(\n `- ${key}:${' '.repeat(Math.max(0, maxWidth - key.length + 3))} ${Array.isArray(value) ? value.join(', ') || '<none>' : (value ?? '<none>')}`,\n )\n }\n }\n }\n if (readOnly) {\n logger.log('')\n logger.log(\n 'Note: the config is in read-only mode, meaning at least one key was temporarily\\n overridden from an env var or command flag.',\n )\n }\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { outputConfigList } from './output-config-list.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'list',\n description: 'Show all local CLI config items and their values',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n full: {\n type: 'boolean',\n default: false,\n description: 'Show full tokens in plaintext (unsafe)',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n `,\n}\n\nexport const cmdConfigList = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { full, json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(outputKind, {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n })\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await outputConfigList({\n full: !!full,\n outputKind,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputConfigSet(\n result: CResult<undefined | string>,\n outputKind: OutputKind,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'markdown') {\n logger.log(`# Update config`)\n logger.log('')\n logger.log(result.message)\n if (result.data) {\n logger.log('')\n logger.log(result.data)\n }\n } else {\n logger.log(`OK`)\n logger.log(result.message)\n if (result.data) {\n logger.log('')\n logger.log(result.data)\n }\n }\n}\n","import { outputConfigSet } from './output-config-set.mts'\nimport { updateConfigValue } from '../../utils/config.mts'\n\nimport type { OutputKind } from '../../types.mts'\nimport type { LocalConfig } from '../../utils/config.mts'\n\nexport async function handleConfigSet({\n key,\n outputKind,\n value,\n}: {\n key: keyof LocalConfig\n outputKind: OutputKind\n value: string\n}) {\n const result = updateConfigValue(key, value)\n\n await outputConfigSet(result, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleConfigSet } from './handle-config-set.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport {\n getSupportedConfigEntries,\n isSupportedConfigKey,\n} from '../../utils/config.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { LocalConfig } from '../../utils/config.mts'\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'set'\n\nconst description = 'Update the value of a local CLI config item'\n\nconst hidden = false\n\nexport const cmdConfigSet = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <KEY> <VALUE>\n\n Options\n ${getFlagListOutput(config.flags)}\n\n This is a crude way of updating the local configuration for this CLI tool.\n\n Note that updating a value here is nothing more than updating a key/value\n store entry. No validation is happening. The server may reject your values\n in some cases. Use at your own risk.\n\n Note: use \\`socket config unset\\` to restore to defaults. Setting a key\n to \\`undefined\\` will not allow default values to be set on it.\n\n Keys:\n\n${getSupportedConfigEntries()\n .map(([key, desc]) => ` - ${key} -- ${desc}`)\n .join('\\n')}\n\n Examples\n $ ${command} apiProxy https://example.com\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const [key = '', ...rest] = cli.input\n\n const value = rest.join(' ')\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: key === 'test' || isSupportedConfigKey(key),\n message: 'Config key should be the first arg',\n fail: key ? 'invalid config key' : 'missing',\n },\n {\n test: !!value, // This is a string, empty string is not ok\n message:\n 'Key value should be the remaining args (use `unset` to unset a value)',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleConfigSet({\n key: key as keyof LocalConfig,\n outputKind,\n value,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputConfigUnset(\n updateResult: CResult<undefined | string>,\n outputKind: OutputKind,\n) {\n if (!updateResult.ok) {\n process.exitCode = updateResult.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(updateResult))\n return\n }\n if (!updateResult.ok) {\n logger.fail(failMsgWithBadge(updateResult.message, updateResult.cause))\n return\n }\n\n if (outputKind === 'markdown') {\n logger.log(`# Update config`)\n logger.log('')\n logger.log(updateResult.message)\n if (updateResult.data) {\n logger.log('')\n logger.log(updateResult.data)\n }\n } else {\n logger.log(`OK`)\n logger.log(updateResult.message)\n if (updateResult.data) {\n logger.log('')\n logger.log(updateResult.data)\n }\n }\n}\n","import { outputConfigUnset } from './output-config-unset.mts'\nimport { updateConfigValue } from '../../utils/config.mts'\n\nimport type { OutputKind } from '../../types.mts'\nimport type { LocalConfig } from '../../utils/config.mts'\n\nexport async function handleConfigUnset({\n key,\n outputKind,\n}: {\n key: keyof LocalConfig\n outputKind: OutputKind\n}) {\n const updateResult = updateConfigValue(key, undefined)\n\n await outputConfigUnset(updateResult, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleConfigUnset } from './handle-config-unset.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport {\n getSupportedConfigEntries,\n isSupportedConfigKey,\n} from '../../utils/config.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { LocalConfig } from '../../utils/config.mts'\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'unset'\n\nconst description = 'Clear the value of a local CLI config item'\n\nconst hidden = false\n\nexport const cmdConfigUnset = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <KEY> <VALUE>\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Removes a value from a config key, allowing the default value to be used\n for it instead.\n\n Keys:\n\n${getSupportedConfigEntries()\n .map(([key, desc]) => ` - ${key} -- ${desc}`)\n .join('\\n')}\n\n Examples\n $ ${command} defaultOrg\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const [key = ''] = cli.input\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: key === 'test' || isSupportedConfigKey(key),\n message: 'Config key should be the first arg',\n fail: key ? 'invalid config key' : 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleConfigUnset({\n key: key as keyof LocalConfig,\n outputKind,\n })\n}\n","import { cmdConfigAuto } from './cmd-config-auto.mts'\nimport { cmdConfigGet } from './cmd-config-get.mts'\nimport { cmdConfigList } from './cmd-config-list.mts'\nimport { cmdConfigSet } from './cmd-config-set.mts'\nimport { cmdConfigUnset } from './cmd-config-unset.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Manage Socket CLI configuration'\n\nexport const cmdConfig: CliSubcommand = {\n description,\n hidden: false,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n auto: cmdConfigAuto,\n get: cmdConfigGet,\n list: cmdConfigList,\n set: cmdConfigSet,\n unset: cmdConfigUnset,\n },\n {\n argv,\n description,\n importMeta,\n name: `${parentName} config`,\n },\n )\n },\n}\n","import { joinAnd } from '@socketsecurity/registry/lib/arrays'\n\nimport constants from '../../constants.mts'\n\nimport type { GhsaDetails } from '../../utils/github.mts'\n\nconst GITHUB_ADVISORIES_URL = 'https://github.com/advisories'\n\nexport type SocketFixBranchParser = (\n branch: string,\n) => SocketFixBranchParseResult | null\n\nexport type SocketFixBranchParseResult = {\n ghsaId: string\n}\n\nexport function createSocketFixBranchParser(\n ghsaId?: string | undefined,\n): SocketFixBranchParser {\n const pattern = getSocketFixBranchPattern(ghsaId)\n return function parse(branch: string): SocketFixBranchParseResult | null {\n const match = pattern.exec(branch) as [string, string] | null\n if (!match) {\n return null\n }\n const { 1: ghsaId } = match\n return { ghsaId } as SocketFixBranchParseResult\n }\n}\n\nexport const genericSocketFixBranchParser = createSocketFixBranchParser()\n\nexport function getSocketFixBranchName(ghsaId: string): string {\n return `socket/fix/${ghsaId}`\n}\n\nexport function getSocketFixBranchPattern(ghsaId?: string | undefined): RegExp {\n return new RegExp(`^socket/fix/(${ghsaId ?? '.+'})$`)\n}\n\nexport function getSocketFixCommitMessage(\n ghsaId: string,\n details?: GhsaDetails | undefined,\n): string {\n const summary = details?.summary\n return `fix: ${ghsaId}${summary ? ` - ${summary}` : ''}`\n}\n\nexport function getSocketFixPullRequestBody(\n ghsaIds: string[],\n ghsaDetails?: Map<string, GhsaDetails>,\n): string {\n const vulnCount = ghsaIds.length\n if (vulnCount === 1) {\n const ghsaId = ghsaIds[0]!\n const details = ghsaDetails?.get(ghsaId)\n const body = `[Socket](${constants.SOCKET_WEBSITE_URL}) fix for [${ghsaId}](${GITHUB_ADVISORIES_URL}/${ghsaId}).`\n if (!details) {\n return body\n }\n const packages = details.vulnerabilities.nodes.map(\n v => `${v.package.name} (${v.package.ecosystem})`,\n )\n return [\n body,\n '',\n '',\n `**Vulnerability Summary:** ${details.summary}`,\n '',\n `**Severity:** ${details.severity}`,\n '',\n `**Affected Packages:** ${joinAnd(packages)}`,\n ].join('\\n')\n }\n return [\n `[Socket](${constants.SOCKET_WEBSITE_URL}) fixes for ${vulnCount} GHSAs.`,\n '',\n '**Fixed Vulnerabilities:**',\n ...ghsaIds.map(id => {\n const details = ghsaDetails?.get(id)\n const item = `- [${id}](${GITHUB_ADVISORIES_URL}/${id})`\n if (details) {\n const packages = details.vulnerabilities.nodes.map(\n v => `${v.package.name}`,\n )\n return `${item} - ${details.summary} (${joinAnd(packages)})`\n }\n return item\n }),\n ].join('\\n')\n}\n\nexport function getSocketFixPullRequestTitle(ghsaIds: string[]): string {\n const vulnCount = ghsaIds.length\n return vulnCount === 1\n ? `Fix for ${ghsaIds[0]}`\n : `Fixes for ${vulnCount} GHSAs`\n}\n","import { RequestError } from '@octokit/request-error'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport {\n getSocketFixBranchPattern,\n getSocketFixPullRequestBody,\n getSocketFixPullRequestTitle,\n} from './git.mts'\nimport {\n type GhsaDetails,\n type Pr,\n cacheFetch,\n getOctokit,\n getOctokitGraphql,\n writeCache,\n} from '../../utils/github.mts'\n\nimport type { OctokitResponse } from '@octokit/types'\nimport type { JsonContent } from '@socketsecurity/registry/lib/fs'\n\nexport type OpenSocketFixPrOptions = {\n baseBranch?: string | undefined\n cwd?: string | undefined\n ghsaDetails?: Map<string, GhsaDetails> | undefined\n}\n\nexport async function openSocketFixPr(\n owner: string,\n repo: string,\n branch: string,\n ghsaIds: string[],\n options?: OpenSocketFixPrOptions | undefined,\n): Promise<OctokitResponse<Pr> | null> {\n const { baseBranch = 'main', ghsaDetails } = {\n __proto__: null,\n ...options,\n } as OpenSocketFixPrOptions\n\n const octokit = getOctokit()\n\n try {\n const octokitPullsCreateParams = {\n owner,\n repo,\n title: getSocketFixPullRequestTitle(ghsaIds),\n head: branch,\n base: baseBranch,\n body: getSocketFixPullRequestBody(ghsaIds, ghsaDetails),\n }\n debugDir('inspect', { octokitPullsCreateParams })\n return await octokit.pulls.create(octokitPullsCreateParams)\n } catch (e) {\n let message = `Failed to open pull request`\n const errors =\n e instanceof RequestError\n ? (e.response?.data as any)?.['errors']\n : undefined\n if (Array.isArray(errors) && errors.length) {\n const details = errors\n .map(\n d =>\n `- ${d.message?.trim() ?? `${d.resource}.${d.field} (${d.code})`}`,\n )\n .join('\\n')\n message += `:\\n${details}`\n }\n debugFn('error', message)\n }\n return null\n}\n\nexport type GQL_MERGE_STATE_STATUS =\n | 'BEHIND'\n | 'BLOCKED'\n | 'CLEAN'\n | 'DIRTY'\n | 'DRAFT'\n | 'HAS_HOOKS'\n | 'UNKNOWN'\n | 'UNSTABLE'\n\nexport type GQL_PR_STATE = 'OPEN' | 'CLOSED' | 'MERGED'\n\nexport type PrMatch = {\n author: string\n baseRefName: string\n headRefName: string\n mergeStateStatus: GQL_MERGE_STATE_STATUS\n number: number\n state: GQL_PR_STATE\n title: string\n}\n\nexport async function cleanupPrs(\n owner: string,\n repo: string,\n ghsaId: string,\n): Promise<PrMatch[]> {\n const contextualMatches = await getSocketPrsWithContext(owner, repo, {\n ghsaId,\n })\n\n if (!contextualMatches.length) {\n return []\n }\n\n const cachesToSave = new Map<string, JsonContent>()\n const octokit = getOctokit()\n\n const settledMatches = await Promise.allSettled(\n contextualMatches.map(async ({ context, match }) => {\n // Update stale PRs.\n // https://docs.github.com/en/graphql/reference/enums#mergestatestatus\n if (match.mergeStateStatus === 'BEHIND') {\n const { number: prNum } = match\n const prRef = `PR #${prNum}`\n try {\n await octokit.repos.merge({\n owner,\n repo,\n base: match.headRefName,\n head: match.baseRefName,\n })\n debugFn('notice', `pr: updating stale ${prRef}`)\n // Update entry entry.\n if (context.apiType === 'graphql') {\n context.entry.mergeStateStatus = 'CLEAN'\n } else if (context.apiType === 'rest') {\n context.entry.mergeable_state = 'clean'\n }\n // Mark cache to be saved.\n cachesToSave.set(context.cacheKey, context.data)\n } catch (e) {\n const message = (e as Error)?.message || 'Unknown error'\n debugFn('error', `pr: failed to update ${prRef} - ${message}`)\n }\n }\n return match\n }),\n )\n\n if (cachesToSave.size) {\n await Promise.allSettled(\n Array.from(cachesToSave).map(({ 0: key, 1: data }) =>\n writeCache(key, data),\n ),\n )\n }\n\n const fulfilledMatches = settledMatches.filter(\n r => r.status === 'fulfilled' && r.value,\n ) as unknown as Array<PromiseFulfilledResult<ContextualPrMatch>>\n\n return fulfilledMatches.map(r => r.value.match)\n}\n\nexport type PrAutoMergeState = {\n enabled: boolean\n details?: string[]\n}\n\nexport type SocketPrsOptions = {\n author?: string | undefined\n ghsaId?: string | undefined\n states?: 'all' | GQL_PR_STATE | GQL_PR_STATE[]\n}\n\nexport async function getSocketPrs(\n owner: string,\n repo: string,\n options?: SocketPrsOptions | undefined,\n): Promise<PrMatch[]> {\n return (await getSocketPrsWithContext(owner, repo, options)).map(d => d.match)\n}\n\ntype ContextualPrMatch = {\n context: {\n apiType: 'graphql' | 'rest'\n cacheKey: string\n data: any\n entry: any\n index: number\n parent: any[]\n }\n match: PrMatch\n}\n\nasync function getSocketPrsWithContext(\n owner: string,\n repo: string,\n options?: SocketPrsOptions | undefined,\n): Promise<ContextualPrMatch[]> {\n const {\n author,\n ghsaId,\n states: statesValue = 'all',\n } = {\n __proto__: null,\n ...options,\n } as SocketPrsOptions\n const branchPattern = getSocketFixBranchPattern(ghsaId)\n const checkAuthor = isNonEmptyString(author)\n const octokit = getOctokit()\n const octokitGraphql = getOctokitGraphql()\n const contextualMatches: ContextualPrMatch[] = []\n const states = (\n typeof statesValue === 'string'\n ? statesValue.toLowerCase() === 'all'\n ? ['OPEN', 'CLOSED', 'MERGED']\n : [statesValue]\n : statesValue\n ).map(s => s.toUpperCase())\n try {\n // Optimistically fetch only the first 50 open PRs using GraphQL to minimize\n // API quota usage. Fallback to REST if no matching PRs are found.\n const gqlCacheKey = `${repo}-pr-graphql-snapshot`\n const gqlResp = await cacheFetch(gqlCacheKey, () =>\n octokitGraphql(\n `\n query($owner: String!, $repo: String!, $states: [PullRequestState!]) {\n repository(owner: $owner, name: $repo) {\n pullRequests(first: 50, states: $states, orderBy: {field: CREATED_AT, direction: DESC}) {\n nodes {\n author {\n login\n }\n baseRefName\n headRefName\n mergeStateStatus\n number\n state\n title\n }\n }\n }\n }\n `,\n {\n owner,\n repo,\n states,\n },\n ),\n )\n\n type GqlPrNode = {\n author?: {\n login: string\n }\n baseRefName: string\n headRefName: string\n mergeStateStatus: GQL_MERGE_STATE_STATUS\n number: number\n state: GQL_PR_STATE\n title: string\n }\n\n const nodes: GqlPrNode[] =\n (gqlResp as any)?.repository?.pullRequests?.nodes ?? []\n for (let i = 0, { length } = nodes; i < length; i += 1) {\n const node = nodes[i]!\n const login = node.author?.login\n const matchesAuthor = checkAuthor ? login === author : true\n const matchesBranch = branchPattern.test(node.headRefName)\n if (matchesAuthor && matchesBranch) {\n contextualMatches.push({\n context: {\n apiType: 'graphql',\n cacheKey: gqlCacheKey,\n data: gqlResp,\n entry: node,\n index: i,\n parent: nodes,\n },\n match: {\n ...node,\n author: login ?? '<unknown>',\n },\n })\n }\n }\n } catch {}\n\n if (contextualMatches.length) {\n return contextualMatches\n }\n\n // Fallback to REST if GraphQL found no matching PRs.\n let allPrs: Pr[] | undefined\n const cacheKey = `${repo}-pull-requests`\n try {\n allPrs = await cacheFetch(\n cacheKey,\n async () =>\n (await octokit.paginate(octokit.pulls.list, {\n owner,\n repo,\n state: 'all',\n per_page: 100,\n })) as Pr[],\n )\n } catch {}\n\n if (!allPrs) {\n return contextualMatches\n }\n\n for (let i = 0, { length } = allPrs; i < length; i += 1) {\n const pr = allPrs[i]!\n const login = pr.user?.login\n const headRefName = pr.head.ref\n const matchesAuthor = checkAuthor ? login === author : true\n const matchesBranch = branchPattern.test(headRefName)\n if (matchesAuthor && matchesBranch) {\n // Upper cased mergeable_state is equivalent to mergeStateStatus.\n // https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request\n const mergeStateStatus = (pr.mergeable_state?.toUpperCase?.() ??\n 'UNKNOWN') as GQL_MERGE_STATE_STATUS\n // The REST API does not have a distinct merged state for pull requests.\n // Instead, a merged pull request is represented as a closed pull request\n // with a non-null merged_at timestamp.\n const state = (\n pr.merged_at ? 'MERGED' : pr.state.toUpperCase()\n ) as GQL_PR_STATE\n contextualMatches.push({\n context: {\n apiType: 'rest',\n cacheKey,\n data: allPrs,\n entry: pr,\n index: i,\n parent: allPrs,\n },\n match: {\n author: login ?? '<unknown>',\n baseRefName: pr.base.ref,\n headRefName,\n mergeStateStatus,\n number: pr.number,\n state,\n title: pr.title,\n },\n })\n }\n }\n return contextualMatches\n}\n","import { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { debugFn, isDebug } from '@socketsecurity/registry/lib/debug'\n\nimport { getSocketPrs } from './pull-request.mts'\nimport constants from '../../constants.mts'\nimport { getBaseBranch, getRepoInfo } from '../../utils/git.mts'\n\nimport type { PrMatch } from './pull-request.mts'\nimport type { RepoInfo } from '../../utils/git.mts'\n\nfunction ciRepoInfo(): RepoInfo | null {\n const { GITHUB_REPOSITORY } = constants.ENV\n if (!GITHUB_REPOSITORY) {\n debugFn('notice', 'miss: GITHUB_REPOSITORY env var')\n }\n const ownerSlashRepo = GITHUB_REPOSITORY\n const slashIndex = ownerSlashRepo.indexOf('/')\n if (slashIndex === -1) {\n return null\n }\n return {\n owner: ownerSlashRepo.slice(0, slashIndex),\n repo: ownerSlashRepo.slice(slashIndex + 1),\n }\n}\n\nexport interface FixEnv {\n baseBranch: string\n gitEmail: string\n githubToken: string\n gitUser: string\n isCi: boolean\n prs: PrMatch[]\n repoInfo: RepoInfo | null\n}\n\nexport async function getFixEnv(): Promise<FixEnv> {\n const baseBranch = await getBaseBranch()\n const gitEmail = constants.ENV.SOCKET_CLI_GIT_USER_EMAIL\n const gitUser = constants.ENV.SOCKET_CLI_GIT_USER_NAME\n const githubToken = constants.ENV.SOCKET_CLI_GITHUB_TOKEN\n const isCi = !!(constants.ENV.CI && gitEmail && gitUser && githubToken)\n\n if (\n // If isCi is false,\n !isCi &&\n // but some CI checks are passing,\n (constants.ENV.CI || gitEmail || gitUser || githubToken) &&\n // then log about it when in debug mode.\n isDebug('notice')\n ) {\n const envVars = [\n ...(constants.ENV.CI ? [] : ['process.env.CI']),\n ...(gitEmail ? [] : ['process.env.SOCKET_CLI_GIT_USER_EMAIL']),\n ...(gitUser ? [] : ['process.env.SOCKET_CLI_GIT_USER_NAME']),\n ...(githubToken ? [] : ['process.env.GITHUB_TOKEN']),\n ]\n debugFn(\n 'notice',\n `miss: fixEnv.isCi is false, expected ${joinAnd(envVars)} to be set`,\n )\n }\n\n let repoInfo: RepoInfo | null = null\n if (isCi) {\n repoInfo = ciRepoInfo()\n }\n if (!repoInfo) {\n if (isCi) {\n debugFn('notice', 'falling back to `git remote get-url origin`')\n }\n repoInfo = await getRepoInfo()\n }\n\n const prs =\n isCi && repoInfo\n ? await getSocketPrs(repoInfo.owner, repoInfo.repo, {\n author: gitUser,\n states: 'all',\n })\n : []\n\n return {\n baseBranch,\n gitEmail,\n githubToken,\n gitUser,\n isCi,\n prs,\n repoInfo,\n }\n}\n","import path from 'node:path'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getFixEnv } from './env-helpers.mts'\nimport { getSocketFixBranchName, getSocketFixCommitMessage } from './git.mts'\nimport { openSocketFixPr } from './pull-request.mts'\nimport { handleApiCall } from '../../utils/api.mts'\nimport { cmdFlagValueToArray } from '../../utils/cmd.mts'\nimport { spawnCoana } from '../../utils/coana.mts'\nimport {\n gitCheckoutBranch,\n gitCommit,\n gitCreateBranch,\n gitDeleteBranch,\n gitPushBranch,\n gitRemoteBranchExists,\n gitResetAndClean,\n gitUnstagedModifiedFiles,\n} from '../../utils/git.mts'\nimport {\n enablePrAutoMerge,\n fetchGhsaDetails,\n setGitRemoteGithubRepoUrl,\n} from '../../utils/github.mts'\nimport { getPackageFilesForScan } from '../../utils/path-resolve.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\nimport { fetchSupportedScanFileNames } from '../scan/fetch-supported-scan-file-names.mts'\n\nimport type { FixConfig } from './types.mts'\nimport type { CResult } from '../../types.mts'\n\nexport async function coanaFix(\n fixConfig: FixConfig,\n): Promise<CResult<{ fixed: boolean }>> {\n const { autoMerge, cwd, ghsas, limit, orgSlug, spinner } = fixConfig\n\n const fixEnv = await getFixEnv()\n debugDir('inspect', { fixEnv })\n\n spinner?.start()\n\n const sockSdkCResult = await setupSdk()\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n\n const sockSdk = sockSdkCResult.data\n\n const supportedFilesCResult = await fetchSupportedScanFileNames({ spinner })\n if (!supportedFilesCResult.ok) {\n return supportedFilesCResult\n }\n\n const supportedFiles = supportedFilesCResult.data\n const scanFilepaths = await getPackageFilesForScan(['.'], supportedFiles, {\n cwd,\n })\n const uploadCResult = await handleApiCall(\n sockSdk.uploadManifestFiles(orgSlug, scanFilepaths),\n {\n desc: 'upload manifests',\n spinner,\n },\n )\n\n if (!uploadCResult.ok) {\n return uploadCResult\n }\n\n const tarHash: string = (uploadCResult as any).data.tarHash\n if (!tarHash) {\n spinner?.stop()\n return {\n ok: false,\n message:\n 'No tar hash returned from Socket API upload-manifest-files endpoint',\n data: uploadCResult.data,\n }\n }\n\n const isAll =\n !ghsas.length ||\n (ghsas.length === 1 && (ghsas[0] === 'all' || ghsas[0] === 'auto'))\n\n const shouldOpenPrs = fixEnv.isCi && fixEnv.repoInfo\n\n if (!shouldOpenPrs) {\n const ids = isAll ? ['all'] : ghsas.slice(0, limit)\n if (!ids.length) {\n spinner?.stop()\n return { ok: true, data: { fixed: false } }\n }\n\n const fixCResult = await spawnCoana(\n [\n 'compute-fixes-and-upgrade-purls',\n cwd,\n '--manifests-tar-hash',\n tarHash,\n '--apply-fixes-to',\n ...(isAll ? ['all'] : ghsas),\n ...(fixConfig.rangeStyle\n ? ['--range-style', fixConfig.rangeStyle]\n : []),\n ...fixConfig.unknownFlags,\n ],\n fixConfig.orgSlug,\n { cwd, spinner, stdio: 'inherit' },\n )\n\n spinner?.stop()\n\n return fixCResult.ok ? { ok: true, data: { fixed: true } } : fixCResult\n }\n\n let ids: string[] | undefined\n if (isAll) {\n const foundCResult = await spawnCoana(\n [\n 'compute-fixes-and-upgrade-purls',\n cwd,\n '--manifests-tar-hash',\n tarHash,\n ...(fixConfig.rangeStyle\n ? ['--range-style', fixConfig.rangeStyle]\n : []),\n ...fixConfig.unknownFlags,\n ],\n fixConfig.orgSlug,\n { cwd, spinner },\n )\n if (foundCResult.ok) {\n const foundIds = cmdFlagValueToArray(\n /(?<=Vulnerabilities found:).*/.exec(foundCResult.data),\n )\n ids = foundIds.slice(0, limit)\n }\n } else {\n ids = ghsas.slice(0, limit)\n }\n\n if (!ids?.length) {\n debugFn('notice', 'miss: no GHSA IDs to process')\n }\n\n if (!fixEnv.repoInfo) {\n debugFn('notice', 'miss: no repo info detected')\n }\n\n if (!ids?.length || !fixEnv.repoInfo) {\n spinner?.stop()\n return { ok: true, data: { fixed: false } }\n }\n\n debugFn('notice', `fetch: ${ids.length} GHSA details for ${joinAnd(ids)}`)\n\n const ghsaDetails = await fetchGhsaDetails(ids)\n const scanBaseNames = new Set(scanFilepaths.map(p => path.basename(p)))\n\n debugFn('notice', `found: ${ghsaDetails.size} GHSA details`)\n\n let count = 0\n let overallFixed = false\n\n // Process each GHSA ID individually, similar to npm-fix/pnpm-fix.\n ghsaLoop: for (let i = 0, { length } = ids; i < length; i += 1) {\n const ghsaId = ids[i]!\n debugFn('notice', `check: ${ghsaId}`)\n\n // Apply fix for single GHSA ID.\n // eslint-disable-next-line no-await-in-loop\n const fixCResult = await spawnCoana(\n [\n 'compute-fixes-and-upgrade-purls',\n cwd,\n '--manifests-tar-hash',\n tarHash,\n '--apply-fixes-to',\n ghsaId,\n ...(fixConfig.rangeStyle\n ? ['--range-style', fixConfig.rangeStyle]\n : []),\n ...fixConfig.unknownFlags,\n ],\n fixConfig.orgSlug,\n { cwd, spinner, stdio: 'inherit' },\n )\n\n if (!fixCResult.ok) {\n logger.error(\n `Update failed for ${ghsaId}: ${fixCResult.message || 'Unknown error'}`,\n )\n continue ghsaLoop\n }\n\n // Check for modified files after applying the fix.\n // eslint-disable-next-line no-await-in-loop\n const unstagedCResult = await gitUnstagedModifiedFiles(cwd)\n const modifiedFiles = unstagedCResult.ok\n ? unstagedCResult.data.filter(relPath =>\n scanBaseNames.has(path.basename(relPath)),\n )\n : []\n\n if (!modifiedFiles.length) {\n debugFn('notice', `skip: no changes for ${ghsaId}`)\n continue ghsaLoop\n }\n\n overallFixed = true\n\n const branch = getSocketFixBranchName(ghsaId)\n\n try {\n // Check if branch already exists.\n // eslint-disable-next-line no-await-in-loop\n if (await gitRemoteBranchExists(branch, cwd)) {\n debugFn('notice', `skip: remote branch \"${branch}\" exists`)\n continue ghsaLoop\n }\n\n debugFn('notice', `pr: creating for ${ghsaId}`)\n\n const details = ghsaDetails.get(ghsaId)\n debugFn(\n 'notice',\n `ghsa: ${ghsaId} details ${details ? 'found' : 'missing'}`,\n )\n\n const pushed =\n // eslint-disable-next-line no-await-in-loop\n (await gitCreateBranch(branch, cwd)) &&\n // eslint-disable-next-line no-await-in-loop\n (await gitCheckoutBranch(branch, cwd)) &&\n // eslint-disable-next-line no-await-in-loop\n (await gitCommit(\n getSocketFixCommitMessage(ghsaId, details),\n modifiedFiles,\n {\n cwd,\n email: fixEnv.gitEmail,\n user: fixEnv.gitUser,\n },\n )) &&\n // eslint-disable-next-line no-await-in-loop\n (await gitPushBranch(branch, cwd))\n\n if (!pushed) {\n logger.warn(`Push failed for ${ghsaId}, skipping PR creation.`)\n // eslint-disable-next-line no-await-in-loop\n await gitResetAndClean(fixEnv.baseBranch, cwd)\n // eslint-disable-next-line no-await-in-loop\n await gitCheckoutBranch(fixEnv.baseBranch, cwd)\n // eslint-disable-next-line no-await-in-loop\n await gitDeleteBranch(branch, cwd)\n continue ghsaLoop\n }\n\n // Set up git remote.\n // eslint-disable-next-line no-await-in-loop\n await setGitRemoteGithubRepoUrl(\n fixEnv.repoInfo.owner,\n fixEnv.repoInfo.repo,\n fixEnv.githubToken!,\n cwd,\n )\n\n // eslint-disable-next-line no-await-in-loop\n const prResponse = await openSocketFixPr(\n fixEnv.repoInfo.owner,\n fixEnv.repoInfo.repo,\n branch,\n // Single GHSA ID.\n [ghsaId],\n {\n baseBranch: fixEnv.baseBranch,\n cwd,\n ghsaDetails,\n },\n )\n\n if (prResponse) {\n const { data } = prResponse\n const prRef = `PR #${data.number}`\n\n logger.success(`Opened ${prRef} for ${ghsaId}.`)\n\n if (autoMerge) {\n logger.indent()\n spinner?.indent()\n // eslint-disable-next-line no-await-in-loop\n const { details, enabled } = await enablePrAutoMerge(data)\n if (enabled) {\n logger.info(`Auto-merge enabled for ${prRef}.`)\n } else {\n const message = `Failed to enable auto-merge for ${prRef}${\n details ? `:\\n${details.map(d => ` - ${d}`).join('\\n')}` : '.'\n }`\n logger.error(message)\n }\n logger.dedent()\n spinner?.dedent()\n }\n }\n\n // Reset back to base branch for next iteration.\n // eslint-disable-next-line no-await-in-loop\n await gitResetAndClean(branch, cwd)\n // eslint-disable-next-line no-await-in-loop\n await gitCheckoutBranch(fixEnv.baseBranch, cwd)\n } catch (e) {\n logger.warn(\n `Unexpected condition: Push failed for ${ghsaId}, skipping PR creation.`,\n )\n debugDir('inspect', { error: e })\n // eslint-disable-next-line no-await-in-loop\n await gitResetAndClean(fixEnv.baseBranch, cwd)\n // eslint-disable-next-line no-await-in-loop\n await gitCheckoutBranch(fixEnv.baseBranch, cwd)\n }\n\n count += 1\n debugFn(\n 'notice',\n `increment: count ${count}/${Math.min(limit, ids.length)}`,\n )\n if (count >= limit) {\n break ghsaLoop\n }\n }\n\n spinner?.stop()\n\n return {\n ok: true,\n data: { fixed: overallFixed },\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputFixResult(\n result: CResult<unknown>,\n outputKind: OutputKind,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log('')\n logger.success('Finished!')\n}\n","import { coanaFix } from './coana-fix.mts'\nimport { outputFixResult } from './output-fix-result.mts'\n\nimport type { FixConfig } from './types.mts'\nimport type { OutputKind } from '../../types.mts'\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\n\nexport type HandleFixConfig = Remap<\n FixConfig & {\n ghsas: string[]\n orgSlug: string\n outputKind: OutputKind\n unknownFlags: string[]\n }\n>\n\nexport async function handleFix({\n autoMerge,\n cwd,\n ghsas,\n limit,\n minSatisfying,\n orgSlug,\n outputKind,\n prCheck,\n purls,\n rangeStyle,\n spinner,\n test,\n testScript,\n unknownFlags,\n}: HandleFixConfig) {\n await outputFixResult(\n await coanaFix({\n autoMerge,\n cwd,\n ghsas,\n limit,\n minSatisfying,\n orgSlug,\n prCheck,\n purls,\n rangeStyle,\n spinner,\n test,\n testScript,\n unknownFlags,\n }),\n outputKind,\n )\n}\n","import path from 'node:path'\n\nimport { PackageURL } from 'packageurl-js'\nimport terminalLink from 'terminal-link'\n\nimport { arrayUnique, joinOr } from '@socketsecurity/registry/lib/arrays'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleFix } from './handle-fix.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { cmdFlagValueToArray } from '../../utils/cmd.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { RangeStyles } from '../../utils/semver.mts'\nimport { getDefaultOrgSlug } from '../ci/fetch-default-org-slug.mts'\n\nimport type { MeowFlag, MeowFlags } from '../../flags.mts'\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\nimport type { RangeStyle } from '../../utils/semver.mts'\n\nexport const CMD_NAME = 'fix'\n\nconst DEFAULT_LIMIT = 10\n\nconst description = 'Update dependencies with \"fixable\" Socket alerts'\n\nconst hidden = false\n\nexport const cmdFix = {\n description,\n hidden,\n run,\n}\n\nconst generalFlags: MeowFlags = {\n autoMerge: {\n type: 'boolean',\n default: false,\n description: `Enable auto-merge for pull requests that Socket opens.\\nSee ${terminalLink(\n 'GitHub documentation',\n 'https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-auto-merge-for-pull-requests-in-your-repository',\n )} for managing auto-merge for pull requests in your repository.`,\n },\n id: {\n type: 'string',\n default: [],\n description: `Provide a list of ${terminalLink(\n 'GHSA IDs',\n 'https://docs.github.com/en/code-security/security-advisories/working-with-global-security-advisories-from-the-github-advisory-database/about-the-github-advisory-database#about-ghsa-ids',\n )} to compute fixes for, as either a comma separated value or as multiple flags`,\n isMultiple: true,\n },\n limit: {\n type: 'number',\n default: DEFAULT_LIMIT,\n description: `The number of fixes to attempt at a time (default ${DEFAULT_LIMIT})`,\n },\n rangeStyle: {\n type: 'string',\n default: 'preserve',\n description: `\nDefine how dependency version ranges are updated in package.json (default 'preserve').\nAvailable styles:\n * caret - Use ^ range for compatible updates (e.g. ^1.2.3)\n * gt - Use > to allow any newer version (e.g. >1.2.3)\n * gte - Use >= to allow any newer version (e.g. >=1.2.3)\n * lt - Use < to allow only lower versions (e.g. <1.2.3)\n * lte - Use <= to allow only lower versions (e.g. <=1.2.3)\n * pin - Use the exact version (e.g. 1.2.3)\n * preserve - Retain the existing version range style as-is\n * tilde - Use ~ range for patch/minor updates (e.g. ~1.2.3)\n `.trim(),\n },\n}\n\nconst hiddenFlags: MeowFlags = {\n autopilot: {\n type: 'boolean',\n default: false,\n description: `Shorthand for --auto-merge --test`,\n hidden: true,\n },\n ghsa: {\n ...generalFlags['id'],\n hidden: true,\n } as MeowFlag,\n maxSatisfying: {\n type: 'boolean',\n default: true,\n description: 'Use the maximum satisfying version for dependency updates',\n hidden: true,\n },\n minSatisfying: {\n type: 'boolean',\n default: false,\n description:\n 'Constrain dependency updates to the minimum satisfying version',\n hidden: true,\n },\n prCheck: {\n type: 'boolean',\n default: true,\n description: 'Check for an existing PR before attempting a fix',\n hidden: true,\n },\n purl: {\n type: 'string',\n default: [],\n description: `Provide a list of ${terminalLink(\n 'PURLs',\n 'https://github.com/package-url/purl-spec?tab=readme-ov-file#purl',\n )} to compute fixes for, as either a comma separated value or as\\nmultiple flags`,\n isMultiple: true,\n shortFlag: 'p',\n hidden: true,\n },\n test: {\n type: 'boolean',\n default: false,\n description: 'Verify the fix by running unit tests',\n hidden: true,\n },\n testScript: {\n type: 'string',\n default: 'test',\n description: \"The test script to run for fix attempts (default 'test')\",\n hidden: true,\n },\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n ...generalFlags,\n ...hiddenFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} ./proj/tree --auto-merge\n `,\n }\n\n const cli = meowOrExit({\n allowUnknownFlags: false,\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n let rangeStyle = cli.flags['rangeStyle'] as RangeStyle\n if (!rangeStyle) {\n rangeStyle = 'preserve'\n }\n\n const rawPurls = cmdFlagValueToArray(cli.flags['purl'])\n const purls = []\n for (const purl of rawPurls) {\n let version\n try {\n version = PackageURL.fromString(purl)?.version\n } catch {}\n if (version) {\n purls.push(purl)\n } else {\n logger.warn(`--purl ${purl} is missing a version and will be ignored.`)\n }\n }\n if (rawPurls.length !== purls.length && !purls.length) {\n process.exitCode = 1\n logger.fail('No valid --purl values provided.')\n return\n }\n\n const outputKind = getOutputKind(cli.flags['json'], cli.flags['markdown'])\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: RangeStyles.includes(rangeStyle),\n message: `Expecting range style of ${joinOr(RangeStyles)}`,\n fail: 'invalid',\n },\n {\n nook: true,\n test: !cli.flags['json'] || !cli.flags['markdown'],\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_NOT_SAVING)\n return\n }\n\n const orgSlugCResult = await getDefaultOrgSlug()\n if (!orgSlugCResult.ok) {\n process.exitCode = orgSlugCResult.code ?? 1\n logger.fail(\n 'Unable to resolve a Socket account organization.\\nEnsure a Socket API token is specified for the organization using the SOCKET_CLI_API_TOKEN environment variable.',\n )\n return\n }\n\n const orgSlug = orgSlugCResult.data\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n let autoMerge = Boolean(cli.flags['autoMerge'])\n let test = Boolean(cli.flags['test'])\n if (cli.flags['autopilot']) {\n autoMerge = true\n test = true\n }\n\n const { spinner } = constants\n // We patched in this feature with `npx custompatch meow` at\n // socket-cli/patches/meow#13.2.0.patch.\n const unknownFlags = cli.unknownFlags ?? []\n const ghsas = arrayUnique([\n ...cmdFlagValueToArray(cli.flags['id']),\n ...cmdFlagValueToArray(cli.flags['ghsa']),\n ])\n const limit = Number(cli.flags['limit']) || DEFAULT_LIMIT\n const maxSatisfying = Boolean(cli.flags['maxSatisfying'])\n const minSatisfying = Boolean(cli.flags['minSatisfying']) || !maxSatisfying\n const prCheck = Boolean(cli.flags['prCheck'])\n const testScript = String(cli.flags['testScript'] || 'test')\n\n await handleFix({\n autoMerge,\n cwd,\n ghsas,\n limit,\n minSatisfying,\n prCheck,\n orgSlug,\n outputKind,\n purls,\n rangeStyle,\n spinner,\n test,\n testScript,\n unknownFlags,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport async function outputInstallCompletion(\n result: CResult<{\n actions: string[]\n bashrcPath: string\n completionCommand: string\n bashrcUpdated: boolean\n foundBashrc: boolean\n sourcingCommand: string\n targetName: string\n targetPath: string\n }>,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log('')\n logger.log(\n `Installation of tab completion for \"${result.data.targetName}\" finished!`,\n )\n logger.log('')\n\n result.data.actions.forEach(action => {\n logger.log(` - ${action}`)\n })\n logger.log('')\n logger.log('Socket tab completion works automatically in new terminals.')\n logger.log('')\n logger.log(\n 'Due to a bash limitation, tab completion cannot be enabled in the',\n )\n logger.log('current shell (bash instance) through NodeJS. You must either:')\n logger.log('')\n logger.log('1. Reload your .bashrc script (best):')\n logger.log('')\n logger.log(` source ~/.bashrc`)\n logger.log('')\n logger.log('2. Run these commands to load the completion script:')\n logger.log('')\n logger.log(` source ${result.data.targetPath}`)\n logger.log(` ${result.data.completionCommand}`)\n logger.log('')\n logger.log('3. Or restart bash somehow (restart terminal or run `bash`)')\n logger.log('')\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport constants from '../../constants.mts'\nimport { getBashrcDetails } from '../../utils/completion.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport async function setupTabCompletion(targetName: string): Promise<\n CResult<{\n actions: string[]\n bashrcPath: string\n bashrcUpdated: boolean\n completionCommand: string\n foundBashrc: boolean\n sourcingCommand: string\n targetName: string\n targetPath: string\n }>\n> {\n const result = getBashrcDetails(targetName)\n if (!result.ok) {\n return result\n }\n\n const { completionCommand, sourcingCommand, targetPath, toAddToBashrc } =\n result.data\n\n // Target dir is something like ~/.local/share/socket/settings/completion (linux)\n const targetDir = path.dirname(targetPath)\n debugFn('notice', 'target: path + dir', targetPath, targetDir)\n\n if (!fs.existsSync(targetDir)) {\n debugFn('notice', 'create: target dir')\n fs.mkdirSync(targetDir, { recursive: true })\n }\n\n updateInstalledTabCompletionScript(targetPath)\n\n let bashrcUpdated = false\n\n // Add to ~/.bashrc if not already there\n const bashrcPath = constants.homePath\n ? path.join(constants.homePath, '.bashrc')\n : ''\n\n const foundBashrc = Boolean(bashrcPath && fs.existsSync(bashrcPath))\n\n if (foundBashrc) {\n const content = fs.readFileSync(bashrcPath, 'utf8')\n if (!content.includes(sourcingCommand)) {\n fs.appendFileSync(bashrcPath, toAddToBashrc)\n bashrcUpdated = true\n }\n }\n\n return {\n ok: true,\n data: {\n actions: [\n `Installed the tab completion script in ${targetPath}`,\n bashrcUpdated\n ? 'Added tab completion loader to ~/.bashrc'\n : foundBashrc\n ? 'Tab completion already found in ~/.bashrc'\n : 'No ~/.bashrc found so tab completion was not completely installed',\n ],\n bashrcPath,\n bashrcUpdated,\n completionCommand,\n foundBashrc,\n sourcingCommand,\n targetName,\n targetPath,\n },\n }\n}\n\nfunction getTabCompletionScriptRaw(): CResult<string> {\n const sourceDir = path.dirname(fileURLToPath(import.meta.url))\n const sourcePath = path.join(sourceDir, 'socket-completion.bash')\n\n if (!fs.existsSync(sourcePath)) {\n return {\n ok: false,\n message: 'Source not found.',\n cause: `Unable to find the source tab completion bash script that Socket should ship. Expected to find it in \\`${sourcePath}\\` but it was not there.`,\n }\n }\n\n return { ok: true, data: fs.readFileSync(sourcePath, 'utf8') }\n}\n\nexport function updateInstalledTabCompletionScript(\n targetPath: string,\n): CResult<undefined> {\n const content = getTabCompletionScriptRaw()\n if (!content.ok) {\n return content\n }\n\n // When installing set the current package.json version.\n // Later, we can call _socket_completion_version to get the installed version.\n fs.writeFileSync(\n targetPath,\n content.data.replaceAll(\n '%SOCKET_VERSION_TOKEN%',\n constants.ENV.INLINED_SOCKET_CLI_VERSION_HASH,\n ),\n 'utf8',\n )\n\n return { ok: true, data: undefined }\n}\n","import { outputInstallCompletion } from './output-install-completion.mts'\nimport { setupTabCompletion } from './setup-tab-completion.mts'\n\nexport async function handleInstallCompletion(targetName: string) {\n const result = await setupTabCompletion(targetName)\n await outputInstallCompletion(result)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleInstallCompletion } from './handle-install-completion.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'completion',\n description: 'Install bash completion for Socket CLI',\n hidden: false,\n flags: {\n ...commonFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [NAME=socket]\n\n Installs bash completion for the Socket CLI. This will:\n 1. Source the completion script in your current shell\n 2. Add the source command to your ~/.bashrc if it's not already there\n\n This command will only setup tab completion, nothing else.\n\n Afterwards you should be able to type \\`socket \\` and then press tab to\n have bash auto-complete/suggest the sub/command or flags.\n\n Currently only supports bash.\n\n The optional name argument allows you to enable tab completion on a command\n name other than \"socket\". Mostly for debugging but also useful if you use a\n different alias for socket on your system.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n\n $ ${command}\n $ ${command} sd\n $ ${command} ./sd\n `,\n}\n\nexport const cmdInstallCompletion = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n const targetName = cli.input[0] || 'socket'\n\n await handleInstallCompletion(String(targetName))\n}\n","import { cmdInstallCompletion } from './cmd-install-completion.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Install Socket CLI tab completion'\n\nexport const cmdInstall: CliSubcommand = {\n description,\n hidden: false,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n completion: cmdInstallCompletion,\n },\n {\n argv,\n description,\n importMeta,\n name: `${parentName} install`,\n },\n )\n },\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport {\n safeReadFileSync,\n safeStatsSync,\n} from '@socketsecurity/registry/lib/fs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { tildify } from '../../utils/tildify.mts'\n\nexport async function outputCmdJson(cwd: string) {\n logger.info('Target cwd:', constants.ENV.VITEST ? '<redacted>' : tildify(cwd))\n\n const sockJsonPath = path.join(cwd, 'socket.json')\n const tildeSockJsonPath = constants.ENV.VITEST\n ? '<redacted>'\n : tildify(sockJsonPath)\n\n if (!existsSync(sockJsonPath)) {\n logger.fail(`Not found: ${tildeSockJsonPath}`)\n process.exitCode = 1\n return\n }\n\n if (!safeStatsSync(sockJsonPath)?.isFile()) {\n logger.fail(\n `This is not a regular file (maybe a directory?): ${tildeSockJsonPath}`,\n )\n process.exitCode = 1\n return\n }\n\n logger.success(`This is the contents of ${tildeSockJsonPath}:`)\n logger.error('')\n\n const data = safeReadFileSync(sockJsonPath)\n logger.log(data)\n}\n","import { outputCmdJson } from './output-cmd-json.mts'\n\nexport async function handleCmdJson(cwd: string) {\n await outputCmdJson(cwd)\n}\n","import path from 'node:path'\n\nimport { handleCmdJson } from './handle-cmd-json.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'json',\n description:\n 'Display the `socket.json` that would be applied for target folder',\n hidden: true,\n flags: {\n ...commonFlags,\n },\n help: command => `\n Usage\n $ ${command} [options] [CWD=.]\n\n Display the \\`socket.json\\` file that would apply when running relevant commands\n in the target directory.\n\n Examples\n $ ${command}\n `,\n}\n\nexport const cmdJson = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n await handleCmdJson(cwd)\n}\n","import { updateConfigValue } from '../../utils/config.mts'\n\nexport function applyLogin(\n apiToken: string,\n enforcedOrgs: string[],\n apiBaseUrl: string | undefined,\n apiProxy: string | undefined,\n) {\n updateConfigValue('enforcedOrgs', enforcedOrgs)\n updateConfigValue('apiToken', apiToken)\n updateConfigValue('apiBaseUrl', apiBaseUrl)\n updateConfigValue('apiProxy', apiProxy)\n}\n","import terminalLink from 'terminal-link'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { confirm, password, select } from '@socketsecurity/registry/lib/prompts'\n\nimport { applyLogin } from './apply-login.mts'\nimport constants from '../../constants.mts'\nimport {\n getConfigValueOrUndef,\n isReadOnlyConfig,\n updateConfigValue,\n} from '../../utils/config.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { getEnterpriseOrgs, getOrgSlugs } from '../../utils/organization.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\nimport { setupTabCompletion } from '../install/setup-tab-completion.mts'\nimport { fetchOrganization } from '../organization/fetch-organization-list.mts'\n\nimport type { Choice, Separator } from '@socketsecurity/registry/lib/prompts'\n\ntype OrgChoice = Choice<string>\ntype OrgChoices = Array<Separator | OrgChoice>\n\nexport async function attemptLogin(\n apiBaseUrl: string | undefined,\n apiProxy: string | undefined,\n) {\n apiBaseUrl ??= getConfigValueOrUndef('apiBaseUrl') ?? undefined\n apiProxy ??= getConfigValueOrUndef('apiProxy') ?? undefined\n const apiTokenInput = await password({\n message: `Enter your ${terminalLink(\n 'Socket.dev API token',\n 'https://docs.socket.dev/docs/api-keys',\n )} (leave blank to use a limited public token)`,\n })\n\n if (apiTokenInput === undefined) {\n logger.fail('Canceled by user')\n return { ok: false, message: 'Canceled', cause: 'Canceled by user' }\n }\n\n const apiToken = apiTokenInput || constants.SOCKET_PUBLIC_API_TOKEN\n\n const sockSdkCResult = await setupSdk({ apiBaseUrl, apiProxy, apiToken })\n if (!sockSdkCResult.ok) {\n process.exitCode = 1\n logger.fail(failMsgWithBadge(sockSdkCResult.message, sockSdkCResult.cause))\n return\n }\n\n const sockSdk = sockSdkCResult.data\n\n const orgsCResult = await fetchOrganization({\n desc: 'token verification',\n sdk: sockSdk,\n })\n if (!orgsCResult.ok) {\n process.exitCode = 1\n logger.fail(failMsgWithBadge(orgsCResult.message, orgsCResult.cause))\n return\n }\n\n const { organizations } = orgsCResult.data\n\n const orgSlugs = getOrgSlugs(organizations)\n\n logger.success(`API token verified: ${joinAnd(orgSlugs)}`)\n\n const enterpriseOrgs = getEnterpriseOrgs(organizations)\n\n const enforcedChoices: OrgChoices = enterpriseOrgs.map(org => ({\n name: org.name ?? 'undefined',\n value: org.id,\n }))\n\n let enforcedOrgs: string[] = []\n if (enforcedChoices.length > 1) {\n const id = await select({\n message:\n \"Which organization's policies should Socket enforce system-wide?\",\n choices: [\n ...enforcedChoices,\n {\n name: 'None',\n value: '',\n description: 'Pick \"None\" if this is a personal device',\n },\n ],\n })\n if (id === undefined) {\n logger.fail('Canceled by user')\n return { ok: false, message: 'Canceled', cause: 'Canceled by user' }\n }\n if (id) {\n enforcedOrgs = [id]\n }\n } else if (enforcedChoices.length) {\n const shouldEnforce = await confirm({\n message: `Should Socket enforce ${(enforcedChoices[0] as OrgChoice)?.name}'s security policies system-wide?`,\n default: true,\n })\n if (shouldEnforce === undefined) {\n logger.fail('Canceled by user')\n return { ok: false, message: 'Canceled', cause: 'Canceled by user' }\n }\n if (shouldEnforce) {\n const existing = enforcedChoices[0] as OrgChoice\n if (existing) {\n enforcedOrgs = [existing.value]\n }\n }\n }\n\n const wantToComplete = await select({\n message: 'Would you like to install bash tab completion?',\n choices: [\n {\n name: 'Yes',\n value: true,\n description:\n 'Sets up tab completion for \"socket\" in your bash env. If you\\'re unsure, this is probably what you want.',\n },\n {\n name: 'No',\n value: false,\n description:\n 'Will skip tab completion setup. Does not change how Socket works.',\n },\n ],\n })\n if (wantToComplete === undefined) {\n logger.fail('Canceled by user')\n return { ok: false, message: 'Canceled', cause: 'Canceled by user' }\n }\n if (wantToComplete) {\n logger.log('')\n logger.log('Setting up tab completion...')\n const setupCResult = await setupTabCompletion('socket')\n if (setupCResult.ok) {\n logger.success(\n 'Tab completion will be enabled after restarting your terminal',\n )\n } else {\n logger.fail(\n 'Failed to install tab completion script. Try `socket install completion` later.',\n )\n }\n }\n\n updateConfigValue('defaultOrg', orgSlugs[0])\n\n const previousPersistedToken = getConfigValueOrUndef('apiToken')\n try {\n applyLogin(apiToken, enforcedOrgs, apiBaseUrl, apiProxy)\n logger.success(\n `API credentials ${previousPersistedToken === apiToken ? 'refreshed' : previousPersistedToken ? 'updated' : 'set'}`,\n )\n if (isReadOnlyConfig()) {\n logger.log('')\n logger.warn(\n 'Note: config is in read-only mode, at least one key was overridden through flag/env, so the login was not persisted!',\n )\n }\n } catch {\n process.exitCode = 1\n logger.fail(`API login failed`)\n }\n}\n","import isInteractive from '@socketregistry/is-interactive/index.cjs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { attemptLogin } from './attempt-login.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { InputError } from '../../utils/errors.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'login'\n\nconst description = 'Setup Socket CLI with an API token and defaults'\n\nconst hidden = false\n\nexport const cmdLogin = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n apiBaseUrl: {\n type: 'string',\n description: 'API server to connect to for login',\n },\n apiProxy: {\n type: 'string',\n description: 'Proxy to use when making connection to API server',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Logs into the Socket API by prompting for an API token\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} --api-proxy=http://localhost:1234\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n if (!isInteractive()) {\n throw new InputError(\n 'Cannot prompt for credentials in a non-interactive shell. Use SOCKET_CLI_API_TOKEN environment variable instead',\n )\n }\n\n const apiBaseUrl = cli.flags['apiBaseUrl'] as string | undefined\n\n const apiProxy = cli.flags['apiProxy'] as string | undefined\n\n await attemptLogin(apiBaseUrl, apiProxy)\n}\n","import { updateConfigValue } from '../../utils/config.mts'\n\nexport function applyLogout() {\n updateConfigValue('apiToken', null)\n updateConfigValue('apiBaseUrl', null)\n updateConfigValue('apiProxy', null)\n updateConfigValue('enforcedOrgs', null)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { applyLogout } from './apply-logout.mts'\nimport { isReadOnlyConfig } from '../../utils/config.mts'\n\nexport function attemptLogout() {\n try {\n applyLogout()\n logger.success('Successfully logged out')\n if (isReadOnlyConfig()) {\n logger.log('')\n logger.warn(\n 'Note: config is in read-only mode, at least one key was overridden through flag/env, so the logout was not persisted!',\n )\n }\n } catch {\n logger.fail('Failed to complete logout steps')\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { attemptLogout } from './attempt-logout.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'logout',\n description: 'Socket API logout',\n hidden: false,\n flags: {\n ...commonFlags,\n },\n help: (command, _config) => `\n Usage\n $ ${command} [options]\n\n Logs out of the Socket API and clears all Socket credentials from disk\n\n Examples\n $ ${command}\n `,\n}\n\nexport const cmdLogout = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n attemptLogout()\n}\n","import { existsSync, rmSync } from 'node:fs'\nimport path from 'node:path'\n\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport shadowBin from '../../shadow/npm/bin.mts'\n\nimport type {\n ShadowBinOptions,\n ShadowBinResult,\n} from '../../shadow/npm/bin.mts'\n\nconst { PACKAGE_LOCK_JSON, YARN, YARN_LOCK } = constants\n\nconst nodejsPlatformTypes = new Set([\n 'javascript',\n 'js',\n 'nodejs',\n 'npm',\n 'pnpm',\n 'ts',\n 'tsx',\n 'typescript',\n])\n\nexport type ArgvObject = {\n [key: string]: boolean | null | number | string | Array<string | number>\n}\n\nfunction argvToArray(argvObj: ArgvObject): string[] {\n if (argvObj['help']) {\n return ['--help']\n }\n const result = []\n for (const { 0: key, 1: value } of Object.entries(argvObj)) {\n if (key === '_' || key === '--') {\n continue\n }\n if (key === 'babel' || key === 'install-deps' || key === 'validate') {\n // cdxgen documents no-babel, no-install-deps, and no-validate flags so\n // use them when relevant.\n result.push(`--${value ? key : `no-${key}`}`)\n } else if (value === true) {\n result.push(`--${key}`)\n } else if (typeof value === 'string') {\n result.push(`--${key}`, String(value))\n } else if (Array.isArray(value)) {\n result.push(`--${key}`, ...value.map(String))\n }\n }\n const pathArgs = argvObj['_'] as string[]\n if (Array.isArray(pathArgs)) {\n result.push(...pathArgs)\n }\n const argsAfterDoubleHyphen = argvObj['--'] as string[]\n if (Array.isArray(argsAfterDoubleHyphen)) {\n result.push('--', ...argsAfterDoubleHyphen)\n }\n return result\n}\n\nexport async function runCdxgen(argvObj: ArgvObject): Promise<ShadowBinResult> {\n let cleanupPackageLock = false\n const argvMutable = { __proto__: null, ...argvObj } as ArgvObject\n const shadowOpts: ShadowBinOptions = {\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 },\n stdio: 'inherit',\n }\n if (\n argvMutable['type'] !== YARN &&\n nodejsPlatformTypes.has(argvMutable['type'] as string) &&\n existsSync(`./${YARN_LOCK}`)\n ) {\n if (existsSync(`./${PACKAGE_LOCK_JSON}`)) {\n argvMutable['type'] = 'npm'\n } else {\n // Use synp to create a package-lock.json from the yarn.lock,\n // based on the node_modules folder, for a more accurate SBOM.\n try {\n const { spawnPromise: synpPromise } = await shadowBin(\n 'npx',\n [\n '--yes',\n `synp@${constants.ENV.INLINED_SOCKET_CLI_SYNP_VERSION}`,\n '--source-file',\n `./${YARN_LOCK}`,\n ],\n shadowOpts,\n )\n await synpPromise\n argvMutable['type'] = 'npm'\n cleanupPackageLock = true\n } catch {}\n }\n }\n\n const shadowResult = await shadowBin(\n 'npx',\n [\n '--yes',\n `@cyclonedx/cdxgen@${constants.ENV.INLINED_SOCKET_CLI_CYCLONEDX_CDXGEN_VERSION}`,\n ...argvToArray(argvMutable),\n ],\n shadowOpts,\n )\n\n shadowResult.spawnPromise.process.on('exit', () => {\n if (cleanupPackageLock) {\n try {\n rmSync(`./${PACKAGE_LOCK_JSON}`)\n } catch {}\n }\n\n const outputPath = argvMutable['output'] as string\n if (outputPath) {\n const fullOutputPath = path.join(process.cwd(), outputPath)\n if (existsSync(fullOutputPath)) {\n logger.log(colors.cyanBright(`${outputPath} created!`))\n }\n }\n })\n\n return shadowResult\n}\n","import terminalLink from 'terminal-link'\nimport yargsParse from 'yargs-parser'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { isPath } from '@socketsecurity/registry/lib/path'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { runCdxgen } from './run-cdxgen.mts'\nimport constants from '../../constants.mts'\nimport { isHelpFlag } from '../../utils/cmd.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\n// TODO: Convert yargs to meow.\nconst toLower = (arg: string) => arg.toLowerCase()\nconst arrayToLower = (arg: string[]) => arg.map(toLower)\n\n// npx @cyclonedx/cdxgen@11.2.7 --help\n//\n// Options:\n// -o, --output Output file. Default bom.json [default: \"bom.json\"]\n// -t, --type Project type. Please refer to https://cyclonedx.github.io/cdxgen/#/PROJECT_TYPES for supp\n// orted languages/platforms. [array]\n// --exclude-type Project types to exclude. Please refer to https://cyclonedx.github.io/cdxgen/#/PROJECT_TY\n// PES for supported languages/platforms.\n// -r, --recurse Recurse mode suitable for mono-repos. Defaults to true. Pass --no-recurse to disable.\n// [boolean] [default: true]\n// -p, --print Print the SBOM as a table with tree. [boolean]\n// -c, --resolve-class Resolve class names for packages. jars only for now. [boolean]\n// --deep Perform deep searches for components. Useful while scanning C/C++ apps, live OS and oci i\n// mages. [boolean]\n// --server-url Dependency track url. Eg: https://deptrack.cyclonedx.io\n// --skip-dt-tls-check Skip TLS certificate check when calling Dependency-Track. [boolean] [default: false]\n// --api-key Dependency track api key\n// --project-group Dependency track project group\n// --project-name Dependency track project name. Default use the directory name\n// --project-version Dependency track project version [string] [default: \"\"]\n// --project-id Dependency track project id. Either provide the id or the project name and version togeth\n// er [string]\n// --parent-project-id Dependency track parent project id [string]\n// --required-only Include only the packages with required scope on the SBOM. Would set compositions.aggrega\n// te to incomplete unless --no-auto-compositions is passed. [boolean]\n// --fail-on-error Fail if any dependency extractor fails. [boolean]\n// --no-babel Do not use babel to perform usage analysis for JavaScript/TypeScript projects. [boolean]\n// --generate-key-and-sign Generate an RSA public/private key pair and then sign the generated SBOM using JSON Web S\n// ignatures. [boolean]\n// --server Run cdxgen as a server [boolean]\n// --server-host Listen address [default: \"127.0.0.1\"]\n// --server-port Listen port [default: \"9090\"]\n// --install-deps Install dependencies automatically for some projects. Defaults to true but disabled for c\n// ontainers and oci scans. Use --no-install-deps to disable this feature.\n// [boolean] [default: true]\n// --validate Validate the generated SBOM using json schema. Defaults to true. Pass --no-validate to di\n// sable. [boolean] [default: true]\n// --evidence Generate SBOM with evidence for supported languages. [boolean] [default: false]\n// --spec-version CycloneDX Specification version to use. Defaults to 1.6\n// [number] [choices: 1.4, 1.5, 1.6, 1.7] [default: 1.6]\n// --filter Filter components containing this word in purl or component.properties.value. Multiple va\n// lues allowed. [array]\n// --only Include components only containing this word in purl. Useful to generate BOM with first p\n// arty components alone. Multiple values allowed. [array]\n// --author The person(s) who created the BOM. Set this value if you're intending the modify the BOM\n// and claim authorship. [array] [default: \"OWASP Foundation\"]\n// --profile BOM profile to use for generation. Default generic.\n// [choices: \"appsec\", \"research\", \"operational\", \"threat-modeling\", \"license-compliance\", \"generic\", \"machine-learning\",\n// \"ml\", \"deep-learning\", \"ml-deep\", \"ml-tiny\"] [default: \"generic\"]\n// --exclude Additional glob pattern(s) to ignore [array]\n// --export-proto Serialize and export BOM as protobuf binary. [boolean] [default: false]\n// --proto-bin-file Path for the serialized protobuf binary. [default: \"bom.cdx\"]\n// --include-formulation Generate formulation section with git metadata and build tools. Defaults to false.\n// [boolean] [default: false]\n// --include-crypto Include crypto libraries as components. [boolean] [default: false]\n// --standard The list of standards which may consist of regulations, industry or organizational-specif\n// ic standards, maturity models, best practices, or any other requirements which can be eva\n// luated against or attested to.\n// [array] [choices: \"asvs-5.0\", \"asvs-4.0.3\", \"bsimm-v13\", \"masvs-2.0.0\", \"nist_ssdf-1.1\", \"pcissc-secure-slc-1.1\", \"scv\n// s-1.0.0\", \"ssaf-DRAFT-2023-11\"]\n// --json-pretty Pretty-print the generated BOM json. [boolean] [default: false]\n// --min-confidence Minimum confidence needed for the identity of a component from 0 - 1, where 1 is 100% con\n// fidence. [number] [default: 0]\n// --technique Analysis technique to use\n// [array] [choices: \"auto\", \"source-code-analysis\", \"binary-analysis\", \"manifest-analysis\", \"hash-comparison\", \"instrume\n// ntation\", \"filename\"]\n// --auto-compositions Automatically set compositions when the BOM was filtered. Defaults to true\n// [boolean] [default: true]\n// -h, --help Show help [boolean]\n// -v, --version Show version number [boolean]\n\n// isSecureMode defined at:\n// https://github.com/CycloneDX/cdxgen/blob/v11.2.7/lib/helpers/utils.js#L66\n// const isSecureMode =\n// ['true', '1'].includes(process.env?.CDXGEN_SECURE_MODE) ||\n// process.env?.NODE_OPTIONS?.includes('--permission')\n\n// Yargs CDXGEN configuration defined at:\n// https://github.com/CycloneDX/cdxgen/blob/v11.2.7/bin/cdxgen.js#L64\nconst yargsConfig = {\n configuration: {\n 'camel-case-expansion': false,\n 'greedy-arrays': false,\n 'parse-numbers': false,\n 'populate--': true,\n 'short-option-groups': false,\n 'strip-aliased': true,\n 'unknown-options-as-args': true,\n },\n coerce: {\n 'exclude-type': arrayToLower,\n 'feature-flags': arrayToLower,\n filter: arrayToLower,\n only: arrayToLower,\n profile: toLower,\n standard: arrayToLower,\n technique: arrayToLower,\n type: arrayToLower,\n },\n default: {\n //author: ['OWASP Foundation'],\n //'auto-compositions': true,\n //babel: true,\n //banner: false, // hidden\n //'deps-slices-file': 'deps.slices.json', // hidden\n //evidence: false,\n //'exclude-type': [],\n //'export-proto': false,\n //'fail-on-error': isSecureMode,\n //'feature-flags': [], // hidden\n //'include-crypto': false,\n //'include-formulation': false,\n //'install-deps': !isSecureMode\n //lifecycle: 'build', // hidden\n //'min-confidence': '0',\n //output: 'bom.json',\n //profile: 'generic',\n //'project-version': '',\n //'proto-bin-file': 'bom.cdx',\n //recurse: true,\n //'skip-dt-tls-check': false,\n //'semantics-slices-file': 'semantics.slices.json',\n //'server-host': '127.0.0.1',\n //'server-port': '9090',\n //'spec-version': '1.6',\n type: ['js'],\n //validate: true,\n },\n alias: {\n help: ['h'],\n output: ['o'],\n print: ['p'],\n recurse: ['r'],\n 'resolve-class': ['c'],\n type: ['t'],\n version: ['v'],\n },\n array: [\n { key: 'author', type: 'string' },\n { key: 'exclude', type: 'string' },\n { key: 'exclude-type', type: 'string' },\n { key: 'feature-flags', type: 'string' }, // hidden\n { key: 'filter', type: 'string' },\n { key: 'only', type: 'string' },\n { key: 'standard', type: 'string' },\n { key: 'technique', type: 'string' },\n { key: 'type', type: 'string' },\n ],\n boolean: [\n 'auto-compositions',\n 'babel',\n 'banner', // hidden\n 'deep',\n 'evidence',\n 'export-proto',\n 'fail-on-error',\n 'generate-key-and-sign',\n 'help',\n 'include-crypto',\n 'include-formulation',\n 'install-deps',\n 'json-pretty',\n 'print',\n 'recurse',\n 'required-only',\n 'resolve-class',\n 'skip-dt-tls-check',\n 'server',\n 'validate',\n 'version',\n ],\n string: [\n 'api-key',\n 'data-flow-slices-file', // hidden\n 'deps-slices-file', // hidden\n 'evinse-output', // hidden\n 'lifecycle',\n 'min-confidence', // number\n 'openapi-spec-file', // hidden\n 'output',\n 'parent-project-id',\n 'profile',\n 'project-group',\n 'project-name',\n 'project-version',\n 'project-id',\n 'proto-bin-file',\n 'reachables-slices-file', // hidden\n 'semantics-slices-file', // hidden\n 'server-host',\n 'server-port',\n 'server-url',\n 'spec-version', // number\n 'usages-slices-file', // hidden\n ],\n}\n\nconst config: CliCommandConfig = {\n commandName: 'cdxgen',\n description: 'Create an SBOM with CycloneDX generator (cdxgen)',\n hidden: false,\n // Stub out flags and help.\n // TODO: Convert yargs to meow.\n flags: {},\n help: () => '',\n}\n\nexport const cmdManifestCdxgen = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n // Don't let meow take over --help.\n argv: argv.filter(a => !isHelpFlag(a)),\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n // TODO: Convert yargs to meow.\n const yargv = {\n ...yargsParse(argv as string[], yargsConfig),\n } as any\n\n const pathArgs: string[] = []\n const unknowns: string[] = []\n for (const a of yargv._) {\n if (isPath(a)) {\n pathArgs.push(a)\n } else {\n unknowns.push(a)\n }\n }\n\n yargv._ = pathArgs\n\n const { length: unknownsCount } = unknowns\n if (unknownsCount) {\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 logger.fail(\n `Unknown ${pluralize('argument', unknownsCount)}: ${unknowns.join(', ')}`,\n )\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n // Change defaults when not passing the --help flag.\n if (!yargv.help) {\n // Make 'lifecycle' default to 'pre-build', which also sets 'install-deps' to `false`,\n // to avoid arbitrary code execution on the cdxgen scan.\n // https://github.com/CycloneDX/cdxgen/issues/1328\n if (yargv.lifecycle === undefined) {\n yargv.lifecycle = 'pre-build'\n yargv['install-deps'] = false\n logger.info(\n `Setting cdxgen --lifecycle to \"${yargv.lifecycle}\" to avoid arbitrary code execution on this scan.\\n Pass \"--lifecycle build\" to generate a BOM consisting of information obtained during the build process.\\n See cdxgen ${terminalLink(\n 'BOM lifecycles documentation',\n 'https://cyclonedx.github.io/cdxgen/#/ADVANCED?id=bom-lifecycles',\n )} for more details.\\n`,\n )\n }\n if (yargv.output === undefined) {\n yargv.output = 'socket-cdx.json'\n }\n }\n\n process.exitCode = 1\n\n const { spawnPromise } = await runCdxgen(yargv)\n\n // See https://nodejs.org/api/child_process.html#event-exit.\n spawnPromise.process.on('exit', (code, signalName) => {\n if (signalName) {\n process.kill(process.pid, signalName)\n } else if (typeof code === 'number') {\n // eslint-disable-next-line n/no-process-exit\n process.exit(code)\n }\n })\n\n await spawnPromise\n}\n","import path from 'node:path'\n\nimport { debugDir } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { detectManifestActions } from './detect-manifest-actions.mts'\nimport { generateAutoManifest } from './generate_auto_manifest.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'auto',\n description: 'Auto-detect build and attempt to generate manifest file',\n hidden: false,\n flags: {\n ...commonFlags,\n verbose: {\n type: 'boolean',\n default: false,\n description:\n 'Enable debug output (only for auto itself; sub-steps need to have it pre-configured), may help when running into errors',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Tries to figure out what language your target repo uses. If it finds a\n supported case then it will try to generate the manifest file for that\n language with the default or detected settings.\n\n Note: you can exclude languages from being auto-generated if you don't want\n them to. Run \\`socket manifest setup\\` in the same dir to disable it.\n\n Examples\n\n $ ${command}\n $ ${command} ./project/foo\n `,\n}\n\nexport const cmdManifestAuto = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n // TODO: Implement json/md further.\n const { json, markdown, verbose: verboseFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const verbose = !!verboseFlag\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n const outputKind = getOutputKind(json, markdown)\n\n if (verbose) {\n logger.group('- ', parentName, config.commandName, ':')\n logger.group('- flags:', cli.flags)\n logger.groupEnd()\n logger.log('- input:', cli.input)\n logger.log('- cwd:', cwd)\n logger.groupEnd()\n }\n\n const sockJson = readOrDefaultSocketJson(cwd)\n\n const detected = await detectManifestActions(sockJson, cwd)\n debugDir('inspect', { detected })\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n if (!detected.count) {\n logger.fail(\n 'Was unable to discover any targets for which we can generate manifest files...',\n )\n logger.log('')\n logger.log(\n '- Make sure this script would work with your target build (see `socket manifest --help` for your target).',\n )\n logger.log(\n '- Make sure to run it from the correct dir (use --cwd to target another dir)',\n )\n logger.log('- Make sure the necessary build tools are available (`PATH`)')\n process.exitCode = 1\n return\n }\n\n await generateAutoManifest({\n detected,\n cwd,\n outputKind,\n verbose,\n })\n\n logger.success(\n `Finished. Should have attempted to generate manifest files for ${detected.count} targets.`,\n )\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleManifestConda } from './handle-manifest-conda.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'conda',\n description:\n '[beta] Convert a Conda environment.yml file to a python requirements.txt',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n file: {\n type: 'string',\n description:\n 'Input file name (by default for Conda this is \"environment.yml\"), relative to cwd',\n },\n stdin: {\n type: 'boolean',\n description: 'Read the input from stdin (supersedes --file)',\n },\n out: {\n type: 'string',\n description: 'Output path (relative to cwd)',\n },\n stdout: {\n type: 'boolean',\n description:\n 'Print resulting requirements.txt to stdout (supersedes --out)',\n },\n verbose: {\n type: 'boolean',\n description: 'Print debug messages',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n Warning: While we don't support Conda necessarily, this tool extracts the pip\n block from an environment.yml and outputs it as a requirements.txt\n which you can scan as if it were a pypi package.\n\n USE AT YOUR OWN RISK\n\n Note: FILE can be a dash (-) to indicate stdin. This way you can pipe the\n contents of a file to have it processed.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n\n $ ${command}\n $ ${command} ./project/foo --file environment.yaml\n `,\n}\n\nexport const cmdManifestConda = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json = false, markdown = false } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n const sockJson = readOrDefaultSocketJson(cwd)\n\n let { file: filename, out, stdin, stdout, verbose } = cli.flags\n\n // Set defaults for any flag/arg that is not given. Check socket.json first.\n if (\n stdin === undefined &&\n sockJson.defaults?.manifest?.conda?.stdin !== undefined\n ) {\n stdin = sockJson.defaults?.manifest?.conda?.stdin\n logger.info('Using default --stdin from socket.json:', stdin)\n }\n if (stdin) {\n filename = '-'\n } else if (!filename) {\n if (sockJson.defaults?.manifest?.conda?.infile) {\n filename = sockJson.defaults?.manifest?.conda?.infile\n logger.info('Using default --file from socket.json:', filename)\n } else {\n filename = 'environment.yml'\n }\n }\n if (\n stdout === undefined &&\n sockJson.defaults?.manifest?.conda?.stdout !== undefined\n ) {\n stdout = sockJson.defaults?.manifest?.conda?.stdout\n logger.info('Using default --stdout from socket.json:', stdout)\n }\n if (stdout) {\n out = '-'\n } else if (!out) {\n if (sockJson.defaults?.manifest?.conda?.outfile) {\n out = sockJson.defaults?.manifest?.conda?.outfile\n logger.info('Using default --out from socket.json:', out)\n } else {\n out = 'requirements.txt'\n }\n }\n if (\n verbose === undefined &&\n sockJson.defaults?.manifest?.conda?.verbose !== undefined\n ) {\n verbose = sockJson.defaults?.manifest?.conda?.verbose\n logger.info('Using default --verbose from socket.json:', verbose)\n } else if (verbose === undefined) {\n verbose = false\n }\n\n if (verbose) {\n logger.group('- ', parentName, config.commandName, ':')\n logger.group('- flags:', cli.flags)\n logger.groupEnd()\n logger.log('- target:', cwd)\n logger.log('- output:', out)\n logger.groupEnd()\n }\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: cli.input.length <= 1,\n message: 'Can only accept one DIR (make sure to escape spaces!)',\n fail: `received ${cli.input.length}`,\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n logger.warn(\n 'Warning: This will approximate your Conda dependencies using PyPI. We do not yet officially support Conda. Use at your own risk.',\n )\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleManifestConda({\n cwd,\n filename: String(filename),\n out: String(out || ''),\n outputKind,\n verbose: Boolean(verbose),\n })\n}\n","import path from 'node:path'\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { convertGradleToMaven } from './convert_gradle_to_maven.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'gradle',\n description:\n '[beta] Use Gradle to generate a manifest file (`pom.xml`) for a Gradle/Java/Kotlin/etc project',\n hidden: false,\n flags: {\n ...commonFlags,\n bin: {\n type: 'string',\n description: 'Location of gradlew binary to use, default: CWD/gradlew',\n },\n gradleOpts: {\n type: 'string',\n description:\n 'Additional options to pass on to ./gradlew, see `./gradlew --help`',\n },\n verbose: {\n type: 'boolean',\n description: 'Print debug messages',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Uses gradle, preferably through your local project \\`gradlew\\`, to generate a\n \\`pom.xml\\` file for each task. If you have no \\`gradlew\\` you can try the\n global \\`gradle\\` binary but that may not work (hard to predict).\n\n The \\`pom.xml\\` is a manifest file similar to \\`package.json\\` for npm or\n or requirements.txt for PyPi), but specifically for Maven, which is Java's\n dependency repository. Languages like Kotlin and Scala piggy back on it too.\n\n There are some caveats with the gradle to \\`pom.xml\\` conversion:\n\n - each task will generate its own xml file and by default it generates one xml\n for every task. (This may be a good thing!)\n\n - it's possible certain features don't translate well into the xml. If you\n think something is missing that could be supported please reach out.\n\n - it works with your \\`gradlew\\` from your repo and local settings and config\n\n Support is beta. Please report issues or give us feedback on what's missing.\n\n Examples\n\n $ ${command} .\n $ ${command} --bin=../gradlew .\n `,\n}\n\nexport const cmdManifestGradle = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json = false, markdown = false } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n // TODO: Implement json/md further.\n const outputKind = getOutputKind(json, markdown)\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n const sockJson = readOrDefaultSocketJson(cwd)\n\n debugFn(\n 'inspect',\n 'override: socket.json gradle',\n sockJson?.defaults?.manifest?.gradle,\n )\n\n let { bin, gradleOpts, verbose } = cli.flags\n\n // Set defaults for any flag/arg that is not given. Check socket.json first.\n if (!bin) {\n if (sockJson.defaults?.manifest?.gradle?.bin) {\n bin = sockJson.defaults?.manifest?.gradle?.bin\n logger.info('Using default --bin from socket.json:', bin)\n } else {\n bin = path.join(cwd, 'gradlew')\n }\n }\n if (!gradleOpts) {\n if (sockJson.defaults?.manifest?.gradle?.gradleOpts) {\n gradleOpts = sockJson.defaults?.manifest?.gradle?.gradleOpts\n logger.info('Using default --gradle-opts from socket.json:', gradleOpts)\n } else {\n gradleOpts = ''\n }\n }\n if (verbose === undefined) {\n if (sockJson.defaults?.manifest?.gradle?.verbose !== undefined) {\n verbose = sockJson.defaults?.manifest?.gradle?.verbose\n logger.info('Using default --verbose from socket.json:', verbose)\n } else {\n verbose = false\n }\n }\n\n if (verbose) {\n logger.group('- ', parentName, config.commandName, ':')\n logger.group('- flags:', cli.flags)\n logger.groupEnd()\n logger.log('- input:', cli.input)\n logger.groupEnd()\n }\n\n // TODO: We're not sure it's feasible to parse source file from stdin. We could\n // try, store contents in a file in some folder, target that folder... what\n // would the file name be?\n\n const wasValidInput = checkCommandInput(outputKind, {\n nook: true,\n test: cli.input.length <= 1,\n message: 'Can only accept one DIR (make sure to escape spaces!)',\n fail: 'received ' + cli.input.length,\n })\n if (!wasValidInput) {\n return\n }\n\n if (verbose) {\n logger.group()\n logger.info('- cwd:', cwd)\n logger.info('- gradle bin:', bin)\n logger.groupEnd()\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await convertGradleToMaven({\n bin: String(bin),\n cwd,\n gradleOpts: String(gradleOpts || '')\n .split(' ')\n .map(s => s.trim())\n .filter(Boolean),\n verbose: Boolean(verbose),\n })\n}\n","import path from 'node:path'\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { convertGradleToMaven } from './convert_gradle_to_maven.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\n// TODO: We may want to dedupe some pieces for all gradle languages. I think it\n// makes sense to have separate commands for them and I think it makes\n// sense for the help panels to note the requested language, rather than\n// `socket manifest kotlin` to print help screens with `gradle` as the\n// command. Room for improvement.\nconst config: CliCommandConfig = {\n commandName: 'kotlin',\n description:\n '[beta] Use Gradle to generate a manifest file (`pom.xml`) for a Kotlin project',\n hidden: false,\n flags: {\n ...commonFlags,\n bin: {\n type: 'string',\n description: 'Location of gradlew binary to use, default: CWD/gradlew',\n },\n gradleOpts: {\n type: 'string',\n description:\n 'Additional options to pass on to ./gradlew, see `./gradlew --help`',\n },\n verbose: {\n type: 'boolean',\n description: 'Print debug messages',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Uses gradle, preferably through your local project \\`gradlew\\`, to generate a\n \\`pom.xml\\` file for each task. If you have no \\`gradlew\\` you can try the\n global \\`gradle\\` binary but that may not work (hard to predict).\n\n The \\`pom.xml\\` is a manifest file similar to \\`package.json\\` for npm or\n or requirements.txt for PyPi), but specifically for Maven, which is Java's\n dependency repository. Languages like Kotlin and Scala piggy back on it too.\n\n There are some caveats with the gradle to \\`pom.xml\\` conversion:\n\n - each task will generate its own xml file and by default it generates one xml\n for every task. (This may be a good thing!)\n\n - it's possible certain features don't translate well into the xml. If you\n think something is missing that could be supported please reach out.\n\n - it works with your \\`gradlew\\` from your repo and local settings and config\n\n Support is beta. Please report issues or give us feedback on what's missing.\n\n Examples\n\n $ ${command} .\n $ ${command} --bin=../gradlew .\n `,\n}\n\nexport const cmdManifestKotlin = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json = false, markdown = false } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n // TODO: Implement json/md further.\n const outputKind = getOutputKind(json, markdown)\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n const sockJson = readOrDefaultSocketJson(cwd)\n\n debugFn(\n 'inspect',\n 'override: socket.json gradle',\n sockJson?.defaults?.manifest?.gradle,\n )\n\n let { bin, gradleOpts, verbose } = cli.flags\n\n // Set defaults for any flag/arg that is not given. Check socket.json first.\n if (!bin) {\n if (sockJson.defaults?.manifest?.gradle?.bin) {\n bin = sockJson.defaults?.manifest?.gradle?.bin\n logger.info('Using default --bin from socket.json:', bin)\n } else {\n bin = path.join(cwd, 'gradlew')\n }\n }\n if (!gradleOpts) {\n if (sockJson.defaults?.manifest?.gradle?.gradleOpts) {\n gradleOpts = sockJson.defaults?.manifest?.gradle?.gradleOpts\n logger.info('Using default --gradle-opts from socket.json:', gradleOpts)\n } else {\n gradleOpts = ''\n }\n }\n if (verbose === undefined) {\n if (sockJson.defaults?.manifest?.gradle?.verbose !== undefined) {\n verbose = sockJson.defaults?.manifest?.gradle?.verbose\n logger.info('Using default --verbose from socket.json:', verbose)\n } else {\n verbose = false\n }\n }\n\n if (verbose) {\n logger.group('- ', parentName, config.commandName, ':')\n logger.group('- flags:', cli.flags)\n logger.groupEnd()\n logger.log('- input:', cli.input)\n logger.groupEnd()\n }\n\n // TODO: We're not sure it's feasible to parse source file from stdin. We could\n // try, store contents in a file in some folder, target that folder... what\n // would the file name be?\n\n const wasValidInput = checkCommandInput(outputKind, {\n nook: true,\n test: cli.input.length <= 1,\n message: 'Can only accept one DIR (make sure to escape spaces!)',\n fail: 'received ' + cli.input.length,\n })\n if (!wasValidInput) {\n return\n }\n\n if (verbose) {\n logger.group()\n logger.info('- cwd:', cwd)\n logger.info('- gradle bin:', bin)\n logger.groupEnd()\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await convertGradleToMaven({\n bin: String(bin),\n cwd,\n gradleOpts: String(gradleOpts || '')\n .split(' ')\n .map(s => s.trim())\n .filter(Boolean),\n verbose: Boolean(verbose),\n })\n}\n","import path from 'node:path'\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { convertSbtToMaven } from './convert_sbt_to_maven.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'scala',\n description:\n \"[beta] Generate a manifest file (`pom.xml`) from Scala's `build.sbt` file\",\n hidden: false,\n flags: {\n ...commonFlags,\n bin: {\n type: 'string',\n description: 'Location of sbt binary to use',\n },\n out: {\n type: 'string',\n description:\n 'Path of output file; where to store the resulting manifest, see also --stdout',\n },\n stdout: {\n type: 'boolean',\n description: 'Print resulting pom.xml to stdout (supersedes --out)',\n },\n sbtOpts: {\n type: 'string',\n description: 'Additional options to pass on to sbt, as per `sbt --help`',\n },\n verbose: {\n type: 'boolean',\n description: 'Print debug messages',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Uses \\`sbt makePom\\` to generate a \\`pom.xml\\` from your \\`build.sbt\\` file.\n This xml file is the dependency manifest (like a package.json\n for Node.js or requirements.txt for PyPi), but specifically for Scala.\n\n There are some caveats with \\`build.sbt\\` to \\`pom.xml\\` conversion:\n\n - the xml is exported as socket.pom.xml as to not confuse existing build tools\n but it will first hit your /target/sbt<version> folder (as a different name)\n\n - the pom.xml format (standard by Scala) does not support certain sbt features\n - \\`excludeAll()\\`, \\`dependencyOverrides\\`, \\`force()\\`, \\`relativePath\\`\n - For details: https://www.scala-sbt.org/1.x/docs/Library-Management.html\n\n - it uses your sbt settings and local configuration verbatim\n\n - it can only export one target per run, so if you have multiple targets like\n development and production, you must run them separately.\n\n You can specify --bin to override the path to the \\`sbt\\` binary to invoke.\n\n Support is beta. Please report issues or give us feedback on what's missing.\n\n This is only for SBT. If your Scala setup uses gradle, please see the help\n sections for \\`socket manifest gradle\\` or \\`socket cdxgen\\`.\n\n Examples\n\n $ ${command}\n $ ${command} ./proj --bin=/usr/bin/sbt --file=boot.sbt\n `,\n}\n\nexport const cmdManifestScala = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json = false, markdown = false } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n // TODO: Implement json/md further.\n const outputKind = getOutputKind(json, markdown)\n\n const sockJson = readOrDefaultSocketJson(cwd)\n\n debugFn(\n 'inspect',\n 'override: socket.json sbt',\n sockJson?.defaults?.manifest?.sbt,\n )\n\n let { bin, out, sbtOpts, stdout, verbose } = cli.flags\n\n // Set defaults for any flag/arg that is not given. Check socket.json first.\n if (!bin) {\n if (sockJson.defaults?.manifest?.sbt?.bin) {\n bin = sockJson.defaults?.manifest?.sbt?.bin\n logger.info('Using default --bin from socket.json:', bin)\n } else {\n bin = 'sbt'\n }\n }\n if (\n stdout === undefined &&\n sockJson.defaults?.manifest?.sbt?.stdout !== undefined\n ) {\n stdout = sockJson.defaults?.manifest?.sbt?.stdout\n logger.info('Using default --stdout from socket.json:', stdout)\n }\n if (stdout) {\n out = '-'\n } else if (!out) {\n if (sockJson.defaults?.manifest?.sbt?.outfile) {\n out = sockJson.defaults?.manifest?.sbt?.outfile\n logger.info('Using default --out from socket.json:', out)\n } else {\n out = './socket.pom.xml'\n }\n }\n if (!sbtOpts) {\n if (sockJson.defaults?.manifest?.sbt?.sbtOpts) {\n sbtOpts = sockJson.defaults?.manifest?.sbt?.sbtOpts\n logger.info('Using default --sbt-opts from socket.json:', sbtOpts)\n } else {\n sbtOpts = ''\n }\n }\n if (\n verbose === undefined &&\n sockJson.defaults?.manifest?.sbt?.verbose !== undefined\n ) {\n verbose = sockJson.defaults?.manifest?.sbt?.verbose\n logger.info('Using default --verbose from socket.json:', verbose)\n } else if (verbose === undefined) {\n verbose = false\n }\n\n if (verbose) {\n logger.group('- ', parentName, config.commandName, ':')\n logger.group('- flags:', cli.flags)\n logger.groupEnd()\n logger.log('- input:', cli.input)\n logger.groupEnd()\n }\n\n // TODO: We're not sure it's feasible to parse source file from stdin. We could\n // try, store contents in a file in some folder, target that folder... what\n // would the file name be?\n\n const wasValidInput = checkCommandInput(outputKind, {\n nook: true,\n test: cli.input.length <= 1,\n message: 'Can only accept one DIR (make sure to escape spaces!)',\n fail: 'received ' + cli.input.length,\n })\n if (!wasValidInput) {\n return\n }\n\n if (verbose) {\n logger.group()\n logger.log('- target:', cwd)\n logger.log('- sbt bin:', bin)\n logger.log('- out:', out)\n logger.groupEnd()\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await convertSbtToMaven({\n bin: String(bin),\n cwd: cwd,\n out: String(out),\n sbtOpts: String(sbtOpts)\n .split(' ')\n .map(s => s.trim())\n .filter(Boolean),\n verbose: Boolean(verbose),\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport async function outputManifestSetup(result: CResult<unknown>) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.success('Setup complete')\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nimport { debugDir } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { input, select } from '@socketsecurity/registry/lib/prompts'\n\nimport { detectManifestActions } from './detect-manifest-actions.mts'\nimport {\n readSocketJsonSync,\n writeSocketJson,\n} from '../../utils/socket-json.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { SocketJson } from '../../utils/socket-json.mts'\n\nexport async function setupManifestConfig(\n cwd: string,\n defaultOnReadError = false,\n): Promise<CResult<unknown>> {\n const detected = await detectManifestActions(null, cwd)\n debugDir('inspect', { detected })\n\n // - repeat\n // - give the user an option to configure one of the supported targets\n // - run through an interactive prompt for selected target\n // - each target will have its own specific options\n // - record them to the socket.yml (or socket-cli.yml ? or just socket.json ?)\n\n const jsonPath = path.join(cwd, `socket.json`)\n if (fs.existsSync(jsonPath)) {\n logger.info(`Found socket.json at ${jsonPath}`)\n } else {\n logger.info(`No socket.json found at ${cwd}, will generate a new one`)\n }\n\n logger.log('')\n logger.log(\n 'Note: This tool will set up flag and argument defaults for certain',\n )\n logger.log(' CLI commands. You can still override them by explicitly')\n logger.log(' setting the flag. It is meant to be a convenience tool.')\n logger.log('')\n logger.log('This command will generate a socket.json file in the target cwd.')\n logger.log(\n 'You can choose to add this file to your repo (handy for collaboration)',\n )\n logger.log('or to add it to the ignored files, or neither. This file is only')\n logger.log('used in CLI workflows.')\n logger.log('')\n\n const choices = [\n {\n name: 'Conda'.padEnd(30, ' '),\n value: 'conda',\n description: 'Generate requirements.txt from a Conda environment.yml',\n },\n {\n name: 'Gradle'.padEnd(30, ' '),\n value: 'gradle',\n description: 'Generate pom.xml files through gradle',\n },\n {\n name: 'Kotlin (gradle)'.padEnd(30, ' '),\n value: 'gradle',\n description: 'Generate pom.xml files (for Kotlin) through gradle',\n },\n {\n name: 'Scala (gradle)'.padEnd(30, ' '),\n value: 'gradle',\n description: 'Generate pom.xml files (for Scala) through gradle',\n },\n {\n name: 'Scala (sbt)'.padEnd(30, ' '),\n value: 'sbt',\n description: 'Generate pom.xml files through sbt',\n },\n ]\n\n choices.forEach(obj => {\n if (detected[obj.value as keyof typeof detected]) {\n obj.name += ' [detected]'\n }\n })\n\n // Surface detected language first, then by alphabet\n choices.sort((a, b) => {\n if (\n detected[a.value as keyof typeof detected] &&\n !detected[b.value as keyof typeof detected]\n ) {\n return -1\n }\n if (\n !detected[a.value as keyof typeof detected] &&\n detected[b.value as keyof typeof detected]\n ) {\n return 1\n }\n return a.value < b.value ? -1 : a.value > b.value ? 1 : 0\n })\n\n // Make exit the last entry...\n choices.push({\n name: 'None, exit configurator',\n value: '',\n description: 'Exit setup',\n })\n\n // TODO: Use detected to list those first.\n const targetEco = (await select({\n message: 'Select ecosystem manifest generator to configure',\n choices,\n })) as string | null\n\n const sockJsonCResult = readSocketJsonSync(cwd, defaultOnReadError)\n if (!sockJsonCResult.ok) {\n return sockJsonCResult\n }\n const sockJson = sockJsonCResult.data\n\n if (!sockJson.defaults) {\n sockJson.defaults = {}\n }\n if (!sockJson.defaults.manifest) {\n sockJson.defaults.manifest = {}\n }\n\n let result: CResult<{ canceled: boolean }>\n switch (targetEco) {\n case 'conda': {\n if (!sockJson.defaults.manifest.conda) {\n sockJson.defaults.manifest.conda = {}\n }\n result = await setupConda(sockJson.defaults.manifest.conda)\n break\n }\n case 'gradle': {\n if (!sockJson.defaults.manifest.gradle) {\n sockJson.defaults.manifest.gradle = {}\n }\n result = await setupGradle(sockJson.defaults.manifest.gradle)\n break\n }\n case 'sbt': {\n if (!sockJson.defaults.manifest.sbt) {\n sockJson.defaults.manifest.sbt = {}\n }\n result = await setupSbt(sockJson.defaults.manifest.sbt)\n break\n }\n default: {\n result = canceledByUser()\n }\n }\n\n if (!result.ok || result.data.canceled) {\n return result\n }\n\n logger.log('')\n logger.log('Setup complete. Writing socket.json')\n logger.log('')\n\n if (\n await select({\n message: `Do you want to write the new config to ${jsonPath} ?`,\n choices: [\n {\n name: 'yes',\n value: true,\n description: 'Update config',\n },\n {\n name: 'no',\n value: false,\n description: 'Do not update the config',\n },\n ],\n })\n ) {\n return await writeSocketJson(cwd, sockJson)\n }\n\n return canceledByUser()\n}\n\nasync function setupConda(\n config: NonNullable<\n NonNullable<NonNullable<SocketJson['defaults']>['manifest']>['conda']\n >,\n): Promise<CResult<{ canceled: boolean }>> {\n const on = await askForEnabled(!config.disabled)\n if (on === undefined) {\n return canceledByUser()\n } else if (on) {\n delete config.disabled\n } else {\n config.disabled = true\n }\n\n const infile = await askForInputFile(config.infile || 'environment.yml')\n if (infile === undefined) {\n return canceledByUser()\n } else if (infile === '-') {\n config.stdin = true\n } else {\n delete config.stdin\n if (infile) {\n config.infile = infile\n } else {\n delete config.infile\n }\n }\n\n const stdout = await askForStdout(config.stdout)\n if (stdout === undefined) {\n return canceledByUser()\n } else if (stdout === 'yes') {\n config.stdout = true\n } else if (stdout === 'no') {\n config.stdout = false\n } else {\n delete config.stdout\n }\n\n if (!config.stdout) {\n const out = await askForOutputFile(config.outfile || 'requirements.txt')\n if (out === undefined) {\n return canceledByUser()\n } else if (out === '-') {\n config.stdout = true\n } else {\n delete config.stdout\n if (out) {\n config.outfile = out\n } else {\n delete config.outfile\n }\n }\n }\n\n const verbose = await askForVerboseFlag(config.verbose)\n if (verbose === undefined) {\n return canceledByUser()\n } else if (verbose === 'yes' || verbose === 'no') {\n config.verbose = verbose === 'yes'\n } else {\n delete config.verbose\n }\n\n return notCanceled()\n}\n\nasync function setupGradle(\n config: NonNullable<\n NonNullable<NonNullable<SocketJson['defaults']>['manifest']>['gradle']\n >,\n): Promise<CResult<{ canceled: boolean }>> {\n const bin = await askForBin(config.bin || './gradlew')\n if (bin === undefined) {\n return canceledByUser()\n } else if (bin) {\n config.bin = bin\n } else {\n delete config.bin\n }\n\n const opts = await input({\n message: '(--gradle-opts) Enter gradle options to pass through',\n default: config.gradleOpts || '',\n required: false,\n // validate: async string => bool\n })\n if (opts === undefined) {\n return canceledByUser()\n } else if (opts) {\n config.gradleOpts = opts\n } else {\n delete config.gradleOpts\n }\n\n const verbose = await askForVerboseFlag(config.verbose)\n if (verbose === undefined) {\n return canceledByUser()\n } else if (verbose === 'yes' || verbose === 'no') {\n config.verbose = verbose === 'yes'\n } else {\n delete config.verbose\n }\n\n return notCanceled()\n}\n\nasync function setupSbt(\n config: NonNullable<\n NonNullable<NonNullable<SocketJson['defaults']>['manifest']>['sbt']\n >,\n): Promise<CResult<{ canceled: boolean }>> {\n const bin = await askForBin(config.bin || 'sbt')\n if (bin === undefined) {\n return canceledByUser()\n } else if (bin) {\n config.bin = bin\n } else {\n delete config.bin\n }\n\n const opts = await input({\n message: '(--sbt-opts) Enter sbt options to pass through',\n default: config.sbtOpts || '',\n required: false,\n // validate: async string => bool\n })\n if (opts === undefined) {\n return canceledByUser()\n } else if (opts) {\n config.sbtOpts = opts\n } else {\n delete config.sbtOpts\n }\n\n const stdout = await askForStdout(config.stdout)\n if (stdout === undefined) {\n return canceledByUser()\n } else if (stdout === 'yes') {\n config.stdout = true\n } else if (stdout === 'no') {\n config.stdout = false\n } else {\n delete config.stdout\n }\n\n if (config.stdout !== true) {\n const out = await askForOutputFile(config.outfile || 'sbt.pom.xml')\n if (out === undefined) {\n return canceledByUser()\n } else if (out === '-') {\n config.stdout = true\n } else {\n delete config.stdout\n if (out) {\n config.outfile = out\n } else {\n delete config.outfile\n }\n }\n }\n\n const verbose = await askForVerboseFlag(config.verbose)\n if (verbose === undefined) {\n return canceledByUser()\n } else if (verbose === 'yes' || verbose === 'no') {\n config.verbose = verbose === 'yes'\n } else {\n delete config.verbose\n }\n\n return notCanceled()\n}\n\nasync function askForStdout(\n defaultValue: boolean | undefined,\n): Promise<string | undefined> {\n return await select({\n message: '(--stdout) Print the resulting pom.xml to stdout?',\n choices: [\n {\n name: 'no',\n value: 'no',\n description: 'Write output to a file, not stdout',\n },\n {\n name: 'yes',\n value: 'yes',\n description: 'Print in stdout (this will supersede --out)',\n },\n {\n name: '(leave default)',\n value: '',\n description: 'Do not store a setting for this',\n },\n ],\n default: defaultValue === true ? 'yes' : defaultValue === false ? 'no' : '',\n })\n}\n\nasync function askForEnabled(\n defaultValue: boolean | undefined,\n): Promise<boolean | undefined> {\n return await select({\n message:\n 'Do you want to enable or disable auto generating manifest files for this language in this dir?',\n choices: [\n {\n name: 'Enable',\n value: true,\n description: 'Generate manifest files for this language when detected',\n },\n {\n name: 'Disable',\n value: false,\n description:\n 'Do not generate manifest files for this language when detected, unless explicitly asking for it',\n },\n {\n name: 'Cancel',\n value: undefined,\n description: 'Exit configurator',\n },\n ],\n default:\n defaultValue === true\n ? 'enable'\n : defaultValue === false\n ? 'disable'\n : '',\n })\n}\n\nasync function askForInputFile(defaultName = ''): Promise<string | undefined> {\n return await input({\n message:\n '(--file) What should be the default file name to read? Should be an absolute path or relative to the cwd. Use `-` to read from stdin instead.' +\n (defaultName ? ' (Backspace to leave default)' : ''),\n default: defaultName,\n required: false,\n // validate: async string => bool\n })\n}\n\nasync function askForOutputFile(defaultName = ''): Promise<string | undefined> {\n return await input({\n message:\n '(--out) What should be the default output file? Should be absolute path or relative to cwd.' +\n (defaultName ? ' (Backspace to leave default)' : ''),\n default: defaultName,\n required: false,\n // validate: async string => bool\n })\n}\n\nasync function askForBin(defaultName = ''): Promise<string | undefined> {\n return await input({\n message:\n '(--bin) What should be the command to execute? Usually your build binary.' +\n (defaultName ? ' (Backspace to leave default)' : ''),\n default: defaultName,\n required: false,\n // validate: async string => bool\n })\n}\n\nasync function askForVerboseFlag(\n current: boolean | undefined,\n): Promise<string | undefined> {\n return await select({\n message: '(--verbose) Should this run in verbose mode by default?',\n choices: [\n {\n name: 'no',\n value: 'no',\n description: 'Do not run this manifest in verbose mode',\n },\n {\n name: 'yes',\n value: 'yes',\n description: 'Run this manifest in verbose mode',\n },\n {\n name: '(leave default)',\n value: '',\n description: 'Do not store a setting for this',\n },\n ],\n default: current === true ? 'yes' : current === false ? 'no' : '',\n })\n}\n\nfunction canceledByUser(): CResult<{ canceled: boolean }> {\n logger.log('')\n logger.info('User canceled')\n logger.log('')\n return { ok: true, data: { canceled: true } }\n}\n\nfunction notCanceled(): CResult<{ canceled: boolean }> {\n return { ok: true, data: { canceled: false } }\n}\n","import { outputManifestSetup } from './output-manifest-setup.mts'\nimport { setupManifestConfig } from './setup-manifest-config.mts'\n\nexport async function handleManifestSetup(\n cwd: string,\n defaultOnReadError: boolean,\n): Promise<void> {\n const result = await setupManifestConfig(cwd, defaultOnReadError)\n\n await outputManifestSetup(result)\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleManifestSetup } from './handle-manifest-setup.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'setup',\n description:\n 'Start interactive configurator to customize default flag values for `socket manifest` in this dir',\n hidden: false,\n flags: {\n ...commonFlags,\n defaultOnReadError: {\n type: 'boolean',\n description:\n 'If reading the socket.json fails, just use a default config? Warning: This might override the existing json file!',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [CWD=.]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n This command will try to detect all supported ecosystems in given CWD. Then\n it starts a configurator where you can setup default values for certain flags\n when creating manifest files in that dir. These configuration details are\n then stored in a local \\`socket.json\\` file (which you may or may not commit\n to the repo). Next time you run \\`socket manifest ...\\` it will load this\n json file and any flags which are not explicitly set in the command but which\n have been registered in the json file will get the default value set to that\n value you stored rather than the hardcoded defaults.\n\n This helps with for example when your build binary is in a particular path\n or when your build tool needs specific opts and you don't want to specify\n them when running the command every time.\n\n You can also disable manifest generation for certain ecosystems.\n\n This generated configuration file will only be used locally by the CLI. You\n can commit it to the repo (useful for collaboration) or choose to add it to\n your .gitignore all the same. Only this CLI will use it.\n\n Examples\n $ ${command}\n $ ${command} ./proj\n `,\n}\n\nexport const cmdManifestSetup = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { defaultOnReadError = false } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleManifestSetup(cwd, Boolean(defaultOnReadError))\n}\n","import { cmdManifestAuto } from './cmd-manifest-auto.mts'\nimport { cmdManifestCdxgen } from './cmd-manifest-cdxgen.mts'\nimport { cmdManifestConda } from './cmd-manifest-conda.mts'\nimport { cmdManifestGradle } from './cmd-manifest-gradle.mts'\nimport { cmdManifestKotlin } from './cmd-manifest-kotlin.mts'\nimport { cmdManifestScala } from './cmd-manifest-scala.mts'\nimport { cmdManifestSetup } from './cmd-manifest-setup.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'manifest',\n description: 'Generate a dependency manifest for certain ecosystems',\n hidden: false,\n flags: {\n ...commonFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <LANGUAGE> <TARGET>\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Generates a declarative dependency manifest (like a package.json for Node.JS\n or requirements.txt for PyPi), but for certain supported ecosystems\n where it's common to use a dynamic manifest, like Scala's sbt.\n\n Only certain languages are supported and there may be language specific\n configurations available. See \\`manifest <language> --help\\` for usage details\n per language.\n\n Currently supported language: scala [beta], gradle [beta], kotlin (through\n gradle) [beta].\n\n Examples\n\n $ ${command} scala .\n\n To have it auto-detect and attempt to run:\n\n $ ${command} auto\n `,\n}\n\nexport const cmdManifest = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n await meowWithSubcommands(\n {\n auto: cmdManifestAuto,\n cdxgen: cmdManifestCdxgen,\n conda: cmdManifestConda,\n gradle: cmdManifestGradle,\n kotlin: cmdManifestKotlin,\n scala: cmdManifestScala,\n setup: cmdManifestSetup,\n },\n {\n argv,\n aliases: {\n yolo: {\n description: config.description,\n hidden: true,\n argv: ['auto'],\n },\n },\n description: config.description,\n importMeta,\n flags: config.flags,\n name: `${parentName} ${config.commandName}`,\n },\n )\n}\n","import { createRequire } from 'node:module'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagApiRequirementsOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst require = createRequire(import.meta.url)\n\nexport const CMD_NAME = 'npm'\n\nconst description = 'Run npm with the Socket wrapper'\n\nconst hidden = false\n\nexport const cmdNpm = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n },\n help: command => `\n Usage\n $ ${command} ...\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Note: Everything after \"npm\" is passed to the npm command.\n Only the \\`--dry-run\\` and \\`--help\\` flags are caught here.\n\n Use \\`socket wrapper on\\` to alias this command as \\`npm\\`.\n\n Examples\n $ ${command}\n $ ${command} install -g cowsay\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n const shadowBin = /*@__PURE__*/ require(constants.shadowNpmBinPath)\n\n process.exitCode = 1\n\n const { spawnPromise } = await shadowBin('npm', argv, { stdio: 'inherit' })\n\n // See https://nodejs.org/api/child_process.html#event-exit.\n spawnPromise.process.on(\n 'exit',\n (code: string | null, signalName: NodeJS.Signals | null) => {\n if (signalName) {\n process.kill(process.pid, signalName)\n } else if (typeof code === 'number') {\n // eslint-disable-next-line n/no-process-exit\n process.exit(code)\n }\n },\n )\n\n await spawnPromise\n}\n","import { createRequire } from 'node:module'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagApiRequirementsOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst require = createRequire(import.meta.url)\n\nconst CMD_NAME = 'npx'\n\nconst description = 'Run npx with the Socket wrapper'\n\nconst hidden = false\n\nexport const cmdNpx = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n },\n help: (command, _config) => `\n Usage\n $ ${command} ...\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Note: Everything after \"npx\" is passed to the npx command.\n Only the \\`--dry-run\\` and \\`--help\\` flags are caught here.\n\n Use \\`socket wrapper on\\` to alias this command as \\`npx\\`.\n\n Examples\n $ ${command} cowsay\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n const shadowBin = /*@__PURE__*/ require(constants.shadowNpmBinPath)\n\n process.exitCode = 1\n\n const { spawnPromise } = await shadowBin('npx', argv, { stdio: 'inherit' })\n\n // See https://nodejs.org/api/child_process.html#event-exit.\n spawnPromise.process.on(\n 'exit',\n (code: string | null, signalName: NodeJS.Signals | null) => {\n if (signalName) {\n process.kill(process.pid, signalName)\n } else if (typeof code === 'number') {\n // eslint-disable-next-line n/no-process-exit\n process.exit(code)\n }\n },\n )\n\n await spawnPromise\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'oops',\n description: 'Trigger an intentional error (for development)',\n hidden: true,\n flags: {\n ...commonFlags,\n ...outputFlags,\n throw: {\n type: 'boolean',\n default: false,\n description:\n 'Throw an explicit error even if --json or --markdown are set',\n },\n },\n help: (parentName, config) => `\n Usage\n $ ${parentName} ${config.commandName}\n\n Don't run me.\n `,\n}\n\nexport const cmdOops = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, throw: justThrow } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n if (json && !justThrow) {\n process.exitCode = 1\n logger.log(\n serializeResultJson({\n ok: false,\n message: 'Oops',\n cause: 'This error was intentionally left blank',\n }),\n )\n }\n\n if (markdown && !justThrow) {\n process.exitCode = 1\n logger.fail(\n failMsgWithBadge('Oops', 'This error was intentionally left blank'),\n )\n return\n }\n\n throw new Error('This error was intentionally left blank')\n}\n","import constants from '../../constants.mts'\n\nimport type { EnvDetails } from '../../utils/package-environment.mts'\n\nconst { BUN, NPM, PNPM, VLT, YARN_BERRY, YARN_CLASSIC } = constants\n\nexport function matchLsCmdViewHumanStdout(stdout: string, name: string) {\n return stdout.includes(` ${name}@`)\n}\n\nexport function matchQueryCmdStdout(stdout: string, name: string) {\n return stdout.includes(`\"${name}\"`)\n}\n\nexport function lsStdoutIncludes(\n pkgEnvDetails: EnvDetails,\n stdout: string,\n name: string,\n): boolean {\n switch (pkgEnvDetails.agent) {\n case BUN:\n case YARN_BERRY:\n case YARN_CLASSIC:\n return matchLsCmdViewHumanStdout(stdout, name)\n case PNPM:\n case VLT:\n case NPM:\n default:\n return matchQueryCmdStdout(stdout, name)\n }\n}\n","import type { EnvDetails } from '../../utils/package-environment.mts'\n\nexport function getDependencyEntries(pkgEnvDetails: EnvDetails) {\n const {\n dependencies,\n devDependencies,\n optionalDependencies,\n peerDependencies,\n } = pkgEnvDetails.editablePkgJson.content\n return [\n [\n 'dependencies',\n dependencies ? { __proto__: null, ...dependencies } : undefined,\n ],\n [\n 'devDependencies',\n devDependencies ? { __proto__: null, ...devDependencies } : undefined,\n ],\n [\n 'peerDependencies',\n peerDependencies ? { __proto__: null, ...peerDependencies } : undefined,\n ],\n [\n 'optionalDependencies',\n optionalDependencies\n ? { __proto__: null, ...optionalDependencies }\n : undefined,\n ],\n ].filter(({ 1: o }) => o) as Array<[string, NonNullable<typeof dependencies>]>\n}\n","import constants from '../../constants.mts'\n\nimport type { NpmOverrides, Overrides, PnpmOrYarnOverrides } from './types.mts'\nimport type { Agent, EnvDetails } from '../../utils/package-environment.mts'\nimport type { PackageJson } from '@socketsecurity/registry/lib/packages'\n\nconst {\n BUN,\n NPM,\n OVERRIDES,\n PNPM,\n RESOLUTIONS,\n VLT,\n YARN_BERRY,\n YARN_CLASSIC,\n} = constants\n\nexport function getOverridesDataBun(\n pkgEnvDetails: EnvDetails,\n pkgJson = pkgEnvDetails.editablePkgJson.content,\n) {\n const overrides = (pkgJson?.[RESOLUTIONS] ?? {}) as PnpmOrYarnOverrides\n return { type: YARN_BERRY, overrides }\n}\n\n// npm overrides documentation:\n// https://docs.npmjs.com/cli/v10/configuring-npm/package-json#overrides\nexport function getOverridesDataNpm(\n pkgEnvDetails: EnvDetails,\n pkgJson = pkgEnvDetails.editablePkgJson.content,\n) {\n const overrides = (pkgJson?.[OVERRIDES] ?? {}) as NpmOverrides\n return { type: NPM, overrides }\n}\n\n// pnpm overrides documentation:\n// https://pnpm.io/package_json#pnpmoverrides\nexport function getOverridesDataPnpm(\n pkgEnvDetails: EnvDetails,\n pkgJson = pkgEnvDetails.editablePkgJson.content,\n) {\n const overrides = ((pkgJson as any)?.[PNPM]?.[OVERRIDES] ??\n {}) as PnpmOrYarnOverrides\n return { type: PNPM, overrides }\n}\n\nexport function getOverridesDataVlt(\n pkgEnvDetails: EnvDetails,\n pkgJson = pkgEnvDetails.editablePkgJson.content,\n) {\n const overrides = (pkgJson?.[OVERRIDES] ?? {}) as NpmOverrides\n return { type: VLT, overrides }\n}\n\n// Yarn resolutions documentation:\n// https://yarnpkg.com/configuration/manifest#resolutions\nexport function getOverridesDataYarn(\n pkgEnvDetails: EnvDetails,\n pkgJson = pkgEnvDetails.editablePkgJson.content,\n) {\n const overrides = (pkgJson?.[RESOLUTIONS] ?? {}) as PnpmOrYarnOverrides\n return { type: YARN_BERRY, overrides }\n}\n\n// Yarn resolutions documentation:\n// https://classic.yarnpkg.com/en/docs/selective-version-resolutions\nexport function getOverridesDataYarnClassic(\n pkgEnvDetails: EnvDetails,\n pkgJson = pkgEnvDetails.editablePkgJson.content,\n) {\n const overrides = (pkgJson?.[RESOLUTIONS] ?? {}) as PnpmOrYarnOverrides\n return { type: YARN_CLASSIC, overrides }\n}\n\nexport type GetOverrides = (\n pkgEnvDetails: EnvDetails,\n pkgJson?: PackageJson | undefined,\n) => GetOverridesResult\n\nexport type GetOverridesResult = { type: Agent; overrides: Overrides }\n\nexport function getOverridesData(\n pkgEnvDetails: EnvDetails,\n pkgJson?: PackageJson | undefined,\n): GetOverridesResult {\n switch (pkgEnvDetails.agent) {\n case BUN:\n return getOverridesDataBun(pkgEnvDetails, pkgJson)\n case PNPM:\n return getOverridesDataPnpm(pkgEnvDetails, pkgJson)\n case VLT:\n return getOverridesDataVlt(pkgEnvDetails, pkgJson)\n case YARN_BERRY:\n return getOverridesDataYarn(pkgEnvDetails, pkgJson)\n case YARN_CLASSIC:\n return getOverridesDataYarnClassic(pkgEnvDetails, pkgJson)\n case NPM:\n default:\n return getOverridesDataNpm(pkgEnvDetails, pkgJson)\n }\n}\n","import { escapeRegExp } from '@socketsecurity/registry/lib/regexps'\n\nimport constants from '../../constants.mts'\n\nimport type { EnvDetails } from '../../utils/package-environment.mts'\n\nconst { BUN, LOCK_EXT, NPM, PNPM, VLT, YARN_BERRY, YARN_CLASSIC } = constants\n\nexport function npmLockSrcIncludes(lockSrc: string, name: string) {\n // Detects the package name in the following cases:\n // \"name\":\n return lockSrc.includes(`\"${name}\":`)\n}\n\nexport function bunLockSrcIncludes(\n lockSrc: string,\n name: string,\n lockName?: string | undefined,\n) {\n // This is a bit counterintuitive. When lockName ends with a .lockb\n // we treat it as a yarn.lock. When lockName ends with a .lock we\n // treat it as a package-lock.json. The bun.lock format is not identical\n // package-lock.json, however it close enough for npmLockIncludes to work.\n const lockfileScanner = lockName?.endsWith(LOCK_EXT)\n ? npmLockSrcIncludes\n : yarnLockSrcIncludes\n return lockfileScanner(lockSrc, name)\n}\n\nexport function pnpmLockSrcIncludes(lockSrc: string, name: string) {\n const escapedName = escapeRegExp(name)\n return new RegExp(\n // Detects the package name.\n // v9.0 and v6.0 lockfile patterns:\n // 'name'\n // name:\n // name@\n // v6.0 lockfile patterns:\n // /name@\n `(?<=^\\\\s*)(?:'${escapedName}'|/?${escapedName}(?=[:@]))`,\n 'm',\n ).test(lockSrc)\n}\n\nexport function vltLockSrcIncludes(lockSrc: string, name: string) {\n // Detects the package name in the following cases:\n // \"name\"\n return lockSrc.includes(`\"${name}\"`)\n}\n\nexport function yarnLockSrcIncludes(lockSrc: string, name: string) {\n const escapedName = escapeRegExp(name)\n return new RegExp(\n // Detects the package name in the following cases:\n // \"name@\n // , \"name@\n // name@\n // , name@\n `(?<=(?:^\\\\s*|,\\\\s*)\"?)${escapedName}(?=@)`,\n 'm',\n ).test(lockSrc)\n}\n\nexport function lockSrcIncludes(\n pkgEnvDetails: EnvDetails,\n lockSrc: string,\n name: string,\n lockName?: string | undefined,\n): boolean {\n switch (pkgEnvDetails.agent) {\n case BUN:\n return bunLockSrcIncludes(lockSrc, name, lockName)\n case PNPM:\n return pnpmLockSrcIncludes(lockSrc, name)\n case VLT:\n return vltLockSrcIncludes(lockSrc, name)\n case YARN_BERRY:\n return yarnLockSrcIncludes(lockSrc, name)\n case YARN_CLASSIC:\n return yarnLockSrcIncludes(lockSrc, name)\n case NPM:\n default:\n return npmLockSrcIncludes(lockSrc, name)\n }\n}\n","import { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants.mts'\n\nimport type { EnvDetails } from '../../utils/package-environment.mts'\n\nconst { BUN, NPM, PNPM, VLT, YARN_BERRY, YARN_CLASSIC } = constants\n\nfunction cleanupQueryStdout(stdout: string): string {\n if (stdout === '') {\n return ''\n }\n let pkgs\n try {\n pkgs = JSON.parse(stdout)\n } catch {}\n if (!Array.isArray(pkgs) || !pkgs.length) {\n return ''\n }\n const names = new Set<string>()\n for (const { _id, name, pkgid } of pkgs) {\n // `npm query` results may not have a \"name\" property, in which case we\n // fallback to \"_id\" and then \"pkgid\".\n // `vlt ls --view json` results always have a \"name\" property.\n const fallback = _id ?? pkgid ?? ''\n const resolvedName = name ?? fallback.slice(0, fallback.indexOf('@', 1))\n // Add package names, except for those under the `@types` scope as those\n // are known to only be dev dependencies.\n if (resolvedName && !resolvedName.startsWith('@types/')) {\n names.add(resolvedName)\n }\n }\n return JSON.stringify(Array.from(names), null, 2)\n}\n\nfunction parsableToQueryStdout(stdout: string) {\n if (stdout === '') {\n return ''\n }\n // Convert the parsable stdout into a json array of unique names.\n // The matchAll regexp looks for a forward (posix) or backward (win32) slash\n // and matches one or more non-slashes until the newline.\n const names = new Set(stdout.matchAll(/(?<=[/\\\\])[^/\\\\]+(?=\\n)/g))\n return JSON.stringify(Array.from(names), null, 2)\n}\n\nasync function npmQuery(npmExecPath: string, cwd: string): Promise<string> {\n let stdout = ''\n try {\n stdout = (\n await spawn(npmExecPath, ['query', ':not(.dev)'], {\n cwd,\n shell: constants.WIN32,\n })\n ).stdout\n } catch {}\n return cleanupQueryStdout(stdout)\n}\n\nexport async function lsBun(\n pkgEnvDetails: EnvDetails,\n options?: AgentListDepsOptions | undefined,\n): Promise<string> {\n const { cwd = process.cwd() } = {\n __proto__: null,\n ...options,\n } as AgentListDepsOptions\n try {\n // Bun does not support filtering by production packages yet.\n // https://github.com/oven-sh/bun/issues/8283\n return (\n await spawn(pkgEnvDetails.agentExecPath, ['pm', 'ls', '--all'], {\n cwd,\n shell: constants.WIN32,\n })\n ).stdout\n } catch {}\n return ''\n}\n\nexport async function lsNpm(\n pkgEnvDetails: EnvDetails,\n options?: AgentListDepsOptions | undefined,\n): Promise<string> {\n const { cwd = process.cwd() } = {\n __proto__: null,\n ...options,\n } as AgentListDepsOptions\n return await npmQuery(pkgEnvDetails.agentExecPath, cwd)\n}\n\nexport async function lsPnpm(\n pkgEnvDetails: EnvDetails,\n options?: AgentListDepsOptions | undefined,\n): Promise<string> {\n const { cwd = process.cwd(), npmExecPath } = {\n __proto__: null,\n ...options,\n } as AgentListDepsOptions\n if (npmExecPath && npmExecPath !== NPM) {\n const result = await npmQuery(npmExecPath, cwd)\n if (result) {\n return result\n }\n }\n let stdout = ''\n try {\n stdout = (\n await spawn(\n pkgEnvDetails.agentExecPath,\n // Pnpm uses the alternative spelling of parsable.\n // https://en.wiktionary.org/wiki/parsable\n ['ls', '--parseable', '--prod', '--depth', 'Infinity'],\n {\n cwd,\n shell: constants.WIN32,\n },\n )\n ).stdout\n } catch {}\n return parsableToQueryStdout(stdout)\n}\n\nexport async function lsVlt(\n pkgEnvDetails: EnvDetails,\n options?: AgentListDepsOptions | undefined,\n): Promise<string> {\n const { cwd = process.cwd() } = {\n __proto__: null,\n ...options,\n } as AgentListDepsOptions\n let stdout = ''\n try {\n // See https://docs.vlt.sh/cli/commands/list#options.\n stdout = (\n await spawn(\n pkgEnvDetails.agentExecPath,\n ['ls', '--view', 'human', ':not(.dev)'],\n {\n cwd,\n shell: constants.WIN32,\n },\n )\n ).stdout\n } catch {}\n return cleanupQueryStdout(stdout)\n}\n\nexport async function lsYarnBerry(\n pkgEnvDetails: EnvDetails,\n options?: AgentListDepsOptions | undefined,\n): Promise<string> {\n const { cwd = process.cwd() } = {\n __proto__: null,\n ...options,\n } as AgentListDepsOptions\n try {\n // Yarn Berry does not support filtering by production packages yet.\n // https://github.com/yarnpkg/berry/issues/5117\n return (\n await spawn(\n pkgEnvDetails.agentExecPath,\n ['info', '--recursive', '--name-only'],\n {\n cwd,\n shell: constants.WIN32,\n },\n )\n ).stdout\n } catch {}\n return ''\n}\n\nexport async function lsYarnClassic(\n pkgEnvDetails: EnvDetails,\n options?: AgentListDepsOptions | undefined,\n): Promise<string> {\n const { cwd = process.cwd() } = {\n __proto__: null,\n ...options,\n } as AgentListDepsOptions\n try {\n // However, Yarn Classic does support it.\n // https://github.com/yarnpkg/yarn/releases/tag/v1.0.0\n // > Fix: Excludes dev dependencies from the yarn list output when the\n // environment is production\n return (\n await spawn(pkgEnvDetails.agentExecPath, ['list', '--prod'], {\n cwd,\n shell: constants.WIN32,\n })\n ).stdout\n } catch {}\n return ''\n}\n\nexport type AgentListDepsOptions = {\n cwd?: string | undefined\n npmExecPath?: string | undefined\n}\n\nexport async function listPackages(\n pkgEnvDetails: EnvDetails,\n options?: AgentListDepsOptions | undefined,\n): Promise<string> {\n switch (pkgEnvDetails.agent) {\n case BUN:\n return await lsBun(pkgEnvDetails, options)\n case PNPM:\n return await lsPnpm(pkgEnvDetails, options)\n case VLT:\n return await lsVlt(pkgEnvDetails, options)\n case YARN_BERRY:\n return await lsYarnBerry(pkgEnvDetails, options)\n case YARN_CLASSIC:\n return await lsYarnClassic(pkgEnvDetails, options)\n case NPM:\n default:\n return await lsNpm(pkgEnvDetails, options)\n }\n}\n","export const CMD_NAME = 'socket optimize'\n","import { hasKeys, isObject } from '@socketsecurity/registry/lib/objects'\n\nimport constants from '../../constants.mts'\n\nimport type { Overrides } from './types.mts'\nimport type { Agent } from '../../utils/package-environment.mts'\nimport type { EditablePackageJson } from '@socketsecurity/registry/lib/packages'\n\nconst {\n BUN,\n NPM,\n OVERRIDES,\n PNPM,\n RESOLUTIONS,\n VLT,\n YARN_BERRY,\n YARN_CLASSIC,\n} = constants\n\nconst depFields = [\n 'dependencies',\n 'devDependencies',\n 'peerDependencies',\n 'peerDependenciesMeta',\n 'optionalDependencies',\n 'bundleDependencies',\n]\n\nfunction getEntryIndexes(\n entries: Array<[string | symbol, any]>,\n keys: Array<string | symbol>,\n): number[] {\n return keys\n .map(n => entries.findIndex(p => p[0] === n))\n .filter(n => n !== -1)\n .sort((a, b) => a - b)\n}\n\nfunction getLowestEntryIndex(\n entries: Array<[string | symbol, any]>,\n keys: Array<string | symbol>,\n) {\n return getEntryIndexes(entries, keys)?.[0] ?? -1\n}\n\nfunction getHighestEntryIndex(\n entries: Array<[string | symbol, any]>,\n keys: Array<string | symbol>,\n) {\n return getEntryIndexes(entries, keys).at(-1) ?? -1\n}\n\nfunction updatePkgJsonField(\n editablePkgJson: EditablePackageJson,\n field: string,\n value: any,\n) {\n const oldValue = editablePkgJson.content[field]\n if (oldValue) {\n // The field already exists so we simply update the field value.\n if (field === PNPM) {\n const isPnpmObj = isObject(oldValue)\n if (hasKeys(value)) {\n editablePkgJson.update({\n [field]: {\n ...(isPnpmObj ? oldValue : {}),\n overrides: {\n ...(isPnpmObj ? (oldValue as any)[OVERRIDES] : {}),\n ...value,\n },\n },\n })\n } else {\n // Properties with undefined values are deleted when saved as JSON.\n editablePkgJson.update(\n (hasKeys(oldValue)\n ? {\n [field]: {\n ...(isPnpmObj ? oldValue : {}),\n overrides: undefined,\n },\n }\n : { [field]: undefined }) as typeof editablePkgJson.content,\n )\n }\n } else if (field === OVERRIDES || field === RESOLUTIONS) {\n // Properties with undefined values are deleted when saved as JSON.\n editablePkgJson.update({\n [field]: hasKeys(value) ? value : undefined,\n } as typeof editablePkgJson.content)\n } else {\n editablePkgJson.update({ [field]: value })\n }\n return\n }\n if (\n (field === OVERRIDES || field === PNPM || field === RESOLUTIONS) &&\n !hasKeys(value)\n ) {\n return\n }\n // Since the field doesn't exist we want to insert it into the package.json\n // in a place that makes sense, e.g. close to the \"dependencies\" field. If\n // we can't find a place to insert the field we'll add it to the bottom.\n const entries = Object.entries(editablePkgJson.content)\n let insertIndex = -1\n let isPlacingHigher = false\n if (field === OVERRIDES) {\n insertIndex = getLowestEntryIndex(entries, [RESOLUTIONS])\n if (insertIndex === -1) {\n isPlacingHigher = true\n insertIndex = getHighestEntryIndex(entries, [...depFields, PNPM])\n }\n } else if (field === RESOLUTIONS) {\n isPlacingHigher = true\n insertIndex = getHighestEntryIndex(entries, [...depFields, OVERRIDES, PNPM])\n } else if (field === PNPM) {\n insertIndex = getLowestEntryIndex(entries, [OVERRIDES, RESOLUTIONS])\n if (insertIndex === -1) {\n isPlacingHigher = true\n insertIndex = getHighestEntryIndex(entries, depFields)\n }\n }\n if (insertIndex === -1) {\n insertIndex = getLowestEntryIndex(entries, ['engines', 'files'])\n }\n if (insertIndex === -1) {\n isPlacingHigher = true\n insertIndex = getHighestEntryIndex(entries, ['exports', 'imports', 'main'])\n }\n if (insertIndex === -1) {\n insertIndex = entries.length\n } else if (isPlacingHigher) {\n insertIndex += 1\n }\n entries.splice(insertIndex, 0, [\n field,\n field === PNPM ? { [OVERRIDES]: value } : value,\n ])\n editablePkgJson.fromJSON(\n `${JSON.stringify(Object.fromEntries(entries), null, 2)}\\n`,\n )\n}\n\nexport function updateOverridesField(\n editablePkgJson: EditablePackageJson,\n overrides: Overrides,\n) {\n updatePkgJsonField(editablePkgJson, OVERRIDES, overrides)\n}\n\nexport function updateResolutionsField(\n editablePkgJson: EditablePackageJson,\n overrides: Overrides,\n) {\n updatePkgJsonField(editablePkgJson, RESOLUTIONS, overrides)\n}\n\nexport function updatePnpmField(\n editablePkgJson: EditablePackageJson,\n overrides: Overrides,\n) {\n updatePkgJsonField(editablePkgJson, PNPM, overrides)\n}\n\nexport function updateManifest(\n agent: Agent,\n editablePkgJson: EditablePackageJson,\n overrides: Overrides,\n): void {\n switch (agent) {\n case BUN:\n updateResolutionsField(editablePkgJson, overrides)\n return\n case PNPM:\n updatePnpmField(editablePkgJson, overrides)\n return\n case VLT:\n updateOverridesField(editablePkgJson, overrides)\n return\n case YARN_BERRY:\n updateResolutionsField(editablePkgJson, overrides)\n return\n case YARN_CLASSIC:\n updateResolutionsField(editablePkgJson, overrides)\n return\n case NPM:\n default:\n updateOverridesField(editablePkgJson, overrides)\n return\n }\n}\n","import path from 'node:path'\n\nimport semver from 'semver'\n\nimport { getManifestData } from '@socketsecurity/registry'\nimport { hasOwn, toSortedObject } from '@socketsecurity/registry/lib/objects'\nimport { fetchPackageManifest } from '@socketsecurity/registry/lib/packages'\nimport { pEach } from '@socketsecurity/registry/lib/promises'\nimport { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nimport { lsStdoutIncludes } from './deps-includes-by-agent.mts'\nimport { getDependencyEntries } from './get-dependency-entries.mts'\nimport {\n getOverridesData,\n getOverridesDataNpm,\n getOverridesDataYarnClassic,\n} from './get-overrides-by-agent.mts'\nimport { lockSrcIncludes } from './lockfile-includes-by-agent.mts'\nimport { listPackages } from './ls-by-agent.mts'\nimport { CMD_NAME } from './shared.mts'\nimport { updateManifest } from './update-manifest-by-agent.mts'\nimport { cmdPrefixMessage } from '../../utils/cmd.mts'\nimport { globWorkspace } from '../../utils/glob.mts'\nimport { npa } from '../../utils/npm-package-arg.mts'\nimport { getMajor } from '../../utils/semver.mts'\n\nimport type { GetOverridesResult } from './get-overrides-by-agent.mts'\nimport type { AliasResult } from '../../utils/npm-package-arg.mts'\nimport type { EnvDetails } from '../../utils/package-environment.mts'\nimport type { Logger } from '@socketsecurity/registry/lib/logger'\nimport type { PackageJson } from '@socketsecurity/registry/lib/packages'\n\ntype AddOverridesOptions = {\n logger?: Logger | undefined\n pin?: boolean | undefined\n prod?: boolean | undefined\n spinner?: Spinner | undefined\n state?: AddOverridesState | undefined\n}\ntype AddOverridesState = {\n added: Set<string>\n addedInWorkspaces: Set<string>\n updated: Set<string>\n updatedInWorkspaces: Set<string>\n warnedPnpmWorkspaceRequiresNpm: boolean\n}\n\nconst manifestNpmOverrides = getManifestData('npm')\n\nexport async function addOverrides(\n pkgEnvDetails: EnvDetails,\n pkgPath: string,\n options?: AddOverridesOptions | undefined,\n): Promise<AddOverridesState> {\n const {\n agent,\n lockName,\n lockSrc,\n npmExecPath,\n pkgPath: rootPath,\n } = pkgEnvDetails\n const {\n logger,\n pin,\n prod,\n spinner,\n state = {\n added: new Set(),\n addedInWorkspaces: new Set(),\n updated: new Set(),\n updatedInWorkspaces: new Set(),\n warnedPnpmWorkspaceRequiresNpm: false,\n },\n } = { __proto__: null, ...options } as AddOverridesOptions\n const workspacePkgJsonPaths = await globWorkspace(agent, pkgPath)\n const isPnpm = agent === 'pnpm'\n const isWorkspace = workspacePkgJsonPaths.length > 0\n const isWorkspaceRoot = pkgPath === rootPath\n const isLockScanned = isWorkspaceRoot && !prod\n const workspace = isWorkspaceRoot ? 'root' : path.relative(rootPath, pkgPath)\n if (\n isWorkspace &&\n isPnpm &&\n // npmExecPath will === the agent name IF it CANNOT be resolved.\n npmExecPath === 'npm' &&\n !state.warnedPnpmWorkspaceRequiresNpm\n ) {\n state.warnedPnpmWorkspaceRequiresNpm = true\n spinner?.stop()\n logger?.warn(\n cmdPrefixMessage(\n CMD_NAME,\n `${agent} workspace support requires \\`npm ls\\`, falling back to \\`${agent} list\\``,\n ),\n )\n spinner?.start()\n }\n\n const overridesDataObjects = [] as GetOverridesResult[]\n if (isWorkspace || pkgEnvDetails.editablePkgJson.content['private']) {\n overridesDataObjects.push(getOverridesData(pkgEnvDetails))\n } else {\n overridesDataObjects.push(\n getOverridesDataNpm(pkgEnvDetails),\n getOverridesDataYarnClassic(pkgEnvDetails),\n )\n }\n\n const depAliasMap = new Map<string, string>()\n const depEntries = getDependencyEntries(pkgEnvDetails)\n const manifestEntries = manifestNpmOverrides.filter(({ 1: data }) =>\n semver.satisfies(\n // Roughly check Node range as semver.coerce will strip leading\n // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).\n semver.coerce(data.engines.node)!,\n pkgEnvDetails.pkgRequirements.node,\n ),\n )\n\n const addingText = `Adding overrides to ${workspace}...`\n let loggedAddingText = false\n\n // Chunk package names to process them in parallel 3 at a time.\n await pEach(\n manifestEntries,\n async ({ 1: data }) => {\n const { name: sockRegPkgName, package: origPkgName, version } = data\n const major = getMajor(version)!\n const sockOverridePrefix = `npm:${sockRegPkgName}@`\n const sockOverrideSpec = `${sockOverridePrefix}${pin ? version : `^${major}`}`\n for (const { 1: depObj } of depEntries) {\n const sockSpec = hasOwn(depObj, sockRegPkgName)\n ? depObj[sockRegPkgName]\n : undefined\n if (sockSpec) {\n depAliasMap.set(sockRegPkgName, sockSpec)\n }\n const origSpec = hasOwn(depObj, origPkgName)\n ? depObj[origPkgName]\n : undefined\n if (origSpec) {\n let thisSpec = origSpec\n // Add package aliases for direct dependencies to avoid npm EOVERRIDE\n // errors...\n // https://docs.npmjs.com/cli/v8/using-npm/package-spec#aliases\n if (\n // ...if the spec doesn't start with a valid Socket override.\n !(\n thisSpec.startsWith(sockOverridePrefix) &&\n // Check the validity of the spec by passing it through npa and\n // seeing if it will coerce to a version.\n semver.coerce((npa(thisSpec) as AliasResult).subSpec.rawSpec)\n ?.version\n )\n ) {\n thisSpec = sockOverrideSpec\n depObj[origPkgName] = thisSpec\n state.added.add(sockRegPkgName)\n if (!isWorkspaceRoot) {\n state.addedInWorkspaces.add(workspace)\n }\n if (!loggedAddingText) {\n spinner?.setText(addingText)\n loggedAddingText = true\n }\n }\n depAliasMap.set(origPkgName, thisSpec)\n }\n }\n if (isWorkspaceRoot) {\n // The lockSrcIncludes and lsStdoutIncludes functions overlap in their\n // first two parameters. lockSrcIncludes accepts an optional third parameter\n // which lsStdoutIncludes will ignore.\n const thingScanner = (\n isLockScanned ? lockSrcIncludes : lsStdoutIncludes\n ) as typeof lockSrcIncludes\n\n const thingToScan = isLockScanned\n ? lockSrc\n : await listPackages(pkgEnvDetails, { cwd: pkgPath, npmExecPath })\n // Chunk package names to process them in parallel 3 at a time.\n await pEach(\n overridesDataObjects,\n async ({ overrides, type }) => {\n const overrideExists = hasOwn(overrides, origPkgName)\n if (\n overrideExists ||\n thingScanner(pkgEnvDetails, thingToScan, origPkgName, lockName)\n ) {\n const oldSpec = overrideExists\n ? overrides[origPkgName]!\n : undefined\n const origDepAlias = depAliasMap.get(origPkgName)\n const sockRegDepAlias = depAliasMap.get(sockRegPkgName)\n const depAlias = sockRegDepAlias ?? origDepAlias\n let newSpec = sockOverrideSpec\n if (type === 'npm' && depAlias) {\n // With npm one may not set an override for a package that one directly\n // depends on unless both the dependency and the override itself share\n // the exact same spec. To make this limitation easier to deal with,\n // overrides may also be defined as a reference to a spec for a direct\n // dependency by prefixing the name of the package to match the version\n // of with a $.\n // https://docs.npmjs.com/cli/v8/configuring-npm/package-json#overrides\n newSpec = `$${sockRegDepAlias ? sockRegPkgName : origPkgName}`\n } else if (typeof oldSpec === 'string') {\n const thisSpec = oldSpec.startsWith('$')\n ? depAlias || newSpec\n : oldSpec || newSpec\n if (thisSpec.startsWith(sockOverridePrefix)) {\n if (\n pin &&\n getMajor(\n // Check the validity of the spec by passing it through npa\n // and seeing if it will coerce to a version. semver.coerce\n // will strip leading v's, carets (^), comparators (<,<=,>,>=,=),\n // and tildes (~). If not coerced to a valid version then\n // default to the manifest entry version.\n semver.coerce(\n (npa(thisSpec) as AliasResult).subSpec.rawSpec,\n )?.version ?? version,\n ) !== major\n ) {\n const otherVersion = (await fetchPackageManifest(thisSpec))\n ?.version\n if (otherVersion && otherVersion !== version) {\n newSpec = `${sockOverridePrefix}${pin ? otherVersion : `^${getMajor(otherVersion)!}`}`\n }\n }\n } else {\n newSpec = oldSpec\n }\n }\n if (newSpec !== oldSpec) {\n overrides[origPkgName] = newSpec\n const addedOrUpdated = overrideExists ? 'updated' : 'added'\n state[addedOrUpdated].add(sockRegPkgName)\n if (!loggedAddingText) {\n spinner?.setText(addingText)\n loggedAddingText = true\n }\n }\n }\n },\n { concurrency: 3 },\n )\n }\n },\n { concurrency: 3 },\n )\n\n if (isWorkspace) {\n // Chunk package names to process them in parallel 3 at a time.\n await pEach(\n workspacePkgJsonPaths,\n async workspacePkgJsonPath => {\n const otherState = await addOverrides(\n pkgEnvDetails,\n path.dirname(workspacePkgJsonPath),\n {\n logger,\n pin,\n prod,\n spinner,\n },\n )\n for (const key of [\n 'added',\n 'addedInWorkspaces',\n 'updated',\n 'updatedInWorkspaces',\n ] satisfies\n // Here we're just telling TS that we're looping over key names\n // of the type and that they're all Set<string> props.\n Array<\n keyof Pick<\n AddOverridesState,\n 'added' | 'addedInWorkspaces' | 'updated' | 'updatedInWorkspaces'\n >\n >) {\n for (const value of otherState[key]) {\n state[key].add(value)\n }\n }\n },\n { concurrency: 3 },\n )\n }\n\n if (state.added.size > 0 || state.updated.size > 0) {\n pkgEnvDetails.editablePkgJson.update(\n Object.fromEntries(depEntries) as PackageJson,\n )\n if (isWorkspaceRoot) {\n for (const { overrides, type } of overridesDataObjects) {\n updateManifest(\n type,\n pkgEnvDetails.editablePkgJson,\n toSortedObject(overrides),\n )\n }\n }\n await pkgEnvDetails.editablePkgJson.save()\n }\n\n return state\n}\n","import { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nimport constants from '../../constants.mts'\nimport { runAgentInstall } from '../../utils/agent.mts'\nimport { cmdPrefixMessage } from '../../utils/cmd.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { EnvDetails } from '../../utils/package-environment.mts'\nimport type { Logger } from '@socketsecurity/registry/lib/logger'\n\nconst { NPM_BUGGY_OVERRIDES_PATCHED_VERSION } = constants\n\nexport type UpdateLockfileOptions = {\n cmdName?: string | undefined\n logger?: Logger | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function updateLockfile(\n pkgEnvDetails: EnvDetails,\n options: UpdateLockfileOptions,\n): Promise<CResult<unknown>> {\n const {\n cmdName = '',\n logger,\n spinner,\n } = {\n __proto__: null,\n ...options,\n } as UpdateLockfileOptions\n\n const wasSpinning = !!spinner?.isSpinning\n\n spinner?.start(`Updating ${pkgEnvDetails.lockName}...`)\n\n try {\n await runAgentInstall(pkgEnvDetails, { spinner })\n if (pkgEnvDetails.features.npmBuggyOverrides) {\n spinner?.stop()\n logger?.log(\n `💡 Re-run ${cmdName ? `${cmdName} ` : ''}whenever ${pkgEnvDetails.lockName} changes.\\n This can be skipped for ${pkgEnvDetails.agent} >=${NPM_BUGGY_OVERRIDES_PATCHED_VERSION}.`,\n )\n }\n } catch (e) {\n spinner?.stop()\n\n debugFn('error', 'fail: update')\n debugDir('inspect', { error: e })\n\n if (wasSpinning) {\n spinner.start()\n }\n\n return {\n ok: false,\n message: 'Update failed',\n cause: cmdPrefixMessage(\n cmdName,\n `${pkgEnvDetails.agent} install failed to update ${pkgEnvDetails.lockName}`,\n ),\n }\n }\n\n spinner?.stop()\n\n if (wasSpinning) {\n spinner.start()\n }\n\n return { ok: true, data: undefined }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { addOverrides } from './add-overrides.mts'\nimport { CMD_NAME } from './shared.mts'\nimport { updateLockfile } from './update-lockfile.mts'\nimport constants from '../../constants.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { EnvDetails } from '../../utils/package-environment.mts'\n\nexport type OptimizeConfig = {\n pin: boolean\n prod: boolean\n}\n\nexport async function applyOptimization(\n pkgEnvDetails: EnvDetails,\n { pin, prod }: OptimizeConfig,\n): Promise<\n CResult<{\n addedCount: number\n updatedCount: number\n pkgJsonChanged: boolean\n updatedInWorkspaces: number\n addedInWorkspaces: number\n }>\n> {\n const { spinner } = constants\n\n spinner.start()\n\n const state = await addOverrides(pkgEnvDetails, pkgEnvDetails.pkgPath, {\n logger,\n pin,\n prod,\n spinner,\n })\n\n const addedCount = state.added.size\n const updatedCount = state.updated.size\n const pkgJsonChanged = addedCount > 0 || updatedCount > 0\n\n if (pkgJsonChanged || pkgEnvDetails.features.npmBuggyOverrides) {\n const result = await updateLockfile(pkgEnvDetails, {\n cmdName: CMD_NAME,\n logger,\n spinner,\n })\n if (!result.ok) {\n spinner.stop()\n return result\n }\n }\n\n spinner.stop()\n return {\n ok: true,\n data: {\n addedCount,\n addedInWorkspaces: state.addedInWorkspaces.size,\n pkgJsonChanged,\n updatedCount,\n updatedInWorkspaces: state.updatedInWorkspaces.size,\n },\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputOptimizeResult(\n result: CResult<{\n addedCount: number\n updatedCount: number\n pkgJsonChanged: boolean\n updatedInWorkspaces: number\n addedInWorkspaces: number\n }>,\n outputKind: OutputKind,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const data = result.data\n\n if (data.updatedCount > 0) {\n logger?.log(\n `${createActionMessage('Updated', data.updatedCount, data.updatedInWorkspaces)}${data.addedCount ? '.' : '🚀'}`,\n )\n }\n if (data.addedCount > 0) {\n logger?.log(\n `${createActionMessage('Added', data.addedCount, data.addedInWorkspaces)} 🚀`,\n )\n }\n if (!data.pkgJsonChanged) {\n logger?.log('Scan complete. No Socket.dev optimized overrides applied.')\n }\n\n logger.log('')\n logger.success('Finished!')\n logger.log('')\n}\n\nfunction createActionMessage(\n verb: string,\n overrideCount: number,\n workspaceCount: number,\n): string {\n return `${verb} ${overrideCount} Socket.dev optimized ${pluralize('override', overrideCount)}${workspaceCount ? ` in ${workspaceCount} ${pluralize('workspace', workspaceCount)}` : ''}`\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { applyOptimization } from './apply-optimization.mts'\nimport { outputOptimizeResult } from './output-optimize-result.mts'\nimport { CMD_NAME } from './shared.mts'\nimport constants from '../../constants.mts'\nimport { cmdPrefixMessage } from '../../utils/cmd.mts'\nimport { detectAndValidatePackageEnvironment } from '../../utils/package-environment.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nconst { VLT } = constants\n\nexport async function handleOptimize({\n cwd,\n outputKind,\n pin,\n prod,\n}: {\n cwd: string\n outputKind: OutputKind\n pin: boolean\n prod: boolean\n}) {\n const pkgEnvCResult = await detectAndValidatePackageEnvironment(cwd, {\n cmdName: CMD_NAME,\n logger,\n prod,\n })\n if (!pkgEnvCResult.ok) {\n await outputOptimizeResult(pkgEnvCResult, outputKind)\n return\n }\n\n const pkgEnvDetails = pkgEnvCResult.data\n if (!pkgEnvDetails) {\n await outputOptimizeResult(\n {\n ok: false,\n message: 'No package found.',\n cause: `No valid package environment found for project path: ${cwd}`,\n },\n outputKind,\n )\n return\n }\n\n const { agent, agentVersion } = pkgEnvDetails\n if (agent === VLT) {\n await outputOptimizeResult(\n {\n ok: false,\n message: 'Unsupported',\n cause: cmdPrefixMessage(\n CMD_NAME,\n `${agent} v${agentVersion} does not support overrides.`,\n ),\n },\n outputKind,\n )\n return\n }\n\n logger.info(`Optimizing packages for ${agent} v${agentVersion}.\\n`)\n\n await outputOptimizeResult(\n await applyOptimization(pkgEnvDetails, { pin, prod }),\n outputKind,\n )\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleOptimize } from './handle-optimize.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'optimize'\n\nconst description = 'Optimize dependencies with @socketregistry overrides'\n\nconst hidden = false\n\nexport const cmdOptimize = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n pin: {\n type: 'boolean',\n default: false,\n description: 'Pin overrides to their latest version',\n },\n prod: {\n type: 'boolean',\n default: false,\n description: 'Only add overrides for production dependencies',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} ./proj/tree --pin\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n const { json, markdown, pin, prod } = cli.flags\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n const outputKind = getOutputKind(json, markdown)\n\n await handleOptimize({\n cwd,\n pin: Boolean(pin),\n outputKind,\n prod: Boolean(prod),\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchDependenciesConfig = {\n limit: number\n offset: number\n}\n\nexport type FetchDependenciesOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchDependencies(\n config: FetchDependenciesConfig,\n options?: FetchDependenciesOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'searchDependencies'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchDependenciesOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n const { limit, offset } = {\n __proto__: null,\n ...config,\n } as FetchDependenciesConfig\n\n return await handleApiCall(sockSdk.searchDependencies({ limit, offset }), {\n desc: 'organization dependencies',\n })\n}\n","// @ts-ignore\nimport chalkTable from 'chalk-table'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputDependencies(\n result: CResult<SocketSdkSuccessResult<'searchDependencies'>['data']>,\n {\n limit,\n offset,\n outputKind,\n }: {\n limit: number\n offset: number\n outputKind: OutputKind\n },\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n outputMarkdown(result.data, { limit, offset })\n}\n\nfunction outputMarkdown(\n result: SocketSdkSuccessResult<'searchDependencies'>['data'],\n {\n limit,\n offset,\n }: {\n limit: number\n offset: number\n },\n) {\n logger.log('# Organization dependencies')\n logger.log('')\n logger.log('Request details:')\n logger.log('- Offset:', offset)\n logger.log('- Limit:', limit)\n logger.log('- Is there more data after this?', result.end ? 'no' : 'yes')\n logger.log('')\n\n const options = {\n columns: [\n { field: 'type', name: colors.cyan('Ecosystem') },\n { field: 'namespace', name: colors.cyan('Namespace') },\n { field: 'name', name: colors.cyan('Name') },\n { field: 'version', name: colors.cyan('Version') },\n { field: 'repository', name: colors.cyan('Repository') },\n { field: 'branch', name: colors.cyan('Branch') },\n { field: 'direct', name: colors.cyan('Direct') },\n ],\n }\n\n logger.log(chalkTable(options, result.rows))\n}\n","import { fetchDependencies } from './fetch-dependencies.mts'\nimport { outputDependencies } from './output-dependencies.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleDependencies({\n limit,\n offset,\n outputKind,\n}: {\n limit: number\n offset: number\n outputKind: OutputKind\n}): Promise<void> {\n const result = await fetchDependencies({ limit, offset })\n\n await outputDependencies(result, { limit, offset, outputKind })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleDependencies } from './handle-dependencies.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'dependencies'\n\nconst description =\n 'Search for any dependency that is being used in your organization'\n\nconst hidden = false\n\nexport const cmdOrganizationDependencies = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n limit: {\n type: 'number',\n default: 50,\n description: 'Maximum number of dependencies returned',\n },\n offset: {\n type: 'number',\n default: 0,\n description: 'Page number',\n },\n ...outputFlags,\n },\n help: (command, config) => `\n Usage\n ${command} [options]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n ${command}\n ${command} --limit 20 --offset 10\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, limit, markdown, offset } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleDependencies({\n limit: Number(limit || 0) || 0,\n offset: Number(offset || 0) || 0,\n outputKind,\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchLicensePolicyOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchLicensePolicy(\n orgSlug: string,\n options?: FetchLicensePolicyOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgLicensePolicy'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchLicensePolicyOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getOrgLicensePolicy(orgSlug), {\n desc: 'organization license policy',\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mdTableOfPairs } from '../../utils/markdown.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputLicensePolicy(\n result: CResult<SocketSdkSuccessResult<'getOrgLicensePolicy'>['data']>,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.info('Use --json to get the full result')\n logger.log('# License policy')\n logger.log('')\n logger.log('This is the license policy for your organization:')\n logger.log('')\n const rules = result.data['license_policy']!\n const entries = rules ? Object.entries(rules) : []\n const mapped: Array<[string, string]> = entries.map(\n ({ 0: key, 1: value }) =>\n [key, (value as any)?.['allowed'] ? ' yes' : ' no'] as const,\n )\n mapped.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n logger.log(mdTableOfPairs(mapped, ['License Name', 'Allowed']))\n logger.log('')\n}\n","import { fetchLicensePolicy } from './fetch-license-policy.mts'\nimport { outputLicensePolicy } from './output-license-policy.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleLicensePolicy(\n orgSlug: string,\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchLicensePolicy(orgSlug)\n\n await outputLicensePolicy(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleLicensePolicy } from './handle-license-policy.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'license'\n\nconst description = 'Retrieve the license policy of an organization'\n\nconst hidden = false\n\nexport const cmdOrganizationPolicyLicense = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: command => `\n Usage\n $ ${command} [options]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Your API token will need the \\`license-policy:read\\` permission otherwise\n the request will fail with an authentication error.\n\n Examples\n $ ${command}\n $ ${command} --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleLicensePolicy(orgSlug, outputKind)\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchSecurityPolicyOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchSecurityPolicy(\n orgSlug: string,\n options?: FetchSecurityPolicyOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgSecurityPolicy'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchSecurityPolicyOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getOrgSecurityPolicy(orgSlug), {\n desc: 'organization security policy',\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mdTableOfPairs } from '../../utils/markdown.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputSecurityPolicy(\n result: CResult<SocketSdkSuccessResult<'getOrgSecurityPolicy'>['data']>,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log('# Security policy')\n logger.log('')\n logger.log(\n `The default security policy setting is: \"${result.data.securityPolicyDefault}\"`,\n )\n logger.log('')\n logger.log(\n 'These are the security policies per setting for your organization:',\n )\n logger.log('')\n const rules = result.data.securityPolicyRules\n const entries: Array<\n [string, { action: 'defer' | 'error' | 'warn' | 'monitor' | 'ignore' }]\n > = rules ? Object.entries(rules) : []\n const mapped: Array<[string, string]> = entries.map(\n ({ 0: key, 1: value }) => [key, value.action],\n )\n mapped.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n logger.log(mdTableOfPairs(mapped, ['name', 'action']))\n logger.log('')\n}\n","import { fetchSecurityPolicy } from './fetch-security-policy.mts'\nimport { outputSecurityPolicy } from './output-security-policy.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleSecurityPolicy(\n orgSlug: string,\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchSecurityPolicy(orgSlug)\n\n await outputSecurityPolicy(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleSecurityPolicy } from './handle-security-policy.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'security'\n\nconst description = 'Retrieve the security policy of an organization'\n\nconst hidden = true\n\nexport const cmdOrganizationPolicySecurity = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: (command, _config) => `\n Usage\n $ ${command} [options]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Your API token will need the \\`security-policy:read\\` permission otherwise\n the request will fail with an authentication error.\n\n Examples\n $ ${command}\n $ ${command} --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleSecurityPolicy(orgSlug, outputKind)\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { getVisibleTokenPrefix } from '../../utils/sdk.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { OrganizationsCResult } from './fetch-organization-list.mts'\nimport type { OutputKind } from '../../types.mts'\n\nexport async function outputOrganizationList(\n orgsCResult: OrganizationsCResult,\n outputKind: OutputKind = 'text',\n): Promise<void> {\n if (!orgsCResult.ok) {\n process.exitCode = orgsCResult.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(orgsCResult))\n return\n }\n\n if (!orgsCResult.ok) {\n logger.fail(failMsgWithBadge(orgsCResult.message, orgsCResult.cause))\n return\n }\n\n const { organizations } = orgsCResult.data\n const visibleTokenPrefix = getVisibleTokenPrefix()\n\n if (outputKind !== 'markdown') {\n logger.log(\n `List of organizations associated with your API token, starting with: ${colors.italic(visibleTokenPrefix)}\\n`,\n )\n // Just dump.\n for (const o of organizations) {\n logger.log(\n `- Name: ${colors.bold(o.name ?? 'undefined')}, ID: ${colors.bold(o.id)}, Plan: ${colors.bold(o.plan)}`,\n )\n }\n return\n }\n\n // | Syntax | Description |\n // | ----------- | ----------- |\n // | Header | Title |\n // | Paragraph | Text |\n let mw1 = 4\n let mw2 = 2\n let mw3 = 4\n for (const o of organizations) {\n mw1 = Math.max(mw1, o.name?.length ?? 0)\n mw2 = Math.max(mw2, o.id.length)\n mw3 = Math.max(mw3, o.plan.length)\n }\n logger.log('# Organizations\\n')\n logger.log(\n `List of organizations associated with your API token, starting with: ${colors.italic(visibleTokenPrefix)}\\n`,\n )\n logger.log(\n `| Name${' '.repeat(mw1 - 4)} | ID${' '.repeat(mw2 - 2)} | Plan${' '.repeat(mw3 - 4)} |`,\n )\n logger.log(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} | ${'-'.repeat(mw3)} |`)\n for (const o of organizations) {\n logger.log(\n `| ${(o.name || '').padEnd(mw1, ' ')} | ${(o.id || '').padEnd(mw2, ' ')} | ${(o.plan || '').padEnd(mw3, ' ')} |`,\n )\n }\n logger.log(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} | ${'-'.repeat(mw3)} |`)\n}\n","import { fetchOrganization } from './fetch-organization-list.mts'\nimport { outputOrganizationList } from './output-organization-list.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleOrganizationList(\n outputKind: OutputKind = 'text',\n): Promise<void> {\n const data = await fetchOrganization()\n\n await outputOrganizationList(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleOrganizationList } from './handle-organization-list.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'list'\n\nconst description = 'List organizations associated with the Socket API token'\n\nconst hidden = false\n\nexport const cmdOrganizationList = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, _config) => `\n Usage\n $ ${command} [options]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleOrganizationList(outputKind)\n}\n","import { cmdOrganizationPolicyLicense } from './cmd-organization-policy-license.mts'\nimport { cmdOrganizationPolicySecurity } from './cmd-organization-policy-security.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Organization policy details'\n\nexport const cmdOrganizationPolicy: CliSubcommand = {\n description,\n // Hidden because it was broken all this time (nobody could be using it)\n // and we're not sure if it's useful to anyone in its current state.\n // Until we do, we'll hide this to keep the help tidier.\n // And later, we may simply move this under `scan`, anyways.\n hidden: false,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n security: cmdOrganizationPolicySecurity,\n license: cmdOrganizationPolicyLicense,\n },\n {\n argv,\n description,\n defaultSub: 'list', // Backwards compat\n importMeta,\n name: `${parentName} policy`,\n },\n )\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchQuotaOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchQuota(\n options?: FetchQuotaOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getQuota'>['data']>> {\n const { sdkOpts } = { __proto__: null, ...options } as FetchQuotaOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getQuota(), { desc: 'token quota' })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputQuota(\n result: CResult<SocketSdkSuccessResult<'getQuota'>['data']>,\n outputKind: OutputKind = 'text',\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'markdown') {\n logger.log('# Quota')\n logger.log('')\n logger.log(`Quota left on the current API token: ${result.data.quota}`)\n logger.log('')\n return\n }\n\n logger.log(`Quota left on the current API token: ${result.data.quota}`)\n logger.log('')\n}\n","import { fetchQuota } from './fetch-quota.mts'\nimport { outputQuota } from './output-quota.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleQuota(\n outputKind: OutputKind = 'text',\n): Promise<void> {\n const data = await fetchQuota()\n\n await outputQuota(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleQuota } from './handle-quota.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'quota',\n description: 'List organizations associated with the Socket API token',\n hidden: true,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, _config) => `\n Usage\n $ ${command} [options]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} --json\n `,\n}\n\nexport const cmdOrganizationQuota = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n const json = Boolean(cli.flags['json'])\n\n const markdown = Boolean(cli.flags['markdown'])\n\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleQuota(outputKind)\n}\n","import { cmdOrganizationDependencies } from './cmd-organization-dependencies.mts'\nimport { cmdOrganizationList } from './cmd-organization-list.mts'\nimport { cmdOrganizationPolicyLicense } from './cmd-organization-policy-license.mts'\nimport { cmdOrganizationPolicySecurity } from './cmd-organization-policy-security.mts'\nimport { cmdOrganizationPolicy } from './cmd-organization-policy.mts'\nimport { cmdOrganizationQuota } from './cmd-organization-quota.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Manage Socket organization account details'\n\nexport const cmdOrganization: CliSubcommand = {\n description,\n hidden: false,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n dependencies: cmdOrganizationDependencies,\n list: cmdOrganizationList,\n quota: cmdOrganizationQuota,\n policy: cmdOrganizationPolicy,\n },\n {\n aliases: {\n deps: {\n description: cmdOrganizationDependencies.description,\n hidden: true,\n argv: ['dependencies'],\n },\n license: {\n description: cmdOrganizationPolicyLicense.description,\n hidden: true,\n argv: ['policy', 'license'],\n },\n security: {\n description: cmdOrganizationPolicySecurity.description,\n hidden: true,\n argv: ['policy', 'security'],\n },\n },\n argv,\n description,\n importMeta,\n name: `${parentName} organization`,\n },\n )\n },\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { queryApiSafeJson } from '../../utils/api.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport interface PurlDataResponse {\n purl: string\n self: {\n purl: string\n score: {\n license: number\n maintenance: number\n overall: number\n quality: number\n supplyChain: number\n vulnerability: number\n }\n capabilities: string[]\n alerts: Array<{\n name: string\n severity: string\n category: string\n example: string\n }>\n }\n transitively: {\n dependencyCount: number\n func: string\n score: {\n license: number\n maintenance: number\n overall: number\n quality: number\n supplyChain: number\n vulnerability: number\n }\n lowest: {\n license: string\n maintenance: string\n overall: string\n quality: string\n supplyChain: string\n vulnerability: string\n }\n capabilities: string[]\n alerts: Array<{\n name: string\n severity: string\n category: string\n example: string\n }>\n }\n}\n\nexport async function fetchPurlDeepScore(\n purl: string,\n): Promise<CResult<PurlDataResponse>> {\n logger.info(`Requesting deep score data for this purl: ${purl}`)\n\n return await queryApiSafeJson<PurlDataResponse>(\n `purl/score/${encodeURIComponent(purl)}`,\n 'the deep package scores',\n )\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mdTable } from '../../utils/markdown.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { PurlDataResponse } from './fetch-purl-deep-score.mts'\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputPurlsDeepScore(\n purl: string,\n result: CResult<PurlDataResponse>,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'markdown') {\n const md = createMarkdownReport(result.data)\n logger.success(`Score report for \"${result.data.purl}\" (\"${purl}\"):\\n`)\n logger.log(md)\n return\n }\n\n logger.log(\n `Score report for \"${purl}\" (use --json for raw and --markdown for formatted reports):`,\n )\n logger.log(result.data)\n logger.log('')\n}\n\nexport function createMarkdownReport(data: PurlDataResponse): string {\n const {\n self: {\n alerts: selfAlerts,\n capabilities: selfCaps,\n purl,\n score: selfScore,\n },\n transitively: {\n alerts,\n capabilities,\n dependencyCount,\n func,\n lowest,\n score,\n },\n } = data\n\n const o: string[] = ['# Complete Package Score', '']\n if (dependencyCount) {\n o.push(\n `This is a Socket report for the package *\"${purl}\"* and its *${dependencyCount}* direct/transitive dependencies.`,\n )\n } else {\n o.push(\n `This is a Socket report for the package *\"${purl}\"*. It has *no dependencies*.`,\n )\n }\n o.push('')\n if (dependencyCount) {\n o.push(\n `It will show you the shallow score for just the package itself and a deep score for all the transitives combined. Additionally you can see which capabilities were found and the top alerts as well as a package that was responsible for it.`,\n )\n } else {\n o.push(\n `It will show you the shallow score for the package itself, which capabilities were found, and its top alerts.`,\n )\n o.push('')\n o.push(\n 'Since it has no dependencies, the shallow score is also the deep score.',\n )\n }\n o.push('')\n if (dependencyCount) {\n // This doesn't make much sense if there are no dependencies. Better to omit it.\n o.push(\n 'The report should give you a good insight into the status of this package.',\n )\n o.push('')\n o.push('## Package itself')\n o.push('')\n o.push(\n 'Here are results for the package itself (excluding data from dependencies).',\n )\n } else {\n o.push('## Report')\n o.push('')\n o.push(\n 'The report should give you a good insight into the status of this package.',\n )\n }\n o.push('')\n o.push('### Shallow Score')\n o.push('')\n o.push('This score is just for the package itself:')\n o.push('')\n o.push(`- Overall: ${selfScore.overall}`)\n o.push(`- Maintenance: ${selfScore.maintenance}`)\n o.push(`- Quality: ${selfScore.quality}`)\n o.push(`- Supply Chain: ${selfScore.supplyChain}`)\n o.push(`- Vulnerability: ${selfScore.vulnerability}`)\n o.push(`- License: ${selfScore.license}`)\n o.push('')\n o.push('### Capabilities')\n o.push('')\n if (selfCaps.length) {\n o.push('These are the capabilities detected in the package itself:')\n o.push('')\n for (const cap of selfCaps) {\n o.push(`- ${cap}`)\n }\n } else {\n o.push('No capabilities were found in the package.')\n }\n o.push('')\n o.push('### Alerts for this package')\n o.push('')\n if (selfAlerts.length) {\n if (dependencyCount) {\n o.push('These are the alerts found for the package itself:')\n } else {\n o.push('These are the alerts found for this package:')\n }\n o.push('')\n o.push(\n mdTable(selfAlerts, ['severity', 'name'], ['Severity', 'Alert Name']),\n )\n } else {\n o.push('There are currently no alerts for this package.')\n }\n o.push('')\n if (dependencyCount) {\n o.push('## Transitive Package Results')\n o.push('')\n o.push(\n 'Here are results for the package and its direct/transitive dependencies.',\n )\n o.push('')\n o.push('### Deep Score')\n o.push('')\n o.push(\n 'This score represents the package and and its direct/transitive dependencies:',\n )\n o.push(\n `The function used to calculate the values in aggregate is: *\"${func}\"*`,\n )\n o.push('')\n o.push(`- Overall: ${score.overall}`)\n o.push(`- Maintenance: ${score.maintenance}`)\n o.push(`- Quality: ${score.quality}`)\n o.push(`- Supply Chain: ${score.supplyChain}`)\n o.push(`- Vulnerability: ${score.vulnerability}`)\n o.push(`- License: ${score.license}`)\n o.push('')\n o.push('### Capabilities')\n o.push('')\n o.push(\n 'These are the packages with the lowest recorded score. If there is more than one with the lowest score, just one is shown here. This may help you figure out the source of low scores.',\n )\n o.push('')\n o.push(`- Overall: ${lowest.overall}`)\n o.push(`- Maintenance: ${lowest.maintenance}`)\n o.push(`- Quality: ${lowest.quality}`)\n o.push(`- Supply Chain: ${lowest.supplyChain}`)\n o.push(`- Vulnerability: ${lowest.vulnerability}`)\n o.push(`- License: ${lowest.license}`)\n o.push('')\n o.push('### Capabilities')\n o.push('')\n if (capabilities.length) {\n o.push('These are the capabilities detected in at least one package:')\n o.push('')\n for (const cap of capabilities) {\n o.push(`- ${cap}`)\n }\n } else {\n o.push(\n 'This package had no capabilities and neither did any of its direct/transitive dependencies.',\n )\n }\n o.push('')\n o.push('### Alerts')\n o.push('')\n if (alerts.length) {\n o.push('These are the alerts found:')\n o.push('')\n\n o.push(\n mdTable(\n alerts,\n ['severity', 'name', 'example'],\n ['Severity', 'Alert Name', 'Example package reporting it'],\n ),\n )\n } else {\n o.push(\n 'This package had no alerts and neither did any of its direct/transitive dependencies',\n )\n }\n o.push('')\n }\n return o.join('\\n')\n}\n","import { fetchPurlDeepScore } from './fetch-purl-deep-score.mts'\nimport { outputPurlsDeepScore } from './output-purls-deep-score.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handlePurlDeepScore(\n purl: string,\n outputKind: OutputKind,\n) {\n const result = await fetchPurlDeepScore(purl)\n\n await outputPurlsDeepScore(purl, result, outputKind)\n}\n","// Either an ecosystem was given or all args must be (namespaced) purls\n// The `pkg:` part is optional here. We'll scan for `eco/name@version`.\n// Not hardcoding the namespace since we don't know what the server accepts.\n// The ecosystem is considered as the first package if it is not an a-z string.\nexport function parsePackageSpecifiers(\n ecosystem: string,\n pkgs: string[],\n): { purls: string[]; valid: boolean } {\n let valid = true\n const purls = []\n if (!ecosystem) {\n valid = false\n } else if (/^[a-zA-Z]+$/.test(ecosystem)) {\n for (let i = 0; i < pkgs.length; ++i) {\n const pkg = pkgs[i] ?? ''\n if (!pkg) {\n valid = false\n break\n } else if (pkg.startsWith('pkg:')) {\n // keep\n purls.push(pkg)\n } else {\n purls.push('pkg:' + ecosystem + '/' + pkg)\n }\n }\n if (!purls.length) {\n valid = false\n }\n } else {\n // Assume ecosystem is a purl, too.\n pkgs.unshift(ecosystem)\n\n for (let i = 0; i < pkgs.length; ++i) {\n const pkg = pkgs[i] ?? ''\n if (!/^(?:pkg:)?[a-zA-Z]+\\/./.test(pkg)) {\n // At least one purl did not start with `pkg:eco/x` or `eco/x`.\n valid = false\n break\n } else if (pkg.startsWith('pkg:')) {\n purls.push(pkg)\n } else {\n purls.push('pkg:' + pkg)\n }\n }\n\n if (!purls.length) {\n valid = false\n }\n }\n\n return { purls, valid }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handlePurlDeepScore } from './handle-purl-deep-score.mts'\nimport { parsePackageSpecifiers } from './parse-package-specifiers.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'score'\n\nconst description =\n 'Look up score for one package which reflects all of its transitive dependencies as well'\n\nconst hidden = false\n\nexport const cmdPackageScore = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <<ECOSYSTEM> <NAME> | <PURL>>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Show deep scoring details for one package. The score will reflect the package\n itself, any of its dependencies, and any of its transitive dependencies.\n\n When you want to know whether to trust a package, this is the command to run.\n\n See also the \\`socket package shallow\\` command, which returns the shallow\n score for any number of packages. That will not reflect the dependency scores.\n\n Only a few ecosystems are supported like npm, pypi, nuget, gem, golang, and maven.\n\n A \"purl\" is a standard package name formatting: \\`pkg:eco/name@version\\`\n This command will automatically prepend \"pkg:\" when not present.\n\n The version is optional but when given should be a direct match. The \\`pkg:\\`\n prefix is optional.\n\n Note: if a package cannot be found it may be too old or perhaps was removed\n before we had the opportunity to process it.\n\n Examples\n $ ${command} npm babel-cli\n $ ${command} npm eslint@1.0.0 --json\n $ ${command} pkg:golang/github.com/steelpoor/tlsproxy@v0.0.0-20250304082521-29051ed19c60\n $ ${command} nuget/needpluscommonlibrary@1.0.0 --markdown\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const [ecosystem = '', purl] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n const { purls, valid } = parsePackageSpecifiers(ecosystem, purl ? [purl] : [])\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: valid,\n message: 'First parameter must be an ecosystem or the whole purl',\n fail: 'bad',\n },\n {\n test: purls.length === 1,\n message: 'Expecting at least one package',\n fail: purls.length === 0 ? 'missing' : 'too many',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handlePurlDeepScore(purls[0] || '', outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { 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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchPurlsShallowScoreOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchPurlsShallowScore(\n purls: string[],\n options?: FetchPurlsShallowScoreOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'batchPackageFetch'>>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchPurlsShallowScoreOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n logger.info(\n `Requesting shallow score data for ${purls.length} package urls (purl): ${purls.join(', ')}`,\n )\n\n const batchPackageCResult = await handleApiCall(\n sockSdk.batchPackageFetch(\n { components: purls.map(purl => ({ purl })) },\n {\n alerts: 'true',\n },\n ),\n { desc: 'looking up package' },\n )\n if (!batchPackageCResult.ok) {\n return batchPackageCResult\n }\n\n // TODO: Seems like there's a bug in the typing since we absolutely have to\n // return the .data here.\n return {\n ok: true,\n data: batchPackageCResult.data as SocketSdkSuccessResult<'batchPackageFetch'>,\n }\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketArtifact } from '../../utils/alert/artifact.mts'\n\n// This is a simplified view of an artifact. Potentially merged with other artifacts.\ninterface DedupedArtifact {\n ecosystem: string // artifact.type\n namespace: string\n name: string\n version: string\n score: {\n supplyChain: number\n maintenance: number\n quality: number\n vulnerability: number\n license: number\n }\n alerts: Map<\n string,\n {\n type: string\n severity: string\n }\n >\n}\n\nexport function outputPurlsShallowScore(\n purls: string[],\n result: CResult<SocketArtifact[]>,\n outputKind: OutputKind,\n): void {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const { missing, rows } = preProcess(result.data, purls)\n\n if (outputKind === 'markdown') {\n const md = generateMarkdownReport(rows, missing)\n logger.log(md)\n return\n }\n\n const txt = generateTextReport(rows, missing)\n logger.log(txt)\n}\n\nfunction formatReportCard(\n artifact: DedupedArtifact,\n colorize: boolean,\n): string {\n const scoreResult = {\n 'Supply Chain Risk': Math.floor((artifact.score?.supplyChain ?? 0) * 100),\n Maintenance: Math.floor((artifact.score?.maintenance ?? 0) * 100),\n Quality: Math.floor((artifact.score?.quality ?? 0) * 100),\n Vulnerabilities: Math.floor((artifact.score?.vulnerability ?? 0) * 100),\n License: Math.floor((artifact.score?.license ?? 0) * 100),\n }\n const alertString = getAlertString(artifact.alerts, { colorize })\n if (!artifact.ecosystem) {\n debugFn('notice', 'miss: artifact ecosystem', artifact)\n }\n const purl = `pkg:${artifact.ecosystem}/${artifact.name}${artifact.version ? '@' + artifact.version : ''}`\n\n // Calculate proper padding based on longest label.\n const maxLabelLength = Math.max(\n ...Object.keys(scoreResult).map(label => label.length),\n )\n const labelPadding = maxLabelLength + 2 // +2 for \": \"\n\n return [\n 'Package: ' + (colorize ? colors.bold(purl) : purl),\n '',\n ...Object.entries(scoreResult).map(\n score =>\n `- ${score[0]}:`.padEnd(labelPadding, ' ') +\n ` ${formatScore(score[1], { colorize })}`,\n ),\n alertString,\n ].join('\\n')\n}\n\ntype FormatScoreOptions = {\n colorize?: boolean | undefined\n padding?: number | undefined\n}\n\nfunction formatScore(\n score: number,\n options?: FormatScoreOptions | undefined,\n): string {\n const { colorize, padding = 3 } = {\n __proto__: null,\n ...options,\n } as FormatScoreOptions\n const padded = String(score).padStart(padding, ' ')\n if (!colorize) {\n return padded\n }\n if (score >= 80) {\n return colors.green(padded)\n }\n if (score >= 60) {\n return colors.yellow(padded)\n }\n return colors.red(padded)\n}\n\ntype AlertStringOptions = {\n colorize?: boolean | undefined\n}\n\nfunction getAlertString(\n alerts: DedupedArtifact['alerts'],\n options?: AlertStringOptions | undefined,\n): string {\n const { colorize } = { __proto__: null, ...options } as AlertStringOptions\n\n if (!alerts.size) {\n return `- Alerts: ${colorize ? colors.green('none') : 'none'}!`\n }\n\n const o = Array.from(alerts.values())\n\n const bad = o\n .filter(alert => alert.severity !== 'low' && alert.severity !== 'middle')\n .sort((a, b) => (a.type < b.type ? -1 : a.type > b.type ? 1 : 0))\n\n const mid = o\n .filter(alert => alert.severity === 'middle')\n .sort((a, b) => (a.type < b.type ? -1 : a.type > b.type ? 1 : 0))\n\n const low = o\n .filter(alert => alert.severity === 'low')\n .sort((a, b) => (a.type < b.type ? -1 : a.type > b.type ? 1 : 0))\n\n // We need to create the no-color string regardless because the actual string\n // contains a bunch of invisible ANSI chars which would screw up length checks.\n const colorless = `- Alerts (${bad.length}/${mid.length.toString()}/${low.length}):`\n const padding = ` ${' '.repeat(Math.max(0, 20 - colorless.length))}`\n\n if (colorize) {\n return (\n `- Alerts (${colors.red(bad.length.toString())}/${colors.yellow(mid.length.toString())}/${low.length}):` +\n padding +\n [\n bad\n .map(a => colors.red(`${colors.dim(`[${a.severity}] `)}${a.type}`))\n .join(', '),\n mid\n .map(a => colors.yellow(`${colors.dim(`[${a.severity}] `)}${a.type}`))\n .join(', '),\n low.map(a => `${colors.dim(`[${a.severity}] `)}${a.type}`).join(', '),\n ]\n .filter(Boolean)\n .join(', ')\n )\n }\n return (\n colorless +\n padding +\n [\n bad.map(a => `[${a.severity}] ${a.type}`).join(', '),\n mid.map(a => `[${a.severity}] ${a.type}`).join(', '),\n low.map(a => `[${a.severity}] ${a.type}`).join(', '),\n ]\n .filter(Boolean)\n .join(', ')\n )\n}\n\nexport function preProcess(\n artifacts: SocketArtifact[],\n requestedPurls: string[],\n): { rows: Map<string, DedupedArtifact>; missing: string[] } {\n // Dedupe results (for example, pypi will emit one package for each system release (win/mac/cpu) even if it's\n // the same package version with same results. The duplication is irrelevant and annoying to the user.\n\n // Make some effort to match the requested data with the response\n // Dedupe and merge results when only the .release value is different\n\n // API does not tell us which purls were not found.\n // Generate all purls to try so we can try to match search request.\n const purls: Set<string> = new Set()\n for (const data of artifacts) {\n purls.add(\n `pkg:${data.type}/${data.namespace ? `${data.namespace}/` : ''}${data.name}@${data.version}`,\n )\n purls.add(`pkg:${data.type}/${data.name}@${data.version}`)\n purls.add(`pkg:${data.type}/${data.name}`)\n purls.add(\n `pkg:${data.type}/${data.namespace ? `${data.namespace}/` : ''}${data.name}`,\n )\n }\n // Try to match the searched purls against this list\n const missing = requestedPurls.filter(purl => {\n if (purls.has(purl)) {\n return false\n }\n if (\n purl.endsWith('@latest') &&\n purls.has(purl.slice(0, -'@latest'.length))\n ) {\n return false\n }\n // Not found.\n return true\n })\n\n // Create a unique set of rows which represents each artifact that is returned\n // while deduping when the artifact (main) meta data only differs due to the\n // .release field (observed with python, at least).\n // Merge the alerts for duped packages. Use lowest score between all of them.\n const rows: Map<string, DedupedArtifact> = new Map()\n for (const artifact of artifacts) {\n const purl = `pkg:${artifact.type}/${artifact.namespace ? `${artifact.namespace}/` : ''}${artifact.name}${artifact.version ? `@${artifact.version}` : ''}`\n if (rows.has(purl)) {\n const row = rows.get(purl)\n if (!row) {\n // Unreachable; Satisfy TS.\n continue\n }\n if ((artifact.score?.supplyChain || 100) < row.score.supplyChain) {\n row.score.supplyChain = artifact.score?.supplyChain || 100\n }\n if ((artifact.score?.maintenance || 100) < row.score.maintenance) {\n row.score.maintenance = artifact.score?.maintenance || 100\n }\n if ((artifact.score?.quality || 100) < row.score.quality) {\n row.score.quality = artifact.score?.quality || 100\n }\n if ((artifact.score?.vulnerability || 100) < row.score.vulnerability) {\n row.score.vulnerability = artifact.score?.vulnerability || 100\n }\n if ((artifact.score?.license || 100) < row.score.license) {\n row.score.license = artifact.score?.license || 100\n }\n\n artifact.alerts?.forEach(({ severity, type }) => {\n row.alerts.set(`${type}:${severity}`, {\n type: (type as string) ?? 'unknown',\n severity: (severity as string) ?? 'none',\n })\n })\n } else {\n const alerts = new Map<string, { type: string; severity: string }>()\n artifact.alerts?.forEach(({ severity, type }) => {\n alerts.set(`${type}:${severity}`, {\n type: (type as string) ?? 'unknown',\n severity: (severity as string) ?? 'none',\n })\n })\n\n rows.set(purl, {\n ecosystem: artifact.type,\n namespace: artifact.namespace || '',\n name: artifact.name!,\n version: artifact.version || '',\n score: {\n supplyChain: artifact.score?.supplyChain || 100,\n maintenance: artifact.score?.maintenance || 100,\n quality: artifact.score?.quality || 100,\n vulnerability: artifact.score?.vulnerability || 100,\n license: artifact.score?.license || 100,\n },\n alerts,\n })\n }\n }\n\n return { rows, missing }\n}\n\nexport function generateMarkdownReport(\n artifacts: Map<string, DedupedArtifact>,\n missing: string[],\n): string {\n const blocks: string[] = []\n const dupes: Set<string> = new Set()\n for (const artifact of artifacts.values()) {\n const block = `## ${formatReportCard(artifact, false)}`\n if (dupes.has(block)) {\n // Omit duplicate blocks.\n continue\n }\n dupes.add(block)\n blocks.push(block)\n }\n return `\n# Shallow Package Report\n\nThis report contains the response for requesting data on some package url(s).\n\nPlease note: The listed scores are ONLY for the package itself. It does NOT\n reflect the scores of any dependencies, transitive or otherwise.\n\n${missing.length ? `\\n## Missing response\\n\\nAt least one package had no response or the purl was not canonical:\\n\\n${missing.map(purl => `- ${purl}\\n`).join('')}` : ''}\n\n${blocks.join('\\n\\n\\n')}\n `.trim()\n}\n\nexport function generateTextReport(\n artifacts: Map<string, DedupedArtifact>,\n missing: string[],\n): string {\n const o: string[] = []\n o.push(`\\n${colors.bold('Shallow Package Score')}\\n`)\n o.push(\n 'Please note: The listed scores are ONLY for the package itself. It does NOT\\n' +\n ' reflect the scores of any dependencies, transitive or otherwise.',\n )\n if (missing.length) {\n o.push(\n `\\nAt least one package had no response or the purl was not canonical:\\n${missing.map(purl => `\\n- ${colors.bold(purl)}`).join('')}`,\n )\n }\n const dupes: Set<string> = new Set()\n for (const artifact of artifacts.values()) {\n const block = formatReportCard(artifact, true)\n if (dupes.has(block)) {\n // Omit duplicate blocks.\n continue\n }\n dupes.add(block)\n o.push('\\n')\n o.push(block)\n }\n o.push('')\n\n return o.join('\\n')\n}\n","import { fetchPurlsShallowScore } from './fetch-purls-shallow-score.mts'\nimport { outputPurlsShallowScore } from './output-purls-shallow-score.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketArtifact } from '../../utils/alert/artifact.mts'\n\nexport async function handlePurlsShallowScore({\n outputKind,\n purls,\n}: {\n outputKind: OutputKind\n purls: string[]\n}) {\n const packageData = await fetchPurlsShallowScore(purls)\n\n outputPurlsShallowScore(\n purls,\n packageData as CResult<SocketArtifact[]>,\n outputKind,\n )\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handlePurlsShallowScore } from './handle-purls-shallow-score.mts'\nimport { parsePackageSpecifiers } from './parse-package-specifiers.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'shallow'\n\nconst description =\n 'Look up info regarding one or more packages but not their transitives'\n\nconst hidden = false\n\nexport const cmdPackageShallow = {\n description,\n hidden,\n alias: {\n shallowScore: {\n description,\n hidden: true,\n argv: [],\n },\n },\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <<ECOSYSTEM> <PKGNAME> [<PKGNAME> ...] | <PURL> [<PURL> ...]>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Show scoring details for one or more packages purely based on their own package.\n This means that any dependency scores are not reflected by the score. You can\n use the \\`socket package score <pkg>\\` command to get its full transitive score.\n\n Only a few ecosystems are supported like npm, pypi, nuget, gem, golang, and maven.\n\n A \"purl\" is a standard package name formatting: \\`pkg:eco/name@version\\`\n This command will automatically prepend \"pkg:\" when not present.\n\n If the first arg is an ecosystem, remaining args that are not a purl are\n assumed to be scoped to that ecosystem. The \\`pkg:\\` prefix is optional.\n\n Note: if a package cannot be found, it may be too old or perhaps was removed\n before we had the opportunity to process it.\n\n Examples\n $ ${command} npm webtorrent\n $ ${command} npm webtorrent@1.9.1\n $ ${command} npm/webtorrent@1.9.1\n $ ${command} pkg:npm/webtorrent@1.9.1\n $ ${command} maven webtorrent babel\n $ ${command} npm/webtorrent golang/babel\n $ ${command} npm npm/webtorrent@1.0.1 babel\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const [ecosystem = '', ...pkgs] = cli.input\n\n const outputKind = getOutputKind(json, markdown)\n\n const { purls, valid } = parsePackageSpecifiers(ecosystem, pkgs)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: valid,\n message:\n 'First parameter should be an ecosystem or all args must be purls',\n fail: 'bad',\n },\n {\n test: purls.length > 0,\n message: 'Expecting at least one package',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handlePurlsShallowScore({\n outputKind,\n purls,\n })\n}\n","import { cmdPackageScore } from './cmd-package-score.mts'\nimport { cmdPackageShallow } from './cmd-package-shallow.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Look up published package details'\n\nexport const cmdPackage: CliSubcommand = {\n description,\n hidden: false,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n score: cmdPackageScore,\n shallow: cmdPackageShallow,\n },\n {\n aliases: {\n deep: {\n description,\n hidden: true,\n argv: ['score'],\n },\n },\n argv,\n description,\n importMeta,\n name: `${parentName} package`,\n },\n )\n },\n}\n","import { z } from 'zod'\n\nexport type PatchManifest = z.infer<typeof PatchManifestSchema>\n\nexport type PatchRecord = z.infer<typeof PatchRecordSchema>\n\nexport const PatchRecordSchema = z.object({\n exportedAt: z.string(),\n files: z.record(\n z.string(), // File path\n z.object({\n beforeHash: z.string(),\n afterHash: z.string(),\n }),\n ),\n vulnerabilities: z.record(\n z.string(), // Vulnerability ID like \"GHSA-jrhj-2j3q-xf3v\"\n z.object({\n cves: z.array(z.string()),\n summary: z.string(),\n severity: z.string(),\n description: z.string(),\n patchExplanation: z.string(),\n }),\n ),\n})\n\nexport const PatchManifestSchema = z.object({\n patches: z.record(\n // Package identifier like \"npm:simplehttpserver@0.0.6\".\n z.string(),\n PatchRecordSchema,\n ),\n})\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputPatchResult(\n result: CResult<{ patchedPackages: string[] }>,\n outputKind: OutputKind,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const { patchedPackages } = result.data\n\n if (patchedPackages.length > 0) {\n logger.success(\n `Successfully processed patches for ${patchedPackages.length} package(s):`,\n )\n for (const pkg of patchedPackages) {\n logger.success(pkg)\n }\n } else {\n logger.info('No packages found requiring patches')\n }\n\n logger.log('')\n logger.success('Patch command completed!')\n}\n","import crypto from 'node:crypto'\nimport { existsSync, promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nimport { readJson } from '@socketsecurity/registry/lib/fs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { PatchManifestSchema } from './manifest-schema.mts'\nimport { outputPatchResult } from './output-patch-result.mts'\nimport constants from '../../constants.mts'\n\nimport type { PatchRecord } from './manifest-schema.mts'\nimport type { CResult, OutputKind } from '../../types.mts'\n\ninterface PURL {\n type: string\n namespace?: string\n name: string\n version: string\n qualifiers?: Record<string, string>\n subpath?: string\n}\n\nasync function applyNPMPatches(\n patches: Array<{ key: string; purl: PURL; patch: PatchRecord }>,\n dryRun: boolean,\n socketDir: string,\n packages: string[],\n) {\n const patchLookup = new Map<\n string,\n { key: string; purl: PURL; patch: PatchRecord }\n >()\n\n for (const patchInfo of patches) {\n const { purl } = patchInfo\n const fullName = purl.namespace\n ? `@${purl.namespace}/${purl.name}`\n : purl.name\n const lookupKey = `${fullName}@${purl.version}`\n patchLookup.set(lookupKey, patchInfo)\n }\n\n const nodeModulesFolders = await findNodeModulesFolders(process.cwd())\n logger.log(`Found ${nodeModulesFolders.length} node_modules folders`)\n\n for (const nodeModulesPath of nodeModulesFolders) {\n try {\n // eslint-disable-next-line no-await-in-loop\n const entries = await fs.readdir(nodeModulesPath)\n\n for (const entry of entries) {\n const entryPath = path.join(nodeModulesPath, entry)\n\n if (entry.startsWith('@')) {\n try {\n // eslint-disable-next-line no-await-in-loop\n const scopedEntries = await fs.readdir(entryPath)\n for (const scopedEntry of scopedEntries) {\n const packagePath = path.join(entryPath, scopedEntry)\n // eslint-disable-next-line no-await-in-loop\n const pkg = await readPackageJson(packagePath)\n\n if (pkg) {\n // Skip if specific packages requested and this isn't one of them\n if (packages.length > 0 && !packages.includes(pkg.name)) {\n continue\n }\n\n const lookupKey = `${pkg.name}@${pkg.version}`\n const patchInfo = patchLookup.get(lookupKey)\n\n if (patchInfo) {\n logger.log(\n `Found match: ${pkg.name}@${pkg.version} at ${packagePath}`,\n )\n logger.log(` Patch key: ${patchInfo.key}`)\n logger.log(` Processing files:`)\n\n for (const [fileName, fileInfo] of Object.entries(\n patchInfo.patch.files,\n )) {\n // eslint-disable-next-line no-await-in-loop\n await processFilePatch(\n packagePath,\n fileName,\n fileInfo,\n dryRun,\n socketDir,\n )\n }\n }\n }\n }\n } catch {\n // Ignore errors reading scoped packages\n }\n } else {\n // eslint-disable-next-line no-await-in-loop\n const pkg = await readPackageJson(entryPath)\n\n if (pkg) {\n // Skip if specific packages requested and this isn't one of them\n if (packages.length > 0 && !packages.includes(pkg.name)) {\n continue\n }\n\n const lookupKey = `${pkg.name}@${pkg.version}`\n const patchInfo = patchLookup.get(lookupKey)\n\n if (patchInfo) {\n logger.log(\n `Found match: ${pkg.name}@${pkg.version} at ${entryPath}`,\n )\n logger.log(` Patch key: ${patchInfo.key}`)\n logger.log(` Processing files:`)\n\n for (const [fileName, fileInfo] of Object.entries(\n patchInfo.patch.files,\n )) {\n // eslint-disable-next-line no-await-in-loop\n await processFilePatch(\n entryPath,\n fileName,\n fileInfo,\n dryRun,\n socketDir,\n )\n }\n }\n }\n }\n }\n } catch (error) {\n logger.error(`Error processing ${nodeModulesPath}:`, error)\n }\n }\n}\n\nasync function computeSHA256(filePath: string): Promise<string | null> {\n try {\n const content = await fs.readFile(filePath)\n const hash = crypto.createHash('sha256')\n hash.update(content)\n return hash.digest('hex')\n } catch {\n return null\n }\n}\n\nasync function findNodeModulesFolders(rootDir: string): Promise<string[]> {\n const nodeModulesPaths: string[] = []\n\n async function searchDir(dir: string) {\n try {\n const entries = await fs.readdir(dir)\n\n for (const entry of entries) {\n if (entry.startsWith('.') || entry === 'dist' || entry === 'build') {\n continue\n }\n\n const fullPath = path.join(dir, entry)\n // eslint-disable-next-line no-await-in-loop\n const stats = await fs.stat(fullPath)\n\n if (stats.isDirectory()) {\n if (entry === 'node_modules') {\n nodeModulesPaths.push(fullPath)\n } else {\n // eslint-disable-next-line no-await-in-loop\n await searchDir(fullPath)\n }\n }\n }\n } catch (error) {\n // Ignore permission errors or missing directories\n }\n }\n\n await searchDir(rootDir)\n return nodeModulesPaths\n}\n\nfunction parsePURL(purlString: string): PURL {\n const [ecosystem, rest] = purlString.split(':', 2)\n const [nameAndNamespace, version] = (rest ?? '').split('@', 2)\n\n let namespace: string | undefined\n let name: string\n\n if (ecosystem === 'npm' && nameAndNamespace?.startsWith('@')) {\n const parts = nameAndNamespace.split('/')\n namespace = parts[0]?.substring(1)\n name = parts.slice(1).join('/')\n } else {\n name = nameAndNamespace ?? ''\n }\n\n return {\n type: ecosystem ?? 'unknown',\n namespace: namespace ?? '',\n name: name ?? '',\n version: version ?? '0.0.0',\n }\n}\n\nasync function processFilePatch(\n packagePath: string,\n fileName: string,\n fileInfo: { beforeHash: string; afterHash: string },\n dryRun: boolean,\n socketDir: string,\n): Promise<void> {\n const filePath = path.join(packagePath, fileName)\n\n if (!existsSync(filePath)) {\n logger.log(`File not found: ${fileName}`)\n return\n }\n\n const currentHash = await computeSHA256(filePath)\n\n if (!currentHash) {\n logger.log(`Failed to compute hash for: ${fileName}`)\n return\n }\n\n if (currentHash === fileInfo.beforeHash) {\n logger.success(`File matches expected hash: ${fileName}`)\n logger.log(`Current hash: ${currentHash}`)\n logger.log(`Ready to patch to: ${fileInfo.afterHash}`)\n\n if (!dryRun) {\n const blobPath = path.join(socketDir, 'blobs', fileInfo.afterHash)\n\n if (!existsSync(blobPath)) {\n logger.fail(`Error: Patch file not found at ${blobPath}`)\n return\n }\n\n try {\n await fs.copyFile(blobPath, filePath)\n logger.success(`Patch applied successfully`)\n } catch (error) {\n logger.log(`Error applying patch: ${error}`)\n }\n } else {\n logger.log(`(dry run - no changes made)`)\n }\n } else if (currentHash === fileInfo.afterHash) {\n logger.success(`File already patched: ${fileName}`)\n logger.log(`Current hash: ${currentHash}`)\n } else {\n logger.fail(`File hash mismatch: ${fileName}`)\n logger.log(`Expected: ${fileInfo.beforeHash}`)\n logger.log(`Current: ${currentHash}`)\n logger.log(`Target: ${fileInfo.afterHash}`)\n }\n}\n\nasync function readPackageJson(\n packagePath: string,\n): Promise<{ name: string; version: string } | null> {\n const pkgJsonPath = path.join(packagePath, 'package.json')\n const pkg = await readJson(pkgJsonPath, { throws: false })\n if (pkg) {\n return {\n name: pkg.name || '',\n version: pkg.version || '',\n }\n }\n return null\n}\n\nexport interface HandlePatchConfig {\n cwd: string\n dryRun: boolean\n outputKind: OutputKind\n packages: string[]\n spinner: typeof constants.spinner\n}\n\nexport async function handlePatch({\n cwd,\n dryRun,\n outputKind,\n packages,\n spinner,\n}: HandlePatchConfig): Promise<void> {\n try {\n const dotSocketDirPath = path.join(cwd, '.socket')\n const manifestPath = path.join(dotSocketDirPath, 'manifest.json')\n\n // Read the manifest file.\n const manifestContent = await fs.readFile(manifestPath, 'utf-8')\n const manifestData = JSON.parse(manifestContent)\n\n // Validate the schema.\n const validated = PatchManifestSchema.parse(manifestData)\n\n // Parse PURLs and group by ecosystem.\n const patchesByEcosystem: Record<\n string,\n Array<{ key: string; purl: PURL; patch: PatchRecord }>\n > = {}\n for (const [key, patch] of Object.entries(validated.patches)) {\n const purl = parsePURL(key)\n if (!patchesByEcosystem[purl.type]) {\n patchesByEcosystem[purl.type] = []\n }\n patchesByEcosystem[purl.type]?.push({\n key,\n purl,\n patch,\n })\n }\n\n spinner.stop()\n\n logger.log('')\n if (packages.length > 0) {\n logger.info(`Checking patches for: ${packages.join(', ')}`)\n } else {\n logger.info('Scanning all dependencies for available patches')\n }\n logger.log('')\n\n if (patchesByEcosystem['npm']) {\n await applyNPMPatches(\n patchesByEcosystem['npm'],\n dryRun,\n dotSocketDirPath,\n packages,\n )\n }\n\n const result: CResult<{ patchedPackages: string[] }> = {\n ok: true,\n data: {\n patchedPackages:\n packages.length > 0 ? packages : ['patched successfully'],\n },\n }\n\n await outputPatchResult(result, outputKind)\n } catch (e) {\n spinner.stop()\n\n let message = 'Failed to apply patches'\n let cause = (e as Error)?.message || 'Unknown error'\n\n if (e instanceof SyntaxError) {\n message = 'Invalid JSON in manifest.json'\n cause = e.message\n } else if (e instanceof Error && 'issues' in e) {\n message = 'Schema validation failed'\n cause = String(e)\n }\n\n const result: CResult<never> = {\n ok: false,\n code: 1,\n message,\n cause,\n }\n\n await outputPatchResult(result, outputKind)\n }\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handlePatch } from './handle-patch.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { cmdFlagValueToArray } from '../../utils/cmd.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'patch'\n\nconst description = 'Apply CVE patches to dependencies'\n\nconst hidden = true\n\nexport const cmdPatch = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n package: {\n type: 'string',\n default: [],\n description:\n 'Specify packages to patch, as either a comma separated value or as multiple flags',\n isMultiple: true,\n shortFlag: 'p',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} --package lodash\n $ ${command} ./proj/tree --package lodash,react\n `,\n }\n\n const cli = meowOrExit({\n allowUnknownFlags: false,\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n const outputKind = getOutputKind(cli.flags['json'], cli.flags['markdown'])\n\n const wasValidInput = checkCommandInput(outputKind, {\n nook: true,\n test: !cli.flags['json'] || !cli.flags['markdown'],\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n })\n if (!wasValidInput) {\n return\n }\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n const dotSocketDirPath = path.join(cwd, '.socket')\n if (!existsSync(dotSocketDirPath)) {\n logger.error('Error: No .socket directory found in current directory')\n return\n }\n\n const manifestPath = path.join(dotSocketDirPath, 'manifest.json')\n if (!existsSync(manifestPath)) {\n logger.error('Error: No manifest.json found in .socket directory')\n }\n\n const { spinner } = constants\n\n const packages = cmdFlagValueToArray(cli.flags['package'])\n\n await handlePatch({\n cwd,\n dryRun,\n outputKind,\n packages,\n spinner,\n })\n}\n","import { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants.mts'\nimport { getNpmBinPath } from '../../utils/npm-paths.mts'\n\nexport async function runRawNpm(\n argv: string[] | readonly string[],\n): Promise<void> {\n process.exitCode = 1\n\n const spawnPromise = spawn(getNpmBinPath(), argv as string[], {\n shell: constants.WIN32,\n stdio: 'inherit',\n })\n\n // See https://nodejs.org/api/child_process.html#event-exit.\n spawnPromise.process.on('exit', (code, signalName) => {\n if (signalName) {\n process.kill(process.pid, signalName)\n } else if (typeof code === 'number') {\n // eslint-disable-next-line n/no-process-exit\n process.exit(code)\n }\n })\n\n await spawnPromise\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { runRawNpm } from './run-raw-npm.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'raw-npm',\n description: 'Run npm without the Socket wrapper',\n hidden: false,\n flags: {\n ...commonFlags,\n },\n help: command => `\n Usage\n $ ${command} ...\n\n Execute \\`npm\\` without gating installs through the Socket API.\n Useful when \\`socket wrapper on\\` is enabled and you want to bypass\n the Socket wrapper. Use at your own risk.\n\n Note: Everything after \"raw-npm\" is passed to the npm command.\n Only the \\`--dry-run\\` and \\`--help\\` flags are caught here.\n\n Examples\n $ ${command} install -g cowsay\n `,\n}\n\nexport const cmdRawNpm = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await runRawNpm(argv)\n}\n","import { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants.mts'\nimport { getNpxBinPath } from '../../utils/npm-paths.mts'\n\nexport async function runRawNpx(\n argv: string[] | readonly string[],\n): Promise<void> {\n process.exitCode = 1\n\n const spawnPromise = spawn(getNpxBinPath(), argv as string[], {\n shell: constants.WIN32,\n stdio: 'inherit',\n })\n\n // See https://nodejs.org/api/child_process.html#event-exit.\n spawnPromise.process.on('exit', (code, signalName) => {\n if (signalName) {\n process.kill(process.pid, signalName)\n } else if (typeof code === 'number') {\n // eslint-disable-next-line n/no-process-exit\n process.exit(code)\n }\n })\n\n await spawnPromise\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { runRawNpx } from './run-raw-npx.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'raw-npx',\n description: 'Run npx without the Socket wrapper',\n hidden: false,\n flags: {\n ...commonFlags,\n },\n help: command => `\n Usage\n $ ${command} ...\n\n Execute \\`npx\\` without gating installs through the Socket API.\n Useful when \\`socket wrapper on\\` is enabled and you want to bypass\n the Socket wrapper. Use at your own risk.\n\n Note: Everything after \"raw-npx\" is passed to the npx command.\n Only the \\`--dry-run\\` and \\`--help\\` flags are caught here.\n\n Examples\n $ ${command} cowsay\n `,\n}\n\nexport const cmdRawNpx = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await runRawNpx(argv)\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchCreateRepoConfig = {\n defaultBranch: string\n description: string\n homepage: string\n orgSlug: string\n repoName: string\n visibility: string\n}\n\nexport type FetchCreateRepoOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchCreateRepo(\n config: FetchCreateRepoConfig,\n options?: FetchCreateRepoOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'createOrgRepo'>['data']>> {\n const {\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n } = config\n\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchCreateRepoOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(\n sockSdk.createOrgRepo(orgSlug, {\n default_branch: defaultBranch,\n description,\n homepage,\n name: repoName,\n visibility,\n }),\n { desc: 'to create a repository' },\n )\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport function outputCreateRepo(\n result: CResult<SocketSdkSuccessResult<'createOrgRepo'>['data']>,\n requestedName: string,\n outputKind: OutputKind,\n): void {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n const { slug } = result.data\n logger.success(\n `OK. Repository created successfully, slug: \\`${slug}\\`${slug !== requestedName ? ' (Warning: slug is not the same as name that was requested!)' : ''}`,\n )\n}\n","import { fetchCreateRepo } from './fetch-create-repo.mts'\nimport { outputCreateRepo } from './output-create-repo.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleCreateRepo(\n {\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n }: {\n orgSlug: string\n repoName: string\n description: string\n homepage: string\n defaultBranch: string\n visibility: string\n },\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchCreateRepo({\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n })\n outputCreateRepo(data, repoName, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleCreateRepo } from './handle-create-repo.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'create'\n\nconst description = 'Create a repository in an organization'\n\nconst hidden = false\n\nexport const cmdRepositoryCreate = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n defaultBranch: {\n type: 'string',\n default: 'main',\n description: 'Repository default branch. Defaults to \"main\"',\n },\n homepage: {\n type: 'string',\n default: '',\n description: 'Repository url',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n repoDescription: {\n type: 'string',\n default: '',\n description: 'Repository description',\n },\n visibility: {\n type: 'string',\n default: 'private',\n description: 'Repository visibility (Default Private)',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <REPO>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n The REPO name should be a \"slug\". Follows the same naming convention as GitHub.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} test-repo\n $ ${command} our-repo --homepage=socket.dev --default-branch=trunk\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const noLegacy = !cli.flags['repoName']\n\n const [repoName = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n nook: true,\n test: noLegacy,\n message: 'Legacy flags are no longer supported. See v1 migration guide.',\n fail: `received legacy flags`,\n },\n {\n test: !!repoName,\n message: 'Repository name as first argument',\n fail: 'missing',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleCreateRepo(\n {\n orgSlug,\n repoName: String(repoName),\n description: String(cli.flags['repoDescription'] || ''),\n homepage: String(cli.flags['homepage'] || ''),\n defaultBranch: String(cli.flags['defaultBranch'] || ''),\n visibility: String(cli.flags['visibility'] || 'private'),\n },\n outputKind,\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchDeleteRepoOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchDeleteRepo(\n orgSlug: string,\n repoName: string,\n options?: FetchDeleteRepoOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'deleteOrgRepo'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchDeleteRepoOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.deleteOrgRepo(orgSlug, repoName), {\n desc: 'to delete a repository',\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputDeleteRepo(\n result: CResult<SocketSdkSuccessResult<'deleteOrgRepo'>['data']>,\n repoName: string,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.success(`OK. Repository \\`${repoName}\\` deleted successfully`)\n}\n","import { fetchDeleteRepo } from './fetch-delete-repo.mts'\nimport { outputDeleteRepo } from './output-delete-repo.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleDeleteRepo(\n orgSlug: string,\n repoName: string,\n outputKind: OutputKind,\n) {\n const data = await fetchDeleteRepo(orgSlug, repoName)\n\n await outputDeleteRepo(data, repoName, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleDeleteRepo } from './handle-delete-repo.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'del'\n\nconst description = 'Delete a repository in an organization'\n\nconst hidden = false\n\nexport const cmdRepositoryDel = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <REPO>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} test-repo\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const noLegacy = !cli.flags['repoName']\n\n const [repoName = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: noLegacy,\n message: 'Legacy flags are no longer supported. See v1 migration guide.',\n fail: `received legacy flags`,\n },\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n test: !!repoName,\n message: 'Repository name as first argument',\n fail: 'missing',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleDeleteRepo(orgSlug, repoName, outputKind)\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchListAllReposOptions = {\n direction?: string | undefined\n sdkOpts?: SetupSdkOptions | undefined\n sort?: string | undefined\n}\n\nexport async function fetchListAllRepos(\n orgSlug: string,\n options?: FetchListAllReposOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgRepoList'>['data']>> {\n const { direction, sdkOpts, sort } = {\n __proto__: null,\n ...options,\n } as FetchListAllReposOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n const rows: SocketSdkSuccessResult<'getOrgRepoList'>['data']['results'] = []\n let protection = 0\n let nextPage = 0\n while (nextPage >= 0) {\n if (++protection > 100) {\n return {\n ok: false,\n message: 'Infinite loop detected',\n cause: `Either there are over 100 pages of results or the fetch has run into an infinite loop. Breaking it off now. nextPage=${nextPage}`,\n }\n }\n // eslint-disable-next-line no-await-in-loop\n const orgRepoListCResult = await handleApiCall(\n sockSdk.getOrgRepoList(orgSlug, {\n sort,\n direction,\n per_page: String(100), // max\n page: String(nextPage),\n }),\n { desc: 'list of repositories' },\n )\n if (!orgRepoListCResult.ok) {\n return orgRepoListCResult\n }\n\n rows.push(...orgRepoListCResult.data.results)\n nextPage = orgRepoListCResult.data.nextPage ?? -1\n }\n\n return {\n ok: true,\n data: {\n results: rows,\n nextPage: null,\n },\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchListReposConfig = {\n direction: string\n orgSlug: string\n page: number\n perPage: number\n sort: string\n}\n\nexport type FetchListReposOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchListRepos(\n config: FetchListReposConfig,\n options?: FetchListReposOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgRepoList'>['data']>> {\n const { direction, orgSlug, page, perPage, sort } = {\n __proto__: null,\n ...config,\n } as FetchListReposConfig\n\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchListReposOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(\n sockSdk.getOrgRepoList(orgSlug, {\n sort,\n direction,\n per_page: String(perPage),\n page: String(page),\n }),\n { desc: 'list of repositories' },\n )\n}\n","// @ts-ignore\nimport chalkTable from 'chalk-table'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputListRepos(\n result: CResult<SocketSdkSuccessResult<'getOrgRepoList'>['data']>,\n outputKind: OutputKind,\n page: number,\n nextPage: number | null,\n sort: string,\n perPage: number,\n direction: 'asc' | 'desc',\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n if (result.ok) {\n logger.log(\n serializeResultJson({\n ok: true,\n data: {\n data: result.data,\n direction,\n nextPage: nextPage ?? 0,\n page,\n perPage,\n sort,\n },\n }),\n )\n } else {\n logger.log(serializeResultJson(result))\n }\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log(\n `Result page: ${page}, results per page: ${perPage === Infinity ? 'all' : perPage}, sorted by: ${sort}, direction: ${direction}`,\n )\n\n const options = {\n columns: [\n { field: 'id', name: colors.magenta('ID') },\n { field: 'name', name: colors.magenta('Name') },\n { field: 'visibility', name: colors.magenta('Visibility') },\n { field: 'default_branch', name: colors.magenta('Default branch') },\n { field: 'archived', name: colors.magenta('Archived') },\n ],\n }\n\n logger.log(chalkTable(options, result.data.results))\n if (nextPage) {\n logger.info(\n `This is page ${page}. Server indicated there are more results available on page ${nextPage}...`,\n )\n logger.info(\n `(Hint: you can use \\`socket repository list --page ${nextPage}\\`)`,\n )\n } else if (perPage === Infinity) {\n logger.info(`This should be the entire list available on the server.`)\n } else {\n logger.info(\n `This is page ${page}. Server indicated this is the last page with results.`,\n )\n }\n}\n","import { fetchListAllRepos } from './fetch-list-all-repos.mts'\nimport { fetchListRepos } from './fetch-list-repos.mts'\nimport { outputListRepos } from './output-list-repos.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleListRepos({\n all,\n direction,\n orgSlug,\n outputKind,\n page,\n perPage,\n sort,\n}: {\n all: boolean\n direction: 'asc' | 'desc'\n orgSlug: string\n outputKind: OutputKind\n page: number\n perPage: number\n sort: string\n}): Promise<void> {\n if (all) {\n const data = await fetchListAllRepos(orgSlug, { direction, sort })\n\n await outputListRepos(data, outputKind, 0, 0, sort, Infinity, direction)\n } else {\n const data = await fetchListRepos({\n direction,\n orgSlug,\n page,\n perPage,\n sort,\n })\n\n if (!data.ok) {\n await outputListRepos(data, outputKind, 0, 0, '', 0, direction)\n } else {\n // Note: nextPage defaults to 0, is null when there's no next page\n await outputListRepos(\n data,\n outputKind,\n page,\n data.data.nextPage,\n sort,\n perPage,\n direction,\n )\n }\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleListRepos } from './handle-list-repos.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'list'\n\nconst description = 'List repositories in an organization'\n\nconst hidden = false\n\nexport const cmdRepositoryList = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n all: {\n type: 'boolean',\n default: false,\n description:\n 'By default view shows the last n repos. This flag allows you to fetch the entire list. Will ignore --page and --per-page.',\n },\n direction: {\n type: 'string',\n default: 'desc',\n description: 'Direction option',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n perPage: {\n type: 'number',\n shortFlag: 'pp',\n default: 30,\n description: 'Number of results per page',\n },\n page: {\n type: 'number',\n shortFlag: 'p',\n default: 1,\n description: 'Page number',\n },\n sort: {\n type: 'string',\n shortFlag: 's',\n default: 'created_at',\n description: 'Sorting option',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { all, direction = 'desc', json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n {\n nook: true,\n test: direction === 'asc' || direction === 'desc',\n message: 'The --direction value must be \"asc\" or \"desc\"',\n fail: 'unexpected value',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleListRepos({\n all: Boolean(all),\n direction: direction === 'asc' ? 'asc' : 'desc',\n orgSlug,\n outputKind,\n page: Number(cli.flags['page']) || 1,\n perPage: Number(cli.flags['perPage']) || 30,\n sort: String(cli.flags['sort'] || 'created_at'),\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchUpdateRepoConfig = {\n defaultBranch: string\n description: string\n homepage: string\n orgSlug: string\n repoName: string\n visibility: string\n}\n\nexport type FetchUpdateRepoOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchUpdateRepo(\n config: FetchUpdateRepoConfig,\n options?: FetchUpdateRepoOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'updateOrgRepo'>['data']>> {\n const {\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n } = { __proto__: null, ...config } as FetchUpdateRepoConfig\n\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchUpdateRepoOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(\n sockSdk.updateOrgRepo(orgSlug, repoName, {\n default_branch: defaultBranch,\n description,\n homepage,\n name: repoName,\n orgSlug,\n visibility,\n }),\n { desc: 'to update a repository' },\n )\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputUpdateRepo(\n result: CResult<SocketSdkSuccessResult<'updateOrgRepo'>['data']>,\n repoName: string,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.success(`Repository \\`${repoName}\\` updated successfully`)\n}\n","import { fetchUpdateRepo } from './fetch-update-repo.mts'\nimport { outputUpdateRepo } from './output-update-repo.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleUpdateRepo(\n {\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n }: {\n orgSlug: string\n repoName: string\n description: string\n homepage: string\n defaultBranch: string\n visibility: string\n },\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchUpdateRepo({\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n })\n\n await outputUpdateRepo(data, repoName, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleUpdateRepo } from './handle-update-repo.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'update'\n\nconst description = 'Update a repository in an organization'\n\nconst hidden = false\n\nexport const cmdRepositoryUpdate = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n defaultBranch: {\n type: 'string',\n shortFlag: 'b',\n default: 'main',\n description: 'Repository default branch',\n },\n homepage: {\n type: 'string',\n shortFlag: 'h',\n default: '',\n description: 'Repository url',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n repoDescription: {\n type: 'string',\n shortFlag: 'd',\n default: '',\n description: 'Repository description',\n },\n visibility: {\n type: 'string',\n shortFlag: 'v',\n default: 'private',\n description: 'Repository visibility (Default Private)',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <REPO>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} test-repo\n $ ${command} test-repo --homepage https://example.com\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const noLegacy = !cli.flags['repoName']\n\n const [repoName = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: noLegacy,\n message: 'Legacy flags are no longer supported. See v1 migration guide.',\n fail: `received legacy flags`,\n },\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n test: !!repoName,\n message: 'Repository name as first argument',\n fail: 'missing',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleUpdateRepo(\n {\n orgSlug,\n repoName: String(repoName),\n description: String(cli.flags['repoDescription'] || ''),\n homepage: String(cli.flags['homepage'] || ''),\n defaultBranch: String(cli.flags['defaultBranch'] || ''),\n visibility: String(cli.flags['visibility'] || 'private'),\n },\n outputKind,\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchViewRepoOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchViewRepo(\n orgSlug: string,\n repoName: string,\n options?: FetchViewRepoOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgRepo'>['data']>> {\n const { sdkOpts } = { __proto__: null, ...options } as FetchViewRepoOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getOrgRepo(orgSlug, repoName), {\n desc: 'repository data',\n })\n}\n","// @ts-ignore\nimport chalkTable from 'chalk-table'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputViewRepo(\n result: CResult<SocketSdkSuccessResult<'createOrgRepo'>['data']>,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const options = {\n columns: [\n { field: 'id', name: colors.magenta('ID') },\n { field: 'name', name: colors.magenta('Name') },\n { field: 'visibility', name: colors.magenta('Visibility') },\n { field: 'default_branch', name: colors.magenta('Default branch') },\n { field: 'homepage', name: colors.magenta('Homepage') },\n { field: 'archived', name: colors.magenta('Archived') },\n { field: 'created_at', name: colors.magenta('Created at') },\n ],\n }\n\n logger.log(chalkTable(options, [result.data]))\n}\n","import { fetchViewRepo } from './fetch-view-repo.mts'\nimport { outputViewRepo } from './output-view-repo.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleViewRepo(\n orgSlug: string,\n repoName: string,\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchViewRepo(orgSlug, repoName)\n\n await outputViewRepo(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleViewRepo } from './handle-view-repo.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'view'\n\nconst description = 'View repositories in an organization'\n\nconst hidden = false\n\nexport const cmdRepositoryView = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <REPO>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} test-repo\n $ ${command} test-repo --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const noLegacy = !cli.flags['repoName']\n\n const [repoName = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: noLegacy,\n message: 'Legacy flags are no longer supported. See v1 migration guide.',\n fail: `received legacy flags`,\n },\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n test: !!repoName,\n message: 'Repository name as first argument',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleViewRepo(orgSlug, String(repoName), outputKind)\n}\n","import { cmdRepositoryCreate } from './cmd-repository-create.mts'\nimport { cmdRepositoryDel } from './cmd-repository-del.mts'\nimport { cmdRepositoryList } from './cmd-repository-list.mts'\nimport { cmdRepositoryUpdate } from './cmd-repository-update.mts'\nimport { cmdRepositoryView } from './cmd-repository-view.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Manage registered repositories'\n\nexport const cmdRepository: CliSubcommand = {\n description,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n create: cmdRepositoryCreate,\n view: cmdRepositoryView,\n list: cmdRepositoryList,\n del: cmdRepositoryDel,\n update: cmdRepositoryUpdate,\n },\n {\n argv,\n description,\n importMeta,\n name: `${parentName} repository`,\n },\n )\n },\n}\n","import type { MeowFlags } from '../../flags.mts'\n\nexport const reachabilityFlags: MeowFlags = {\n reachAnalysisMemoryLimit: {\n type: 'number',\n default: 8192,\n description:\n 'The maximum memory in MB to use for the reachability analysis. The default is 8192MB.',\n },\n reachAnalysisTimeout: {\n type: 'number',\n default: 0,\n description:\n 'Set timeout for the reachability analysis. Split analysis runs may cause the total scan time to exceed this timeout significantly.',\n },\n reachDisableAnalytics: {\n type: 'boolean',\n default: false,\n description:\n 'Disable reachability analytics sharing with Socket. Also disables caching-based optimizations.',\n },\n reachEcosystems: {\n type: 'string',\n isMultiple: true,\n description:\n 'List of ecosystems to conduct reachability analysis on, as either a comma separated value or as multiple flags. Defaults to all ecosystems.',\n },\n reachExcludePaths: {\n type: 'string',\n isMultiple: true,\n description:\n 'List of paths to exclude from reachability analysis, as either a comma separated value or as multiple flags.',\n },\n reachSkipCache: {\n type: 'boolean',\n default: false,\n description:\n 'Skip caching-based optimizations. By default, the reachability analysis will use cached configurations from previous runs to speed up the analysis.',\n },\n}\n","import { select } from '@socketsecurity/registry/lib/prompts'\n\nexport async function suggestTarget(): Promise<string[]> {\n // We could prefill this with sub-dirs of the current\n // dir ... but is that going to be useful?\n const proceed = await select<boolean>({\n message: 'No TARGET given. Do you want to use the current directory?',\n choices: [\n {\n name: 'Yes',\n value: true,\n description: 'Target the current directory',\n },\n {\n name: 'No',\n value: false,\n description:\n 'Do not use the current directory (this will end in a no-op)',\n },\n ],\n })\n return proceed ? ['.'] : []\n}\n","import path from 'node:path'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleCreateNewScan } from './handle-create-new-scan.mts'\nimport { outputCreateNewScan } from './output-create-new-scan.mts'\nimport { reachabilityFlags } from './reachability-flags.mts'\nimport { suggestOrgSlug } from './suggest-org-slug.mts'\nimport { suggestTarget } from './suggest_target.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { cmdFlagValueToArray } from '../../utils/cmd.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getEcosystemChoicesForMeow } from '../../utils/ecosystem.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport {\n detectDefaultBranch,\n getRepoName,\n gitBranch,\n} from '../../utils/git.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\nimport { detectManifestActions } from '../manifest/detect-manifest-actions.mts'\n\nimport type { MeowFlags } from '../../flags.mts'\nimport type { PURL_Type } from '../../utils/ecosystem.mts'\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'create'\n\nconst description = 'Create a new Socket scan and report'\n\nconst hidden = false\n\nconst generalFlags: MeowFlags = {\n ...commonFlags,\n ...outputFlags,\n autoManifest: {\n type: 'boolean',\n description:\n 'Run `socket manifest auto` before collecting manifest files. This is necessary for languages like Scala, Gradle, and Kotlin, See `socket manifest auto --help`.',\n },\n branch: {\n type: 'string',\n shortFlag: 'b',\n description: 'Branch name',\n },\n commitHash: {\n type: 'string',\n shortFlag: 'ch',\n default: '',\n description: 'Commit hash',\n },\n commitMessage: {\n type: 'string',\n shortFlag: 'm',\n default: '',\n description: 'Commit message',\n },\n committers: {\n type: 'string',\n shortFlag: 'c',\n default: '',\n description: 'Committers',\n },\n cwd: {\n type: 'string',\n description: 'working directory, defaults to process.cwd()',\n },\n defaultBranch: {\n type: 'boolean',\n default: false,\n description:\n 'Set the default branch of the repository to the branch of this full-scan. Should only need to be done once, for example for the \"main\" or \"master\" branch.',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n pullRequest: {\n type: 'number',\n shortFlag: 'pr',\n description: 'Pull request number',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n reach: {\n type: 'boolean',\n default: false,\n description: 'Run tier 1 full application reachability analysis',\n },\n readOnly: {\n type: 'boolean',\n default: false,\n description:\n 'Similar to --dry-run except it can read from remote, stops before it would create an actual report',\n },\n repo: {\n type: 'string',\n shortFlag: 'r',\n description: 'Repository name',\n },\n report: {\n type: 'boolean',\n description:\n 'Wait for the scan creation to complete, then basically run `socket scan report` on it',\n },\n setAsAlertsPage: {\n type: 'boolean',\n default: true,\n aliases: ['pendingHead'],\n description:\n 'When true and if this is the \"default branch\" then this Scan will be the one reflected on your alerts page. See help for details. Defaults to true.',\n },\n tmp: {\n type: 'boolean',\n shortFlag: 't',\n default: false,\n description:\n 'Set the visibility (true/false) of the scan in your dashboard.',\n },\n}\n\nexport const cmdScanCreate = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...generalFlags,\n ...reachabilityFlags,\n },\n // TODO: Your project's \"socket.yml\" file's \"projectIgnorePaths\".\n help: command => `\n Usage\n $ ${command} [options] [TARGET...]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(generalFlags)}\n\n Reachability Options (when --reach is used)\n ${getFlagListOutput(reachabilityFlags)}\n\n Uploads the specified dependency manifest files for Go, Gradle, JavaScript,\n Kotlin, Python, and Scala. Files like \"package.json\" and \"requirements.txt\".\n If any folder is specified, the ones found in there recursively are uploaded.\n\n Details on TARGET:\n\n - Defaults to the current dir (cwd) if none given\n - Multiple targets can be specified\n - If a target is a file, only that file is checked\n - If it is a dir, the dir is scanned for any supported manifest files\n - Dirs MUST be within the current dir (cwd), you can use --cwd to change it\n - Supports globbing such as \"**/package.json\", \"**/requirements.txt\", etc.\n - Ignores any file specified in your project's \".gitignore\"\n - Also a sensible set of default ignores from the \"ignore-by-default\" module\n\n The --repo and --branch flags tell Socket to associate this Scan with that\n repo/branch. The names will show up on your dashboard on the Socket website.\n\n Note: for a first run you probably want to set --default-branch to indicate\n the default branch name, like \"main\" or \"master\".\n\n The \"alerts page\" (https://socket.dev/dashboard/org/YOURORG/alerts) will show\n the results from the last scan designated as the \"pending head\" on the branch\n configured on Socket to be the \"default branch\". When creating a scan the\n --set-as-alerts-page flag will default to true to update this. You can prevent\n this by using --no-set-as-alerts-page. This flag is ignored for any branch that\n is not designated as the \"default branch\". It is disabled when using --tmp.\n\n You can use \\`socket scan setup\\` to configure certain repo flag defaults.\n\n Examples\n $ ${command}\n $ ${command} ./proj --json\n $ ${command} --repo=test-repo --branch=main ./package.json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const {\n commitHash,\n commitMessage,\n committers,\n cwd: cwdOverride,\n defaultBranch,\n interactive = true,\n json,\n markdown,\n org: orgFlag,\n pullRequest,\n reach,\n reachAnalysisMemoryLimit,\n reachAnalysisTimeout,\n reachDisableAnalytics,\n reachSkipCache,\n readOnly,\n setAsAlertsPage: pendingHeadFlag,\n tmp,\n } = cli.flags as {\n cwd: string\n commitHash: string\n commitMessage: string\n committers: string\n defaultBranch: boolean\n interactive: boolean\n json: boolean\n markdown: boolean\n org: string\n pullRequest: number\n readOnly: boolean\n setAsAlertsPage: boolean\n tmp: boolean\n // Reachability flags.\n reach: boolean\n reachAnalysisTimeout: number\n reachAnalysisMemoryLimit: number\n reachDisableAnalytics: boolean\n reachSkipCache: boolean\n }\n\n const dryRun = !!cli.flags['dryRun']\n\n // Process comma-separated values for isMultiple flags.\n const reachEcosystemsRaw = cmdFlagValueToArray(cli.flags['reachEcosystems'])\n const reachExcludePaths = cmdFlagValueToArray(cli.flags['reachExcludePaths'])\n\n // Validate ecosystem values.\n const reachEcosystems: PURL_Type[] = []\n const validEcosystems = getEcosystemChoicesForMeow()\n for (const ecosystem of reachEcosystemsRaw) {\n if (!validEcosystems.includes(ecosystem)) {\n throw new Error(\n `Invalid ecosystem: \"${ecosystem}\". Valid values are: ${joinAnd(validEcosystems)}`,\n )\n }\n reachEcosystems.push(ecosystem as PURL_Type)\n }\n\n let {\n autoManifest,\n branch: branchName,\n repo: repoName,\n report,\n } = cli.flags as {\n autoManifest?: boolean | undefined\n branch: string\n repo: string\n report?: boolean | undefined\n }\n\n let [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const processCwd = process.cwd()\n const cwd =\n cwdOverride && cwdOverride !== processCwd\n ? path.resolve(processCwd, String(cwdOverride))\n : processCwd\n\n const sockJson = readOrDefaultSocketJson(cwd)\n\n // Note: This needs meow booleanDefault=undefined.\n if (typeof autoManifest !== 'boolean') {\n if (sockJson.defaults?.scan?.create?.autoManifest !== undefined) {\n autoManifest = sockJson.defaults.scan.create.autoManifest\n logger.info(\n 'Using default --auto-manifest from socket.json:',\n autoManifest,\n )\n } else {\n autoManifest = false\n }\n }\n if (!branchName) {\n if (sockJson.defaults?.scan?.create?.branch) {\n branchName = sockJson.defaults.scan.create.branch\n logger.info('Using default --branch from socket.json:', branchName)\n } else {\n branchName = (await gitBranch(cwd)) || (await detectDefaultBranch(cwd))\n }\n }\n if (!repoName) {\n if (sockJson.defaults?.scan?.create?.repo) {\n repoName = sockJson.defaults.scan.create.repo\n logger.info('Using default --repo from socket.json:', repoName)\n } else {\n repoName = await getRepoName(cwd)\n }\n }\n if (typeof report !== 'boolean') {\n if (sockJson.defaults?.scan?.create?.report !== undefined) {\n report = sockJson.defaults.scan.create.report\n logger.info('Using default --report from socket.json:', report)\n } else {\n report = false\n }\n }\n\n // If we updated any inputs then we should print the command line to repeat\n // the command without requiring user input, as a suggestion.\n let updatedInput = false\n\n // Accept zero or more paths. Default to cwd() if none given.\n let targets = cli.input || [cwd]\n\n if (!targets.length && !dryRun && interactive) {\n targets = await suggestTarget()\n updatedInput = true\n }\n\n // We're going to need an api token to suggest data because those suggestions\n // must come from data we already know. Don't error on missing api token yet.\n // If the api-token is not set, ignore it for the sake of suggestions.\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n const pendingHead = tmp ? false : pendingHeadFlag\n\n // If the current cwd is unknown and is used as a repo slug anyways, we will\n // first need to register the slug before we can use it.\n // Only do suggestions with an apiToken and when not in dryRun mode\n if (hasApiToken && !dryRun && interactive) {\n if (!orgSlug) {\n const suggestion = await suggestOrgSlug()\n if (suggestion === undefined) {\n await outputCreateNewScan(\n {\n ok: false,\n message: 'Canceled by user',\n cause: 'Org selector was canceled by user',\n },\n {\n interactive: false,\n outputKind,\n },\n )\n return\n }\n if (suggestion) {\n orgSlug = suggestion\n }\n updatedInput = true\n }\n }\n\n const detected = await detectManifestActions(sockJson, cwd)\n if (detected.count > 0 && !autoManifest) {\n logger.info(\n `Detected ${detected.count} manifest targets we could try to generate. Please set the --auto-manifest flag if you want to include languages covered by \\`socket manifest auto\\` in the Scan.`,\n )\n }\n\n if (updatedInput && orgSlug && targets.length) {\n logger.info(\n 'Note: You can invoke this command next time to skip the interactive questions:',\n )\n logger.error('```')\n logger.error(\n ` socket scan create [other flags...] ${orgSlug} ${targets.join(' ')}`,\n )\n logger.error('```')\n logger.error('')\n logger.info(\n 'You can also run `socket scan setup` to persist these flag defaults to a socket.json file.',\n )\n logger.error('')\n }\n\n // Validation helpers for better readability.\n const hasReachEcosystems = reachEcosystems.length > 0\n\n const hasReachExcludePaths = reachExcludePaths.length > 0\n\n const isUsingNonDefaultMemoryLimit =\n reachAnalysisMemoryLimit !==\n reachabilityFlags['reachAnalysisMemoryLimit']?.default\n\n const isUsingNonDefaultTimeout =\n reachAnalysisTimeout !== reachabilityFlags['reachAnalysisTimeout']?.default\n\n const isUsingNonDefaultAnalytics =\n reachDisableAnalytics !==\n reachabilityFlags['reachDisableAnalytics']?.default\n\n const isUsingAnyReachabilityFlags =\n isUsingNonDefaultMemoryLimit ||\n isUsingNonDefaultTimeout ||\n isUsingNonDefaultAnalytics ||\n hasReachEcosystems ||\n hasReachExcludePaths ||\n reachSkipCache\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n test: !!targets.length,\n message: 'At least one TARGET (e.g. `.` or `./package.json`)',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n {\n nook: true,\n test: !defaultBranch || !!branchName,\n message: 'When --default-branch is set, --branch is mandatory',\n fail: 'missing branch name',\n },\n {\n nook: true,\n test: !pendingHead || !!branchName,\n message: 'When --pending-head is set, --branch is mandatory',\n fail: 'missing branch name',\n },\n {\n nook: true,\n test: reach || !isUsingAnyReachabilityFlags,\n message: 'Reachability analysis flags require --reach to be enabled',\n fail: 'add --reach flag to use --reach-* options',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleCreateNewScan({\n autoManifest: Boolean(autoManifest),\n branchName: branchName as string,\n commitHash: (commitHash && String(commitHash)) || '',\n commitMessage: (commitMessage && String(commitMessage)) || '',\n committers: (committers && String(committers)) || '',\n cwd,\n defaultBranch: Boolean(defaultBranch),\n interactive: Boolean(interactive),\n orgSlug,\n outputKind,\n pendingHead: Boolean(pendingHead),\n pullRequest: Number(pullRequest),\n reach: {\n runReachabilityAnalysis: Boolean(reach),\n reachDisableAnalytics: Boolean(reachDisableAnalytics),\n reachAnalysisTimeout: Number(reachAnalysisTimeout),\n reachAnalysisMemoryLimit: Number(reachAnalysisMemoryLimit),\n reachEcosystems,\n reachExcludePaths,\n reachSkipCache: Boolean(reachSkipCache),\n },\n readOnly: Boolean(readOnly),\n repoName,\n report,\n targets,\n tmp: Boolean(tmp),\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchDeleteOrgFullScanOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchDeleteOrgFullScan(\n orgSlug: string,\n scanId: string,\n options?: FetchDeleteOrgFullScanOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'deleteOrgFullScan'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchDeleteOrgFullScanOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.deleteOrgFullScan(orgSlug, scanId), {\n desc: 'to delete a scan',\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputDeleteScan(\n result: CResult<SocketSdkSuccessResult<'deleteOrgFullScan'>['data']>,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.success('Scan deleted successfully')\n}\n","import { fetchDeleteOrgFullScan } from './fetch-delete-org-full-scan.mts'\nimport { outputDeleteScan } from './output-delete-scan.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleDeleteScan(\n orgSlug: string,\n scanId: string,\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchDeleteOrgFullScan(orgSlug, scanId)\n\n await outputDeleteScan(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleDeleteScan } from './handle-delete-scan.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'del'\n\nconst description = 'Delete a scan'\n\nconst hidden = false\n\nexport const cmdScanDel = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <SCAN_ID>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0 --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const [scanId = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug, defaultOrgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: !!defaultOrgSlug,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n test: !!scanId,\n message: 'Scan ID to delete',\n fail: 'missing',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleDeleteScan(orgSlug, scanId, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { queryApiSafeJson } from '../../utils/api.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function fetchDiffScan({\n id1,\n id2,\n orgSlug,\n}: {\n id1: string\n id2: string\n orgSlug: string\n}): Promise<CResult<SocketSdkSuccessResult<'GetOrgDiffScan'>['data']>> {\n logger.info('Scan ID 1:', id1)\n logger.info('Scan ID 2:', id2)\n logger.info('Note: this request may take some time if the scans are big')\n\n return await queryApiSafeJson<\n SocketSdkSuccessResult<'GetOrgDiffScan'>['data']\n >(\n `orgs/${orgSlug}/full-scans/diff?before=${encodeURIComponent(id1)}&after=${encodeURIComponent(id2)}`,\n 'a scan diff',\n )\n}\n","import fs from 'node:fs'\nimport util from 'node:util'\n\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputDiffScan(\n result: CResult<SocketSdkSuccessResult<'GetOrgDiffScan'>['data']>,\n {\n depth,\n file,\n outputKind,\n }: {\n depth: number\n file: string\n outputKind: OutputKind\n },\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (!result.ok) {\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const dashboardUrl = result.data.diff_report_url\n const dashboardMessage = dashboardUrl\n ? `\\n View this diff scan in the Socket dashboard: ${colors.cyan(dashboardUrl)}`\n : ''\n\n // When forcing json, or dumping to file, serialize to string such that it\n // won't get truncated. The only way to dump the full raw JSON to stdout is\n // to use `--json --file -` (the dash is a standard notation for stdout)\n if (outputKind === 'json' || file) {\n await handleJson(result, file, dashboardMessage)\n return\n }\n\n if (outputKind === 'markdown') {\n await handleMarkdown(result.data)\n return\n }\n\n // In this case neither the --json nor the --file flag was passed\n // Dump the JSON to CLI and let NodeJS deal with truncation\n\n logger.log('Diff scan result:')\n logger.log(\n util.inspect(result.data, {\n showHidden: false,\n depth: depth > 0 ? depth : null,\n colors: true,\n maxArrayLength: null,\n }),\n )\n logger.info(\n `\\n 📝 To display the detailed report in the terminal, use the --json flag. For a friendlier report, use the --markdown flag.\\n`,\n )\n logger.info(dashboardMessage)\n}\n\nasync function handleJson(\n data: CResult<SocketSdkSuccessResult<'GetOrgDiffScan'>['data']>,\n file: string,\n dashboardMessage: string,\n) {\n const json = serializeResultJson(data)\n\n if (file && file !== '-') {\n logger.log(`Writing json to \\`${file}\\``)\n fs.writeFile(file, json, err => {\n if (err) {\n logger.fail(`Writing to \\`${file}\\` failed...`)\n logger.error(err)\n } else {\n logger.success(`Data successfully written to \\`${file}\\``)\n }\n logger.error(dashboardMessage)\n })\n } else {\n // only .log goes to stdout\n logger.info(`\\n Diff scan result: \\n`)\n logger.log(json)\n logger.info(dashboardMessage)\n }\n}\n\nasync function handleMarkdown(\n data: SocketSdkSuccessResult<'GetOrgDiffScan'>['data'],\n) {\n const SOCKET_SBOM_URL_PREFIX = `${constants.SOCKET_WEBSITE_URL}/dashboard/org/SocketDev/sbom/`\n\n logger.log('# Scan diff result')\n logger.log('')\n logger.log('This Socket.dev report shows the changes between two scans:')\n logger.log(\n `- [${data.before.id}](${SOCKET_SBOM_URL_PREFIX}${data.before.id})`,\n )\n logger.log(`- [${data.after.id}](${SOCKET_SBOM_URL_PREFIX}${data.after.id})`)\n logger.log('')\n logger.log(\n `You can [view this report in your dashboard](${data.diff_report_url})`,\n )\n logger.log('')\n logger.log('## Changes')\n logger.log('')\n logger.log(`- directDependenciesChanged: ${data.directDependenciesChanged}`)\n logger.log(`- Added packages: ${data.artifacts.added.length}`)\n\n if (data.artifacts.added.length > 0) {\n data.artifacts.added.slice(0, 10).forEach(artifact => {\n logger.log(` - ${artifact.type} ${artifact.name}@${artifact.version}`)\n })\n if (data.artifacts.added.length > 10) {\n logger.log(` ... and ${data.artifacts.added.length - 10} more`)\n }\n }\n\n logger.log(`- Removed packages: ${data.artifacts.removed.length}`)\n if (data.artifacts.removed.length > 0) {\n data.artifacts.removed.slice(0, 10).forEach(artifact => {\n logger.log(` - ${artifact.type} ${artifact.name}@${artifact.version}`)\n })\n if (data.artifacts.removed.length > 10) {\n logger.log(` ... and ${data.artifacts.removed.length - 10} more`)\n }\n }\n\n logger.log(`- Replaced packages: ${data.artifacts.replaced.length}`)\n if (data.artifacts.replaced.length > 0) {\n data.artifacts.replaced.slice(0, 10).forEach(artifact => {\n logger.log(` - ${artifact.type} ${artifact.name}@${artifact.version}`)\n })\n if (data.artifacts.replaced.length > 10) {\n logger.log(` ... and ${data.artifacts.replaced.length - 10} more`)\n }\n }\n\n logger.log(`- Updated packages: ${data.artifacts.updated.length}`)\n if (data.artifacts.updated.length > 0) {\n data.artifacts.updated.slice(0, 10).forEach(artifact => {\n logger.log(` - ${artifact.type} ${artifact.name}@${artifact.version}`)\n })\n if (data.artifacts.updated.length > 10) {\n logger.log(` ... and ${data.artifacts.updated.length - 10} more`)\n }\n }\n\n const unchanged = data.artifacts.unchanged ?? []\n logger.log(`- Unchanged packages: ${unchanged.length}`)\n if (unchanged.length > 0) {\n const firstUpToTen = unchanged.slice(0, 10)\n for (const artifact of firstUpToTen) {\n logger.log(` - ${artifact.type} ${artifact.name}@${artifact.version}`)\n }\n if (unchanged.length > 10) {\n logger.log(` ... and ${unchanged.length - 10} more`)\n }\n }\n\n logger.log('')\n logger.log(`## Scan ${data.before.id}`)\n logger.log('')\n logger.log(\n 'This Scan was considered to be the \"base\" / \"from\" / \"before\" Scan.',\n )\n logger.log('')\n for (const [key, value] of Object.entries(data.before)) {\n if (key === 'pull_request' && !value) {\n continue\n }\n if (!['id', 'organization_id', 'repository_id'].includes(key)) {\n logger.group(\n `- ${key === 'repository_slug' ? 'repo' : key === 'organization_slug' ? 'org' : key}: ${value}`,\n )\n logger.groupEnd()\n }\n }\n\n logger.log('')\n logger.log(`## Scan ${data.after.id}`)\n logger.log('')\n logger.log('This Scan was considered to be the \"head\" / \"to\" / \"after\" Scan.')\n logger.log('')\n for (const [key, value] of Object.entries(data.after)) {\n if (key === 'pull_request' && !value) {\n continue\n }\n if (!['id', 'organization_id', 'repository_id'].includes(key)) {\n logger.group(\n `- ${key === 'repository_slug' ? 'repo' : key === 'organization_slug' ? 'org' : key}: ${value}`,\n )\n logger.groupEnd()\n }\n }\n\n logger.log('')\n}\n","import { fetchDiffScan } from './fetch-diff-scan.mts'\nimport { outputDiffScan } from './output-diff-scan.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleDiffScan({\n depth,\n file,\n id1,\n id2,\n orgSlug,\n outputKind,\n}: {\n depth: number\n file: string\n id1: string\n id2: string\n orgSlug: string\n outputKind: OutputKind\n}): Promise<void> {\n const data = await fetchDiffScan({\n id1,\n id2,\n orgSlug,\n })\n\n await outputDiffScan(data, {\n depth,\n file,\n outputKind,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleDiffScan } from './handle-diff-scan.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'diff'\n\nconst description = 'See what changed between two Scans'\n\nconst hidden = false\n\nexport const cmdScanDiff = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n depth: {\n type: 'number',\n default: 2,\n description:\n 'Max depth of JSON to display before truncating, use zero for no limit (without --json/--file)',\n },\n file: {\n type: 'string',\n shortFlag: 'f',\n default: '',\n description:\n 'Path to a local file where the output should be saved. Use `-` to force stdout.',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <SCAN_ID1> <SCAN_ID2>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n This command displays the package changes between two scans. The full output\n can be pretty large depending on the size of your repo and time range. It is\n best stored to disk (with --json) to be further analyzed by other tools.\n\n Note: While it will work in any order, the first Scan ID is assumed to be the\n older ID, even if it is a newer Scan. This is only relevant for the\n added/removed list (similar to diffing two files with git).\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} aaa0aa0a-aaaa-0000-0a0a-0000000a00a0 aaa1aa1a-aaaa-1111-1a1a-1111111a11a1\n $ ${command} aaa0aa0a-aaaa-0000-0a0a-0000000a00a0 aaa1aa1a-aaaa-1111-1a1a-1111111a11a1 --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const SOCKET_SBOM_URL_PREFIX = `${constants.SOCKET_WEBSITE_URL}/dashboard/org/SocketDev/sbom/`\n\n const SOCKET_SBOM_URL_PREFIX_LENGTH = SOCKET_SBOM_URL_PREFIX.length\n\n const { depth, file, json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n let [id1 = '', id2 = ''] = cli.input\n // Support dropping in full socket urls to an sbom.\n if (id1.startsWith(SOCKET_SBOM_URL_PREFIX)) {\n id1 = id1.slice(SOCKET_SBOM_URL_PREFIX_LENGTH)\n }\n if (id2.startsWith(SOCKET_SBOM_URL_PREFIX)) {\n id2 = id2.slice(SOCKET_SBOM_URL_PREFIX_LENGTH)\n }\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: !!(id1 && id2),\n message:\n 'Specify two Scan IDs.\\nA Scan ID looks like `aaa0aa0a-aaaa-0000-0a0a-0000000a00a0`.',\n fail:\n !id1 && !id2\n ? 'missing both Scan IDs'\n : !id2\n ? 'missing second Scan ID'\n : 'missing first Scan ID', // Not sure how this can happen but ok.\n },\n {\n test: !!orgSlug,\n nook: true,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleDiffScan({\n id1: String(id1 || ''),\n id2: String(id2 || ''),\n depth: Number(depth),\n orgSlug,\n outputKind,\n file: String(file || ''),\n })\n}\n","import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport { pipeline } from 'node:stream/promises'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { confirm, select } from '@socketsecurity/registry/lib/prompts'\n\nimport { fetchSupportedScanFileNames } from './fetch-supported-scan-file-names.mts'\nimport { handleCreateNewScan } from './handle-create-new-scan.mts'\nimport { isReportSupportedFile } from '../../utils/glob.mts'\nimport { fetchListAllRepos } from '../repository/fetch-list-all-repos.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function createScanFromGithub({\n all,\n githubApiUrl,\n githubToken,\n interactive,\n orgGithub,\n orgSlug,\n outputKind,\n repos,\n}: {\n all: boolean\n githubApiUrl: string\n githubToken: string\n interactive: boolean\n orgSlug: string\n orgGithub: string\n outputKind: OutputKind\n repos: string\n}): Promise<CResult<undefined>> {\n let targetRepos: string[] = repos\n .trim()\n .split(',')\n .map(r => r.trim())\n .filter(Boolean)\n if (all || targetRepos.length === 0) {\n // Fetch from Socket API\n const result = await fetchListAllRepos(orgSlug, {\n direction: 'asc',\n sort: 'name',\n })\n if (!result.ok) {\n return result\n }\n targetRepos = result.data.results.map(obj => obj.slug || '')\n }\n\n targetRepos = targetRepos.map(s => s.trim()).filter(Boolean)\n\n logger.info(`Have ${targetRepos.length} repo names to Scan!`)\n logger.log('')\n\n if (!targetRepos.filter(Boolean).length) {\n return {\n ok: false,\n message: 'No repo found',\n cause:\n 'You did not set the --repos value and/or the server responded with zero repos when asked for some. Unable to proceed.',\n }\n }\n\n // Non-interactive or explicitly requested; just do it.\n if (interactive && targetRepos.length > 1 && !all && !repos) {\n const which = await selectFocus(targetRepos)\n if (!which.ok) {\n return which\n }\n targetRepos = which.data\n }\n\n // 10 is an arbitrary number. Maybe confirm whenever count>1 ?\n // Do not ask to confirm when the list was given explicit.\n if (interactive && (all || !repos) && targetRepos.length > 10) {\n const sure = await makeSure(targetRepos.length)\n if (!sure.ok) {\n return sure\n }\n }\n\n let scansCreated = 0\n for (const repoSlug of targetRepos) {\n // eslint-disable-next-line no-await-in-loop\n const scanCResult = await scanRepo(repoSlug, {\n githubApiUrl,\n githubToken,\n orgSlug,\n orgGithub,\n outputKind,\n repos,\n })\n if (scanCResult.ok) {\n const { scanCreated } = scanCResult.data\n if (scanCreated) {\n scansCreated += 1\n }\n }\n }\n\n logger.success(targetRepos.length, 'GitHub repos detected')\n logger.success(scansCreated, 'with supported Manifest files')\n\n return {\n ok: true,\n data: undefined,\n }\n}\n\nasync function scanRepo(\n repoSlug: string,\n {\n githubApiUrl,\n githubToken,\n orgGithub,\n orgSlug,\n outputKind,\n repos,\n }: {\n githubApiUrl: string\n githubToken: string\n orgSlug: string\n orgGithub: string\n outputKind: OutputKind\n repos: string\n },\n): Promise<CResult<{ scanCreated: boolean }>> {\n logger.info(\n `Requesting repo details from GitHub API for: \\`${orgGithub}/${repoSlug}\\`...`,\n )\n logger.group()\n const result = await scanOneRepo(repoSlug, {\n githubApiUrl,\n githubToken,\n orgSlug,\n orgGithub,\n outputKind,\n repos,\n })\n logger.groupEnd()\n logger.log('')\n return result\n}\n\nasync function scanOneRepo(\n repoSlug: string,\n {\n githubApiUrl,\n githubToken,\n orgGithub,\n orgSlug,\n outputKind,\n }: {\n githubApiUrl: string\n githubToken: string\n orgSlug: string\n orgGithub: string\n outputKind: OutputKind\n repos: string\n },\n): Promise<CResult<{ scanCreated: boolean }>> {\n const repoResult = await getRepoDetails({\n orgGithub,\n repoSlug,\n githubApiUrl,\n githubToken,\n })\n if (!repoResult.ok) {\n return repoResult\n }\n const { defaultBranch, repoApiUrl } = repoResult.data\n\n logger.info(`Default branch: \\`${defaultBranch}\\``)\n\n const treeResult = await getRepoBranchTree({\n defaultBranch,\n githubToken,\n orgGithub,\n repoSlug,\n repoApiUrl,\n })\n if (!treeResult.ok) {\n return treeResult\n }\n const files = treeResult.data\n\n if (!files.length) {\n logger.warn(\n 'No files were reported for the default branch. Moving on to next repo.',\n )\n return { ok: true, data: { scanCreated: false } }\n }\n\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), repoSlug))\n debugFn('notice', 'init: temp dir for scan root', tmpDir)\n\n const downloadResult = await testAndDownloadManifestFiles({\n files,\n tmpDir,\n repoSlug,\n defaultBranch,\n orgGithub,\n repoApiUrl,\n githubToken,\n })\n if (!downloadResult.ok) {\n return downloadResult\n }\n\n const commitResult = await getLastCommitDetails({\n orgGithub,\n repoSlug,\n defaultBranch,\n repoApiUrl,\n githubToken,\n })\n if (!commitResult.ok) {\n return commitResult\n }\n\n const { lastCommitMessage, lastCommitSha, lastCommitter } = commitResult.data\n\n // Make request for full scan\n // I think we can just kick off the socket scan create command now...\n\n await handleCreateNewScan({\n autoManifest: false,\n branchName: defaultBranch,\n commitHash: lastCommitSha,\n commitMessage: lastCommitMessage || '',\n committers: lastCommitter || '',\n cwd: tmpDir,\n defaultBranch: true,\n interactive: false,\n orgSlug,\n outputKind,\n pendingHead: true,\n pullRequest: 0,\n reach: {\n runReachabilityAnalysis: false,\n reachDisableAnalytics: false,\n reachAnalysisTimeout: 0,\n reachAnalysisMemoryLimit: 0,\n reachEcosystems: [],\n reachExcludePaths: [],\n reachSkipCache: false,\n },\n readOnly: false,\n repoName: repoSlug,\n report: false,\n targets: ['.'],\n tmp: false,\n })\n\n return { ok: true, data: { scanCreated: true } }\n}\n\nasync function testAndDownloadManifestFiles({\n defaultBranch,\n files,\n githubToken,\n orgGithub,\n repoApiUrl,\n repoSlug,\n tmpDir,\n}: {\n files: string[]\n tmpDir: string\n repoSlug: string\n defaultBranch: string\n orgGithub: string\n repoApiUrl: string\n githubToken: string\n}): Promise<CResult<unknown>> {\n logger.info(\n `File tree for ${defaultBranch} contains`,\n files.length,\n `entries. Searching for supported manifest files...`,\n )\n logger.group()\n let fileCount = 0\n let firstFailureResult\n for (const file of files) {\n // eslint-disable-next-line no-await-in-loop\n const result = await testAndDownloadManifestFile({\n file,\n tmpDir,\n defaultBranch,\n repoApiUrl,\n githubToken,\n })\n if (result.ok) {\n if (result.data.isManifest) {\n fileCount += 1\n }\n } else if (!firstFailureResult) {\n firstFailureResult = result\n }\n }\n logger.groupEnd()\n logger.info('Found and downloaded', fileCount, 'manifest files')\n\n if (!fileCount) {\n if (firstFailureResult) {\n logger.fail(\n 'While no supported manifest files were downloaded, at least one error encountered trying to do so. Showing the first error.',\n )\n return firstFailureResult\n }\n return {\n ok: false,\n message: 'No manifest files found',\n cause: `No supported manifest files were found in the latest commit on the branch ${defaultBranch} for repo ${orgGithub}/${repoSlug}. Skipping full scan.`,\n }\n }\n\n return { ok: true, data: undefined }\n}\n\nasync function testAndDownloadManifestFile({\n defaultBranch,\n file,\n githubToken,\n repoApiUrl,\n tmpDir,\n}: {\n file: string\n tmpDir: string\n defaultBranch: string\n repoApiUrl: string\n githubToken: string\n}): Promise<CResult<{ isManifest: boolean }>> {\n debugFn('notice', 'testing: file', file)\n\n const supportedFilesCResult = await fetchSupportedScanFileNames()\n const supportedFiles = supportedFilesCResult.ok\n ? supportedFilesCResult.data\n : undefined\n\n if (!supportedFiles || !isReportSupportedFile(file, supportedFiles)) {\n debugFn('notice', 'skip: not a known pattern')\n // Not an error.\n return { ok: true, data: { isManifest: false } }\n }\n\n debugFn(\n 'notice',\n 'found: manifest file, going to attempt to download it;',\n file,\n )\n\n const result = await downloadManifestFile({\n file,\n tmpDir,\n defaultBranch,\n repoApiUrl,\n githubToken,\n })\n\n return result.ok ? { ok: true, data: { isManifest: true } } : result\n}\n\nasync function downloadManifestFile({\n defaultBranch,\n file,\n githubToken,\n repoApiUrl,\n tmpDir,\n}: {\n file: string\n tmpDir: string\n defaultBranch: string\n repoApiUrl: string\n githubToken: string\n}): Promise<CResult<undefined>> {\n debugFn('notice', 'request: download url from GitHub')\n\n const fileUrl = `${repoApiUrl}/contents/${file}?ref=${defaultBranch}`\n debugDir('inspect', { fileUrl })\n\n const downloadUrlResponse = await fetch(fileUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n debugFn('notice', 'complete: request')\n\n const downloadUrlText = await downloadUrlResponse.text()\n debugFn('inspect', 'response: raw download url', downloadUrlText)\n\n let downloadUrl\n try {\n downloadUrl = JSON.parse(downloadUrlText).download_url\n } catch {\n logger.fail(\n `GitHub response contained invalid JSON for download url for: ${file}`,\n )\n\n return {\n ok: false,\n message: 'Invalid JSON response',\n cause: `Server responded with invalid JSON for download url ${downloadUrl}`,\n }\n }\n\n const localPath = path.join(tmpDir, file)\n debugFn(\n 'notice',\n 'download: manifest file started',\n downloadUrl,\n '->',\n localPath,\n )\n\n // Now stream the file to that file...\n const result = await streamDownloadWithFetch(localPath, downloadUrl)\n if (!result.ok) {\n // Do we proceed? Bail? Hrm...\n logger.fail(\n `Failed to download manifest file, skipping to next file. File: ${file}`,\n )\n return result\n }\n\n debugFn('notice', 'download: manifest file completed')\n\n return { ok: true, data: undefined }\n}\n\n// Courtesy of gemini:\nasync function streamDownloadWithFetch(\n localPath: string,\n downloadUrl: string,\n): Promise<CResult<string>> {\n let response // Declare response here to access it in catch if needed\n\n try {\n response = await fetch(downloadUrl)\n\n if (!response.ok) {\n const errorMsg = `Download failed due to bad server response: ${response.status} ${response.statusText} for ${downloadUrl}`\n logger.fail(errorMsg)\n return { ok: false, message: 'Download Failed', cause: errorMsg }\n }\n\n if (!response.body) {\n logger.fail(\n `Download failed because the server response was empty, for ${downloadUrl}`,\n )\n return {\n ok: false,\n message: 'Download Failed',\n cause: 'Response body is null or undefined.',\n }\n }\n\n // Make sure the dir exists. It may be nested and we need to construct that\n // before starting the download.\n const dir = path.dirname(localPath)\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n\n const fileStream = fs.createWriteStream(localPath)\n\n // Using stream.pipeline for better error handling and cleanup\n\n await pipeline(response.body, fileStream)\n // 'pipeline' will automatically handle closing streams and propagating errors.\n // It resolves when the piping is fully complete and fileStream is closed.\n return { ok: true, data: localPath }\n } catch (error) {\n logger.fail(\n 'An error was thrown while trying to download a manifest file... url:',\n downloadUrl,\n )\n debugDir('inspect', { error })\n\n // If an error occurs and fileStream was created, attempt to clean up.\n if (fs.existsSync(localPath)) {\n // Check if fileStream was even opened before trying to delete\n // This check might be too simplistic depending on when error occurs\n fs.unlink(localPath, unlinkErr => {\n if (unlinkErr) {\n logger.fail(\n `Error deleting partial file ${localPath}: ${unlinkErr.message}`,\n )\n }\n })\n }\n // Construct a more informative error message\n let detailedError = `Error during download of ${downloadUrl}: ${(error as { message: string }).message}`\n if ((error as { cause: string }).cause) {\n // Include cause if available (e.g., from network errors)\n detailedError += `\\nCause: ${(error as { cause: string }).cause}`\n }\n if (response && !response.ok) {\n // If error was due to bad HTTP status\n detailedError += ` (HTTP Status: ${response.status} ${response.statusText})`\n }\n debugFn('error', detailedError)\n return { ok: false, message: 'Download Failed', cause: detailedError }\n }\n}\n\nasync function getLastCommitDetails({\n defaultBranch,\n githubToken,\n orgGithub,\n repoApiUrl,\n repoSlug,\n}: {\n orgGithub: string\n repoSlug: string\n defaultBranch: string\n repoApiUrl: string\n githubToken: string\n}): Promise<\n CResult<{\n lastCommitSha: string\n lastCommitter: string | undefined\n lastCommitMessage: string\n }>\n> {\n logger.info(\n `Requesting last commit for default branch ${defaultBranch} for ${orgGithub}/${repoSlug}...`,\n )\n\n const commitApiUrl = `${repoApiUrl}/commits?sha=${defaultBranch}&per_page=1`\n debugFn('inspect', 'url: commit', commitApiUrl)\n\n const commitResponse = await fetch(commitApiUrl, {\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n\n const commitText = await commitResponse.text()\n debugFn('inspect', 'response: commit', commitText)\n\n let lastCommit\n try {\n lastCommit = JSON.parse(commitText)?.[0]\n } catch {\n logger.fail(`GitHub response contained invalid JSON for last commit`)\n logger.error(commitText)\n return {\n ok: false,\n message: 'Invalid JSON response',\n cause: `Server responded with invalid JSON for last commit of repo ${repoSlug}`,\n }\n }\n\n const lastCommitSha = lastCommit.sha\n const lastCommitter = Array.from(\n new Set([lastCommit.commit.author.name, lastCommit.commit.committer.name]),\n )[0]\n const lastCommitMessage = lastCommit.message\n\n if (!lastCommitSha) {\n return {\n ok: false,\n message: 'Missing commit SHA',\n cause: 'Unable to get last commit for repo',\n }\n }\n\n if (!lastCommitter) {\n return {\n ok: false,\n message: 'Missing committer',\n cause: 'Last commit does not have information about who made the commit',\n }\n }\n\n return { ok: true, data: { lastCommitSha, lastCommitter, lastCommitMessage } }\n}\n\nasync function selectFocus(repos: string[]): Promise<CResult<string[]>> {\n const proceed = await select<string>({\n message: 'Please select the repo to process:',\n choices: repos\n .map(slug => ({\n name: slug,\n value: slug,\n description: `Create scan for the ${slug} repo through GitHub`,\n }))\n .concat({\n name: '(Exit)',\n value: '',\n description: 'Cancel this action and exit',\n }),\n })\n if (!proceed) {\n return {\n ok: false,\n message: 'Canceled by user',\n cause: 'User chose to cancel the action',\n }\n }\n return { ok: true, data: [proceed] }\n}\n\nasync function makeSure(count: number): Promise<CResult<undefined>> {\n if (\n !(await confirm({\n message: `Are you sure you want to run this for ${count} repos?`,\n default: false,\n }))\n ) {\n return {\n ok: false,\n message: 'User canceled',\n cause: 'Action canceled by user',\n }\n }\n return { ok: true, data: undefined }\n}\n\nasync function getRepoDetails({\n githubApiUrl,\n githubToken,\n orgGithub,\n repoSlug,\n}: {\n orgGithub: string\n repoSlug: string\n githubApiUrl: string\n githubToken: string\n}): Promise<\n CResult<{ defaultBranch: string; repoDetails: unknown; repoApiUrl: string }>\n> {\n const repoApiUrl = `${githubApiUrl}/repos/${orgGithub}/${repoSlug}`\n debugDir('inspect', { repoApiUrl })\n\n const repoDetailsResponse = await fetch(repoApiUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n logger.success(`Request completed.`)\n\n const repoDetailsText = await repoDetailsResponse.text()\n debugFn('inspect', 'response: repo', repoDetailsText)\n\n let repoDetails\n try {\n repoDetails = JSON.parse(repoDetailsText)\n } catch {\n logger.fail(`GitHub response contained invalid JSON for repo ${repoSlug}`)\n logger.error(repoDetailsText)\n return {\n ok: false,\n message: 'Invalid JSON response',\n cause: `Server responded with invalid JSON for repo ${repoSlug}`,\n }\n }\n\n const defaultBranch = repoDetails.default_branch\n if (!defaultBranch) {\n return {\n ok: false,\n message: 'Default Branch Not Found',\n cause: `Repo ${repoSlug} does not have a default branch set or it was not reported`,\n }\n }\n\n return { ok: true, data: { defaultBranch, repoDetails, repoApiUrl } }\n}\n\nasync function getRepoBranchTree({\n defaultBranch,\n githubToken,\n orgGithub,\n repoApiUrl,\n repoSlug,\n}: {\n defaultBranch: string\n githubToken: string\n orgGithub: string\n repoApiUrl: string\n repoSlug: string\n}): Promise<CResult<string[]>> {\n logger.info(\n `Requesting default branch file tree; branch \\`${defaultBranch}\\`, repo \\`${orgGithub}/${repoSlug}\\`...`,\n )\n\n const treeApiUrl = `${repoApiUrl}/git/trees/${defaultBranch}?recursive=1`\n debugFn('inspect', 'url: tree', treeApiUrl)\n\n const treeResponse = await fetch(treeApiUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n\n const treeText = await treeResponse.text()\n debugFn('inspect', 'response: tree', treeText)\n\n let treeDetails\n try {\n treeDetails = JSON.parse(treeText)\n } catch {\n logger.fail(\n `GitHub response contained invalid JSON for default branch of repo ${repoSlug}`,\n )\n logger.error(treeText)\n return {\n ok: false,\n message: 'Invalid JSON response',\n cause: `Server responded with invalid JSON for repo ${repoSlug}`,\n }\n }\n\n if (treeDetails.message) {\n if (treeDetails.message === 'Git Repository is empty.') {\n logger.warn(\n `GitHub reports the default branch of repo ${repoSlug} to be empty. Moving on to next repo.`,\n )\n return { ok: true, data: [] }\n }\n\n logger.fail('Negative response from GitHub:', treeDetails.message)\n return {\n ok: false,\n message: 'Unexpected error response',\n cause: `GitHub responded with an unexpected error while asking for details on the default branch: ${treeDetails.message}`,\n }\n }\n\n if (!treeDetails.tree || !Array.isArray(treeDetails.tree)) {\n debugDir('inspect', { treeDetails: { tree: treeDetails.tree } })\n\n return {\n ok: false,\n message: `Tree response for default branch ${defaultBranch} for ${orgGithub}/${repoSlug} was not a list`,\n }\n }\n\n const files = (treeDetails.tree as Array<{ type: string; path: string }>)\n .filter(obj => obj.type === 'blob')\n .map(obj => obj.path)\n\n return { ok: true, data: files }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputScanGithub(\n result: CResult<unknown>,\n outputKind: OutputKind,\n) {\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log('')\n logger.success('Finished!')\n}\n","import { createScanFromGithub } from './create-scan-from-github.mts'\nimport { outputScanGithub } from './output-scan-github.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleCreateGithubScan({\n all,\n githubApiUrl,\n githubToken,\n interactive,\n orgGithub,\n orgSlug,\n outputKind,\n repos,\n}: {\n all: boolean\n githubApiUrl: string\n githubToken: string\n interactive: boolean\n orgSlug: string\n orgGithub: string\n outputKind: OutputKind\n repos: string\n}) {\n const ghScanCResult = await createScanFromGithub({\n all: Boolean(all),\n githubApiUrl,\n githubToken,\n interactive: Boolean(interactive),\n orgSlug,\n orgGithub,\n outputKind,\n repos: String(repos || ''),\n })\n\n await outputScanGithub(ghScanCResult, outputKind)\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleCreateGithubScan } from './handle-create-github-scan.mts'\nimport { outputScanGithub } from './output-scan-github.mts'\nimport { suggestOrgSlug } from './suggest-org-slug.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'github'\n\nconst description = 'Create a scan for given GitHub repo'\n\nconst hidden = true\n\nexport const cmdScanGithub = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n all: {\n type: 'boolean',\n description:\n 'Apply for all known repositories reported by the Socket API. Supersedes `repos`.',\n },\n githubToken: {\n type: 'string',\n description:\n 'Required GitHub token for authentication.\\nMay set environment variable GITHUB_TOKEN or SOCKET_CLI_GITHUB_TOKEN instead.',\n },\n githubApiUrl: {\n type: 'string',\n description:\n 'Base URL of the GitHub API (default: https://api.github.com)',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n orgGithub: {\n type: 'string',\n description:\n 'Alternate GitHub Org if the name is different than the Socket Org',\n },\n repos: {\n type: 'string',\n description:\n 'List of repos to target in a comma-separated format (e.g., repo1,repo2). If not specified, the script will pull the list from Socket and ask you to pick one. Use --all to use them all.',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n This is similar to the \\`socket scan create\\` command except it pulls the files\n from GitHub. See the help for that command for more details.\n\n A GitHub Personal Access Token (PAT) will at least need read access to the repo\n (\"contents\", read-only) for this command to work.\n\n Note: This command cannot run the \\`socket manifest auto\\` things because that\n requires local access to the repo while this command runs entirely through the\n GitHub for file access.\n\n You can use \\`socket scan setup\\` to configure certain repo flag defaults.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} ./proj\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const {\n githubToken = constants.ENV.SOCKET_CLI_GITHUB_TOKEN,\n interactive = true,\n json,\n markdown,\n org: orgFlag,\n } = cli.flags as {\n githubToken: string\n interactive: boolean\n json: boolean\n markdown: boolean\n org: string\n orgGithub: string\n }\n\n const dryRun = !!cli.flags['dryRun']\n\n let { all, githubApiUrl, orgGithub, repos } = cli.flags as {\n all: boolean\n githubApiUrl: string\n orgGithub: string\n repos: string\n }\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n let [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n const sockJson = readOrDefaultSocketJson(cwd)\n\n if (all === undefined) {\n if (sockJson.defaults?.scan?.github?.all !== undefined) {\n all = sockJson.defaults?.scan?.github?.all\n } else {\n all = false\n }\n }\n if (!githubApiUrl) {\n if (sockJson.defaults?.scan?.github?.githubApiUrl !== undefined) {\n githubApiUrl = sockJson.defaults.scan.github.githubApiUrl\n } else {\n githubApiUrl = 'https://api.github.com'\n }\n }\n if (!orgGithub) {\n if (sockJson.defaults?.scan?.github?.orgGithub !== undefined) {\n orgGithub = sockJson.defaults.scan.github.orgGithub\n } else {\n // Default to Socket org slug. Often that's fine. Vanity and all that.\n orgGithub = orgSlug\n }\n }\n if (!all && !repos) {\n if (sockJson.defaults?.scan?.github?.repos !== undefined) {\n repos = sockJson.defaults.scan.github.repos\n } else {\n repos = ''\n }\n }\n\n // We will also be needing that GitHub token.\n const hasGithubApiToken = !!githubToken\n\n // We're going to need an api token to suggest data because those suggestions\n // must come from data we already know. Don't error on missing api token yet.\n // If the api-token is not set, ignore it for the sake of suggestions.\n const hasSocketApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n // If the current cwd is unknown and is used as a repo slug anyways, we will\n // first need to register the slug before we can use it.\n // Only do suggestions with an apiToken and when not in dryRun mode\n if (hasSocketApiToken && !dryRun && interactive) {\n if (!orgSlug) {\n const suggestion = await suggestOrgSlug()\n if (suggestion === undefined) {\n await outputScanGithub(\n {\n ok: false,\n message: 'Canceled by user',\n cause: 'Org selector was canceled by user',\n },\n outputKind,\n )\n return\n }\n if (suggestion) {\n orgSlug = suggestion\n }\n }\n }\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasSocketApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n {\n test: hasGithubApiToken,\n message: 'This command requires a GitHub API token for access',\n fail: 'missing',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n // Note exiting earlier to skirt a hidden auth requirement\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleCreateGithubScan({\n all: Boolean(all),\n githubApiUrl,\n githubToken,\n interactive: Boolean(interactive),\n orgSlug,\n orgGithub,\n outputKind,\n repos,\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchOrgFullScanListConfig = {\n branch: string\n direction: string\n from_time: string\n orgSlug: string\n page: number\n perPage: number\n repo: string\n sort: string\n}\n\nexport type FetchOrgFullScanListOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchOrgFullScanList(\n config: FetchOrgFullScanListConfig,\n options?: FetchOrgFullScanListOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgFullScanList'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchOrgFullScanListOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n const { branch, direction, from_time, orgSlug, page, perPage, repo, sort } = {\n __proto__: null,\n ...config,\n } as FetchOrgFullScanListConfig\n\n return await handleApiCall(\n sockSdk.getOrgFullScanList(orgSlug, {\n ...(branch ? { branch } : {}),\n ...(repo ? { repo } : {}),\n sort,\n direction,\n from: from_time,\n page: String(page),\n per_page: String(perPage),\n }),\n { desc: 'list of scans' },\n )\n}\n","// @ts-ignore\nimport chalkTable from 'chalk-table'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputListScans(\n result: CResult<SocketSdkSuccessResult<'getOrgFullScanList'>['data']>,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const options = {\n columns: [\n { field: 'id', name: colors.magenta('ID') },\n { field: 'report_url', name: colors.magenta('Scan URL') },\n { field: 'repo', name: colors.magenta('Repo') },\n { field: 'branch', name: colors.magenta('Branch') },\n { field: 'created_at', name: colors.magenta('Created at') },\n ],\n }\n\n const formattedResults = result.data.results.map(d => {\n return {\n id: d.id,\n report_url: colors.underline(`${d.html_report_url}`),\n created_at: d.created_at\n ? new Date(d.created_at).toLocaleDateString('en-us', {\n year: 'numeric',\n month: 'numeric',\n day: 'numeric',\n })\n : '',\n repo: d.repo,\n branch: d.branch,\n }\n })\n\n logger.log(chalkTable(options, formattedResults))\n}\n","import { fetchOrgFullScanList } from './fetch-list-scans.mts'\nimport { outputListScans } from './output-list-scans.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleListScans({\n branch,\n direction,\n from_time,\n orgSlug,\n outputKind,\n page,\n perPage,\n repo,\n sort,\n}: {\n branch: string\n direction: string\n from_time: string\n orgSlug: string\n outputKind: OutputKind\n page: number\n perPage: number\n repo: string\n sort: string\n}): Promise<void> {\n const data = await fetchOrgFullScanList({\n branch,\n direction,\n from_time,\n orgSlug,\n page,\n perPage,\n repo,\n sort,\n })\n\n await outputListScans(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleListScans } from './handle-list-scans.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type {\n CliCommandConfig,\n CliSubcommand,\n} from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'list'\n\nconst description = 'List the scans for an organization'\n\nconst hidden = false\n\nexport const cmdScanList: CliSubcommand = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n branch: {\n type: 'string',\n description: 'Filter to show only scans with this branch name',\n },\n direction: {\n type: 'string',\n shortFlag: 'd',\n default: 'desc',\n description: 'Direction option (`desc` or `asc`) - Default is `desc`',\n },\n fromTime: {\n type: 'string',\n shortFlag: 'f',\n default: '',\n description: 'From time - as a unix timestamp',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n page: {\n type: 'number',\n shortFlag: 'p',\n default: 1,\n description: 'Page number - Default is 1',\n },\n perPage: {\n type: 'number',\n shortFlag: 'pp',\n default: 30,\n description: 'Results per page - Default is 30',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n sort: {\n type: 'string',\n shortFlag: 's',\n default: 'created_at',\n description:\n 'Sorting option (`name` or `created_at`) - default is `created_at`',\n },\n untilTime: {\n type: 'string',\n shortFlag: 'u',\n default: '',\n description: 'Until time - as a unix timestamp',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [REPO [BRANCH]]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Optionally filter by REPO. If you specify a repo, you can also specify a\n branch to filter by. (Note: If you don't specify a repo then you must use\n \\`--branch\\` to filter by branch across all repos).\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} webtools badbranch --markdown\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { branch: branchFlag, json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const noLegacy = !cli.flags['repo']\n\n const [repo = '', branchArg = ''] = cli.input\n\n const branch = String(branchFlag || branchArg || '')\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: noLegacy,\n message: 'Legacy flags are no longer supported. See v1 migration guide.',\n fail: `received legacy flags`,\n },\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'dot is an invalid org, most likely you forgot the org name here?',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n {\n nook: true,\n test: !branchFlag || !branchArg,\n message:\n 'You should not set --branch and also give a second arg for branch name',\n fail: 'received flag and second arg',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleListScans({\n branch: branch ? String(branch) : '',\n direction: String(cli.flags['direction'] || ''),\n from_time: String(cli.flags['fromTime'] || ''),\n orgSlug,\n outputKind,\n page: Number(cli.flags['page'] || 1),\n perPage: Number(cli.flags['perPage'] || 30),\n repo: repo ? String(repo) : '',\n sort: String(cli.flags['sort'] || ''),\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchScanMetadataOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchScanMetadata(\n orgSlug: string,\n scanId: string,\n options?: FetchScanMetadataOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgFullScanMetadata'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchScanMetadataOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getOrgFullScanMetadata(orgSlug, scanId), {\n desc: 'meta data for a full scan',\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputScanMetadata(\n result: CResult<SocketSdkSuccessResult<'getOrgFullScanMetadata'>['data']>,\n scanId: string,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'markdown') {\n logger.log('# Scan meta data\\n')\n }\n logger.log(`Scan ID: ${scanId}\\n`)\n for (const [key, value] of Object.entries(result.data)) {\n if (\n [\n 'id',\n 'updated_at',\n 'organization_id',\n 'repository_id',\n 'commit_hash',\n 'html_report_url',\n ].includes(key)\n ) {\n continue\n }\n logger.log(`- ${key}:`, value)\n }\n if (outputKind === 'markdown') {\n logger.log(\n `\\nYou can view this report at: [${result.data.html_report_url}](${result.data.html_report_url})\\n`,\n )\n } else {\n logger.log(\n `\\nYou can view this report at: ${result.data.html_report_url}]\\n`,\n )\n }\n}\n","import { fetchScanMetadata } from './fetch-scan-metadata.mts'\nimport { outputScanMetadata } from './output-scan-metadata.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleOrgScanMetadata(\n orgSlug: string,\n scanId: string,\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchScanMetadata(orgSlug, scanId)\n\n await outputScanMetadata(data, scanId, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleOrgScanMetadata } from './handle-scan-metadata.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type {\n CliCommandConfig,\n CliSubcommand,\n} from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'metadata'\n\nconst description = \"Get a scan's metadata\"\n\nconst hidden = false\n\nexport const cmdScanMetadata: CliSubcommand = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <SCAN_ID>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0 --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const [scanId = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail:\n orgSlug === '.'\n ? 'dot is an invalid org, most likely you forgot the org name here?'\n : 'missing',\n },\n {\n test: !!scanId,\n message: 'Scan ID to inspect as argument',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleOrgScanMetadata(orgSlug, scanId, outputKind)\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { ReachabilityAnalysisResult } from './perform-reachability-analysis.mts'\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputScanReach(\n result: CResult<ReachabilityAnalysisResult>,\n { cwd, outputKind }: { cwd: string; outputKind: OutputKind },\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log('')\n logger.success('Reachability analysis completed successfully!')\n logger.info(\n `Reachability report has been written to: ${path.join(cwd, constants.DOT_SOCKET_DOT_FACTS_JSON)}`,\n )\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { fetchSupportedScanFileNames } from './fetch-supported-scan-file-names.mts'\nimport { outputScanReach } from './output-scan-reach.mts'\nimport { performReachabilityAnalysis } from './perform-reachability-analysis.mts'\nimport constants from '../../constants.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getPackageFilesForScan } from '../../utils/path-resolve.mts'\n\nimport type { ReachabilityOptions } from './perform-reachability-analysis.mts'\nimport type { OutputKind } from '../../types.mts'\n\nexport type HandleScanReachConfig = {\n cwd: string\n interactive: boolean\n orgSlug: string\n outputKind: OutputKind\n reachabilityOptions: ReachabilityOptions\n targets: string[]\n}\n\nexport async function handleScanReach({\n cwd,\n interactive: _interactive,\n orgSlug,\n outputKind,\n reachabilityOptions,\n targets,\n}: HandleScanReachConfig) {\n const { spinner } = constants\n\n // Get supported file names\n const supportedFilesCResult = await fetchSupportedScanFileNames({ spinner })\n if (!supportedFilesCResult.ok) {\n await outputScanReach(supportedFilesCResult, { cwd, outputKind })\n return\n }\n\n spinner.start(\n 'Searching for local manifest files to include in reachability analysis...',\n )\n\n const supportedFiles = supportedFilesCResult.data\n const packagePaths = await getPackageFilesForScan(targets, supportedFiles, {\n cwd,\n })\n\n spinner.successAndStop(\n `Found ${packagePaths.length} ${pluralize('manifest file', packagePaths.length)} for reachability analysis.`,\n )\n\n const wasValidInput = checkCommandInput(outputKind, {\n nook: true,\n test: packagePaths.length > 0,\n fail: 'found no eligible files to analyze',\n message:\n 'TARGET (file/dir) must contain matching / supported file types for reachability analysis',\n })\n if (!wasValidInput) {\n return\n }\n\n logger.success(\n `Found ${packagePaths.length} local ${pluralize('file', packagePaths.length)}`,\n )\n\n spinner.start('Running reachability analysis...')\n\n const result = await performReachabilityAnalysis({\n cwd,\n orgSlug,\n packagePaths,\n reachabilityOptions,\n spinner,\n uploadManifests: true,\n })\n\n spinner.stop()\n\n await outputScanReach(result, { cwd, outputKind })\n}\n","import path from 'node:path'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleScanReach } from './handle-scan-reach.mts'\nimport { reachabilityFlags } from './reachability-flags.mts'\nimport { suggestTarget } from './suggest_target.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { cmdFlagValueToArray } from '../../utils/cmd.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getEcosystemChoicesForMeow } from '../../utils/ecosystem.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { MeowFlags } from '../../flags.mts'\nimport type { PURL_Type } from '../../utils/ecosystem.mts'\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'reach'\n\nconst description = 'Compute tier 1 reachability'\n\nconst hidden = true\n\nconst generalFlags: MeowFlags = {\n ...commonFlags,\n ...outputFlags,\n cwd: {\n type: 'string',\n description: 'working directory, defaults to process.cwd()',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n}\n\nexport const cmdScanReach = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...generalFlags,\n ...reachabilityFlags,\n },\n help: command =>\n `\n Usage\n $ ${command} [options] [CWD=.]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(generalFlags)}\n\n Reachability Options\n ${getFlagListOutput(reachabilityFlags)}\n\n Runs the Socket reachability analysis without creating a scan in Socket.\n The output is written to .socket.facts.json in the current working directory.\n\n Note: Manifest files are uploaded to Socket's backend services because the\n reachability analysis requires creating a Software Bill of Materials (SBOM)\n from these files before the analysis can run.\n\n Examples\n $ ${command}\n $ ${command} ./proj\n $ ${command} ./proj --reach-ecosystems npm,pypi\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const {\n cwd: cwdOverride,\n interactive = true,\n json,\n markdown,\n org: orgFlag,\n reachAnalysisMemoryLimit,\n reachAnalysisTimeout,\n reachDisableAnalytics,\n reachSkipCache,\n } = cli.flags as {\n cwd: string\n interactive: boolean\n json: boolean\n markdown: boolean\n org: string\n reachAnalysisTimeout: number\n reachAnalysisMemoryLimit: number\n reachDisableAnalytics: boolean\n reachSkipCache: boolean\n }\n\n const dryRun = !!cli.flags['dryRun']\n\n // Process comma-separated values for isMultiple flags.\n const reachEcosystemsRaw = cmdFlagValueToArray(cli.flags['reachEcosystems'])\n const reachExcludePaths = cmdFlagValueToArray(cli.flags['reachExcludePaths'])\n\n // Validate ecosystem values.\n const reachEcosystems: PURL_Type[] = []\n const validEcosystems = getEcosystemChoicesForMeow()\n for (const ecosystem of reachEcosystemsRaw) {\n if (!validEcosystems.includes(ecosystem)) {\n throw new Error(\n `Invalid ecosystem: \"${ecosystem}\". Valid values are: ${joinAnd(validEcosystems)}`,\n )\n }\n reachEcosystems.push(ecosystem as PURL_Type)\n }\n\n const processCwd = process.cwd()\n const cwd =\n cwdOverride && cwdOverride !== processCwd\n ? path.resolve(processCwd, String(cwdOverride))\n : processCwd\n\n // Accept zero or more paths. Default to cwd() if none given.\n let targets = cli.input || [cwd]\n\n // Use suggestTarget if no targets specified and in interactive mode\n if (!targets.length && !dryRun && interactive) {\n targets = await suggestTarget()\n }\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires an API token for access',\n fail: 'try `socket login`',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleScanReach({\n cwd,\n orgSlug,\n outputKind,\n targets,\n interactive,\n reachabilityOptions: {\n reachAnalysisTimeout: Number(reachAnalysisTimeout),\n reachAnalysisMemoryLimit: Number(reachAnalysisMemoryLimit),\n reachDisableAnalytics: Boolean(reachDisableAnalytics),\n reachEcosystems,\n reachExcludePaths,\n reachSkipCache: Boolean(reachSkipCache),\n },\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleScanReport } from './handle-scan-report.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type {\n CliCommandConfig,\n CliSubcommand,\n} from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'report'\n\nconst description =\n 'Check whether a scan result passes the organizational policies (security, license)'\n\nconst hidden = false\n\nexport const cmdScanReport: CliSubcommand = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n fold: {\n type: 'string',\n default: 'none',\n description: 'Fold reported alerts to some degree',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n reportLevel: {\n type: 'string',\n default: 'warn',\n description: 'Which policy level alerts should be reported',\n },\n short: {\n type: 'boolean',\n default: false,\n description: 'Report only the healthy status',\n },\n license: {\n type: 'boolean',\n default: false,\n description: 'Also report the license policy status. Default: false',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <SCAN_ID> [OUTPUT_PATH]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n When no output path is given the contents is sent to stdout.\n\n By default the result is a nested object that looks like this:\n \\`{\n [ecosystem]: {\n [pkgName]: {\n [version]: {\n [file]: {\n [line:col]: alert\n }}}}\\`\n So one alert for each occurrence in every file, version, etc, a huge response.\n\n You can --fold these up to given level: 'pkg', 'version', 'file', and 'none'.\n For example: \\`socket scan report --fold=version\\` will dedupe alerts to only\n show one alert of a particular kind, no matter how often it was foud in a\n file or in how many files it was found. At most one per version that has it.\n\n By default only the warn and error policy level alerts are reported. You can\n override this and request more ('defer' < 'ignore' < 'monitor' < 'warn' < 'error')\n\n Short responses look like this:\n --json: \\`{healthy:bool}\\`\n --markdown: \\`healthy = bool\\`\n neither: \\`OK/ERR\\`\n\n Examples\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0 --json --fold=version\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0 --license --markdown --short\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const {\n fold = 'none',\n json,\n license,\n markdown,\n org: orgFlag,\n reportLevel = 'warn',\n } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const [scanId = '', file = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'dot is an invalid org, most likely you forgot the org name here?',\n },\n {\n test: !!scanId,\n message: 'Scan ID to report on',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleScanReport({\n orgSlug,\n scanId,\n includeLicensePolicy: !!license,\n outputKind,\n filePath: file,\n fold: fold as 'none' | 'file' | 'pkg' | 'version',\n short: !!cli.flags['short'],\n reportLevel: reportLevel as\n | 'warn'\n | 'error'\n | 'defer'\n | 'ignore'\n | 'monitor',\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport async function outputScanConfigResult(result: CResult<unknown>) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log('')\n logger.log('Finished')\n logger.log('')\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { input, select } from '@socketsecurity/registry/lib/prompts'\n\nimport constants from '../../constants.mts'\nimport {\n detectDefaultBranch,\n getRepoName,\n gitBranch,\n} from '../../utils/git.mts'\nimport {\n readSocketJsonSync,\n writeSocketJson,\n} from '../../utils/socket-json.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { SocketJson } from '../../utils/socket-json.mts'\n\nexport async function setupScanConfig(\n cwd: string,\n defaultOnReadError = false,\n): Promise<CResult<unknown>> {\n const jsonPath = path.join(cwd, `socket.json`)\n if (fs.existsSync(jsonPath)) {\n logger.info(`Found socket.json at ${jsonPath}`)\n } else {\n logger.info(`No socket.json found at ${cwd}, will generate a new one`)\n }\n\n logger.log('')\n logger.log(\n 'Note: This tool will set up flag and argument defaults for certain',\n )\n logger.log(' CLI commands. You can still override them by explicitly')\n logger.log(' setting the flag. It is meant to be a convenience tool.')\n logger.log('')\n logger.log(\n 'This command will generate a `socket.json` file in the target cwd.',\n )\n logger.log('You can choose to add this file to your repo (handy for collab)')\n logger.log('or to add it to the ignored files, or neither. This file is only')\n logger.log('used in CLI workflows.')\n logger.log('')\n logger.log('Note: For details on a flag you can run `socket <cmd> --help`')\n logger.log('')\n\n const sockJsonCResult = readSocketJsonSync(cwd, defaultOnReadError)\n if (!sockJsonCResult.ok) {\n return sockJsonCResult\n }\n\n const sockJson = sockJsonCResult.data\n if (!sockJson.defaults) {\n sockJson.defaults = {}\n }\n if (!sockJson.defaults.scan) {\n sockJson.defaults.scan = {}\n }\n\n const targetCommand = await select({\n message: 'Which scan command do you want to configure?',\n choices: [\n {\n name: 'socket scan create',\n value: 'create',\n },\n {\n name: 'socket scan github',\n value: 'github',\n },\n {\n name: '(cancel)',\n value: '',\n description: 'Exit configurator, make no changes',\n },\n ],\n })\n switch (targetCommand) {\n case 'create': {\n if (!sockJson.defaults.scan.create) {\n sockJson.defaults.scan.create = {}\n }\n const result = await configureScan(sockJson.defaults.scan.create, cwd)\n if (!result.ok || result.data.canceled) {\n return result\n }\n break\n }\n case 'github': {\n if (!sockJson.defaults.scan.github) {\n sockJson.defaults.scan.github = {}\n }\n const result = await configureGithub(sockJson.defaults.scan.github)\n if (!result.ok || result.data.canceled) {\n return result\n }\n break\n }\n default: {\n return canceledByUser()\n }\n }\n\n logger.log('')\n logger.log('Setup complete. Writing socket.json')\n logger.log('')\n\n if (\n await select({\n message: `Do you want to write the new config to ${jsonPath} ?`,\n choices: [\n {\n name: 'yes',\n value: true,\n description: 'Update config',\n },\n {\n name: 'no',\n value: false,\n description: 'Do not update the config',\n },\n ],\n })\n ) {\n return await writeSocketJson(cwd, sockJson)\n }\n\n return canceledByUser()\n}\n\nasync function configureScan(\n config: NonNullable<\n NonNullable<NonNullable<SocketJson['defaults']>['scan']>['create']\n >,\n cwd = process.cwd(),\n): Promise<CResult<{ canceled: boolean }>> {\n const defaultRepoName = await input({\n message:\n '(--repo) What repo name (slug) should be reported to Socket for this dir?',\n default: config.repo || (await getRepoName(cwd)),\n required: false,\n // validate: async string => bool\n })\n if (defaultRepoName === undefined) {\n return canceledByUser()\n }\n if (defaultRepoName) {\n // Store it even if it's constants.SOCKET_DEFAULT_REPOSITORY because if we\n // change this default then an existing user probably would not expect the change.\n config.repo = defaultRepoName\n } else {\n delete config.repo\n }\n\n const defaultBranchName = await input({\n message:\n '(--branch) What branch name (slug) should be reported to Socket for this dir?',\n default:\n config.branch ||\n (await gitBranch(cwd)) ||\n (await detectDefaultBranch(cwd)),\n required: false,\n // validate: async string => bool\n })\n if (defaultBranchName === undefined) {\n return canceledByUser()\n }\n if (defaultBranchName) {\n // Store it even if it's constants.SOCKET_DEFAULT_BRANCH because if we change\n // this default then an existing user probably would not expect the change.\n config.branch = defaultBranchName\n } else {\n delete config.branch\n }\n\n const autoManifest = await select({\n message:\n '(--auto-manifest) Do you want to run `socket manifest auto` before creating a scan? You would need this for sbt, gradle, etc.',\n choices: [\n {\n name: 'no',\n value: 'no',\n description: 'Do not generate local manifest files',\n },\n {\n name: 'yes',\n value: 'yes',\n description:\n 'Locally generate manifest files for languages like gradle, sbt, and conda (see `socket manifest auto`), before creating a scan',\n },\n {\n name: '(leave default)',\n value: '',\n description: 'Do not store a setting for this',\n },\n ],\n default:\n config.autoManifest === true\n ? 'yes'\n : config.autoManifest === false\n ? 'no'\n : '',\n })\n if (autoManifest === undefined) {\n return canceledByUser()\n }\n if (autoManifest === 'yes') {\n config.autoManifest = true\n } else if (autoManifest === 'no') {\n config.autoManifest = false\n } else {\n delete config.autoManifest\n }\n\n const alwaysReport = await select({\n message: '(--report) Do you want to enable --report by default?',\n choices: [\n {\n name: 'no',\n value: 'no',\n description: 'Do not wait for Scan result and report by default',\n },\n {\n name: 'yes',\n value: 'yes',\n description:\n 'After submitting a Scan request, wait for scan to complete, then show a report (like --report would)',\n },\n {\n name: '(leave default)',\n value: '',\n description: 'Do not store a setting for this',\n },\n ],\n default:\n config.report === true ? 'yes' : config.report === false ? 'no' : '',\n })\n if (alwaysReport === undefined) {\n return canceledByUser()\n }\n if (alwaysReport === 'yes') {\n config.report = true\n } else if (alwaysReport === 'no') {\n config.report = false\n } else {\n delete config.report\n }\n\n return notCanceled()\n}\n\nasync function configureGithub(\n config: NonNullable<\n NonNullable<NonNullable<SocketJson['defaults']>['scan']>['github']\n >,\n): Promise<CResult<{ canceled: boolean }>> {\n // Do not store the GitHub API token. Just leads to a security rabbit hole.\n\n const all = await select({\n message:\n '(--all) Do you by default want to fetch all repos from the GitHub API and scan all known repos?',\n choices: [\n {\n name: 'no',\n value: 'no',\n description: 'Fetch repos if not given and ask which repo to run on',\n },\n {\n name: 'yes',\n value: 'yes',\n description: 'Run on all remote repos by default',\n },\n {\n name: '(leave default)',\n value: '',\n description: 'Do not store a setting for this',\n },\n ],\n default: config.all === true ? 'yes' : config.all === false ? 'no' : '',\n })\n if (all === undefined) {\n return canceledByUser()\n }\n if (all === 'yes') {\n config.all = true\n } else if (all === 'no') {\n config.all = false\n } else {\n delete config.all\n }\n\n if (!all) {\n const defaultRepos = await input({\n message:\n '(--repos) Please enter the default repos to run this on, leave empty (backspace) to fetch from GitHub and ask interactive',\n default: config.repos,\n required: false,\n // validate: async string => bool\n })\n if (defaultRepos === undefined) {\n return canceledByUser()\n }\n if (defaultRepos) {\n config.repos = defaultRepos\n } else {\n delete config.repos\n }\n }\n\n const defaultGithubApiUrl = await input({\n message:\n '(--github-api-url) Do you want to override the default github url?',\n\n default: config.githubApiUrl || constants.ENV.GITHUB_API_URL,\n required: false,\n // validate: async string => bool\n })\n if (defaultGithubApiUrl === undefined) {\n return canceledByUser()\n }\n if (\n defaultGithubApiUrl &&\n defaultGithubApiUrl !== constants.ENV.GITHUB_API_URL\n ) {\n config.githubApiUrl = defaultGithubApiUrl\n } else {\n delete config.githubApiUrl\n }\n\n const defaultOrgGithub = await input({\n message:\n '(--org-github) Do you want to change the org slug that is used when talking to the GitHub API? Defaults to your Socket org slug.',\n default: config.orgGithub || '',\n required: false,\n // validate: async string => bool\n })\n if (defaultOrgGithub === undefined) {\n return canceledByUser()\n }\n if (defaultOrgGithub) {\n config.orgGithub = defaultOrgGithub\n } else {\n delete config.orgGithub\n }\n\n return notCanceled()\n}\n\nfunction canceledByUser(): CResult<{ canceled: boolean }> {\n logger.log('')\n logger.info('User canceled')\n logger.log('')\n return { ok: true, data: { canceled: true } }\n}\n\nfunction notCanceled(): CResult<{ canceled: boolean }> {\n return { ok: true, data: { canceled: false } }\n}\n","import { outputScanConfigResult } from './output-scan-config-result.mts'\nimport { setupScanConfig } from './setup-scan-config.mts'\n\nexport async function handleScanConfig(\n cwd: string,\n defaultOnReadError = false,\n) {\n const result = await setupScanConfig(cwd, defaultOnReadError)\n\n await outputScanConfigResult(result)\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleScanConfig } from './handle-scan-config.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'setup',\n description:\n 'Start interactive configurator to customize default flag values for `socket scan` in this dir',\n hidden: false,\n flags: {\n ...commonFlags,\n defaultOnReadError: {\n type: 'boolean',\n description:\n 'If reading the socket.json fails, just use a default config? Warning: This might override the existing json file!',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Interactive configurator to create a local json file in the target directory\n that helps to set flag defaults for \\`socket scan create\\`.\n\n This helps to configure the (Socket reported) repo and branch names, as well\n as which branch name is the \"default branch\" (main, master, etc). This way\n you don't have to specify these flags when creating a scan in this dir.\n\n This generated configuration file will only be used locally by the CLI. You\n can commit it to the repo (useful for collaboration) or choose to add it to\n your .gitignore all the same. Only this CLI will use it.\n\n Examples\n\n $ ${command}\n $ ${command} ./proj\n `,\n}\n\nexport const cmdScanSetup = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n const { defaultOnReadError = false } = cli.flags\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n await handleScanConfig(cwd, Boolean(defaultOnReadError))\n}\n","import { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport { queryApiSafeText } from '../../utils/api.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { SocketArtifact } from '../../utils/alert/artifact.mts'\n\nexport async function fetchScan(\n orgSlug: string,\n scanId: string,\n): Promise<CResult<SocketArtifact[]>> {\n const result = await queryApiSafeText(\n `orgs/${orgSlug}/full-scans/${encodeURIComponent(scanId)}`,\n 'a scan',\n )\n\n if (!result.ok) {\n return result\n }\n\n const jsonsString = result.data\n\n // This is nd-json; each line is a json object\n const lines = jsonsString.split('\\n').filter(Boolean)\n let ok = true\n const data = lines.map(line => {\n try {\n return JSON.parse(line)\n } catch (e) {\n ok = false\n debugFn('error', 'caught: JSON.parse error')\n debugDir('inspect', { error: e, line })\n return null\n }\n }) as unknown as SocketArtifact[]\n\n if (ok) {\n return { ok: true, data }\n }\n\n return {\n ok: false,\n message: 'Invalid Socket API response',\n cause:\n 'The Socket API responded with at least one line that was not valid JSON. Please report if this persists.',\n }\n}\n","import fs from 'node:fs/promises'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mdTable } from '../../utils/markdown.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketArtifact } from '../../utils/alert/artifact.mts'\n\nexport async function outputScanView(\n result: CResult<SocketArtifact[]>,\n orgSlug: string,\n scanId: string,\n filePath: string,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (!result.ok) {\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (\n outputKind === 'json' ||\n (outputKind === 'text' && filePath && filePath.endsWith('.json'))\n ) {\n const json = serializeResultJson(result)\n\n if (filePath && filePath !== '-') {\n logger.info('Writing json results to', filePath)\n try {\n await fs.writeFile(filePath, json, 'utf8')\n logger.info(`Data successfully written to ${filePath}`)\n } catch (e) {\n process.exitCode = 1\n logger.fail('There was an error trying to write the markdown to disk')\n logger.error(e)\n logger.log(\n serializeResultJson({\n ok: false,\n message: 'File Write Failure',\n cause: 'Failed to write json to disk',\n }),\n )\n }\n return\n }\n\n logger.log(json)\n return\n }\n\n const display = result.data.map(art => {\n const author = Array.isArray(art.author)\n ? `${art.author[0]}${art.author.length > 1 ? ' et.al.' : ''}`\n : art.author\n return {\n type: art.type,\n name: art.name,\n version: art.version,\n author,\n score: JSON.stringify(art.score),\n }\n })\n\n const md = mdTable<any>(display, [\n 'type',\n 'version',\n 'name',\n 'author',\n 'score',\n ])\n\n const report =\n `\n# Scan Details\n\nThese are the artifacts and their scores found.\n\nScan ID: ${scanId}\n\n${md}\n\nView this report at: ${constants.SOCKET_WEBSITE_URL}/dashboard/org/${orgSlug}/sbom/${scanId}\n `.trim() + '\\n'\n\n if (filePath && filePath !== '-') {\n try {\n await fs.writeFile(filePath, report, 'utf8')\n logger.log(`Data successfully written to ${filePath}`)\n } catch (e) {\n process.exitCode = 1\n logger.fail('There was an error trying to write the markdown to disk')\n logger.error(e)\n }\n } else {\n logger.log(report)\n }\n}\n","import { fetchScan } from './fetch-scan.mts'\nimport { outputScanView } from './output-scan-view.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleScanView(\n orgSlug: string,\n scanId: string,\n filePath: string,\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchScan(orgSlug, scanId)\n\n await outputScanView(data, orgSlug, scanId, filePath, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleApiCall } from '../../utils/api.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\n\nimport type { SetupSdkOptions } from '../../utils/sdk.mts'\n\nexport type StreamScanOptions = {\n file?: string | undefined\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function streamScan(\n orgSlug: string,\n scanId: string,\n options?: StreamScanOptions | undefined,\n) {\n const { file, sdkOpts } = {\n __proto__: null,\n ...options,\n } as StreamScanOptions\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n logger.info('Requesting data from API...')\n\n // Note: this will write to stdout or target file. It's not a noop\n return await handleApiCall(\n sockSdk.getOrgFullScan(orgSlug, scanId, file === '-' ? undefined : file),\n { desc: 'a scan' },\n )\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleScanView } from './handle-scan-view.mts'\nimport { streamScan } from './stream-scan.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type {\n CliCommandConfig,\n CliSubcommand,\n} from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'view'\n\nconst description = 'View the raw results of a scan'\n\nconst hidden = false\n\nexport const cmdScanView: CliSubcommand = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n stream: {\n type: 'boolean',\n default: false,\n description:\n 'Only valid with --json. Streams the response as \"ndjson\" (chunks of valid json blobs).',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <SCAN_ID> [OUTPUT_FILE]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n When no output path is given the contents is sent to stdout.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0 ./stream.txt\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag, stream } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const [scanId = '', file = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'dot is an invalid org, most likely you forgot the org name here?',\n },\n {\n test: !!scanId,\n message: 'Scan ID to view',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n {\n nook: true,\n test: !stream || !!json,\n message: 'You can only use --stream when using --json',\n fail: 'Either remove --stream or add --json',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n if (json && stream) {\n await streamScan(orgSlug, scanId, { file })\n } else {\n await handleScanView(orgSlug, scanId, file, outputKind)\n }\n}\n","import { cmdScanCreate } from './cmd-scan-create.mts'\nimport { cmdScanDel } from './cmd-scan-del.mts'\nimport { cmdScanDiff } from './cmd-scan-diff.mts'\nimport { cmdScanGithub } from './cmd-scan-github.mts'\nimport { cmdScanList } from './cmd-scan-list.mts'\nimport { cmdScanMetadata } from './cmd-scan-metadata.mts'\nimport { cmdScanReach } from './cmd-scan-reach.mts'\nimport { cmdScanReport } from './cmd-scan-report.mts'\nimport { cmdScanSetup } from './cmd-scan-setup.mts'\nimport { cmdScanView } from './cmd-scan-view.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Manage Socket scans'\n\nexport const cmdScan: CliSubcommand = {\n description,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n create: cmdScanCreate,\n del: cmdScanDel,\n diff: cmdScanDiff,\n github: cmdScanGithub,\n list: cmdScanList,\n metadata: cmdScanMetadata,\n reach: cmdScanReach,\n report: cmdScanReport,\n setup: cmdScanSetup,\n view: cmdScanView,\n },\n {\n aliases: {\n meta: {\n description: cmdScanMetadata.description,\n hidden: true,\n argv: ['metadata'],\n },\n reachability: {\n description: cmdScanReach.description,\n hidden: true,\n argv: ['reach'],\n },\n },\n argv,\n description,\n importMeta,\n name: `${parentName} scan`,\n },\n )\n },\n}\n","import { queryApiSafeJson } from '../../utils/api.mts'\n\nimport type { ThreadFeedResponse } from './types.mts'\nimport type { CResult } from '../../types.mts'\n\nexport async function fetchThreatFeed({\n direction,\n ecosystem,\n filter,\n orgSlug,\n page,\n perPage,\n pkg,\n version,\n}: {\n direction: string\n ecosystem: string\n filter: string\n orgSlug: string\n page: string\n perPage: number\n pkg: string\n version: string\n}): Promise<CResult<ThreadFeedResponse>> {\n const queryParams = new URLSearchParams([\n ['direction', direction],\n ['ecosystem', ecosystem],\n filter ? ['filter', filter] : ['', ''],\n ['page_cursor', page],\n ['per_page', String(perPage)],\n pkg ? ['name', pkg] : ['', ''],\n version ? ['version', version] : ['', ''],\n ])\n\n return await queryApiSafeJson(\n `orgs/${orgSlug}/threat-feed?${queryParams}`,\n 'the Threat Feed data',\n )\n}\n","import { createRequire } from 'node:module'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { msAtHome } from '../../utils/ms-at-home.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { ThreadFeedResponse, ThreatResult } from './types.mts'\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { Widgets } from 'blessed'\n\nconst require = createRequire(import.meta.url)\n\nexport async function outputThreatFeed(\n result: CResult<ThreadFeedResponse>,\n outputKind: OutputKind,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (!result.data?.results?.length) {\n logger.warn('Did not receive any data to display...')\n return\n }\n\n const formattedOutput = formatResults(result.data.results)\n const descriptions = result.data.results.map(d => d.description)\n\n // Note: this temporarily takes over the terminal (just like `man` does).\n const ScreenWidget = /*@__PURE__*/ require('blessed/lib/widgets/screen.js')\n const screen: Widgets.Screen = new ScreenWidget({\n ...constants.blessedOptions,\n })\n // Register these keys first so you can always exit, even when it gets stuck\n // If we don't do this and the code crashes, the user must hard-kill the\n // node process just to exit it. That's very bad UX.\n // eslint-disable-next-line n/no-process-exit\n screen.key(['escape', 'q', 'C-c'], () => process.exit(0))\n\n const TableWidget = /*@__PURE__*/ require('blessed-contrib/lib/widget/table.js')\n const detailsBoxHeight = 20 // bottom N rows for details box\n const tipsBoxHeight = 1 // 1 row for tips box\n\n const table: any = new TableWidget({\n keys: 'true',\n fg: 'white',\n selectedFg: 'white',\n selectedBg: 'magenta',\n interactive: 'true',\n label: 'Threat feed',\n width: '100%',\n top: 0,\n bottom: detailsBoxHeight + tipsBoxHeight,\n border: {\n type: 'line',\n fg: 'cyan',\n },\n columnWidth: [10, 30, 20, 18, 15, 200],\n // TODO: The truncation doesn't seem to work too well yet but when we add\n // `pad` alignment fails, when we extend columnSpacing alignment fails.\n columnSpacing: 1,\n truncate: '_',\n })\n\n const BoxWidget = /*@__PURE__*/ require('blessed/lib/widgets/box.js')\n const tipsBox: Widgets.BoxElement = new BoxWidget({\n bottom: detailsBoxHeight, // sits just above the details box\n height: tipsBoxHeight,\n width: '100%',\n style: {\n fg: 'yellow',\n bg: 'black',\n },\n tags: true,\n content: '↑/↓: Move Enter: Select q/ESC: Quit',\n })\n const detailsBox: Widgets.BoxElement = new BoxWidget({\n bottom: 0,\n height: detailsBoxHeight,\n width: '100%',\n border: {\n type: 'line',\n fg: 'cyan',\n },\n label: 'Details',\n content:\n 'Use arrow keys to navigate. Press Enter to select a threat. Press q to exit.',\n style: {\n fg: 'white',\n },\n })\n\n table.setData({\n headers: [\n ' Ecosystem',\n ' Name',\n ' Version',\n ' Threat type',\n ' Detected at',\n ' Details',\n ],\n data: formattedOutput,\n })\n\n // Initialize details box with the first selection if available\n if (formattedOutput.length > 0) {\n const selectedRow = formattedOutput[0]\n if (selectedRow) {\n detailsBox.setContent(formatDetailBox(selectedRow, descriptions, 0))\n }\n }\n\n // allow control the table with the keyboard\n table.focus()\n\n // Stacking order: table (top), tipsBox (middle), detailsBox (bottom)\n screen.append(table)\n screen.append(tipsBox)\n screen.append(detailsBox)\n\n // Update details box when selection changes\n table.rows.on('select item', () => {\n const selectedIndex = table.rows.selected\n if (selectedIndex !== undefined && selectedIndex >= 0) {\n const selectedRow = formattedOutput[selectedIndex]\n if (selectedRow) {\n // Note: the spacing works around issues with the table; it refuses to pad!\n detailsBox.setContent(\n formatDetailBox(selectedRow, descriptions, selectedIndex),\n )\n screen.render()\n }\n }\n })\n\n screen.render()\n\n screen.key(['return'], () => {\n const selectedIndex = table.rows.selected\n screen.destroy()\n const selectedRow = formattedOutput[selectedIndex]\n logger.log('Last selection:\\n', selectedRow)\n })\n}\n\nfunction formatDetailBox(\n selectedRow: string[],\n descriptions: string[],\n selectedIndex: number,\n): string {\n return (\n `Ecosystem: ${selectedRow[0]?.trim()}\\n` +\n `Name: ${selectedRow[1]?.trim()}\\n` +\n `Version: ${selectedRow[2]?.trim()}\\n` +\n `Threat type: ${selectedRow[3]?.trim()}\\n` +\n `Detected at: ${selectedRow[4]?.trim()}\\n` +\n `Details: ${selectedRow[5]?.trim()}\\n` +\n `Description: ${descriptions[selectedIndex]?.trim()}`\n )\n}\n\nfunction formatResults(data: ThreatResult[]) {\n return data.map(d => {\n const ecosystem = d.purl.split('pkg:')[1]!.split('/')[0]!\n const name = d.purl.split('/')[1]!.split('@')[0]!\n const version = d.purl.split('@')[1]!\n\n const timeDiff = msAtHome(d.createdAt)\n\n // Note: the spacing works around issues with the table; it refuses to pad!\n return [\n ecosystem,\n decodeURIComponent(name),\n ` ${version}`,\n ` ${d.threatType}`,\n ` ${timeDiff}`,\n d.locationHtmlUrl,\n ]\n })\n}\n","import { fetchThreatFeed } from './fetch-threat-feed.mts'\nimport { outputThreatFeed } from './output-threat-feed.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleThreatFeed({\n direction,\n ecosystem,\n filter,\n orgSlug,\n outputKind,\n page,\n perPage,\n pkg,\n version,\n}: {\n direction: string\n ecosystem: string\n filter: string\n outputKind: OutputKind\n orgSlug: string\n page: string\n perPage: number\n pkg: string\n version: string\n}): Promise<void> {\n const data = await fetchThreatFeed({\n direction,\n ecosystem,\n filter,\n orgSlug,\n page,\n perPage,\n pkg,\n version,\n })\n\n await outputThreatFeed(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleThreatFeed } from './handle-threat-feed.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'threat-feed'\n\nconst ECOSYSTEMS = new Set(['gem', 'golang', 'maven', 'npm', 'nuget', 'pypi'])\n\nconst TYPE_FILTERS = new Set([\n 'anom',\n 'c',\n 'fp',\n 'joke',\n 'mal',\n 'secret',\n 'spy',\n 'tp',\n 'typo',\n 'u',\n 'vuln',\n])\n\nconst description = '[Beta] View the threat-feed'\n\nconst hidden = false\n\nexport const cmdThreatFeed = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n direction: {\n type: 'string',\n default: 'desc',\n description: 'Order asc or desc by the createdAt attribute',\n },\n eco: {\n type: 'string',\n default: '',\n description: 'Only show threats for a particular ecosystem',\n },\n filter: {\n type: 'string',\n default: 'mal',\n description: 'Filter what type of threats to return',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n page: {\n type: 'string',\n default: '1',\n description: 'Page token',\n },\n perPage: {\n type: 'number',\n shortFlag: 'pp',\n default: 30,\n description: 'Number of items per page',\n },\n pkg: {\n type: 'string',\n default: '',\n description: 'Filter by this package name',\n },\n version: {\n type: 'string',\n default: '',\n description: 'Filter by this package version',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [ECOSYSTEM] [TYPE_FILTER]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n - Special access\n\n This feature requires a Threat Feed license. Please contact\n sales@socket.dev if you are interested in purchasing this access.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Valid ecosystems:\n\n - gem\n - golang\n - maven\n - npm\n - nuget\n - pypi\n\n Valid type filters:\n\n - anom Anomaly\n - c Do not filter\n - fp False Positives\n - joke Joke / Fake\n - mal Malware and Possible Malware [default]\n - secret Secrets\n - spy Telemetry\n - tp False Positives and Unreviewed\n - typo Typo-squat\n - u Unreviewed\n - vuln Vulnerability\n\n Note: if you filter by package name or version, it will do so for anything\n unless you also filter by that ecosystem and/or package name. When in\n doubt, look at the threat-feed and see the names in the name/version\n column. That's what you want to search for.\n\n You can put filters as args instead, we'll try to match the strings with the\n correct filter type but since this would not allow you to search for a package\n called \"mal\", you can also specify the filters through flags.\n\n First arg that matches a typo, eco, or version enum is used as such. First arg\n that matches none of them becomes the package name filter. Rest is ignored.\n\n Note: The version filter is a prefix search, pkg name is a substring search.\n\n Examples\n $ ${command}\n $ ${command} maven --json\n $ ${command} typo\n $ ${command} npm joke 1.0.0 --per-page=5 --page=2 --direction=asc\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const {\n eco,\n json,\n markdown,\n org: orgFlag,\n pkg,\n type: typef,\n version,\n } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n let ecoFilter = String(eco || '')\n let versionFilter = String(version || '')\n let typeFilter = String(typef || '')\n let nameFilter = String(pkg || '')\n\n const argSet = new Set(cli.input)\n cli.input.some(str => {\n if (ECOSYSTEMS.has(str)) {\n ecoFilter = str\n argSet.delete(str)\n return true\n }\n })\n\n cli.input.some(str => {\n if (/^v?\\d+\\.\\d+\\.\\d+$/.test(str)) {\n versionFilter = str\n argSet.delete(str)\n return true\n }\n })\n\n cli.input.some(str => {\n if (TYPE_FILTERS.has(str)) {\n typeFilter = str\n argSet.delete(str)\n return true\n }\n })\n\n const haves = new Set([ecoFilter, versionFilter, typeFilter])\n cli.input.some(str => {\n if (!haves.has(str)) {\n nameFilter = str\n argSet.delete(str)\n return true\n }\n })\n\n if (argSet.size) {\n logger.info(\n `Warning: ignoring these excessive args: ${Array.from(argSet).join(', ')}`,\n )\n }\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleThreatFeed({\n direction: String(cli.flags['direction'] || 'desc'),\n ecosystem: ecoFilter,\n filter: typeFilter,\n outputKind,\n orgSlug,\n page: String(cli.flags['page'] || '1'),\n perPage: Number(cli.flags['perPage']) || 30,\n pkg: nameFilter,\n version: versionFilter,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport async function outputUninstallCompletion(\n result: CResult<{ action: string; left: string[] }>,\n targetName: string,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log(result.message)\n logger.log('')\n logger.log(\n 'To remove the tab completion from the current shell (instance of bash) you',\n )\n logger.log(\n 'can run this command (due to a bash limitation NodeJS cannot do this):',\n )\n logger.log('')\n logger.log(` complete -r ${targetName}`)\n logger.log('')\n logger.log(\n 'Next time you open a terminal it should no longer be there, regardless.',\n )\n logger.log('')\n if (result.data.left.length) {\n logger.log(\n 'Detected more Socket Alias completions left in bashrc. Run `socket uninstall <cmd>` to remove them too.',\n )\n logger.log('')\n result.data.left.forEach(str => {\n logger.log(` - \\`${str}\\``)\n })\n logger.log('')\n }\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nimport constants from '../../constants.mts'\nimport {\n COMPLETION_CMD_PREFIX,\n getBashrcDetails,\n} from '../../utils/completion.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport async function teardownTabCompletion(\n targetName: string,\n): Promise<CResult<{ action: string; left: string[] }>> {\n const result = getBashrcDetails(targetName)\n if (!result.ok) {\n return result\n }\n\n const { completionCommand, sourcingCommand, toAddToBashrc } = result.data\n\n // Remove from ~/.bashrc if found\n const bashrc = constants.homePath\n ? path.join(constants.homePath, '.bashrc')\n : ''\n\n if (bashrc && fs.existsSync(bashrc)) {\n const content = fs.readFileSync(bashrc, 'utf8')\n\n if (content.includes(toAddToBashrc)) {\n const newContent = content\n // Try to remove the whole thing with comment first\n .replaceAll(toAddToBashrc, '')\n // Comment may have been edited away, try to remove the command at least\n .replaceAll(sourcingCommand, '')\n .replaceAll(completionCommand, '')\n\n fs.writeFileSync(bashrc, newContent, 'utf8')\n\n return {\n ok: true,\n data: {\n action: 'removed',\n left: findRemainingCompletionSetups(newContent),\n },\n message: 'Removed completion from ~/.bashrc',\n }\n } else {\n const left = findRemainingCompletionSetups(content)\n return {\n ok: true,\n data: {\n action: 'missing',\n left,\n },\n message: `Completion was not found in ~/.bashrc${left.length ? ' (you may need to manually edit your .bashrc to clean this up...)' : ''}`,\n }\n }\n } else {\n return {\n ok: true, // Eh. I think this makes most sense.\n data: { action: 'not found', left: [] },\n message: '~/.bashrc not found, skipping',\n }\n }\n}\n\nfunction findRemainingCompletionSetups(bashrc: string): string[] {\n return bashrc\n .split('\\n')\n .map(s => s.trim())\n .filter(s => s.startsWith(COMPLETION_CMD_PREFIX))\n .map(s => s.slice(COMPLETION_CMD_PREFIX.length).trim())\n}\n","import { outputUninstallCompletion } from './output-uninstall-completion.mts'\nimport { teardownTabCompletion } from './teardown-tab-completion.mts'\n\nexport async function handleUninstallCompletion(targetName: string) {\n const result = await teardownTabCompletion(targetName)\n await outputUninstallCompletion(result, targetName)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleUninstallCompletion } from './handle-uninstall-completion.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'completion',\n description: 'Uninstall bash completion for Socket CLI',\n hidden: false,\n flags: {\n ...commonFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [COMMAND_NAME=socket]\n\n Uninstalls bash tab completion for the Socket CLI. This will:\n 1. Remove tab completion from your current shell for given command\n 2. Remove the setup for given command from your ~/.bashrc\n\n The optional name is required if you installed tab completion for an alias\n other than the default \"socket\". This will NOT remove the command, only the\n tab completion that is registered for it in bash.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n\n $ ${command}\n $ ${command} sd\n `,\n}\n\nexport const cmdUninstallCompletion = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nexport async function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n const targetName = cli.input[0] || 'socket'\n\n await handleUninstallCompletion(String(targetName))\n}\n","import { cmdUninstallCompletion } from './cmd-uninstall-completion.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Uninstall Socket CLI tab completion'\n\nexport const cmdUninstall: CliSubcommand = {\n description,\n hidden: false,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n completion: cmdUninstallCompletion,\n },\n {\n argv,\n description,\n importMeta,\n name: `${parentName} uninstall`,\n },\n )\n },\n}\n","import fs from 'node:fs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nexport function addSocketWrapper(file: string): void {\n return fs.appendFile(\n file,\n 'alias npm=\"socket npm\"\\nalias npx=\"socket npx\"\\n',\n err => {\n if (err) {\n return new Error(`There was an error setting up the alias: ${err}`)\n }\n logger.success(\n `The alias was added to ${file}. Running 'npm install' will now be wrapped in Socket's \"safe npm\" 🎉`,\n )\n logger.log(\n ` If you want to disable it at any time, run \\`socket wrapper --disable\\``,\n )\n logger.log('')\n logger.info(\n `This will only be active in new terminal sessions going forward.`,\n )\n logger.log(\n ` You will need to restart your terminal or run this command to activate the alias in the current session:`,\n )\n logger.log('')\n logger.log(` source ${file}`)\n logger.log('')\n logger.log(`(You only need to do this once)`)\n },\n )\n}\n","import fs from 'node:fs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nexport function checkSocketWrapperSetup(file: string): boolean {\n const fileContent = fs.readFileSync(file, 'utf8')\n const linesWithSocketAlias = fileContent\n .split('\\n')\n .filter(\n l => l === 'alias npm=\"socket npm\"' || l === 'alias npx=\"socket npx\"',\n )\n\n if (linesWithSocketAlias.length) {\n logger.log(\n `The Socket npm/npx wrapper is set up in your bash profile (${file}).`,\n )\n logger.log('')\n logger.log(\n `If you haven't already since enabling; Restart your terminal or run this command to activate it in the current session:`,\n )\n logger.log('')\n logger.log(` source ${file}`)\n logger.log('')\n\n return true\n }\n return false\n}\n","import fs, { existsSync } from 'node:fs'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { confirm } from '@socketsecurity/registry/lib/prompts'\n\nimport { addSocketWrapper } from './add-socket-wrapper.mts'\nimport { checkSocketWrapperSetup } from './check-socket-wrapper-setup.mts'\nimport constants from '../../constants.mts'\nimport { getBashrcDetails } from '../../utils/completion.mts'\nimport { updateInstalledTabCompletionScript } from '../install/setup-tab-completion.mts'\n\nexport async function postinstallWrapper() {\n const { bashRcPath, zshRcPath } = constants\n const socketWrapperEnabled =\n (existsSync(bashRcPath) && checkSocketWrapperSetup(bashRcPath)) ||\n (existsSync(zshRcPath) && checkSocketWrapperSetup(zshRcPath))\n\n if (!socketWrapperEnabled) {\n await setupShadowNpm(\n `\nThe Socket CLI is now successfully installed! 🎉\n\nTo better protect yourself against supply-chain attacks, our Socket npm wrapper can warn you about malicious packages whenever you run 'npm install'.\n\nDo you want to install the Socket npm wrapper (this will create an alias to the \\`socket npm\\` command)?\n `.trim(),\n )\n }\n\n // Attempt to update the existing tab completion\n let updatedTabCompletion = false\n try {\n const details = getBashrcDetails('') // Note: command is not relevant, we just want the config path\n if (details.ok) {\n if (fs.existsSync(details.data.targetPath)) {\n // Replace the file with the one from this installation\n const result = updateInstalledTabCompletionScript(\n details.data.targetPath,\n )\n if (result.ok) {\n // This will work no matter what alias(es) were registered since that\n // is controlled by bashrc and they all share the same tab script.\n logger.success('Updated the installed Socket tab completion script')\n updatedTabCompletion = true\n }\n }\n }\n } catch (e) {\n debugFn('error', 'caught: tab completion setup error')\n debugDir('inspect', { error: e })\n // Ignore. Skip tab completion setup.\n }\n if (!updatedTabCompletion) {\n // Setting up tab completion requires bashrc modification. I'm not sure if\n // it's cool to just do that from an npm install...\n logger.log('Run `socket install completion` to setup bash tab completion')\n }\n}\n\nasync function setupShadowNpm(query: string): Promise<void> {\n logger.log(`\n _____ _ _\n| __|___ ___| |_ ___| |_\n|__ | . | _| '_| -_| _|\n|_____|___|___|_,_|___|_|\n\n`)\n if (\n await confirm({\n message: query,\n default: true,\n })\n ) {\n const { bashRcPath, zshRcPath } = constants\n try {\n if (existsSync(bashRcPath)) {\n addSocketWrapper(bashRcPath)\n }\n if (existsSync(zshRcPath)) {\n addSocketWrapper(zshRcPath)\n }\n } catch (e) {\n throw new Error(\n `There was an issue setting up the alias: ${(e as any)?.['message']}`,\n )\n }\n }\n}\n","import { readFileSync, writeFileSync } from 'node:fs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nexport function removeSocketWrapper(filepath: string): void {\n let content: string | undefined\n try {\n content = readFileSync(filepath, 'utf8')\n } catch (e) {\n logger.fail(`There was an error removing the alias${e ? ':' : '.'}`)\n if (e) {\n logger.error(e)\n }\n return\n }\n\n const linesWithoutSocketAlias = content\n .split('\\n')\n .filter(\n l => l !== 'alias npm=\"socket npm\"' && l !== 'alias npx=\"socket npx\"',\n )\n const updatedContent = linesWithoutSocketAlias.join('\\n')\n try {\n writeFileSync(filepath, updatedContent, 'utf8')\n } catch (e) {\n if (e) {\n logger.error(e)\n }\n return\n }\n\n logger.success(\n `The alias was removed from ${filepath}. Running 'npm install' will now run the standard npm command in new terminals going forward.`,\n )\n logger.log('')\n logger.info(\n `Note: We cannot deactivate the alias from current terminal sessions. You have to restart existing terminal sessions to finalize this step.`,\n )\n}\n","import { existsSync } from 'node:fs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { addSocketWrapper } from './add-socket-wrapper.mts'\nimport { checkSocketWrapperSetup } from './check-socket-wrapper-setup.mts'\nimport { postinstallWrapper } from './postinstall-wrapper.mts'\nimport { removeSocketWrapper } from './remove-socket-wrapper.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'wrapper',\n description: 'Enable or disable the Socket npm/npx wrapper',\n hidden: false,\n flags: {\n ...commonFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} <\"on\" | \"off\">\n\n Options\n ${getFlagListOutput(config.flags)}\n\n While enabled, the wrapper makes it so that when you call npm/npx on your\n machine, it will automatically actually run \\`socket npm\\` / \\`socket npx\\`\n instead.\n\n Examples\n $ ${command} on\n $ ${command} off\n `,\n}\n\nexport const cmdWrapper = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n // I don't think meow would mess with this but ...\n if (argv[0] === '--postinstall') {\n await postinstallWrapper()\n return\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n // TODO: Implement json/md further.\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n let enable = false\n let disable = false\n const [arg] = cli.input\n if (arg === 'on' || arg === 'enable' || arg === 'enabled') {\n enable = true\n disable = false\n } else if (arg === 'off' || arg === 'disable' || arg === 'disabled') {\n enable = false\n disable = true\n }\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: enable || disable,\n message: 'Must specify \"on\" or \"off\" argument',\n fail: 'missing',\n },\n {\n nook: true,\n test: cli.input.length <= 1,\n message: 'expecting exactly one argument',\n fail: `got multiple`,\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n const { bashRcPath, zshRcPath } = constants\n if (enable) {\n if (existsSync(bashRcPath) && !checkSocketWrapperSetup(bashRcPath)) {\n addSocketWrapper(bashRcPath)\n }\n if (existsSync(zshRcPath) && !checkSocketWrapperSetup(zshRcPath)) {\n addSocketWrapper(zshRcPath)\n }\n } else {\n if (existsSync(bashRcPath)) {\n removeSocketWrapper(bashRcPath)\n }\n if (existsSync(zshRcPath)) {\n removeSocketWrapper(zshRcPath)\n }\n }\n if (!existsSync(bashRcPath) && !existsSync(zshRcPath)) {\n logger.fail('There was an issue setting up the alias in your bash profile')\n }\n}\n","#!/usr/bin/env node\n\nimport { cmdAnalytics } from './commands/analytics/cmd-analytics.mts'\nimport { cmdAuditLog } from './commands/audit-log/cmd-audit-log.mts'\nimport { cmdCI } from './commands/ci/cmd-ci.mts'\nimport { cmdConfig } from './commands/config/cmd-config.mts'\nimport { cmdFix } from './commands/fix/cmd-fix.mts'\nimport { cmdInstall } from './commands/install/cmd-install.mts'\nimport { cmdJson } from './commands/json/cmd-json.mts'\nimport { cmdLogin } from './commands/login/cmd-login.mts'\nimport { cmdLogout } from './commands/logout/cmd-logout.mts'\nimport { cmdManifestCdxgen } from './commands/manifest/cmd-manifest-cdxgen.mts'\nimport { cmdManifest } from './commands/manifest/cmd-manifest.mts'\nimport { cmdNpm } from './commands/npm/cmd-npm.mts'\nimport { cmdNpx } from './commands/npx/cmd-npx.mts'\nimport { cmdOops } from './commands/oops/cmd-oops.mts'\nimport { cmdOptimize } from './commands/optimize/cmd-optimize.mts'\nimport { cmdOrganizationDependencies } from './commands/organization/cmd-organization-dependencies.mts'\nimport { cmdOrganizationPolicyLicense } from './commands/organization/cmd-organization-policy-license.mts'\nimport { cmdOrganizationPolicySecurity } from './commands/organization/cmd-organization-policy-security.mts'\nimport { cmdOrganization } from './commands/organization/cmd-organization.mts'\nimport { cmdPackage } from './commands/package/cmd-package.mts'\nimport { cmdPatch } from './commands/patch/cmd-patch.mts'\nimport { cmdRawNpm } from './commands/raw-npm/cmd-raw-npm.mts'\nimport { cmdRawNpx } from './commands/raw-npx/cmd-raw-npx.mts'\nimport { cmdRepository } from './commands/repository/cmd-repository.mts'\nimport { cmdScan } from './commands/scan/cmd-scan.mts'\nimport { cmdThreatFeed } from './commands/threat-feed/cmd-threat-feed.mts'\nimport { cmdUninstall } from './commands/uninstall/cmd-uninstall.mts'\nimport { cmdWrapper } from './commands/wrapper/cmd-wrapper.mts'\n\nexport const rootCommands = {\n analytics: cmdAnalytics,\n 'audit-log': cmdAuditLog,\n ci: cmdCI,\n cdxgen: cmdManifestCdxgen,\n config: cmdConfig,\n dependencies: cmdOrganizationDependencies,\n fix: cmdFix,\n install: cmdInstall,\n json: cmdJson,\n license: cmdOrganizationPolicyLicense,\n login: cmdLogin,\n logout: cmdLogout,\n manifest: cmdManifest,\n npm: cmdNpm,\n npx: cmdNpx,\n oops: cmdOops,\n optimize: cmdOptimize,\n organization: cmdOrganization,\n package: cmdPackage,\n patch: cmdPatch,\n 'raw-npm': cmdRawNpm,\n 'raw-npx': cmdRawNpx,\n repository: cmdRepository,\n scan: cmdScan,\n security: cmdOrganizationPolicySecurity,\n 'threat-feed': cmdThreatFeed,\n uninstall: cmdUninstall,\n wrapper: cmdWrapper,\n}\n\nexport const rootAliases = {\n audit: {\n description: `${cmdAuditLog.description} (alias)`,\n hidden: false,\n argv: ['audit-log'],\n },\n auditLog: {\n description: cmdAuditLog.description,\n hidden: true,\n argv: ['audit-log'],\n },\n auditLogs: {\n description: cmdAuditLog.description,\n hidden: true,\n argv: ['audit-log'],\n },\n ['audit-logs']: {\n description: cmdAuditLog.description,\n hidden: true,\n argv: ['audit-log'],\n },\n deps: {\n description: `${cmdOrganizationDependencies.description} (alias)`,\n hidden: false,\n argv: ['dependencies'],\n },\n feed: {\n description: `${cmdThreatFeed.description} (alias)`,\n hidden: false,\n argv: ['threat-feed'],\n },\n org: {\n description: `${cmdOrganization.description} (alias)`,\n hidden: false,\n argv: ['organization'],\n },\n orgs: {\n description: cmdOrganization.description,\n hidden: true,\n argv: ['organization'],\n },\n organizations: {\n description: cmdOrganization.description,\n hidden: true,\n argv: ['organization'],\n },\n organisation: {\n description: cmdOrganization.description,\n hidden: true,\n argv: ['organization'],\n },\n organisations: {\n description: cmdOrganization.description,\n hidden: true,\n argv: ['organization'],\n },\n pkg: {\n description: `${cmdPackage.description} (alias)`,\n hidden: false,\n argv: ['package'],\n },\n repo: {\n description: `${cmdRepository.description} (alias)`,\n hidden: false,\n argv: ['repos'],\n },\n repos: {\n description: cmdRepository.description,\n hidden: true,\n argv: ['repos'],\n },\n repositories: {\n description: cmdRepository.description,\n hidden: true,\n argv: ['repos'],\n },\n}\n","#!/usr/bin/env node\n\nimport { fileURLToPath, pathToFileURL } from 'node:url'\n\nimport meow from 'meow'\nimport { messageWithCauses, stackWithCauses } from 'pony-cause'\nimport lookupRegistryAuthToken from 'registry-auth-token'\nimport lookupRegistryUrl from 'registry-url'\nimport updateNotifier from 'tiny-updater'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { rootAliases, rootCommands } from './commands.mts'\nimport constants from './constants.mts'\nimport { AuthError, InputError, captureException } from './utils/errors.mts'\nimport { failMsgWithBadge } from './utils/fail-msg-with-badge.mts'\nimport { meowWithSubcommands } from './utils/meow-with-subcommands.mts'\nimport { serializeResultJson } from './utils/serialize-result-json.mts'\n\nconst __filename = fileURLToPath(import.meta.url)\n\nvoid (async () => {\n const registryUrl = lookupRegistryUrl()\n await updateNotifier({\n authInfo: lookupRegistryAuthToken(registryUrl, { recursive: true }),\n name: constants.SOCKET_CLI_BIN_NAME,\n registryUrl,\n ttl: 86_400_000 /* 24 hours in milliseconds */,\n version: constants.ENV.INLINED_SOCKET_CLI_VERSION,\n })\n\n try {\n await meowWithSubcommands(rootCommands, {\n aliases: rootAliases,\n argv: process.argv.slice(2),\n name: constants.SOCKET_CLI_BIN_NAME,\n importMeta: { url: `${pathToFileURL(__filename)}` } as ImportMeta,\n })\n } catch (e) {\n process.exitCode = 1\n debugFn('error', 'Uncaught error (BAD!):')\n debugDir('inspect', { error: e })\n\n let errorBody: string | undefined\n let errorTitle: string\n let errorMessage = ''\n if (e instanceof AuthError) {\n errorTitle = 'Authentication error'\n errorMessage = e.message\n } else if (e instanceof InputError) {\n errorTitle = 'Invalid input'\n errorMessage = e.message\n errorBody = e.body\n } else if (e instanceof Error) {\n errorTitle = 'Unexpected error'\n errorMessage = messageWithCauses(e)\n errorBody = stackWithCauses(e)\n } else {\n errorTitle = 'Unexpected error with no details'\n }\n\n // Try to parse the flags, find out if --json is set.\n const isJson = (() => {\n const cli = meow({\n argv: process.argv.slice(2),\n // Prevent meow from potentially exiting early.\n autoHelp: false,\n autoVersion: false,\n flags: {},\n importMeta: { url: `${pathToFileURL(__filename)}` } as ImportMeta,\n })\n return !!cli.flags['json']\n })()\n\n if (isJson) {\n logger.log(\n serializeResultJson({\n ok: false,\n message: errorTitle,\n cause: errorMessage,\n }),\n )\n } else {\n // Add 2 newlines in stderr to bump below any spinner.\n logger.error('\\n')\n logger.fail(failMsgWithBadge(errorTitle, errorMessage))\n if (errorBody) {\n debugDir('inspect', { errorBody })\n }\n }\n\n await captureException(e)\n }\n})()\n"],"names":["sdkOpts","__proto__","desc","time","process","logger","ok","message","cause","rows","cols","screen","label","barWidth","barSpacing","xOffset","maxHeight","barBgColor","data","formattedData","totalTopAlerts","sortedTopFiveAlerts","top_five_alert_types","formatted","style","line","text","baseline","xLabelPadding","xPadding","wholeNumbersOnly","legend","width","x","y","result","run","parentName","commandName","flags","file","type","description","scope","repoName","markdown","nook","test","fail","repo","filePath","perPage","outputJson","outputMarkdown","page","payload","generated","nextPage","org","user_email","debugFn","error","formattedOutput","row","keys","fg","selectedFg","selectedBg","interactive","top","border","columnWidth","columnSpacing","truncate","bottom","height","bg","tags","content","headers","table","detailsBox","default","help","typeFilter","logType","cwd","tmp","branch","commit_hash","commit_message","committers","make_default_branch","set_as_pending_head","spinner","method","body","tier1_reachability_scan_id","report_run_id","scanStatus","updateProgress","policyStatus","finishedFetching","scan","version","alerts","healthy","addAlert","options","reportLevel","policy","url","manifest","ecomap","pkgmap","vermap","filemap","short","includeLicensePolicy","depth","value","Package","Policy","scanId","outputKind","uploadManifests","organizations","tarHash","env","stdio","tier1ReachabilityScanId","sockJson","cdxgen","count","conda","gradle","sbt","debugLog","verbose","pass","stdout","stderr","poms","strings","reject","keeping","collecting","arr","fs","sbtOpts","bin","gradleOpts","packagePaths","reachabilityOptions","scanPaths","branchName","fold","commitMessage","commitHash","defaultBranch","pendingHead","pullRequest","reach","reachAnalysisTimeout","reachAnalysisMemoryLimit","reachDisableAnalytics","reachEcosystems","reachExcludePaths","reachSkipCache","runReachabilityAnalysis","readOnly","report","hidden","autoManifest","name","key","failed","obj","config","full","auto","get","list","set","unset","baseBranch","ghsaDetails","title","head","base","octokitPullsCreateParams","states","length","context","apiType","cacheKey","entry","index","parent","match","allPrs","state","per_page","baseRefName","GITHUB_REPOSITORY","repoInfo","author","fixEnv","fixed","ghsaLoop","overallFixed","enabled","unknownFlags","autoMerge","id","isMultiple","limit","rangeStyle","autopilot","ghsa","maxSatisfying","minSatisfying","prCheck","purl","shortFlag","testScript","allowUnknownFlags","purls","toAddToBashrc","recursive","bashrcUpdated","actions","targetPath","completion","updateConfigValue","apiBaseUrl","apiProxy","apiToken","sdk","choices","enforcedOrgs","applyLogout","attemptLogout","YARN_LOCK","ipc","argvMutable","spawnPromise","cleanupPackageLock","rmSync","configuration","coerce","filter","only","profile","standard","technique","alias","array","boolean","string","argv","pathArgs","unknowns","yargv","detected","stdin","out","json","filename","required","canceled","defaultOnReadError","kotlin","scala","setup","aliases","yolo","throw","YARN_CLASSIC","peerDependencies","overrides","pkgs","pkgid","names","npmExecPath","editablePkgJson","isPlacingHigher","insertIndex","entries","updatePkgJsonField","updateResolutionsField","updatePnpmField","updateOverridesField","pkgPath","added","addedInWorkspaces","updated","updatedInWorkspaces","warnedPnpmWorkspaceRequiresNpm","overridesDataObjects","semver","package","depAliasMap","thisSpec","depObj","loggedAddingText","newSpec","concurrency","NPM_BUGGY_OVERRIDES_PATCHED_VERSION","cmdName","prod","VLT","agentVersion","pin","offset","columns","field","mw1","mw2","mw3","security","license","defaultSub","dependencies","quota","deps","self","capabilities","score","transitively","o","valid","components","Maintenance","Quality","Vulnerabilities","License","colorize","padding","artifact","namespace","supplyChain","maintenance","quality","vulnerability","missing","dupes","blocks","outputPurlsShallowScore","shallowScore","shallow","deep","exportedAt","beforeHash","afterHash","summary","severity","patchExplanation","z","patchedPackages","patchLookup","hash","nodeModulesPaths","throws","patchesByEcosystem","patch","code","visibility","default_branch","slug","outputCreateRepo","homepage","repoDescription","sort","results","all","direction","create","view","del","update","setAsAlertsPage","targets","updatedInput","orgSlug","showHidden","colors","maxArrayLength","id1","id2","repos","targetRepos","scanCreated","scansCreated","githubToken","repoApiUrl","lastCommitter","tmpDir","fileCount","firstFailureResult","isManifest","fileUrl","response","detailedError","repoSlug","lastCommitMessage","repoDetails","treeDetails","githubApiUrl","orgGithub","from","created_at","year","month","day","fromTime","untilTime","stream","diff","github","metadata","meta","reachability","eco","pkg","cli","ecoFilter","argSet","versionFilter","nameFilter","ecosystem","action","left","zshRcPath","updatedTabCompletion","writeFileSync","enable","disable","analytics","ci","fix","install","login","logout","npm","npx","oops","optimize","organization","repository","uninstall","wrapper","audit","auditLog","auditLogs","feed","orgs","organisation","organisations","repositories","authInfo","importMeta","errorTitle","errorMessage","errorBody","autoHelp","autoVersion"],"mappings":"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ACsBC;AAAS;AAC/B;AACF;AACF;AAEA;AAIE;;AASA;;AASA;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACA;AACAlD;AAEA;;;;AAIA;AACAmD;AACEC;;AAEA;AACF;AAEA;AACEC;AACAC;AACAC;AACAC;AACAC;;AAEApC;AACAqC;;AAEAC;AACE7B;AACAwB;;AAEFM;AAAwB;AACxB;AACAC;AACAC;AACF;AAEA;AACA;AACEC;AAA0B;AAC1BC;AACA3C;AACAR;AACEyC;AACAW;;AAEFC;AACAC;AACF;AACA;AACEJ;AACAC;AACA3C;AACAsC;AACE7B;AACAwB;;AAEFrD;;AAEAY;AACEyC;AACF;AACF;;AAGEc;AACA7D;AACF;;AAEA;;;AAGA;AACAP;AACAA;AACAA;;AAEA;AACAqE;AACE;AACA;AACE;AACAC;;AAEF;AACF;;AAIAtE;AACE;;AAEA;;AAIF;AACF;;ACpTO;;;;;AAKLwC;AAOF;AACE;;;;;AAKEA;AACF;;;;;;AAOEA;AACF;AACF;;AChBO;AAEP;AAEA;AAEO;;;AAGLf;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACA6B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;;AAGFY;AACEb;AACAC;;AAEFS;AACEV;AACAyC;AACAxC;AACF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;AAEwBqB;;AAAoBP;;;;;;AAU5CiC;AAEA;AAEA;AAMA;AAEA;AAGItC;AACAC;AACAxC;AACAyC;AACF;AAEEF;;AAEAvC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;;;AAGEiD;AACAH;AACAkC;AACF;AACF;;ACpJO;;;;;;;AAYHzC;AACF;AAAM3C;;;;AAGJqF;;;;AAIAC;AACF;AAAMtF;;;AAEN;AACA;AACE;AACF;AACA;AAEA;AAEI;AAAmBuF;;AACnB;AAAmBC;;AACnB;AAAsBC;;AACtB;AAAmBC;;AACnBC;AACA;;;AACA3C;AACA4C;;AAEF;AACE3F;AAAyB;AAE/B;;ACtDO;;;AAGY4F;AAAQ;AACvB7F;;;AAIF;AACA;AACE;AACF;AACA;;AAGEC;AACA4F;AACF;AACF;;ACtBA;AACA;AACA;AACA;AACA;AACO;AAIL;AACA;AACA;AACEC;AACAC;AACEC;AACAC;AACF;AACF;AACF;;ACVA;AACA;AACA;AACA;AACO;;;AAUyBlG;AAAQ;AACpCC;;;AAGF;AACA;AACE;AACF;AACA;;;;;AAMQ6F;AAAQ;;AAGdK;AACAC;AACF;;AAGEC;AACAD;AACF;;AAGE;;;AAKA;;AAIA;AACF;;AAGE;;AAMA;AACE;AACF;AAEA;;AAEA;AACA;;AAEA;;AAEI;;AAEA9F;AACAsD;;AACsBC;AAAUpC;AAAK;AACrC;AACF;AACF;AAEA;;;AAEWnB;AAAUY;;AACrB;;;AAKEZ;AACAC;AACAC;;AAGJ;;AAKE;;AAOA;AACF;AAEA4F;AAEA;;;AAOM9F;AACAC;AACAC;;;;;AAMAF;AACAC;AACAC;;AAEJ;AAEA8F;AACAF;AACF;AAEA;AACE;AACF;AACA;AACE;AACF;;;AAII9F;AACAC;AACAC;;AAEJ;;AAGEF;AACAY;;;AAGA;;AAEJ;;AC5IA;AACA;AACO;;;;;;AASH4E;AAQF;AAEA;AAEAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAIA;AACA;AACE;AACAS;;;;AAII9D;AACA+D;AACF;AAEAC;AAEI;;AAEA;AACE;AAAc;AACZC;;AAEEC;AAUF;AACA;AACF;AACA;AAAa;AACX;AACEA;AAUF;AACA;AACF;AACA;AAAgB;;AAEZA;AAUF;AACA;AACF;AAEA;AAAe;AACb;AAMEA;AAUF;AAC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A;;AAKUyF;AAAQ;;;AAIdzF;AAGAA;AAGAyF;;AAGE;AACA;AACA;AACA;AACAR;AACF;AAEAqD;;;;AACsBC;AAAO;;;;AACNC;;AACzB;AACE;AACE/C;AACF;AACEA;AACF;AACF;AACF;;AChIO;;;;;AAKL4C;AAOF;AACE;;;AAEQ5C;AAAQ;AAEhBzF;AACAA;AACAA;;;;;AAME;AACA;AACA;AACA;AACA;AACA;AAA2DiF;AAAI;;AAI/D;AACEjF;AACAA;;AAEF;;;AAGEA;AACA;;AAEEA;AACAA;;AAEF;AACA;AACF;;;AAGEyI;AACA;AACF;AACA;;AAEEzI;AAGA;AACF;AACA;AACA;AACA;;AAEEA;;AAEAA;AACAA;AACF;;AAEEA;AACAA;AAGAA;AACAyI;AACA;AACEzI;AACAA;AAGF;AACAA;AACAA;AACA;AACF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEAyI;AACAzI;AACF;;;;;AAQA;AACEA;AACAA;;AAEF;AACF;AACF;;AChHA;AACE;AACF;AAEO;AAKL;;AAEE;AACEA;AACF;;;;AAKI;AACA0I;AACF;AACA3I;;AAEA;;AAEE;AACEC;AACF;;AAEF;AACAD;;AAEI;AACEC;AAGF;;AAEF;AACE;AACEA;AACF;AACA2I;AACF;AACF;AACF;;;AAII1I;AACAC;AACAC;;AAEJ;AACF;;AAGE;AACEH;AACF;AAEA;;AAEIC;AACAC;;;AAGJ;AAEAuE;;;AAIIxE;AACAC;;;AAGJ;AACF;;AAGED;AACAY;;;AAGA;;AAEJ;;AAEA;AACO;;;;;;AAMH;;AAEE;AACA;AACF;AACA;AACE;AACE;AACA;AACF;AACA;AACE;AACA;AACA;AACF;;AAEE;AACA;;AAEE;AACE;AACA;AACA;AACF;AACF;AACF;AACA;AACE+H;AACF;AACE;AACA;AACF;AACF;AACA;AAAA;;AAGEC;AACF;AACF;;AAGF;;ACxIO;AAKL;AACE9I;AACF;AAEA;;AAEIC;AACA;AACF;AACAA;AACA;AACF;;AAGE;;AAGEA;AACF;;AAEA;AAEA;AACF;;;AAIE8I;AACAA;AACAA;A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C;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AAGA;;;;;;AAOI9C;AACF;;;AAEcQ;;;;AAMd;AAEA;;AAIItC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACE0K;AACAnD;AACF;AACF;;AChGO;AAKL;AACExH;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEA;;AAGEA;AACAA;;AAEA;AACEA;AACAA;AAGF;AACF;;AAEE;AACEA;AACAA;AAGF;AACF;AACF;;AC1CO;;AAELuH;AAIF;AACE;AAEA;AACF;;ACCA;AACEtF;AACAI;AACAkI;AACArI;AACE;;;AAGF4C;AACF;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;;AAIA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;;AAEcQ;;;;AAMd;AAEA;;AAIItC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACE0K;AACAnD;AACF;AACF;;ACxFO;;AAELA;AAIF;AACE;AACA;;;;AAIE;AACE;AACA;AACA;AACEJ;AACAwD;;AAEAxD;AACF;AACA;AACEyD;AACF;AACF;AACA;;AAEA;AACA5K;AAIUC;AACAC;AACAW;;AAEEgK;AACAR;;AAEJ;AAEEpK;AACAY;;AAEEgK;AACAR;AACF;AACF;AAGV;;AAMErK;AACAA;;AAEAA;AACA;AACE;AACA;;AAEA;AACE;AACA;AACEmH;AACF;AACA;;AAIA;AACF;AACF;AACA;AACEnH;AACAA;AAGF;AACF;AACF;;ACnFA;AACEiC;AACAI;AACAkI;AACArI;AACE;AACA;AACA4I;AACE1I;AACAyC;AACAxC;AACF;;AAEFyC;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;;;AAEoBQ;;;AAIpB;AAEA;AACEC;AACAC;AACAxC;AAEAyC;AACF;;AAEE;AACF;AAEA;AACE3C;AACA;AACF;AAEA;;AAEEuH;AACF;AACF;;AC1EO;AAIL;AACExH;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGEA;AACAA;AACAA;;AAEEA;AACAA;AACF;AACF;AACEA;AACAA;;AAEEA;AACAA;AACF;AACF;AACF;;AClCO;;;AAGLmH;AAKF;AACE;AAEA;AACF;;ACDO;AAEP;AAEA;AAEO;;;AAGLpF;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;;AAIA;AACA;AACA;;;;;;AAOI9C;AACF;;;AAEcQ;;;;AAMd;AAEA;AAEA;;AAIItC;AACAyC;AACF;;AAEiB;AACfzC;AAEAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACE0K;;AAEAvD;AACF;AACF;;AClHO;AAIL;AACEpH;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGEA;AACAA;AACAA;;AAEEA;AACAA;AACF;AACF;AACEA;AACAA;;AAEEA;AACAA;AACF;AACF;AACF;;AClCO;;AAELuH;AAIF;AACE;AAEA;AACF;;ACCO;AAEP;AAEA;AAEO;;;AAGLxF;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;;AAIA;AACA;AACA;;;;;;AAOI9C;AACF;;;AAEcQ;;;;AAMd;AAEA;;AAIItC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACE0K;AACAnD;AACF;AACF;;ACjGA;AAEO;;AAELgD;AACA;AAA8BvI;AAAW;AACvC;AAEI+I;AACAC;AACAC;AACAC;AACAC;AACF;;;;;AAMA;AAEJ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AkI;;AAEF+C;;AAEE/C;;AAEFgD;AACEnL;AACAyC;AACAxC;AACAkI;;AAEFiD;AACEpL;AACAyC;AACAxC;AAEAkI;;AAEFkD;AACErL;AACAyC;AACAxC;AACAkI;;AAEFmD;AACEtL;AACAyC;;AAKAqI;AACAS;AACApD;;AAEF7H;AACEN;AACAyC;AACAxC;AACAkI;;AAEFqD;AACExL;AACAyC;AACAxC;AACAkI;AACF;AACF;AAEA;AAGIvI;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACA;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;AAII+I;;;;AAIA7L;AACF;;AAIA;;AAEEoL;AACF;;;AAIA;AACE;;;;AAIA;AACEU;AACF;AACE9N;AACF;AACF;AACA;;AAEEA;AACA;AACF;AAEA;AAEA;AAGI0C;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACA;AACED;AACAC;AAGA;AACF;AAEA;;AAGA;AACA;AACAiF;;;AAIA;AACE+H;AACAtK;AACF;;AAEQ+C;AAAQ;AAChB;AACA;AACA;;AAKA;;AAEA;;AAEA;AAEA;;;;;;;;;;;;;;AAcEsH;AACF;AACF;;ACjRO;AAYL;AACEhN;AAEAC;AACA;AACF;AAEAA;;AAIAA;;AAGEA;AACF;AACAA;AACAA;AACAA;AACAA;AAGAA;AACAA;AACAA;AACAA;AACAA;AACAA;AACAA;AACAA;;;AAGAA;AACAA;AACAA;AACF;;AC1CO;AAYL;AACA;AACE;AACF;;;;;AAEwD+N;;;AAGxD;AACA;;AAGA;AACExK;AACAwF;AAA0BiF;AAAgB;AAC5C;;;;AAMA;AACA;AAIA;AAEA;;AAEE;AACEjF;AACAkF;AACF;AACF;;AAGEhO;AACAY;AACEqN;;;;;;;AAcAC;AACF;;AAEJ;AAEA;AACE;;AAGA;;AAEIlO;AACAC;;;AAGJ;;AAESD;AAAUY;;AACrB;AAEO;AAGL;AACA;AACE;AACF;;AAEA;AACA;;;AAUSZ;AAAUY;;AACrB;;ACjHO;AACL;;AAEF;;ACIA;AACEoB;AACAI;AACAkI;AACArI;;;AAGA4C;AACF;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;AAIA;AACEhC;AACA;AACF;;AAIA;AACF;;ACtEA;AAEO;;AAELuK;AACA;AAA8BvI;AAAW;AACvC;AAEIoM;AACF;;;;;AAMA;AAEJ;AACF;;ACXO;AACLpO;;AAGA;AAIA;AACEA;;AAEA;AACF;;AAGEA;;AAIA;AACF;AAEAA;AACAA;AAEA;AACAA;AACF;;ACrCO;;AAEP;;ACIA;AACEiC;AACAI;AAEAkI;AACArI;;;;AAIF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGLH;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;AAGA;AACA;AACAiD;;AAGF;;AClDO;AAMLoJ;AACAA;AACAA;AACAA;AACF;;ACYO;AAILC;AACAC;AACA;AACErO;AAIF;;AAGEF;;AACSC;AAAWC;AAAqBC;;AAC3C;AAEA;AAEA;;;AAA8DqO;AAAS;AACvE;;AAEExO;AACA;AACF;AAEA;AAEA;AACEH;AACA4O;AACF;AACA;;AAEEzO;AACA;AACF;;AAEQyH;;AAER;;AAIA;AAEA;AACEgD;;AAEF;;AAGA;AACE;AACEvK;AAEAwO;AAGIjE;AACAtD;AACA9E;;AAGN;;AAEErC;;AACSC;AAAWC;AAAqBC;;AAC3C;AACA;;AAEA;AACF;AACE;;AAEE0E;AACF;;AAEE7E;;AACSC;AAAWC;AAAqBC;;AAC3C;AACA;AACE;AACA;AACEwO;AACF;AACF;AACF;AAEA;AACEzO;AACAwO;AAEIjE;AACAtD;AACA9E;AAEF;AAEEoI;AACAtD;AACA9E;;AAIN;;AAEErC;;AACSC;AAAWC;AAAqBC;;AAC3C;AACA;AACEH;AACAA;AACA;;AAEEA;AAGF;AACEA;AAGF;AACF;AAEAqO;AAEA;;;AAGErO;;AAIEA;AACAA;AAGF;AACF;;AAEEA;AACF;AACF;;ACzJO;AAEP;AAEA;AAEO;;;AAGL+B;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACAoM;AACElM;AACAC;;AAEFkM;AACEnM;AACAC;AACF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;AAIA;AACEhC;AACA;AACF;AAEA;AACE;AAGF;AAEA;AAEA;AAEA;AACF;;ACxFO;AACLqO;AACAA;AACAA;AACAA;AACF;;ACFO;;AAEHO;AACA5O;;AAEEA;AACAA;AAGF;AACF;AACEA;AACF;AACF;;ACTA;AACEiC;AACAI;AACAkI;AACArI;;;AAGA4C;AACF;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;AAIA;AACEhC;AACA;AACF;AAEA6O;AACF;;ACtCA;;;AAAiCC;AAAU;AAE3C;AAeA;AACE;;AAEA;;AAEA;AAAa;AAAQ;AAAS;AAC5B;AACE;AACF;;AAEE;AACA;AACAhN;AACF;AACEA;AACF;;;AAGEA;AACF;AACF;AACA;AACA;AACEA;AACF;AACA;AACA;AACEA;AACF;AACA;AACF;AAEO;;AAEL;AAAsBlC;;;AACtB;AACEmP;AACE;AACA;;;AAIFnH;;;AAOA;AACEoH;AACF;AACE;AACA;;;AAEUC;;AAUR;AACAD;AACAE;;AAEJ;AACF;;;AAaE;;AAEIC;;AAEJ;AAEA;AACA;AACE;AACA;;AAEA;AACF;AACF;AAEA;AACF;;ACrHA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACEC;AACE;AACA;AACA;AACA;AACA;AACA;AACA;;AAEFC;AACE;AACA;AACAC;AACAC;AACAC;AACAC;AACAC;AACAtN;;AAEFyC;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEF8K;;;;;;;;;AASAC;AACIlF;AAAetI;AAAe;AAC9BsI;AAAgBtI;AAAe;AAC/BsI;AAAqBtI;AAAe;AACpCsI;AAAsBtI;;AAAkB;AAC1C;AAAEsI;AAAetI;AAAe;AAC9BsI;AAAatI;AAAe;AAC5BsI;AAAiBtI;AAAe;AAChCsI;AAAkBtI;AAAe;AACjCsI;AAAatI;AAAe;AAEhCyN;AAGY;AACV;AAmBFC;AAE2B;;AACL;;AACH;AACjB;AACkB;;AACG;AACrB;AAQ0B;;AACD;AACzB;AAGgB;AAChB;AAAsB;AAE1B;AAEA;AACE7N;AACAI;AACAkI;AACA;AACA;;;AAGF;AAEO;;;AAGLxI;AACF;AAEA;AAGIC;AAAmC;;AAGnC;AACA+N;;;AAGA/N;AACF;;;AAIA;AACA;AACE;;;;AAKF;AACE;AACEgO;AACF;AACEC;AACF;AACF;;;AAIQtE;AAAsB;AAC9B;AACE;AACA;AACA;;AAEA3L;AAGA;AACF;AAEA;AACEA;AACA;AACF;;AAEA;AACA;AACE;AACA;AACA;AACA;;AAEEkQ;AACAlQ;AAMF;AACA;;AAEA;AACF;;;AAIQiP;AAAa;;AAErB;;AAEE;;AAEA;AACE;AACAlP;AACF;AACF;AAEA;AACF;;AC3SA;AACEkC;AACAI;AACAkI;AACArI;AACE;AACAmG;AACEjG;AACAyC;AACAxC;AAEF;;AAEFyC;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AAC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;;;;;;AAYJ;AAEO;;AAIGtD;AAAoB;AAC1BrF;;;;AAIA;AACA;AACA;;;;;AAWF;AACF;AAEO;;AAIGqF;AAAoB;AAC1BrF;;;;AAIA;AACA;AACA;AACA;AACA;;;;;AAOF;AACF;AAOO;;AAKH;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACA;AACE;AACJ;AACF;;AC5NO;;ACQP;;;;;;;;AAQEqR;AACF;AAEA;AASA;AAIE;AAIF;AAEA;;AAKA;AAEA;AAIE;AACF;AAEA;AAKE;AACA;AACE;;AAEE;AACA;;AAEI;AACE;AACAE;;;AAGA;AACF;AACF;AACF;AACE;AACAK;AAGQ;AACE;AACAL;AACF;AACF;AACE;AAAmB;AAE7B;;AAEA;;;AAGA;AACF;;AAC2B;AAAe;AAC1C;AACA;AACF;AACA;AAIE;AACF;AACA;AACA;AACA;;;;;;AAME;AACEM;;AAEF;AACF;AACEA;AACAC;AACF;;AAEE;AACED;AACAC;AACF;AACF;AACA;;AAEA;AACA;AACED;AACAC;AACF;AACA;;;AAGEA;AACF;AACAC;AAEqB;;;AAKvB;AAEO;AAILC;AACF;AAEO;AAILA;AACF;AAEO;AAILA;AACF;AAEO;AAKL;AACE;AACEC;AACA;AACF;AACEC;AACA;AACF;AACEC;AACA;AACF;AACEF;AACA;AACF;AACEA;AACA;AACF;AACA;AACEE;AACA;AACJ;AACF;;AChJA;AAEO;;;;;;AAUHC;AACF;;;;;;AAME5F;AACE6F;AACAC;AACAC;AACAC;AACAC;AACF;AACF;AAAMzS;;;;AAEN;AACA;AACA;AACA;AACA;;AAIE;AACA2R;;;AAKAvR;;AAOF;;;AAIEsS;AACF;AACEA;AAIF;AAEA;AACA;AACA;AAAuD;;AAEnD;AACA;AACAC;AAKJ;;;AAGA;AACA;AAEW;AAAQ;;AACP9H;AAAsB+H;AAAsBrM;AAAQ;AAC5D;AACA;AACA;AACA;AAAa;;AACX;AAGA;AACEsM;AACF;AACA;AAGA;;AAEE;AACA;AACA;AACA;AACE;AACA;AAEE;AACA;AACAF;AAIFG;AACAC;AACAvG;;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CE;AACF;AACA;;AAGEC;AACF;AACF;;ACpBO;AAIL;AACEE;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEAA;AACAA;AACAA;AACAA;AACAA;AACA;;AAEA;AACK;AAAQ;AAAS;;AAItBA;AACAA;AACF;;ACnCO;AAIL;AAEA;AACF;;ACKO;AAEP;AAEA;AAEO;;;AAGL+B;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACA6B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;AAEF;;;AAGN;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOIL;AACF;;;;AAEwBqB;;;;AAMxB;AAEA;AAMA;AAEA;AAGIZ;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACF;;AC7GO;;AAIGL;AAAQ;AACdC;;;AAIF;AACA;AACE;AACF;AACA;;AAGEC;AACF;AACF;;ACpBO;AAIL;AACEE;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEAA;AACAA;;AAIAA;AACAA;AAGAA;AACA;;AAIA;AACK;AAAQ;;;AAGbA;AACAA;AACF;;ACzCO;AAIL;AAEA;AACF;;ACKO;AAEP;AAEA;AAEO;;;AAGL+B;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACA6B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;AAEF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;AAEwBqB;;;;AAMxB;AAEA;AAMA;AAEA;AAGIZ;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACF;;AC7GO;AAIL;AACED;AACF;;AAGEC;AACA;AACF;AAEA;AACEA;AACA;AACF;;AAEQyH;;AACR;;;AAME;AACA;AACEzH;AAGF;AACA;AACF;;AAEA;AACA;AACA;AACA;;;;AAIA;AACEwT;AACAC;AACAC;AACF;AACA1T;;AAIAA;;AAIA;;AAIA;;AAEF;;AClEO;AAGL;AAEA;AACF;;ACKO;AAEP;AAEA;AAEO;;;AAGL+B;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;AAEcQ;;;AAId;AAEA;AAEA;AAGIC;AACAC;AACAxC;AAEAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;;AAGF;;AC5FA;AAEO;;AAEL;AACA;AACA;AACA;AACAuK;AACA;AAA8BvI;AAAW;AACvC;AAEI2R;AACAC;AACF;;;AAIEC;AAAoB;;;AAGtB;AAEJ;AACF;;ACnBO;;AAGGlU;AAAQ;AAAMC;;;AAEtB;AACA;AACE;AACF;AACA;;AAEiDC;AAAoB;AACvE;;ACfO;AAIL;AACEE;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGEA;AACAA;;AAEAA;AACA;AACF;;AAGAA;AACF;;AC9BO;AAGL;AAEA;AACF;;ACEA;AACEiC;AACAI;AACAkI;AACArI;AACE;;;AAGF4C;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;;;AAQA;AAEA;AAEA;AAGIS;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;;AAGF;;AC7EA;AAEO;;AAELuK;AACA;AAA8BvI;AAAW;AACvC;AAEI8R;AACA7I;AACA8I;AACAtN;AACF;AAEEqK;AACEkD;;AAEEzJ;;;AAGFqJ;;AAEErJ;AACAwF;;AAEF4D;;AAEEpJ;AACAwF;AACF;;;;;;AAMJ;AAEJ;AACF;;ACOO;AAGL/P;;AAMF;;ACvDO;AAKL;AACED;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGE;AACAA;AACAA;AACA;AACF;AAEAA;AAGAA;AACAA;AACF;AAEO;;AAEHiU;AACE7N;AACA8N;;AAEAC;;AAEFC;;;;;;AAMED;AACF;AACF;AAEA;AACA;;AAIA;AACEE;AAGF;AACAA;AACA;AACEA;AAGF;AACEA;AAGAA;AACAA;AAGF;AACAA;AACA;AACE;AACAA;AAGAA;AACAA;AACAA;AACAA;AAGF;AACEA;AACAA;AACAA;AAGF;AACAA;AACAA;AACAA;AACAA;AACAA;;;;;;;AAOAA;AACAA;AACAA;;AAEEA;AACAA;AACA;AACEA;AACF;AACF;AACEA;AACF;AACAA;AACAA;AACAA;;AAEE;AACEA;AACF;AACEA;AACF;AACAA;AACAA;AAGF;AACEA;AACF;AACAA;AACA;AACEA;AACAA;AACAA;AAGAA;AACAA;AACAA;AACAA;AAGAA;AAGAA;;;;;;;AAOAA;AACAA;AACAA;AACAA;AAGAA;;;;;;;AAOAA;AACAA;AACAA;;AAEEA;AACAA;AACA;AACEA;AACF;AACF;AACEA;AAGF;AACAA;AACAA;AACAA;;AAEEA;AACAA;;AASF;AACEA;AAGF;AACAA;AACF;AACA;AACF;;AChNO;AAIL;AAEA;AACF;;ACZA;AACA;AACA;AACA;AACO;;;;AAOHC;;AAEA;AACE;;AAEEA;AACA;;AAEA;AACAxG;AACF;;AAEA;AACF;AACA;AACEwG;AACF;AACF;AACE;AACAlD;AAEA;AACE;AACA;AACE;AACAkD;AACA;;AAEAxG;AACF;AACEA;AACF;AACF;AAEA;AACEwG;AACF;AACF;;;AAEgBA;;AAClB;;AClCO;AAEP;AAGA;AAEO;;;AAGLvS;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;AAEcQ;;;;AAMd;AAEA;;;AAEe8R;AAAM;AAErB;AAGI5R;AACAxC;AACAyC;AACF;AAEED;AACAxC;;AAEF;AAEEuC;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;;AAGF;;ACzHO;;AAIGL;AAAQ;AACdC;;;AAIF;AACA;AACE;AACF;AACA;AAEAI;;AAMMuU;AAAiC7G;AAAK;AAAI;AAE1CtH;AACF;AAEAvG;AAA2B;AAE/B;AACE;AACF;;AAEA;AACA;;AAEEI;;;AAGJ;;ACxCA;;AAsBO;AAKL;AACEF;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;;AAEiBI;;;AAGf;AACAJ;AACA;AACF;AAEA;AACAA;AACF;AAEA;AAIE;AACE;AACAwU;AACAC;AACAC;AACAC;;AAEF;AAAsDC;AAAS;AAC/D;AACErR;AACF;;;AAGA;;AAIA;;;AAQmCqR;;AAIrC;AAOA;;;AAIoBC;AAAY;AAC5BjV;;;AAGF;;AAEE;AACF;;AAEE;AACF;;AAEE;AACF;AACA;AACF;AAMA;;AAIUgV;AAAS;AAAMhV;;;AAEvB;;AAEA;;;;;;AAgBA;AACA;AACA;;AAGA;AACE;AAeF;AACA;AAWF;AAEO;AAIL;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACEkO;AAGAA;AACAA;;AAIF;AACA;AACA;AACE;AACE;AACF;;AAKE;AACF;AACA;AACA;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACE;AACA;AACE;;AAEE;AACA;AACF;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AAEAgH;;AAAsC1S;AAAK;;;;AAIzC;AACF;AACF;AACE;AACA0S;;AAAsC1S;AAAK;;;;AAIzC;AACF;AAEAhC;;AAEE2U;;AAEA5O;AACAgO;AACEa;AACAC;AACAC;AACAC;AACAvB;;AAEFxN;AACF;AACF;AACF;;;AAEegP;;AACjB;AAEO;;AAKL;;;AAGE;AACE;AACA;AACF;AACAC;AACAC;AACF;;AAEF;;AAEA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AAEO;;;AAMLjB;;;AAQA;AACA;;AAEE;AACA;AACE;AACA;AACF;AACAgB;AACAhB;AACAA;AACF;AACAA;AAEA;AACF;;ACrVO;;AAELvG;AAIF;AACE;AAEAyH;AAKF;;ACJO;AAEP;AAGA;AAEO;;;AAGL5F;AACE6F;;AAEEjL;AACAwF;AACF;;AAEFhO;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;AAEcQ;;;;AAMd;;;AAEe8R;AAAM;AAErB;AAGI5R;AACAxC;AAEAyC;AACF;AAEED;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;;AAEE8N;AACF;AACF;;ACjIA;AAEO;;AAELvD;AACA;AAA8BvI;AAAW;AACvC;AAEImS;AACAsB;AACF;AAEE3E;AACE4E;;AAEEnL;;AAEF;;;;;;AAMJ;AAEJ;AACF;;AC1BO;AACLoL;;AAEc;;AAEVC;AACAC;AACF;;AAGY;;;AAGVC;AACAC;AACA1T;AACA2T;AACF;AAEJ;AAEO;;AAEH;AACAC;AAGJ;;AC1BO;AAIL;AACElW;AACF;;AAGEC;AACA;AACF;AAEA;AACEA;AACA;AACF;;AAEQkW;;AAER;;AAIE;AACElW;AACF;AACF;AACEA;AACF;AAEAA;AACAA;AACF;;ACjBA;AAME;AAKA;;AACU0N;AAAK;AACb;;AAIAyI;AACF;;;AAKA;;AAEI;;AAGA;;AAGE;;AAEI;;AAEA;;AAEE;AACA;AAEA;AACE;AACA;AACE;AACF;;AAGA;AAEA;AACEnW;;AAIAA;AAEA;AAGE;;AAQF;AACF;AACF;AACF;AACF;AACE;AAAA;AAEJ;AACE;AACA;AAEA;AACE;AACA;AACE;AACF;;AAGA;AAEA;AACEA;;AAIAA;AAEA;AAGE;;AAQF;AACF;AACF;AACF;AACF;;;AAGF;AACF;AACF;AAEA;;;AAGI;AACAoW;AACA;AACF;AACE;AACF;AACF;AAEA;;;;;AAOM;AACE;AACE;AACF;;AAGA;;AAGA;;AAEIC;AACF;AACE;;AAEF;AACF;AACF;;AAEA;AAAA;AAEJ;;AAGA;AACF;AAEA;AACE;AACA;AAEA;AACA;;AAGE;;;AAGF;;AAEA;;;;;;;AAQF;AAEA;;AASE;AACErW;AACA;AACF;AAEA;;AAGEA;AACA;AACF;AAEA;AACEA;AACAA;;;AAIE;AAEA;AACEA;AACA;AACF;;AAGE;AACAA;;AAEAA;AACF;AACF;AACEA;AACF;AACF;AACEA;AACAA;AACF;AACEA;;AAEAA;;AAEF;AACF;AAEA;;AAIE;AAA0CsW;AAAc;AACxD;;AAEI7L;AACAtE;;AAEJ;AACA;AACF;AAUO;;;;;AAKLV;AACiB;;;;;AAKf;;AAEA;;AAEA;AACA;;AAEA;;AAKA;AACE;AACA;AACE8Q;AACF;AACAA;;;AAGEC;AACF;AACF;;AAIAxW;AACA;;AAEA;AACEA;AACF;AACAA;AAEA;AACE;AAMF;AAEA;AACEC;AACAY;;AAGA;;AAGF;;;;AAKA;;AAGEX;;;AAGAA;AACAC;AACF;AAEA;AACEF;AACAwW;;AAEAtW;;AAGF;AACF;AACF;;AC9VO;AAEP;AAEA;AAEO;;;AAGL4B;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACAsQ;AACEpQ;AACAyC;AACAxC;AAEA6K;AACAS;AACF;;AAEF7I;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;AAII+I;;;;AAIA7L;AACF;;AAGA;AAEA;AACES;AACAC;AACAxC;AACAyC;AACF;;AAEE;AACF;;AAGA;AACA;AACAsC;;AAGA;AACEjF;AACA;AACF;;AAGA;AACEA;AACF;;AAEQyF;AAAQ;;AAIhB;;;;;AAKEA;AACF;AACF;;AChHO;;;;AAOHmC;AACF;;AAEA;;AAEE;;AAEA;AACE;AACA7H;AACF;AACF;AAEA;AACF;;ACjBA;AACEkC;AACAI;AACAkI;AACArI;;;;AAIF;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGLH;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;AAIA;AACEhC;AACA;AACF;;AAGF;;ACrDO;;;;AAOH4H;AACF;;AAEA;;AAEE;;AAEA;AACE;AACA7H;AACF;AACF;AAEA;AACF;;ACjBA;AACEkC;AACAI;AACAkI;AACArI;;;;AAIF;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGLH;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;AAIA;AACEhC;AACA;AACF;;AAGF;;ACtCO;;;;;;;AAUH0W;AACF;;AAEQ/W;AAAQ;AACdC;;;AAIF;AACA;AACE;AACF;AACA;;AAII+W;;;AAGAlM;AACAiM;AACF;AACE7W;AAA+B;AAErC;;AC9CO;AAKL;AACEE;AACF;;AAEEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AACQ4W;;AACR5W;AAGF;;ACvBO;;;;;;AAOH0W;AAQF;AAGA;;;;;;AAMEA;AACF;AACAG;AACF;;ACfO;AAEP;AAEA;AAEO;;;AAGL9U;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACAwH;AACEtH;AACAyC;AACAxC;;AAEFyU;AACE1U;AACAyC;AACAxC;;AAEF0B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;;AAGF0U;AACE3U;AACAyC;AACAxC;;AAEFqU;AACEtU;AACAyC;AACAxC;AACF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;AAEwBqB;;;;;;AAUxB;AAEA;AAMA;AAEA;AAGIZ;;AAEAvC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGEzC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;;AAGIuC;;;;;;AAQN;;ACzJO;;AAKG5C;AAAQ;AACdC;;;AAIF;AACA;AACE;AACF;AACA;;AAGEC;AACF;AACF;;ACtBO;AAKL;AACEE;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEAA;AACF;;ACtBO;;AAOL;AACF;;ACIO;AAEP;AAEA;AAEO;;;AAGL+B;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACA6B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;AAEF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;;;;AAOI9C;AACF;;;;AAEwBqB;;;;;;AAUxB;AAEA;AAMA;AAEA;AAGIZ;AACAC;AACAxC;AACAyC;AACF;AAEEF;;AAEAvC;AACAyC;AACF;;AAGEzC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACF;;ACtHO;;;;AAIuBgX;AAAK;AAC/BpX;;;AAIF;AACA;AACE;AACF;AACA;;;;;AAME;;AAEIK;AACAC;;;AAGJ;AACA;;;;AAKImM;AAAuB;;AAEzB;AACExM;AAA6B;AAEjC;AACE;AACF;;;AAIF;;AAGEI;AACAY;AACEoW;AACA7T;AACF;;AAEJ;;AC7CO;;;;;;AAIsC4T;AAAK;AAC9CpX;;;;AAIMD;AAAQ;AACdC;;;AAIF;AACA;AACE;AACF;AACA;;;;AAMIyM;;AAEF;AACExM;AAA6B;AAEnC;;AChDA;AAYO;AASL;AACEE;AACF;;;AAIIC;AAEIC;AACAY;;;;;;AAMEmW;AACF;AACF;AAEJ;AACEhX;AACF;AACA;AACF;AACA;AACEA;AACA;AACF;AAEAA;AAIA;AACEsT;AACIC;AAAa9I;AAA2B;AACxC8I;AAAe9I;AAA6B;AAC5C8I;AAAqB9I;AAAmC;AACxD8I;AAAyB9I;AAAuC;AAChE8I;AAAmB9I;;;AAIzBzK;AACA;;AAIEA;AAGF;AACEA;AACF;AACEA;AAGF;AACF;;ACzEO;;;;;;;AAOLgX;AASF;AACE;AACE;;AAA2DA;AAAK;AAEhE;AACF;AACE;;;;;AAKEA;AACF;AAEA;AACE;AACF;AACE;AACA;AASF;AACF;AACF;;AClCO;AAEP;AAEA;AAEO;;;AAGLjV;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACAgV;AACE9U;AACAyC;AACAxC;;AAGF8U;AACE/U;AACAyC;AACAxC;;AAEF0B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;;AAGFS;AACEV;AACAuL;AACA9I;AACAxC;;AAEFY;AACEb;AACAuL;AACA9I;AACAxC;;AAEF2U;AACE5U;AACAuL;AACA9I;AACAxC;AACF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;AAEamV;;;AAAoC9T;;;;AAMjD;AAEA;AAMA;AAEA;AAGIZ;;AAEAvC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACEkX;AACAC;;;;;;AAMF;AACF;;ACnJO;;;;;;;AAUHT;AACF;AAAM9W;;;;AAEED;AAAQ;AACdC;;;AAIF;AACA;AACE;AACF;AACA;;AAII+W;;;AAGAlM;;AAEAiM;AACF;AACE7W;AAA+B;AAErC;;AC/CO;AAKL;AACEE;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEAA;AACF;;ACtBO;;;;;;AAOH0W;AAQF;AAGA;;;;;;AAMEA;AACF;AAEA;AACF;;AChBO;AAEP;AAEA;AAEO;;;AAGL3U;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACAwH;AACEtH;AACAuL;AACA9I;AACAxC;;AAEFyU;AACE1U;AACAuL;AACA9I;AACAxC;;AAEF0B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;;AAGF0U;AACE3U;AACAuL;AACA9I;AACAxC;;AAEFqU;AACEtU;AACAuL;AACA9I;AACAxC;AACF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;AAEwBqB;;;;;;AAUxB;AAEA;AAMA;AAEA;AAGIZ;AACAC;AACAxC;AACAyC;AACF;AAEEF;;AAEAvC;AACAyC;AACF;;AAGEzC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;;AAGIuC;;;;;;AAQN;;AC3JO;;AAKG5C;AAAQ;AAAMC;;;AAEtB;AACA;AACE;AACF;AACA;;AAGEC;AACF;AACF;;AC3BA;AAYO;AAIL;AACEE;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEA;AACEsT;AACIC;AAAa9I;AAA2B;AACxC8I;AAAe9I;AAA6B;AAC5C8I;AAAqB9I;AAAmC;AACxD8I;AAAyB9I;AAAuC;AAChE8I;AAAmB9I;AAAiC;AACpD8I;AAAmB9I;AAAiC;AACpD8I;AAAqB9I;;;AAI3BzK;AACF;;ACrCO;;AAOL;AACF;;ACIO;AAEP;AAEA;AAEO;;;AAGL+B;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACA6B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;AAEF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;AAEwBqB;;;;;;AAUxB;AAEA;AAMA;AAEA;AAGIZ;AACAC;AACAxC;AACAyC;AACF;AAEEF;;AAEAvC;AACAyC;AACF;;AAGEzC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;;AAGF;;AClIA;AAEO;;AAEL;AAA8BgC;AAAW;AACvC;AAEIoV;AACAC;AACApM;AACAqM;AACAC;AACF;;;;;AAMA;AAEJ;AACF;;AC5BO;AACLxN;AACE3H;AACAyC;AACAxC;;AAGFyH;AACE1H;AACAyC;AACAxC;;AAGF2H;AACE5H;AACAyC;AACAxC;;AAGF4H;AACE7H;AACA8K;AACA7K;;AAGF6H;AACE9H;AACA8K;AACA7K;;AAGF8H;AACE/H;AACAyC;AACAxC;AAEF;AACF;;ACrCO;AACL;AACA;AACA;AACEnC;AACAwO;AAEIjE;AACAtD;AACA9E;AACF;AAEEoI;AACAtD;AACA9E;;AAIN;AACA;AACF;;ACaO;AAEP;AAEA;AAEA;AACE;AACA;AACAmI;AACEpI;AACAC;;AAGF8C;AACE/C;AACAuL;AACAtL;;AAEFoH;AACErH;AACAuL;AACA9I;AACAxC;;AAEFmH;AACEpH;AACAuL;AACA9I;AACAxC;;AAEFiD;AACElD;AACAuL;AACA9I;AACAxC;;AAEF4C;AACE7C;AACAC;;AAEFqH;AACEtH;AACAyC;AACAxC;;AAGF0B;AACE3B;AACAyC;AACAxC;;AAGFuH;AACExH;AACAuL;AACAtL;;AAEFgB;AACEjB;AACAC;;AAGFwH;AACEzH;AACAyC;AACAxC;;AAEFgI;AACEjI;AACAyC;AACAxC;;AAGFO;AACER;AACAuL;AACAtL;;AAEFiI;AACElI;AACAC;;AAGFmV;AACEpV;AACAyC;;AAEAxC;;AAGF6C;AACE9C;AACAuL;AACA9I;AACAxC;AAEF;AACF;AAEO;;;AAGLN;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;;;AAGF;;AAEJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;;;;;AAOIF;AACF;;;;;AAMEiD;;AAEAlB;;;AAGAV;;;;;;;;AAQAmU;AACAtS;;;;AAyBF;;;;AAIA;;AAEA;AACA;AACE;;AAIA;AACA+E;AACF;;;AAIE9E;AACAvC;AACA0H;;AAQF;AAMA;;AAMA;;AAEA;AACA;;;AAGItK;AAIF;AACEwK;AACF;AACF;;;;AAIIxK;AACF;AACEsJ;AACF;AACF;;;;AAIItJ;AACF;AACEuC;AACF;AACF;AACA;;;AAGIvC;AACF;AACEsK;AACF;AACF;;AAEA;AACA;;;AAGA;;;AAIEmN;AACAC;AACF;;AAEA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;;AAEI;;AAEE;AAEIzX;AACAC;AACAC;AACF;AAEE4D;AACAwD;AACF;AAEF;AACF;AACA;AACEoQ;AACF;AACAD;AACF;AACF;;;;AAOA;AAEA;AACE1X;AAGAA;AACAA;AAGAA;AACAA;AACAA;AAGAA;AACF;;AAEA;AACA;AAEA;;;;AAaA;AAQA;AAGIyC;;AAEAvC;AACAyC;AACF;AAEED;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACEwK;AACAlB;;;;;AAKAI;AACA3F;;;AAGA4F;AACAC;AACAC;AACEO;AACAJ;AACAF;AACAC;;;;;AAKFM;;;;;AAKF;AACF;;ACnfO;;AAKG1K;AAAQ;AACdC;;;AAIF;AACA;AACE;AACF;AACA;;AAGEC;AACF;AACF;;ACtBO;AAIL;AACEE;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEAA;AACF;;ACrBO;;AAOL;AACF;;ACIO;AAEP;AAEA;AAEO;;;AAGL+B;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACA6B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;AAEF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;AAEwBqB;;;;;AAQxB;AAEA;AAMA;AAEA;;;AAKInD;AACAyC;AACF;;AAGEzC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACF;;ACrHO;;;AAGL2X;AAKF;AACE3X;AACAA;AACAA;AAEA;AAMF;;ACZO;;;AAKHuH;AAKF;AAEA;AACExH;AACF;AAEA;;AAEIC;AACA;AACF;AACAA;AACA;AACF;AAEA;AACA;;AAIA;AACA;AACA;AACA;AACE;AACA;AACF;;AAGE;AACA;AACF;;AAEA;AACA;;AAEAA;;AAGI4X;AACA1Q;AACA2Q;AACAC;AACF;AAEF9X;AAGAA;AACF;AAEA;AAKE;AAEA;AACEA;;AAEE;AACEA;AACAA;AACF;AACEA;AACF;AACAA;AACF;AACF;AACE;AACAA;AACAA;AACAA;AACF;AACF;AAEA;AAGE;AAEAA;AACAA;AACAA;AACAA;AAGAA;AACAA;;AAIAA;AACAA;AACAA;;AAEAA;;AAGEa;AACEb;AACF;;AAEEA;AACF;AACF;AAEAA;;AAEEa;AACEb;AACF;;AAEEA;AACF;AACF;AAEAA;;AAEEa;AACEb;AACF;;AAEEA;AACF;AACF;AAEAA;;AAEEa;AACEb;AACF;;AAEEA;AACF;AACF;;;AAIA;;AAEE;AACEA;AACF;AACA;;AAEA;AACF;AAEAA;;AAEAA;AACAA;AAGAA;AACA;AACE;AACE;AACF;AACA;;;AAKA;AACF;AAEAA;;AAEAA;AACAA;AACAA;AACA;AACE;AACE;AACF;AACA;;;AAKA;AACF;AAEAA;AACF;;AC9MO;;;;;;AAMLuH;AAQF;AACE;;;AAGEoQ;AACF;;;;AAKEpQ;AACF;AACF;;ACdO;AAEP;AAEA;AAEO;;;AAGLxF;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACAgF;AACE9E;AACAyC;AACAxC;;AAGFF;AACEC;AACAuL;AACA9I;AACAxC;;AAGF0B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;AAEF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;AAEA;AAEA;;;;;;AAEqCqB;;;;AAMrC;AACA;AACA;AACE0U;AACF;AACA;AACEC;AACF;AAEA;AAEA;AAMA;AAEA;AAGItV;AACAxC;AAEAyC;AAMF;;AAGEF;AACAvC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACE+X;AACAC;AACA9Q;;;AAGA/E;AACF;AACF;;AChKO;;;;;;;;AAQL8V;AAUF;;AAME;AACE;AACA;AACEd;AACAH;AACF;AACA;AACE;AACF;AACAkB;AACF;AAEAA;;AAGAlY;;;AAIIC;AACAC;AACAC;;AAGJ;;AAEA;AACA;AACE;AACA;AACE;AACF;;AAEF;;AAEA;AACA;AACA;;AAEE;AACE;AACF;AACF;;AAGA;AACE;AACA;;;;;;AAME8X;AACF;;;AAEUE;;AACR;AACEC;AACF;AACF;AACF;;AAGApY;;AAGEC;AACAY;;AAEJ;AAEA;;;;;;AAQIoX;AAQF;;;AAMA;;;;;;;AASAjY;AACA;AACF;AAEA;;;;;AAOIuH;AAQF;AAEA;;;;AAIE8Q;AACF;AACA;AACE;AACF;;;AACuBC;;AAEvBtY;AAEA;;;;;AAKEsY;AACF;AACA;AACE;AACF;AACA;AAEA;AACEtY;;AAGSC;AAAUY;AAAQsX;AAAmB;;AAChD;AAEA;AACA5U;AAEA;;;;;;;AAOE8U;AACF;AACA;AACE;AACF;AAEA;;;;;AAKEA;AACF;AACA;AACE;AACF;;;;AAE0CE;;;AAE1C;AACA;;AAEA;AACE/N;AACAlB;AACAG;;;AAGAxE;AACAyE;AACA3F;;;AAGA4F;AACAC;AACAC;AACEO;AACAJ;AACAF;AACAC;AACAE;AACAC;AACAC;;AAEFE;AACA9H;AACA+H;;AAEApF;AACF;;AAESjF;AAAUY;AAAQsX;AAAkB;;AAC/C;AAEA;;;;;;;AAOEK;AASF;AACExY;;;AAOA;AACA;AACE;AACA;;;;;AAKEqY;AACF;;AAEE;AACEI;AACF;AACF;AACEC;AACF;AACF;;;;AAKE;AACE1Y;AAGA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AKN;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;;;;AAOIF;AACF;;AAGEiD;AACAlB;;;AAGAV;;;;AAIA8G;;;;AAeF;;;;AAIA;;AAEA;AACA;AACE;;AAIA;AACAF;AACF;AAEA;;;AAMA;;;AAGA;;AAEEwN;AACF;AAEA;AAMA;AAEA;AAEA;AAGIhV;;AAEAvC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;;;;;;AAMEoJ;AACEU;AACAC;AACAC;;;;AAIF;AACF;AACF;;AC7LO;AAEP;AAGA;AAEO;;;AAGLjI;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACAqH;AACEnH;AACAyC;AACAxC;;AAEF0B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;;AAGFmE;AACEpE;AACAyC;AACAxC;;AAEF2E;AACE5E;AACAyC;AACAxC;;AAEFuR;AACExR;AACAyC;AACAxC;AACF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;AAGEuH;;;;AAIAlG;AACAmD;;;;AAOF;AAEA;AAEA;AAMA;AAEA;AAGI/D;;AAEAvC;AACAyC;AACF;;AAGEzC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;;;;;AAKE6C;AACA0G;;AAEA/C;AAMF;AACF;;AClMO;AACL;AACEzG;AACF;AAEA;AACEC;AACA;AACF;AAEAA;AACAA;AACAA;AACF;;ACCO;;AAKL;AACEA;AACF;AACEA;AACF;AAEAA;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;AAGEzC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;;AAGE;AAAoCmC;AAAK;AAC3C;;AAEA;AACF;;AC3IA;AAEO;;AAEL;AAA8BH;AAAW;AACvC;AAEIoV;AACAE;AACAuC;AACAC;AACA7O;AACA8O;AACAlQ;AACAS;AACAuG;AACAwG;AACF;AAEEvG;AACEkJ;;AAEEzP;;;AAGF0P;;AAEE1P;;AAEF;;;;;;AAMJ;AAEJ;AACF;;AC/CO;;;;;;;;AAQLpE;AAUF;AACE;;AAcF;;ACzBA;AAEO;AAIL;AACEpG;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGEA;AACA;AACF;;AAGA;;AAEA;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACA;AACAM;AAEA;;;;AAIA;AACEqD;AACAC;AACAC;AACAC;AACAC;AACAxD;AACAoB;AACAqC;;AAEAC;AACE7B;AACAwB;;AAEFM;AACA;AACA;AACAC;AACAC;AACF;AAEA;AACA;AACEC;AAA0B;AAC1BC;AACA3C;AACAR;AACEyC;AACAW;;AAEFC;AACAC;AACF;AACA;AACEJ;AACAC;AACA3C;AACAsC;AACE7B;AACAwB;;AAEFrD;AACAkE;AAEAtD;AACEyC;AACF;AACF;;AAGEc;AAQA7D;AACF;;AAEA;AACA;AACE;AACA;;AAEA;AACF;;AAEA;;;AAGA;AACAP;AACAA;AACAA;;AAEA;AACAqE;AACE;AACA;AACE;AACA;AACE;;;AAKF;AACF;AACF;;AAIArE;AACE;;AAEA;AACAN;AACF;AACF;AAEA;AAKE;AASF;AAEA;AACE;;;AAGE;AAEA;;AAEA;;AASF;AACF;;AC1LO;;;;;;;;;AASLmG;AAWF;AACE;;;;;;;;AAQEA;AACF;AAEA;AACF;;ACrBO;AAEP;AAEA;AAcA;AAEA;AAEO;;;AAGLpE;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACAiV;AACE/U;AACAyC;AACAxC;;AAEF6X;AACE9X;AACAyC;AACAxC;;AAEFiN;AACElN;AACAyC;AACAxC;;AAEF0B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;;AAGFY;AACEb;AACAyC;AACAxC;;AAEFS;AACEV;AACAuL;AACA9I;AACAxC;;AAEF8X;AACE/X;AACAyC;AACAxC;;AAEF8D;AACE/D;AACAyC;AACAxC;AACF;;AAEFyC;AACJ;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;;AAMEqB;;AAEAjB;AACA+D;;;;AAOF;AACA;AACA;AACA;;AAGAiU;AACE;AACEC;AACAC;AACA;AACF;AACF;AAEAF;AACE;AACEG;AACAD;AACA;AACF;AACF;AAEAF;AACE;AACErV;AACAuV;AACA;AACF;AACF;AAEA;AACAF;AACE;AACEI;AACAF;AACA;AACF;AACF;;AAGEta;AAGF;AAEA;AAEA;AAMA;AAEA;AAGIyC;;AAEAvC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;;AAEEya;AACAnL;;;;;AAKA6K;AACAhU;AACF;AACF;;ACnRO;AAIL;AACEpG;AAEAC;AACA;AACF;AAEAA;AACAA;AACAA;AAGAA;AAGAA;AACAA;AACAA;AACAA;AAGAA;AACA;AACEA;AAGAA;;AAEEA;AACF;AACAA;AACF;AACF;;AC/BO;AAGL;AACA;AACE;AACF;;;;AAE4C+N;;;AAE5C;AACA;;;AAOE;AACE;AACE;AAAA;AAEA;AAAA;;;AAOA9N;AACAY;AACE6Z;;;AAGFxa;;AAEJ;AACE;;AAEED;AACAY;AACE6Z;A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","debugId":"8481439d-81fb-4c40-8fb9-cbf6be031d3"}
|
|
1
|
+
{"version":3,"file":"cli.js","sources":["../src/commands/analytics/fetch-org-analytics.mts","../src/commands/analytics/fetch-repo-analytics.mts","../src/commands/analytics/output-analytics.mts","../src/commands/analytics/handle-analytics.mts","../src/commands/analytics/cmd-analytics.mts","../src/commands/audit-log/fetch-audit-log.mts","../src/commands/audit-log/output-audit-log.mts","../src/commands/audit-log/handle-audit-log.mts","../src/commands/audit-log/cmd-audit-log.mts","../src/commands/scan/fetch-create-org-full-scan.mts","../src/commands/scan/fetch-supported-scan-file-names.mts","../src/commands/scan/finalize-tier1-scan.mts","../src/commands/scan/fetch-report-data.mts","../src/commands/scan/generate-report.mts","../src/commands/scan/output-scan-report.mts","../src/commands/scan/handle-scan-report.mts","../src/commands/scan/output-create-new-scan.mts","../src/commands/scan/perform-reachability-analysis.mts","../src/commands/manifest/detect-manifest-actions.mts","../src/commands/manifest/convert_gradle_to_maven.mts","../src/commands/manifest/convert_sbt_to_maven.mts","../src/commands/manifest/convert-conda-to-requirements.mts","../src/commands/manifest/output-requirements.mts","../src/commands/manifest/handle-manifest-conda.mts","../src/commands/manifest/generate_auto_manifest.mts","../src/commands/scan/handle-create-new-scan.mts","../src/commands/ci/handle-ci.mts","../src/commands/ci/cmd-ci.mts","../src/commands/config/discover-config-value.mts","../src/commands/config/output-config-auto.mts","../src/commands/config/handle-config-auto.mts","../src/commands/config/cmd-config-auto.mts","../src/commands/config/output-config-get.mts","../src/commands/config/handle-config-get.mts","../src/commands/config/cmd-config-get.mts","../src/commands/config/output-config-list.mts","../src/commands/config/cmd-config-list.mts","../src/commands/config/output-config-set.mts","../src/commands/config/handle-config-set.mts","../src/commands/config/cmd-config-set.mts","../src/commands/config/output-config-unset.mts","../src/commands/config/handle-config-unset.mts","../src/commands/config/cmd-config-unset.mts","../src/commands/config/cmd-config.mts","../src/commands/fix/git.mts","../src/commands/fix/pull-request.mts","../src/commands/fix/env-helpers.mts","../src/commands/fix/coana-fix.mts","../src/commands/fix/output-fix-result.mts","../src/commands/fix/handle-fix.mts","../src/commands/fix/cmd-fix.mts","../src/commands/install/output-install-completion.mts","../src/commands/install/setup-tab-completion.mts","../src/commands/install/handle-install-completion.mts","../src/commands/install/cmd-install-completion.mts","../src/commands/install/cmd-install.mts","../src/commands/json/output-cmd-json.mts","../src/commands/json/handle-cmd-json.mts","../src/commands/json/cmd-json.mts","../src/commands/login/apply-login.mts","../src/commands/login/attempt-login.mts","../src/commands/login/cmd-login.mts","../src/commands/logout/apply-logout.mts","../src/commands/logout/attempt-logout.mts","../src/commands/logout/cmd-logout.mts","../src/commands/manifest/run-cdxgen.mts","../src/commands/manifest/cmd-manifest-cdxgen.mts","../src/commands/manifest/cmd-manifest-auto.mts","../src/commands/manifest/cmd-manifest-conda.mts","../src/commands/manifest/cmd-manifest-gradle.mts","../src/commands/manifest/cmd-manifest-kotlin.mts","../src/commands/manifest/cmd-manifest-scala.mts","../src/commands/manifest/output-manifest-setup.mts","../src/commands/manifest/setup-manifest-config.mts","../src/commands/manifest/handle-manifest-setup.mts","../src/commands/manifest/cmd-manifest-setup.mts","../src/commands/manifest/cmd-manifest.mts","../src/commands/npm/cmd-npm.mts","../src/commands/npx/cmd-npx.mts","../src/commands/oops/cmd-oops.mts","../src/commands/optimize/deps-includes-by-agent.mts","../src/commands/optimize/get-dependency-entries.mts","../src/commands/optimize/get-overrides-by-agent.mts","../src/commands/optimize/lockfile-includes-by-agent.mts","../src/commands/optimize/ls-by-agent.mts","../src/commands/optimize/shared.mts","../src/commands/optimize/update-manifest-by-agent.mts","../src/commands/optimize/add-overrides.mts","../src/commands/optimize/update-lockfile.mts","../src/commands/optimize/apply-optimization.mts","../src/commands/optimize/output-optimize-result.mts","../src/commands/optimize/handle-optimize.mts","../src/commands/optimize/cmd-optimize.mts","../src/commands/organization/fetch-dependencies.mts","../src/commands/organization/output-dependencies.mts","../src/commands/organization/handle-dependencies.mts","../src/commands/organization/cmd-organization-dependencies.mts","../src/commands/organization/fetch-license-policy.mts","../src/commands/organization/output-license-policy.mts","../src/commands/organization/handle-license-policy.mts","../src/commands/organization/cmd-organization-policy-license.mts","../src/commands/organization/fetch-security-policy.mts","../src/commands/organization/output-security-policy.mts","../src/commands/organization/handle-security-policy.mts","../src/commands/organization/cmd-organization-policy-security.mts","../src/commands/organization/output-organization-list.mts","../src/commands/organization/handle-organization-list.mts","../src/commands/organization/cmd-organization-list.mts","../src/commands/organization/cmd-organization-policy.mts","../src/commands/organization/fetch-quota.mts","../src/commands/organization/output-quota.mts","../src/commands/organization/handle-quota.mts","../src/commands/organization/cmd-organization-quota.mts","../src/commands/organization/cmd-organization.mts","../src/commands/package/fetch-purl-deep-score.mts","../src/commands/package/output-purls-deep-score.mts","../src/commands/package/handle-purl-deep-score.mts","../src/commands/package/parse-package-specifiers.mts","../src/commands/package/cmd-package-score.mts","../src/commands/package/fetch-purls-shallow-score.mts","../src/commands/package/output-purls-shallow-score.mts","../src/commands/package/handle-purls-shallow-score.mts","../src/commands/package/cmd-package-shallow.mts","../src/commands/package/cmd-package.mts","../src/commands/patch/manifest-schema.mts","../src/commands/patch/output-patch-result.mts","../src/commands/patch/handle-patch.mts","../src/commands/patch/cmd-patch.mts","../src/commands/raw-npm/run-raw-npm.mts","../src/commands/raw-npm/cmd-raw-npm.mts","../src/commands/raw-npx/run-raw-npx.mts","../src/commands/raw-npx/cmd-raw-npx.mts","../src/commands/repository/fetch-create-repo.mts","../src/commands/repository/output-create-repo.mts","../src/commands/repository/handle-create-repo.mts","../src/commands/repository/cmd-repository-create.mts","../src/commands/repository/fetch-delete-repo.mts","../src/commands/repository/output-delete-repo.mts","../src/commands/repository/handle-delete-repo.mts","../src/commands/repository/cmd-repository-del.mts","../src/commands/repository/fetch-list-all-repos.mts","../src/commands/repository/fetch-list-repos.mts","../src/commands/repository/output-list-repos.mts","../src/commands/repository/handle-list-repos.mts","../src/commands/repository/cmd-repository-list.mts","../src/commands/repository/fetch-update-repo.mts","../src/commands/repository/output-update-repo.mts","../src/commands/repository/handle-update-repo.mts","../src/commands/repository/cmd-repository-update.mts","../src/commands/repository/fetch-view-repo.mts","../src/commands/repository/output-view-repo.mts","../src/commands/repository/handle-view-repo.mts","../src/commands/repository/cmd-repository-view.mts","../src/commands/repository/cmd-repository.mts","../src/commands/scan/reachability-flags.mts","../src/commands/scan/suggest_target.mts","../src/commands/scan/cmd-scan-create.mts","../src/commands/scan/fetch-delete-org-full-scan.mts","../src/commands/scan/output-delete-scan.mts","../src/commands/scan/handle-delete-scan.mts","../src/commands/scan/cmd-scan-del.mts","../src/commands/scan/fetch-diff-scan.mts","../src/commands/scan/output-diff-scan.mts","../src/commands/scan/handle-diff-scan.mts","../src/commands/scan/cmd-scan-diff.mts","../src/commands/scan/create-scan-from-github.mts","../src/commands/scan/output-scan-github.mts","../src/commands/scan/handle-create-github-scan.mts","../src/commands/scan/cmd-scan-github.mts","../src/commands/scan/fetch-list-scans.mts","../src/commands/scan/output-list-scans.mts","../src/commands/scan/handle-list-scans.mts","../src/commands/scan/cmd-scan-list.mts","../src/commands/scan/fetch-scan-metadata.mts","../src/commands/scan/output-scan-metadata.mts","../src/commands/scan/handle-scan-metadata.mts","../src/commands/scan/cmd-scan-metadata.mts","../src/commands/scan/output-scan-reach.mts","../src/commands/scan/handle-scan-reach.mts","../src/commands/scan/cmd-scan-reach.mts","../src/commands/scan/cmd-scan-report.mts","../src/commands/scan/output-scan-config-result.mts","../src/commands/scan/setup-scan-config.mts","../src/commands/scan/handle-scan-config.mts","../src/commands/scan/cmd-scan-setup.mts","../src/commands/scan/fetch-scan.mts","../src/commands/scan/output-scan-view.mts","../src/commands/scan/handle-scan-view.mts","../src/commands/scan/stream-scan.mts","../src/commands/scan/cmd-scan-view.mts","../src/commands/scan/cmd-scan.mts","../src/commands/threat-feed/fetch-threat-feed.mts","../src/commands/threat-feed/output-threat-feed.mts","../src/commands/threat-feed/handle-threat-feed.mts","../src/commands/threat-feed/cmd-threat-feed.mts","../src/commands/uninstall/output-uninstall-completion.mts","../src/commands/uninstall/teardown-tab-completion.mts","../src/commands/uninstall/handle-uninstall-completion.mts","../src/commands/uninstall/cmd-uninstall-completion.mts","../src/commands/uninstall/cmd-uninstall.mts","../src/commands/wrapper/add-socket-wrapper.mts","../src/commands/wrapper/check-socket-wrapper-setup.mts","../src/commands/wrapper/postinstall-wrapper.mts","../src/commands/wrapper/remove-socket-wrapper.mts","../src/commands/wrapper/cmd-wrapper.mts","../src/commands.mts","../src/cli.mts"],"sourcesContent":["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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchOrgAnalyticsDataOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchOrgAnalyticsData(\n time: number,\n options?: FetchOrgAnalyticsDataOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgAnalytics'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchOrgAnalyticsDataOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getOrgAnalytics(time.toString()), {\n desc: 'analytics data',\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type RepoAnalyticsDataOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchRepoAnalyticsData(\n repo: string,\n time: number,\n options?: RepoAnalyticsDataOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getRepoAnalytics'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as RepoAnalyticsDataOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getRepoAnalytics(repo, time.toString()), {\n desc: 'analytics data',\n })\n}\n","import fs from 'node:fs/promises'\nimport { createRequire } from 'node:module'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mdTableStringNumber } from '../../utils/markdown.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\nimport type { Widgets } from 'blessed' // Note: Widgets does not seem to actually work as code :'(\nimport type { grid as ContribGrid } from 'blessed-contrib'\n\nconst require = createRequire(import.meta.url)\n\nconst METRICS = [\n 'total_critical_alerts',\n 'total_high_alerts',\n 'total_medium_alerts',\n 'total_low_alerts',\n 'total_critical_added',\n 'total_medium_added',\n 'total_low_added',\n 'total_high_added',\n 'total_critical_prevented',\n 'total_high_prevented',\n 'total_medium_prevented',\n 'total_low_prevented',\n] as const\n\n// Note: This maps `new Date(date).getMonth()` to English three letters\nconst Months = [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec',\n] as const\n\nexport async function outputAnalytics(\n result: CResult<\n | SocketSdkSuccessResult<'getOrgAnalytics'>['data']\n | SocketSdkSuccessResult<'getRepoAnalytics'>['data']\n >,\n {\n filePath,\n outputKind,\n repo,\n scope,\n time,\n }: {\n scope: string\n time: number\n repo: string\n outputKind: OutputKind\n filePath: string\n },\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (!result.ok) {\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'json') {\n const serialized = serializeResultJson(result)\n\n if (filePath) {\n try {\n await fs.writeFile(filePath, serialized, 'utf8')\n logger.success(`Data successfully written to ${filePath}`)\n } catch (e) {\n process.exitCode = 1\n logger.log(\n serializeResultJson({\n ok: false,\n message: 'File Write Failure',\n cause: 'There was an error trying to write the json to disk',\n }),\n )\n }\n } else {\n logger.log(serialized)\n }\n\n return\n }\n\n const fdata =\n scope === 'org' ? formatDataOrg(result.data) : formatDataRepo(result.data)\n\n if (outputKind === 'markdown') {\n const serialized = renderMarkdown(fdata, time, repo)\n\n // TODO: Do we want to write to file even if there was an error...?\n if (filePath) {\n try {\n await fs.writeFile(filePath, serialized, 'utf8')\n logger.success(`Data successfully written to ${filePath}`)\n } catch (e) {\n logger.error(e)\n }\n } else {\n logger.log(serialized)\n }\n } else {\n displayAnalyticsScreen(fdata)\n }\n}\n\nexport interface FormattedData {\n top_five_alert_types: Record<string, number>\n total_critical_alerts: Record<string, number>\n total_high_alerts: Record<string, number>\n total_medium_alerts: Record<string, number>\n total_low_alerts: Record<string, number>\n total_critical_added: Record<string, number>\n total_medium_added: Record<string, number>\n total_low_added: Record<string, number>\n total_high_added: Record<string, number>\n total_critical_prevented: Record<string, number>\n total_high_prevented: Record<string, number>\n total_medium_prevented: Record<string, number>\n total_low_prevented: Record<string, number>\n}\n\nexport function renderMarkdown(\n data: FormattedData,\n days: number,\n repoSlug: string,\n): string {\n return (\n `\n# Socket Alert Analytics\n\nThese are the Socket.dev analytics for the ${repoSlug ? `${repoSlug} repo` : 'org'} of the past ${days} days\n\n${[\n [\n 'Total critical alerts',\n mdTableStringNumber('Date', 'Counts', data['total_critical_alerts']),\n ],\n [\n 'Total high alerts',\n mdTableStringNumber('Date', 'Counts', data['total_high_alerts']),\n ],\n [\n 'Total critical alerts added to the main branch',\n mdTableStringNumber('Date', 'Counts', data['total_critical_added']),\n ],\n [\n 'Total high alerts added to the main branch',\n mdTableStringNumber('Date', 'Counts', data['total_high_added']),\n ],\n [\n 'Total critical alerts prevented from the main branch',\n mdTableStringNumber('Date', 'Counts', data['total_critical_prevented']),\n ],\n [\n 'Total high alerts prevented from the main branch',\n mdTableStringNumber('Date', 'Counts', data['total_high_prevented']),\n ],\n [\n 'Total medium alerts prevented from the main branch',\n mdTableStringNumber('Date', 'Counts', data['total_medium_prevented']),\n ],\n [\n 'Total low alerts prevented from the main branch',\n mdTableStringNumber('Date', 'Counts', data['total_low_prevented']),\n ],\n]\n .map(([title, table]) =>\n `\n## ${title}\n\n${table}\n`.trim(),\n )\n .join('\\n\\n')}\n\n## Top 5 alert types\n\n${mdTableStringNumber('Name', 'Counts', data['top_five_alert_types'])}\n`.trim() + '\\n'\n )\n}\n\nfunction displayAnalyticsScreen(data: FormattedData): void {\n const ScreenWidget = /*@__PURE__*/ require('blessed/lib/widgets/screen.js')\n const screen: Widgets.Screen = new ScreenWidget({\n ...constants.blessedOptions,\n })\n const GridLayout = /*@__PURE__*/ require('blessed-contrib/lib/layout/grid.js')\n const grid = new GridLayout({ rows: 5, cols: 4, screen })\n\n renderLineCharts(\n grid,\n screen,\n 'Total critical alerts',\n [0, 0, 1, 2],\n data['total_critical_alerts'],\n )\n renderLineCharts(\n grid,\n screen,\n 'Total high alerts',\n [0, 2, 1, 2],\n data['total_high_alerts'],\n )\n renderLineCharts(\n grid,\n screen,\n 'Total critical alerts added to the main branch',\n [1, 0, 1, 2],\n data['total_critical_added'],\n )\n renderLineCharts(\n grid,\n screen,\n 'Total high alerts added to the main branch',\n [1, 2, 1, 2],\n data['total_high_added'],\n )\n renderLineCharts(\n grid,\n screen,\n 'Total critical alerts prevented from the main branch',\n [2, 0, 1, 2],\n data['total_critical_prevented'],\n )\n renderLineCharts(\n grid,\n screen,\n 'Total high alerts prevented from the main branch',\n [2, 2, 1, 2],\n data['total_high_prevented'],\n )\n renderLineCharts(\n grid,\n screen,\n 'Total medium alerts prevented from the main branch',\n [3, 0, 1, 2],\n data['total_medium_prevented'],\n )\n renderLineCharts(\n grid,\n screen,\n 'Total low alerts prevented from the main branch',\n [3, 2, 1, 2],\n data['total_low_prevented'],\n )\n\n const BarChart = /*@__PURE__*/ require('blessed-contrib/lib/widget/charts/bar.js')\n const bar = grid.set(4, 0, 1, 2, BarChart, {\n label: 'Top 5 alert types',\n barWidth: 10,\n barSpacing: 17,\n xOffset: 0,\n maxHeight: 9,\n barBgColor: 'magenta',\n })\n\n // Must append before setting data.\n screen.append(bar)\n\n bar.setData({\n titles: Object.keys(data.top_five_alert_types),\n data: Object.values(data.top_five_alert_types),\n })\n\n screen.render()\n // eslint-disable-next-line n/no-process-exit\n screen.key(['escape', 'q', 'C-c'], () => process.exit(0))\n}\n\nexport function formatDataRepo(\n data: SocketSdkSuccessResult<'getRepoAnalytics'>['data'],\n): FormattedData {\n const sortedTopFiveAlerts: Record<string, number> = {}\n const totalTopAlerts: Record<string, number> = {}\n\n const formattedData = {} as Omit<FormattedData, 'top_five_alert_types'>\n for (const metric of METRICS) {\n formattedData[metric] = {}\n }\n\n for (const entry of data) {\n const topFiveAlertTypes = entry['top_five_alert_types']\n for (const type of Object.keys(topFiveAlertTypes)) {\n const count = topFiveAlertTypes[type] ?? 0\n if (!totalTopAlerts[type]) {\n totalTopAlerts[type] = count\n } else if (count > (totalTopAlerts[type] ?? 0)) {\n totalTopAlerts[type] = count\n }\n }\n }\n for (const entry of data) {\n for (const metric of METRICS) {\n formattedData[metric]![formatDate(entry['created_at'])] = entry[metric]\n }\n }\n\n const topFiveAlertEntries = Object.entries(totalTopAlerts)\n .sort(([_keya, a], [_keyb, b]) => b - a)\n .slice(0, 5)\n for (const [key, value] of topFiveAlertEntries) {\n sortedTopFiveAlerts[key] = value\n }\n\n return {\n ...formattedData,\n top_five_alert_types: sortedTopFiveAlerts,\n }\n}\n\nexport function formatDataOrg(\n data: SocketSdkSuccessResult<'getOrgAnalytics'>['data'],\n): FormattedData {\n const sortedTopFiveAlerts: Record<string, number> = {}\n const totalTopAlerts: Record<string, number> = {}\n\n const formattedData = {} as Omit<FormattedData, 'top_five_alert_types'>\n for (const metric of METRICS) {\n formattedData[metric] = {}\n }\n\n for (const entry of data) {\n const topFiveAlertTypes = entry['top_five_alert_types']\n for (const type of Object.keys(topFiveAlertTypes)) {\n const count = topFiveAlertTypes[type] ?? 0\n if (!totalTopAlerts[type]) {\n totalTopAlerts[type] = count\n } else {\n totalTopAlerts[type] += count\n }\n }\n }\n\n for (const metric of METRICS) {\n const formatted = formattedData[metric]\n for (const entry of data) {\n const date = formatDate(entry['created_at'])\n if (!formatted[date]) {\n formatted[date] = entry[metric]!\n } else {\n formatted[date] += entry[metric]!\n }\n }\n }\n\n const topFiveAlertEntries = Object.entries(totalTopAlerts)\n .sort(([_keya, a], [_keyb, b]) => b - a)\n .slice(0, 5)\n for (const [key, value] of topFiveAlertEntries) {\n sortedTopFiveAlerts[key] = value\n }\n\n return {\n ...formattedData,\n top_five_alert_types: sortedTopFiveAlerts,\n }\n}\n\nfunction formatDate(date: string): string {\n return `${Months[new Date(date).getMonth()]} ${new Date(date).getDate()}`\n}\n\nfunction renderLineCharts(\n grid: ContribGrid,\n screen: Widgets.Screen,\n title: string,\n coords: number[],\n data: Record<string, number>,\n): void {\n const LineChart = /*@__PURE__*/ require('blessed-contrib/lib/widget/charts/line.js')\n const line = grid.set(...coords, LineChart, {\n style: { line: 'cyan', text: 'cyan', baseline: 'black' },\n xLabelPadding: 0,\n xPadding: 0,\n xOffset: 0,\n wholeNumbersOnly: true,\n legend: {\n width: 1,\n },\n label: title,\n })\n\n screen.append(line)\n\n const lineData = {\n x: Object.keys(data),\n y: Object.values(data),\n }\n\n line.setData([lineData])\n}\n","import { fetchOrgAnalyticsData } from './fetch-org-analytics.mts'\nimport { fetchRepoAnalyticsData } from './fetch-repo-analytics.mts'\nimport { outputAnalytics } from './output-analytics.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function handleAnalytics({\n filePath,\n outputKind,\n repo,\n scope,\n time,\n}: {\n scope: string\n time: number\n repo: string\n outputKind: OutputKind\n filePath: string\n}) {\n let result: CResult<\n | SocketSdkSuccessResult<'getOrgAnalytics'>['data']\n | SocketSdkSuccessResult<'getRepoAnalytics'>['data']\n >\n if (scope === 'org') {\n result = await fetchOrgAnalyticsData(time)\n } else if (repo) {\n result = await fetchRepoAnalyticsData(repo, time)\n } else {\n result = {\n ok: false,\n message: 'Missing repository name in command',\n }\n }\n if (result.ok && !result.data.length) {\n result = {\n ok: true,\n message: `The analytics data for this ${scope === 'org' ? 'organization' : 'repository'} is not yet available.`,\n data: [],\n }\n }\n\n await outputAnalytics(result, {\n filePath,\n outputKind,\n repo,\n scope,\n time,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleAnalytics } from './handle-analytics.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'analytics'\n\nconst description = 'Look up analytics data'\n\nconst hidden = false\n\nexport const cmdAnalytics = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n file: {\n type: 'string',\n description: 'Path to store result, only valid with --json/--markdown',\n },\n },\n help: (command, { flags }) =>\n `\n Usage\n $ ${command} [options] [ \"org\" | \"repo\" <reponame>] [TIME]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n The scope is either org or repo level, defaults to org.\n\n When scope is repo, a repo slug must be given as well.\n\n The TIME argument must be number 7, 30, or 90 and defaults to 30.\n\n Options\n ${getFlagListOutput(flags)}\n\n Examples\n $ ${command} org 7\n $ ${command} repo test-repo 30\n $ ${command} 90\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n // Supported inputs:\n // - [] (no args)\n // - ['org']\n // - ['org', '30']\n // - ['repo', 'name']\n // - ['repo', 'name', '30']\n // - ['30']\n // Validate final values in the next step\n let scope = 'org'\n let time = '30'\n let repoName = ''\n\n if (cli.input[0] === 'org') {\n if (cli.input[1]) {\n time = cli.input[1]\n }\n } else if (cli.input[0] === 'repo') {\n scope = 'repo'\n if (cli.input[1]) {\n repoName = cli.input[1]\n }\n if (cli.input[2]) {\n time = cli.input[2]\n }\n } else if (cli.input[0]) {\n time = cli.input[0]\n }\n\n const { file, json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const noLegacy =\n !cli.flags['scope'] && !cli.flags['repo'] && !cli.flags['time']\n\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: noLegacy,\n message: 'Legacy flags are no longer supported. See v1 migration guide.',\n fail: `received legacy flags`,\n },\n {\n nook: true,\n test: scope === 'org' || !!repoName,\n message: 'When scope=repo, repo name should be the second argument',\n fail: 'missing',\n },\n {\n nook: true,\n test:\n scope === 'org' ||\n (repoName !== '7' && repoName !== '30' && repoName !== '90'),\n message: 'When scope is repo, the second arg should be repo, not time',\n fail: 'missing',\n },\n {\n test: time === '7' || time === '30' || time === '90',\n message: 'The time filter must either be 7, 30 or 90',\n fail: 'invalid range set, see --help for command arg details.',\n },\n {\n nook: true,\n test: !file || !!json || !!markdown,\n message:\n 'The `--file` flag is only valid when using `--json` or `--markdown`',\n fail: 'bad',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n return await handleAnalytics({\n scope,\n time: time === '90' ? 90 : time === '30' ? 30 : 7,\n repo: repoName,\n outputKind,\n filePath: String(file || ''),\n })\n}\n","import { handleApiCall } from '../../utils/api.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SetupSdkOptions } from '../../utils/sdk.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchAuditLogsConfig = {\n logType: string\n orgSlug: string\n outputKind: OutputKind\n page: number\n perPage: number\n}\n\nexport type FetchAuditLogOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchAuditLog(\n config: FetchAuditLogsConfig,\n options?: FetchAuditLogOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getAuditLogEvents'>['data']>> {\n const { sdkOpts } = { __proto__: null, ...options } as FetchAuditLogOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n const { logType, orgSlug, outputKind, page, perPage } = {\n __proto__: null,\n ...config,\n } as FetchAuditLogsConfig\n\n return await handleApiCall(\n sockSdk.getAuditLogEvents(orgSlug, {\n // I'm not sure this is used at all.\n outputJson: String(outputKind === 'json'),\n // I'm not sure this is used at all.\n outputMarkdown: String(outputKind === 'markdown'),\n orgSlug,\n type: logType,\n page: String(page),\n per_page: String(perPage),\n }),\n { desc: `audit log for ${orgSlug}` },\n )\n}\n","import { createRequire } from 'node:module'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mdTable } from '../../utils/markdown.mts'\nimport { msAtHome } from '../../utils/ms-at-home.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\nimport type { Widgets } from 'blessed'\n\nconst require = createRequire(import.meta.url)\n\nexport async function outputAuditLog(\n result: CResult<SocketSdkSuccessResult<'getAuditLogEvents'>['data']>,\n {\n logType,\n orgSlug,\n outputKind,\n page,\n perPage,\n }: {\n logType: string\n outputKind: OutputKind\n orgSlug: string\n page: number\n perPage: number\n },\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(\n await outputAsJson(result, {\n logType,\n orgSlug,\n page,\n perPage,\n }),\n )\n }\n\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'markdown') {\n logger.log(\n await outputAsMarkdown(result.data, {\n logType,\n orgSlug,\n page,\n perPage,\n }),\n )\n return\n }\n\n await outputWithBlessed(result.data, orgSlug)\n}\n\nfunction formatResult(\n selectedRow?: SocketSdkSuccessResult<'getAuditLogEvents'>['data']['results'][number],\n keepQuotes = false,\n): string {\n if (!selectedRow) {\n return '(none)'\n }\n // Format the object with spacing but keep the payload compact because\n // that can contain just about anything and spread many lines.\n const obj = { ...selectedRow, payload: 'REPLACEME' }\n const json = JSON.stringify(obj, null, 2).replace(\n /\"payload\": \"REPLACEME\"/,\n `\"payload\": ${JSON.stringify(selectedRow.payload ?? {})}`,\n )\n if (keepQuotes) {\n return json\n }\n return json.replace(/^\\s*\"([^\"]+)?\"/gm, ' $1')\n}\n\nexport async function outputAsJson(\n auditLogs: CResult<SocketSdkSuccessResult<'getAuditLogEvents'>['data']>,\n {\n logType,\n orgSlug,\n page,\n perPage,\n }: {\n logType: string\n orgSlug: string\n page: number\n perPage: number\n },\n): Promise<string> {\n if (!auditLogs.ok) {\n return serializeResultJson(auditLogs)\n }\n\n return serializeResultJson({\n ok: true,\n data: {\n desc: 'Audit logs for given query',\n generated: constants.ENV.VITEST\n ? constants.REDACTED\n : new Date().toISOString(),\n logType,\n nextPage: auditLogs.data.nextPage,\n org: orgSlug,\n page,\n perPage,\n logs: auditLogs.data.results.map(log => {\n // Note: The subset is pretty arbitrary\n const {\n created_at,\n event_id,\n ip_address,\n type,\n user_agent,\n user_email,\n } = log\n return {\n event_id,\n created_at,\n ip_address,\n type,\n user_agent,\n user_email,\n }\n }),\n },\n })\n}\n\nexport async function outputAsMarkdown(\n auditLogs: SocketSdkSuccessResult<'getAuditLogEvents'>['data'],\n {\n logType,\n orgSlug,\n page,\n perPage,\n }: {\n orgSlug: string\n page: number\n perPage: number\n logType: string\n },\n): Promise<string> {\n try {\n const table = mdTable<any>(auditLogs.results, [\n 'event_id',\n 'created_at',\n 'type',\n 'user_email',\n 'ip_address',\n 'user_agent',\n ])\n\n return `\n# Socket Audit Logs\n\nThese are the Socket.dev audit logs as per requested query.\n- org: ${orgSlug}\n- type filter: ${logType || '(none)'}\n- page: ${page}\n- next page: ${auditLogs.nextPage}\n- per page: ${perPage}\n- generated: ${constants.ENV.VITEST ? constants.REDACTED : new Date().toISOString()}\n\n${table}\n`\n } catch (e) {\n process.exitCode = 1\n logger.fail(\n 'There was a problem converting the logs to Markdown, please try the `--json` flag',\n )\n debugFn('error', 'caught: markdown conversion error')\n debugDir('inspect', { error: e })\n return 'Failed to generate the markdown report'\n }\n}\n\nasync function outputWithBlessed(\n data: SocketSdkSuccessResult<'getAuditLogEvents'>['data'],\n orgSlug: string,\n) {\n const filteredLogs = data.results\n const formattedOutput = filteredLogs.map(logs => [\n logs.event_id ?? '',\n msAtHome(logs.created_at ?? ''),\n logs.type ?? '',\n logs.user_email ?? '',\n logs.ip_address ?? '',\n logs.user_agent ?? '',\n ])\n const headers = [\n ' Event id',\n ' Created at',\n ' Event type',\n ' User email',\n ' IP address',\n ' User agent',\n ]\n\n // Note: this temporarily takes over the terminal (just like `man` does).\n const ScreenWidget = /*@__PURE__*/ require('blessed/lib/widgets/screen.js')\n const screen: Widgets.Screen = new ScreenWidget({\n ...constants.blessedOptions,\n })\n // Register these keys first so you can always exit, even when it gets stuck\n // If we don't do this and the code crashes, the user must hard-kill the\n // node process just to exit it. That's very bad UX.\n // eslint-disable-next-line n/no-process-exit\n screen.key(['escape', 'q', 'C-c'], () => process.exit(0))\n\n const TableWidget = /*@__PURE__*/ require('blessed-contrib/lib/widget/table.js')\n const tipsBoxHeight = 1 // 1 row for tips box\n const detailsBoxHeight = 20 // bottom N rows for details box. 20 gives 4 lines for condensed payload before it scrolls out of view\n\n const maxWidths = headers.map(s => s.length + 1)\n formattedOutput.forEach(row => {\n row.forEach((str, i) => {\n maxWidths[i] = Math.max(str.length, maxWidths[i] ?? str.length)\n })\n })\n\n const table: any = new TableWidget({\n keys: 'true',\n fg: 'white',\n selectedFg: 'white',\n selectedBg: 'magenta',\n interactive: 'true',\n label: `Audit Logs for ${orgSlug}`,\n width: '100%',\n top: 0,\n bottom: detailsBoxHeight + tipsBoxHeight,\n border: {\n type: 'line',\n fg: 'cyan',\n },\n columnWidth: maxWidths, //[10, 30, 40, 25, 15, 200],\n // Note: spacing works as long as you don't reserve more than total width\n columnSpacing: 4,\n truncate: '_',\n })\n\n const BoxWidget = /*@__PURE__*/ require('blessed/lib/widgets/box.js')\n const tipsBox: Widgets.BoxElement = new BoxWidget({\n bottom: detailsBoxHeight, // sits just above the details box\n height: tipsBoxHeight,\n width: '100%',\n style: {\n fg: 'yellow',\n bg: 'black',\n },\n tags: true,\n content: `↑/↓: Move Enter: Select q/ESC: Quit`,\n })\n const detailsBox: Widgets.BoxElement = new BoxWidget({\n bottom: 0,\n height: detailsBoxHeight,\n width: '100%',\n border: {\n type: 'line',\n fg: 'cyan',\n },\n label: 'Details',\n content: formatResult(filteredLogs[0], true),\n style: {\n fg: 'white',\n },\n })\n\n table.setData({\n headers: headers,\n data: formattedOutput,\n })\n\n // allow control the table with the keyboard\n table.focus()\n\n // Stacking order: table (top), tipsBox (middle), detailsBox (bottom)\n screen.append(table)\n screen.append(tipsBox)\n screen.append(detailsBox)\n\n // Update details box when selection changes\n table.rows.on('select item', () => {\n const selectedIndex = table.rows.selected\n if (selectedIndex !== undefined && selectedIndex >= 0) {\n const selectedRow = filteredLogs[selectedIndex]\n detailsBox.setContent(formatResult(selectedRow))\n screen.render()\n }\n })\n\n screen.render()\n\n screen.key(['return'], () => {\n const selectedIndex = table.rows.selected\n screen.destroy()\n const selectedRow = formattedOutput[selectedIndex]\n ? formatResult(filteredLogs[selectedIndex], true)\n : '(none)'\n logger.log(`Last selection:\\n${selectedRow.trim()}`)\n })\n}\n","import { fetchAuditLog } from './fetch-audit-log.mts'\nimport { outputAuditLog } from './output-audit-log.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleAuditLog({\n logType,\n orgSlug,\n outputKind,\n page,\n perPage,\n}: {\n logType: string\n outputKind: OutputKind\n orgSlug: string\n page: number\n perPage: number\n}): Promise<void> {\n const auditLogs = await fetchAuditLog({\n logType,\n orgSlug,\n outputKind,\n page,\n perPage,\n })\n\n await outputAuditLog(auditLogs, {\n logType,\n orgSlug,\n outputKind,\n page,\n perPage,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleAuditLog } from './handle-audit-log.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'audit-log'\n\nconst description = 'Look up the audit log for an organization'\n\nconst hidden = false\n\nexport const cmdAuditLog = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input.\\nUse --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n page: {\n type: 'number',\n description: 'Result page to fetch',\n },\n perPage: {\n type: 'number',\n default: 30,\n description: 'Results per page - default is 30',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [FILTER]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n This feature requires an Enterprise Plan. To learn more about getting access\n to this feature and many more, please visit ${constants.SOCKET_WEBSITE_URL}/pricing\n\n The type FILTER arg is an enum. Defaults to any. It should be one of these:\n associateLabel, cancelInvitation, changeMemberRole, changePlanSubscriptionSeats,\n createApiToken, createLabel, deleteLabel, deleteLabelSetting, deleteReport,\n deleteRepository, disassociateLabel, joinOrganization, removeMember,\n resetInvitationLink, resetOrganizationSettingToDefault, rotateApiToken,\n sendInvitation, setLabelSettingToDefault, syncOrganization, transferOwnership,\n updateAlertTriage, updateApiTokenCommitter, updateApiTokenMaxQuota,\n updateApiTokenName', updateApiTokenScopes, updateApiTokenVisibility,\n updateLabelSetting, updateOrganizationSetting, upgradeOrganizationPlan\n\n The page arg should be a positive integer, offset 1. Defaults to 1.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} deleteReport --page 2 --per-page 10\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag, page, perPage } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const noLegacy = !cli.flags['type']\n\n let [typeFilter = ''] = cli.input\n\n typeFilter = String(typeFilter)\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: noLegacy,\n message: 'Legacy flags are no longer supported. See v1 migration guide.',\n fail: `received legacy flags`,\n },\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: /^[a-zA-Z]*$/.test(typeFilter),\n message: 'The filter must be an a-zA-Z string, it is an enum',\n fail: 'it was given but not a-zA-Z',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleAuditLog({\n orgSlug,\n outputKind,\n page: Number(page || 0),\n perPage: Number(perPage || 0),\n logType: typeFilter.charAt(0).toUpperCase() + typeFilter.slice(1),\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchCreateOrgFullScanConfigs = {\n branchName: string\n commitHash: string\n commitMessage: string\n committers: string\n pullRequest: number\n repoName: string\n}\n\nexport type FetchCreateOrgFullScanOptions = {\n cwd?: string | undefined\n defaultBranch?: boolean | undefined\n pendingHead?: boolean | undefined\n sdkOpts?: SetupSdkOptions | undefined\n tmp?: boolean | undefined\n}\n\nexport async function fetchCreateOrgFullScan(\n packagePaths: string[],\n orgSlug: string,\n config: FetchCreateOrgFullScanConfigs,\n options?: FetchCreateOrgFullScanOptions,\n): Promise<CResult<SocketSdkSuccessResult<'CreateOrgFullScan'>['data']>> {\n const {\n branchName,\n commitHash,\n commitMessage,\n committers,\n pullRequest,\n repoName,\n } = { __proto__: null, ...config } as FetchCreateOrgFullScanConfigs\n\n const {\n cwd = process.cwd(),\n defaultBranch,\n pendingHead,\n sdkOpts,\n tmp,\n } = { __proto__: null, ...options } as FetchCreateOrgFullScanOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(\n sockSdk.createOrgFullScan(orgSlug, packagePaths, cwd, {\n ...(branchName ? { branch: branchName } : {}),\n ...(commitHash ? { commit_hash: commitHash } : {}),\n ...(commitMessage ? { commit_message: commitMessage } : {}),\n ...(committers ? { committers } : {}),\n make_default_branch: String(defaultBranch),\n ...(pullRequest ? { pull_request: String(pullRequest) } : {}),\n repo: repoName,\n set_as_pending_head: String(pendingHead),\n tmp: String(tmp),\n }),\n { desc: 'to create a scan' },\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 { Spinner } from '@socketsecurity/registry/lib/spinner'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchSupportedScanFileNamesOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function fetchSupportedScanFileNames(\n options?: FetchSupportedScanFileNamesOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getReportSupportedFiles'>['data']>> {\n const { sdkOpts, spinner } = {\n __proto__: null,\n ...options,\n } as FetchSupportedScanFileNamesOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getSupportedScanFiles(), {\n desc: 'supported scan file types',\n spinner,\n })\n}\n","import { sendApiRequest } from '../../utils/api.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport type FinalizeTier1ScanOptions = {\n tier1_reachability_scan_id: string\n report_run_id: string\n}\n\n/**\n * Finalize a tier1 reachability scan.\n * - Associates the tier1 reachability scan metadata with the full scan.\n * - Sets the tier1 reachability scan to \"finalized\" state.\n */\nexport async function finalizeTier1Scan(\n tier1ReachabilityScanId: string,\n scanId: string,\n): Promise<CResult<unknown>> {\n // we do not use the SDK here because the tier1-reachability-scan/finalize is a hidden\n // endpoint that is not part of the OpenAPI specification.\n return await sendApiRequest('tier1-reachability-scan/finalize', {\n method: 'POST',\n body: {\n tier1_reachability_scan_id: tier1ReachabilityScanId,\n report_run_id: scanId,\n },\n })\n}\n","import { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { handleApiCallNoSpinner, queryApiSafeText } from '../../utils/api.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { SocketArtifact } from '../../utils/alert/artifact.mts'\nimport type { SetupSdkOptions } from '../../utils/sdk.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchScanData = {\n includeLicensePolicy?: boolean | undefined\n sdkOpts?: SetupSdkOptions | undefined\n}\n\n/**\n * This fetches all the relevant pieces of data to generate a report, given a\n * full scan ID.\n */\nexport async function fetchScanData(\n orgSlug: string,\n scanId: string,\n options?: FetchScanData | undefined,\n): Promise<\n CResult<{\n scan: SocketArtifact[]\n securityPolicy: SocketSdkSuccessResult<'getOrgSecurityPolicy'>['data']\n }>\n> {\n const { includeLicensePolicy, sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchScanData\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n let policyStatus = 'requested...'\n let scanStatus = 'requested...'\n let finishedFetching = false\n\n const { spinner } = constants\n\n function updateScan(desc: string) {\n scanStatus = desc\n updateProgress()\n }\n\n function updatePolicy(desc: string) {\n policyStatus = desc\n updateProgress()\n }\n\n function updateProgress() {\n if (finishedFetching) {\n spinner.stop()\n logger.info(\n `Scan result: ${scanStatus}. Security policy: ${policyStatus}.`,\n )\n } else {\n spinner.start(\n `Scan result: ${scanStatus}. Security policy: ${policyStatus}.`,\n )\n }\n }\n\n async function fetchScanResult(): Promise<CResult<SocketArtifact[]>> {\n const result = await queryApiSafeText(\n `orgs/${orgSlug}/full-scans/${encodeURIComponent(scanId)}${includeLicensePolicy ? '?include_license_details=true' : ''}`,\n )\n\n updateScan(`response received`)\n\n if (!result.ok) {\n return result\n }\n\n const ndJsonString = result.data\n\n // This is nd-json; each line is a json object.\n const lines = ndJsonString.split('\\n').filter(Boolean)\n let ok = true\n const data = lines.map(line => {\n try {\n return JSON.parse(line)\n } catch (e) {\n ok = false\n debugFn('error', 'caught: JSON.parse error')\n debugDir('inspect', { error: e, line })\n return\n }\n }) as unknown as SocketArtifact[]\n\n if (ok) {\n updateScan('success')\n return { ok: true, data }\n }\n\n updateScan('received invalid JSON response')\n\n return {\n ok: false,\n message: 'Invalid Socket API response',\n cause:\n 'The Socket API responded with at least one line that was not valid JSON. Please report if this persists.',\n }\n }\n\n async function fetchSecurityPolicy(): Promise<\n CResult<SocketSdkSuccessResult<'getOrgSecurityPolicy'>['data']>\n > {\n const result = await handleApiCallNoSpinner(\n sockSdk.getOrgSecurityPolicy(orgSlug),\n 'GetOrgSecurityPolicy',\n )\n\n updatePolicy('received policy')\n\n return result\n }\n\n updateProgress()\n\n const [scan, securityPolicy]: [\n CResult<SocketArtifact[]>,\n CResult<SocketSdkSuccessResult<'getOrgSecurityPolicy'>['data']>,\n ] = await Promise.all([\n fetchScanResult().catch(e => {\n updateScan('failure; unknown blocking error occurred')\n return {\n ok: false as const,\n message: 'Socket API error',\n cause: `Error requesting scan: ${e?.message || '(no error message found)'}${e?.cause ? ` (cause: ${e.cause})` : ''}`,\n }\n }),\n fetchSecurityPolicy().catch(e => {\n updatePolicy('failure; unknown blocking error occurred')\n return {\n ok: false as const,\n message: 'Socket API error',\n cause: `Error requesting policy: ${e?.message || '(no error message found)'}${e?.cause ? ` (cause: ${e.cause})` : ''}`,\n }\n }),\n ]).finally(() => {\n finishedFetching = true\n updateProgress()\n })\n\n if (!scan.ok) {\n return scan\n }\n if (!securityPolicy.ok) {\n return securityPolicy\n }\n\n if (!Array.isArray(scan.data)) {\n return {\n ok: false,\n message: 'Failed to fetch',\n cause: 'Was unable to fetch scan result, bailing',\n }\n }\n\n return {\n ok: true,\n data: {\n scan: scan.data satisfies SocketArtifact[],\n securityPolicy: securityPolicy.data,\n },\n }\n}\n","import constants from '../../constants.mts'\nimport { getSocketDevPackageOverviewUrlFromPurl } from '../../utils/socket-url.mts'\n\nimport type { FOLD_SETTING, REPORT_LEVEL } from './types.mts'\nimport type { CResult } from '../../types.mts'\nimport type { SocketArtifact } from '../../utils/alert/artifact.mts'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\ntype AlertKey = string\ntype EcoMap = Map<string, ReportLeafNode | PackageMap>\ntype FileMap = Map<string, ReportLeafNode | Map<AlertKey, ReportLeafNode>>\ntype PackageMap = Map<string, ReportLeafNode | VersionMap>\ntype VersionMap = Map<string, ReportLeafNode | FileMap>\n\nexport type ViolationsMap = Map<string, EcoMap>\n\nexport interface ShortScanReport {\n healthy: boolean\n}\nexport interface ScanReport {\n orgSlug: string\n scanId: string\n options: {\n fold: FOLD_SETTING\n reportLevel: REPORT_LEVEL\n }\n healthy: boolean\n alerts: ViolationsMap\n}\n\nexport type ReportLeafNode = {\n type: string\n policy: REPORT_LEVEL\n url: string\n manifest: string[]\n}\n\nconst UNKNOWN_VALUE = '<unknown>'\n\n// Note: The returned cResult will only be ok:false when the generation\n// failed. It won't reflect the healthy state.\nexport function generateReport(\n scan: SocketArtifact[],\n securityPolicy: SocketSdkSuccessResult<'getOrgSecurityPolicy'>['data'],\n {\n fold,\n orgSlug,\n reportLevel,\n scanId,\n short,\n spinner,\n }: {\n fold: FOLD_SETTING\n orgSlug: string\n reportLevel: REPORT_LEVEL\n scanId: string\n short?: boolean | undefined\n spinner?: Spinner | undefined\n },\n): CResult<ScanReport | { healthy: boolean }> {\n const now = Date.now()\n\n spinner?.start('Generating report...')\n\n // Create an object that includes:\n // healthy: boolean\n // worst violation level;\n // per eco\n // per package\n // per version\n // per offending file\n // reported issue -> policy action\n\n // In the context of a report;\n // - the alert.severity is irrelevant\n // - the securityPolicyDefault is irrelevant\n // - the report defaults to healthy:true with no alerts\n // - the appearance of an alert will trigger the policy action;\n // - error: healthy will end up as false, add alerts to report\n // - warn: healthy unchanged, add alerts to report\n // - monitor/ignore: no action\n // - defer: unknown (no action)\n\n // Note: the server will emit alerts for license policy violations but\n // those are only included if you set the flag when requesting the scan\n // data. The alerts map to a single security policy key that determines\n // what to do with any violation, regardless of the concrete license.\n // That rule is called \"License Policy Violation\".\n // The license policy part is implicitly handled here. Either they are\n // included and may show up, or they are not and won't show up.\n\n const violations = new Map()\n\n let healthy = true\n\n const securityRules = securityPolicy.securityPolicyRules\n if (securityRules) {\n // Note: reportLevel: error > warn > monitor > ignore > defer\n scan.forEach(artifact => {\n const {\n alerts,\n name: pkgName = UNKNOWN_VALUE,\n type: ecosystem,\n version = UNKNOWN_VALUE,\n } = artifact\n\n alerts?.forEach(\n (alert: NonNullable<SocketArtifact['alerts']>[number]) => {\n const alertName = alert.type as keyof typeof securityRules // => policy[type]\n const action = securityRules[alertName]?.action || ''\n switch (action) {\n case constants.REPORT_LEVEL_ERROR: {\n healthy = false\n if (!short) {\n addAlert(\n artifact,\n violations,\n fold,\n ecosystem,\n pkgName,\n version,\n alert,\n action,\n )\n }\n break\n }\n case constants.REPORT_LEVEL_WARN: {\n if (!short && reportLevel !== constants.REPORT_LEVEL_ERROR) {\n addAlert(\n artifact,\n violations,\n fold,\n ecosystem,\n pkgName,\n version,\n alert,\n action,\n )\n }\n break\n }\n case constants.REPORT_LEVEL_MONITOR: {\n if (\n !short &&\n reportLevel !== constants.REPORT_LEVEL_WARN &&\n reportLevel !== constants.REPORT_LEVEL_ERROR\n ) {\n addAlert(\n artifact,\n violations,\n fold,\n ecosystem,\n pkgName,\n version,\n alert,\n action,\n )\n }\n break\n }\n\n case constants.REPORT_LEVEL_IGNORE: {\n if (\n !short &&\n reportLevel !== constants.REPORT_LEVEL_MONITOR &&\n reportLevel !== constants.REPORT_LEVEL_WARN &&\n reportLevel !== constants.REPORT_LEVEL_ERROR\n ) {\n addAlert(\n artifact,\n violations,\n fold,\n ecosystem,\n pkgName,\n version,\n alert,\n action,\n )\n }\n break\n }\n\n case constants.REPORT_LEVEL_DEFER: {\n // Not sure but ignore for now. Defer to later ;)\n if (!short && reportLevel === constants.REPORT_LEVEL_DEFER) {\n addAlert(\n artifact,\n violations,\n fold,\n ecosystem,\n pkgName,\n version,\n alert,\n action,\n )\n }\n break\n }\n\n default: {\n // This value was not emitted from the Socket API at the time of writing.\n }\n }\n },\n )\n })\n }\n\n spinner?.successAndStop(`Generated reported in ${Date.now() - now} ms`)\n\n if (short) {\n return {\n ok: true,\n data: { healthy },\n }\n }\n\n const report = {\n healthy,\n orgSlug,\n scanId,\n options: { fold, reportLevel },\n alerts: violations,\n }\n\n if (!healthy) {\n return {\n ok: true,\n message:\n 'The report contains at least one alert that violates the policies set by your organization',\n data: report,\n }\n }\n\n return {\n ok: true,\n data: report,\n }\n}\n\nfunction createLeaf(\n art: SocketArtifact,\n alert: NonNullable<SocketArtifact['alerts']>[number],\n policyAction: REPORT_LEVEL,\n): ReportLeafNode {\n const leaf: ReportLeafNode = {\n type: alert.type,\n policy: policyAction,\n url: getSocketDevPackageOverviewUrlFromPurl(art),\n manifest: art.manifestFiles?.map(o => o.file) ?? [],\n }\n return leaf\n}\n\nfunction addAlert(\n art: SocketArtifact,\n violations: ViolationsMap,\n fold: FOLD_SETTING,\n ecosystem: string,\n pkgName: string,\n version: string,\n alert: NonNullable<SocketArtifact['alerts']>[number],\n policyAction: REPORT_LEVEL,\n): void {\n if (!violations.has(ecosystem)) {\n violations.set(ecosystem, new Map())\n }\n const ecoMap: EcoMap = violations.get(ecosystem)!\n if (fold === constants.FOLD_SETTING_PKG) {\n const existing = ecoMap.get(pkgName) as ReportLeafNode | undefined\n if (!existing || isStricterPolicy(existing.policy, policyAction)) {\n ecoMap.set(pkgName, createLeaf(art, alert, policyAction))\n }\n } else {\n if (!ecoMap.has(pkgName)) {\n ecoMap.set(pkgName, new Map())\n }\n const pkgMap = ecoMap.get(pkgName) as PackageMap\n if (fold === constants.FOLD_SETTING_VERSION) {\n const existing = pkgMap.get(version) as ReportLeafNode | undefined\n if (!existing || isStricterPolicy(existing.policy, policyAction)) {\n pkgMap.set(version, createLeaf(art, alert, policyAction))\n }\n } else {\n if (!pkgMap.has(version)) {\n pkgMap.set(version, new Map())\n }\n const file = alert.file || UNKNOWN_VALUE\n const verMap = pkgMap.get(version) as VersionMap\n\n if (fold === constants.FOLD_SETTING_FILE) {\n const existing = verMap.get(file) as ReportLeafNode | undefined\n if (!existing || isStricterPolicy(existing.policy, policyAction)) {\n verMap.set(file, createLeaf(art, alert, policyAction))\n }\n } else {\n if (!verMap.has(file)) {\n verMap.set(file, new Map())\n }\n const key = `${alert.type} at ${alert.start}:${alert.end}`\n const fileMap: FileMap = verMap.get(file) as FileMap\n const existing = fileMap.get(key) as ReportLeafNode | undefined\n if (!existing || isStricterPolicy(existing.policy, policyAction)) {\n fileMap.set(key, createLeaf(art, alert, policyAction))\n }\n }\n }\n }\n}\n\nfunction isStricterPolicy(was: REPORT_LEVEL, is: REPORT_LEVEL): boolean {\n // error > warn > monitor > ignore > defer > {unknown}\n if (was === constants.REPORT_LEVEL_ERROR) {\n return false\n }\n if (is === constants.REPORT_LEVEL_ERROR) {\n return true\n }\n if (was === constants.REPORT_LEVEL_WARN) {\n return false\n }\n if (is === constants.REPORT_LEVEL_WARN) {\n return false\n }\n if (was === constants.REPORT_LEVEL_MONITOR) {\n return false\n }\n if (is === constants.REPORT_LEVEL_MONITOR) {\n return false\n }\n if (was === constants.REPORT_LEVEL_IGNORE) {\n return false\n }\n if (is === constants.REPORT_LEVEL_IGNORE) {\n return false\n }\n if (was === constants.REPORT_LEVEL_DEFER) {\n return false\n }\n if (is === constants.REPORT_LEVEL_DEFER) {\n return false\n }\n // unreachable?\n return false\n}\n","import fs from 'node:fs/promises'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { generateReport } from './generate-report.mts'\nimport constants, { EXT_JSON, JSON, TEXT } from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mapToObject } from '../../utils/map-to-object.mts'\nimport { mdTable } from '../../utils/markdown.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\nimport { walkNestedMap } from '../../utils/walk-nested-map.mts'\n\nimport type { ReportLeafNode, ScanReport } from './generate-report.mts'\nimport type { FOLD_SETTING, REPORT_LEVEL } from './types.mts'\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketArtifact } from '../../utils/alert/artifact.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type OutputScanReportConfig = {\n orgSlug: string\n scanId: string\n includeLicensePolicy: boolean\n outputKind: OutputKind\n filepath: string\n fold: FOLD_SETTING\n reportLevel: REPORT_LEVEL\n short: boolean\n}\n\nexport async function outputScanReport(\n result: CResult<{\n scan: SocketArtifact[]\n securityPolicy: SocketSdkSuccessResult<'getOrgSecurityPolicy'>['data']\n }>,\n {\n filepath,\n fold,\n includeLicensePolicy,\n orgSlug,\n outputKind,\n reportLevel,\n scanId,\n short,\n }: OutputScanReportConfig,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (!result.ok) {\n if (outputKind === JSON) {\n logger.log(serializeResultJson(result))\n return\n }\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const scanReport = generateReport(\n result.data.scan,\n result.data.securityPolicy,\n {\n orgSlug,\n scanId,\n fold,\n reportLevel,\n short,\n spinner: constants.spinner,\n },\n )\n\n if (!scanReport.ok) {\n // Note: this means generation failed, it does not reflect the healthy state\n process.exitCode = scanReport.code ?? 1\n\n // If report generation somehow failed then .data should not be set.\n if (outputKind === JSON) {\n logger.log(serializeResultJson(scanReport))\n return\n }\n logger.fail(failMsgWithBadge(scanReport.message, scanReport.cause))\n return\n }\n\n // I don't think we emit the default error message with banner for an unhealhty report, do we?\n // if (!scanReport.data.healhty) {\n // logger.fail(failMsgWithBadge(scanReport.message, scanReport.cause))\n // return\n // }\n\n if (\n outputKind === JSON ||\n (outputKind === TEXT && filepath && filepath.endsWith(EXT_JSON))\n ) {\n const json = short\n ? serializeResultJson(scanReport)\n : toJsonReport(scanReport.data as ScanReport, includeLicensePolicy)\n\n if (filepath && filepath !== '-') {\n logger.log('Writing json report to', filepath)\n return await fs.writeFile(filepath, json)\n }\n\n logger.log(json)\n return\n }\n\n if (outputKind === 'markdown' || (filepath && filepath.endsWith('.md'))) {\n const md = short\n ? `healthy = ${scanReport.data.healthy}`\n : toMarkdownReport(\n scanReport.data as ScanReport, // not short so must be regular report\n includeLicensePolicy,\n )\n\n if (filepath && filepath !== '-') {\n logger.log('Writing markdown report to', filepath)\n return await fs.writeFile(filepath, md)\n }\n\n logger.log(md)\n logger.log('')\n return\n }\n\n if (short) {\n logger.log(scanReport.data.healthy ? 'OK' : 'ERR')\n } else {\n logger.dir(scanReport.data, { depth: null })\n }\n}\n\nexport function toJsonReport(\n report: ScanReport,\n includeLicensePolicy?: boolean | undefined,\n): string {\n const obj = mapToObject(report.alerts)\n\n const newReport = {\n includeLicensePolicy,\n ...report,\n alerts: obj,\n }\n\n return serializeResultJson({\n ok: true,\n data: newReport,\n })\n}\n\nexport function toMarkdownReport(\n report: ScanReport,\n includeLicensePolicy?: boolean | undefined,\n): string {\n const reportLevel = report.options.reportLevel\n\n const alertFolding =\n report.options.fold === constants.FOLD_SETTING_NONE\n ? 'none'\n : `up to ${report.options.fold}`\n\n const flatData = Array.from(walkNestedMap(report.alerts)).map(\n ({ keys, value }: { keys: string[]; value: ReportLeafNode }) => {\n const { manifest, policy, type, url } = value\n return {\n 'Alert Type': type,\n Package: keys[1] || '<unknown>',\n 'Introduced by': keys[2] || '<unknown>',\n url,\n 'Manifest file': joinAnd(manifest),\n Policy: policy,\n }\n },\n )\n\n const minPolicyLevel =\n reportLevel === constants.REPORT_LEVEL_DEFER ? 'everything' : reportLevel\n\n const md =\n `\n# Scan Policy Report\n\nThis report tells you whether the results of a Socket scan results violate the\nsecurity${includeLicensePolicy ? ' or license' : ''} policy set by your organization.\n\n## Health status\n\n${\n report.healthy\n ? `The scan *PASSES* all requirements set by your security${includeLicensePolicy ? ' and license' : ''} policy.`\n : 'The scan *VIOLATES* one or more policies set to the \"error\" level.'\n}\n\n## Settings\n\nConfiguration used to generate this report:\n\n- Organization: ${report.orgSlug}\n- Scan ID: ${report.scanId}\n- Alert folding: ${alertFolding}\n- Minimal policy level for alert to be included in report: ${minPolicyLevel}\n- Include license alerts: ${includeLicensePolicy ? 'yes' : 'no'}\n\n## Alerts\n\n${\n report.alerts.size\n ? `All the alerts from the scan with a policy set to at least \"${reportLevel}\".`\n : `The scan contained no alerts with a policy set to at least \"${reportLevel}\".`\n}\n\n${\n !report.alerts.size\n ? ''\n : mdTable(flatData, [\n 'Policy',\n 'Alert Type',\n 'Package',\n 'Introduced by',\n 'url',\n 'Manifest file',\n ])\n}\n `.trim() + '\\n'\n\n return md\n}\n","import { fetchScanData } from './fetch-report-data.mts'\nimport { outputScanReport } from './output-scan-report.mts'\n\nimport type { FOLD_SETTING, REPORT_LEVEL } from './types.mts'\nimport type { OutputKind } from '../../types.mts'\n\nexport type HandleScanReportConfig = {\n orgSlug: string\n scanId: string\n includeLicensePolicy: boolean\n outputKind: OutputKind\n filepath: string\n fold: FOLD_SETTING\n reportLevel: REPORT_LEVEL\n short: boolean\n}\n\nexport async function handleScanReport({\n filepath,\n fold,\n includeLicensePolicy,\n orgSlug,\n outputKind,\n reportLevel,\n scanId,\n short,\n}: HandleScanReportConfig): Promise<void> {\n const scanDataCResult = await fetchScanData(orgSlug, scanId, {\n includeLicensePolicy,\n })\n\n await outputScanReport(scanDataCResult, {\n filepath,\n fold,\n scanId: scanId,\n includeLicensePolicy,\n orgSlug,\n outputKind,\n reportLevel,\n short,\n })\n}\n","import open from 'open'\nimport terminalLink from 'terminal-link'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { confirm } from '@socketsecurity/registry/lib/prompts'\n\nimport constants from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type CreateNewScanOptions = {\n interactive?: boolean | undefined\n outputKind?: OutputKind | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function outputCreateNewScan(\n result: CResult<SocketSdkSuccessResult<'CreateOrgFullScan'>['data']>,\n options?: CreateNewScanOptions | undefined,\n) {\n const {\n interactive = false,\n outputKind = 'text',\n spinner = constants.spinner,\n } = { __proto__: null, ...options } as CreateNewScanOptions\n\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n const wasSpinning = !!spinner?.isSpinning\n\n spinner?.stop()\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n if (wasSpinning) {\n spinner.start()\n }\n return\n }\n\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n if (wasSpinning) {\n spinner.start()\n }\n return\n }\n\n if (!result.data.id) {\n logger.fail('Did not receive a scan ID from the API.')\n process.exitCode = 1\n }\n\n if (outputKind === 'markdown') {\n logger.log('# Create New Scan')\n logger.log('')\n if (result.data.id) {\n logger.log(\n `A [new Scan](${result.data.html_report_url}) was created with ID: ${result.data.id}`,\n )\n logger.log('')\n } else {\n logger.log(\n `The server did not return a Scan ID while trying to create a new Scan. This could be an indication something went wrong.`,\n )\n }\n logger.log('')\n if (wasSpinning) {\n spinner.start()\n }\n return\n }\n\n logger.log('')\n logger.success('Scan completed successfully!')\n\n const htmlReportUrl = result.data.html_report_url\n if (htmlReportUrl) {\n logger.log(`View report at: ${terminalLink(htmlReportUrl, htmlReportUrl)}`)\n } else {\n logger.log('No report available.')\n }\n\n if (\n interactive &&\n (await confirm(\n {\n message: 'Would you like to open it in your browser?',\n default: false,\n },\n { spinner },\n ))\n ) {\n await open(`${result.data.html_report_url}`)\n }\n\n if (wasSpinning) {\n spinner.start()\n }\n}\n","import path from 'node:path'\n\nimport terminalLink from 'terminal-link'\n\nimport constants from '../../constants.mts'\nimport { handleApiCall } from '../../utils/api.mts'\nimport {\n extractTier1ReachabilityScanId,\n spawnCoana,\n} from '../../utils/coana.mts'\nimport { hasEnterpriseOrgPlan } from '../../utils/organization.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\nimport { fetchOrganization } from '../organization/fetch-organization-list.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { PURL_Type } from '../../utils/ecosystem.mts'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nexport type ReachabilityOptions = {\n reachAnalysisTimeout: number\n reachAnalysisMemoryLimit: number\n reachDisableAnalytics: boolean\n reachEcosystems: PURL_Type[]\n reachExcludePaths: string[]\n reachSkipCache: boolean\n}\n\nexport type ReachabilityAnalysisOptions = {\n branchName?: string | undefined\n cwd?: string | undefined\n orgSlug?: string | undefined\n packagePaths?: string[] | undefined\n reachabilityOptions: ReachabilityOptions\n repoName?: string | undefined\n spinner?: Spinner | undefined\n uploadManifests?: boolean | undefined\n}\n\nexport type ReachabilityAnalysisResult = {\n reachabilityReport: string\n tier1ReachabilityScanId: string | undefined\n}\n\nexport async function performReachabilityAnalysis(\n options?: ReachabilityAnalysisOptions | undefined,\n): Promise<CResult<ReachabilityAnalysisResult>> {\n const {\n branchName,\n cwd = process.cwd(),\n orgSlug,\n packagePaths,\n reachabilityOptions,\n repoName,\n spinner,\n uploadManifests = true,\n } = { __proto__: null, ...options } as ReachabilityAnalysisOptions\n\n // Check if user has enterprise plan for reachability analysis\n const orgsCResult = await fetchOrganization()\n if (!orgsCResult.ok) {\n return {\n ok: false,\n message: 'Unable to verify plan permissions',\n cause:\n 'Failed to fetch organization information to verify enterprise plan access',\n }\n }\n\n const { organizations } = orgsCResult.data\n\n if (!hasEnterpriseOrgPlan(organizations)) {\n return {\n ok: false,\n message: 'Tier 1 Reachability analysis requires an enterprise plan',\n cause: `Please ${terminalLink('upgrade your plan', 'https://socket.dev/pricing')}. This feature is only available for organizations with an enterprise plan.`,\n }\n }\n\n let tarHash: string | undefined\n\n if (uploadManifests && orgSlug && packagePaths) {\n // Setup SDK for uploading manifests\n const sockSdkCResult = await setupSdk()\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n\n const sockSdk = sockSdkCResult.data\n\n const wasSpinning = !!spinner?.isSpinning\n\n // Exclude any .socket.facts.json files that happen to be in the scan\n // folder before the analysis was run.\n const filepathsToUpload = packagePaths.filter(\n p =>\n path.basename(p).toLowerCase() !== constants.DOT_SOCKET_DOT_FACTS_JSON,\n )\n\n spinner?.start('Uploading manifests for reachability analysis...')\n\n const uploadCResult = await handleApiCall(\n sockSdk.uploadManifestFiles(orgSlug, filepathsToUpload),\n {\n desc: 'upload manifests',\n spinner,\n },\n )\n\n spinner?.stop()\n\n if (!uploadCResult.ok) {\n if (wasSpinning) {\n spinner.start()\n }\n return uploadCResult\n }\n\n tarHash = (uploadCResult.data as { tarHash?: string })?.tarHash\n if (!tarHash) {\n if (wasSpinning) {\n spinner.start()\n }\n return {\n ok: false,\n message: 'Failed to get manifest tar hash',\n cause: 'Server did not return a tar hash for the uploaded manifests',\n }\n }\n\n spinner?.start()\n spinner?.success(`Manifests uploaded successfully. Tar hash: ${tarHash}`)\n }\n\n spinner?.start()\n spinner?.infoAndStop('Running reachability analysis with Coana...')\n\n // Build Coana arguments.\n const coanaArgs = [\n 'run',\n cwd,\n '--output-dir',\n cwd,\n '--socket-mode',\n constants.DOT_SOCKET_DOT_FACTS_JSON,\n '--disable-report-submission',\n ...(reachabilityOptions.reachAnalysisTimeout\n ? ['--analysis-timeout', `${reachabilityOptions.reachAnalysisTimeout}`]\n : []),\n ...(reachabilityOptions.reachAnalysisMemoryLimit\n ? ['--memory-limit', `${reachabilityOptions.reachAnalysisMemoryLimit}`]\n : []),\n ...(reachabilityOptions.reachDisableAnalytics\n ? ['--disable-analytics-sharing']\n : []),\n ...(tarHash\n ? ['--run-without-docker', '--manifests-tar-hash', tarHash]\n : []),\n // Empty reachEcosystems implies scanning all ecosystems.\n ...(reachabilityOptions.reachEcosystems.length\n ? ['--purl-types', ...reachabilityOptions.reachEcosystems]\n : []),\n ...(reachabilityOptions.reachExcludePaths.length\n ? ['--exclude-dirs', ...reachabilityOptions.reachExcludePaths]\n : []),\n ...(reachabilityOptions.reachSkipCache ? ['--skip-cache-usage'] : []),\n ]\n\n // Build environment variables.\n const coanaEnv: NodeJS.ProcessEnv = {}\n // do not pass default repo and branch name to coana to avoid mixing\n // buckets (cached configuration) from projects that are likely very different.\n if (repoName && repoName !== constants.SOCKET_DEFAULT_REPOSITORY) {\n coanaEnv['SOCKET_REPO_NAME'] = repoName\n }\n if (branchName && branchName !== constants.SOCKET_DEFAULT_BRANCH) {\n coanaEnv['SOCKET_BRANCH_NAME'] = branchName\n }\n\n // Run Coana with the manifests tar hash.\n const coanaResult = await spawnCoana(coanaArgs, orgSlug, {\n cwd,\n env: coanaEnv,\n spinner,\n stdio: 'inherit',\n })\n\n const wasSpinning = !!spinner?.isSpinning\n if (wasSpinning) {\n spinner.start()\n }\n\n return coanaResult.ok\n ? {\n ok: true,\n data: {\n // Use the DOT_SOCKET_DOT_FACTS_JSON file for the scan.\n reachabilityReport: constants.DOT_SOCKET_DOT_FACTS_JSON,\n tier1ReachabilityScanId: extractTier1ReachabilityScanId(\n constants.DOT_SOCKET_DOT_FACTS_JSON,\n ),\n },\n }\n : coanaResult\n}\n","// The point here is to attempt to detect the various supported manifest files\n// the CLI can generate. This would be environments that we can't do server side\n\nimport { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport { debugLog } from '@socketsecurity/registry/lib/debug'\n\nimport type { SocketJson } from '../../utils/socket-json.mts'\n\nexport interface GeneratableManifests {\n cdxgen: boolean\n count: number\n conda: boolean\n gradle: boolean\n sbt: boolean\n}\n\nexport async function detectManifestActions(\n // Passing in null means we attempt detection for every supported language\n // regardless of local socket.json status. Sometimes we want that.\n sockJson: SocketJson | null,\n cwd = process.cwd(),\n): Promise<GeneratableManifests> {\n const output = {\n cdxgen: false, // TODO\n count: 0,\n conda: false,\n gradle: false,\n sbt: false,\n }\n\n if (sockJson?.defaults?.manifest?.sbt?.disabled) {\n debugLog(\n 'notice',\n '[DEBUG] - sbt auto-detection is disabled in socket.json',\n )\n } else if (existsSync(path.join(cwd, 'build.sbt'))) {\n debugLog('notice', '[DEBUG] - Detected a Scala sbt build file')\n\n output.sbt = true\n output.count += 1\n }\n\n if (sockJson?.defaults?.manifest?.gradle?.disabled) {\n debugLog(\n 'notice',\n '[DEBUG] - gradle auto-detection is disabled in socket.json',\n )\n } else if (existsSync(path.join(cwd, 'gradlew'))) {\n debugLog('notice', '[DEBUG] - Detected a gradle build file')\n output.gradle = true\n output.count += 1\n }\n\n if (sockJson?.defaults?.manifest?.conda?.disabled) {\n debugLog(\n 'notice',\n '[DEBUG] - conda auto-detection is disabled in socket.json',\n )\n } else {\n const envyml = path.join(cwd, 'environment.yml')\n const hasEnvyml = existsSync(envyml)\n const envyaml = path.join(cwd, 'environment.yaml')\n const hasEnvyaml = !hasEnvyml && existsSync(envyaml)\n if (hasEnvyml || hasEnvyaml) {\n debugLog('notice', '[DEBUG] - Detected an environment.yml Conda file')\n output.conda = true\n output.count += 1\n }\n }\n\n return output\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants.mts'\n\nexport async function convertGradleToMaven({\n bin,\n cwd,\n gradleOpts,\n verbose,\n}: {\n bin: string\n cwd: string\n verbose: boolean\n gradleOpts: string[]\n}) {\n // TODO: Implement json/md.\n\n // Note: use resolve because the bin could be an absolute path, away from cwd\n // TODO: what about $PATH resolved commands? (`gradlew` without dir prefix)\n const rBin = path.resolve(cwd, bin)\n const binExists = fs.existsSync(rBin)\n const cwdExists = fs.existsSync(cwd)\n\n logger.group('gradle2maven:')\n logger.info(`- executing: \\`${rBin}\\``)\n if (!binExists) {\n logger.warn(\n `Warning: It appears the executable could not be found. An error might be printed later because of that.`,\n )\n }\n logger.info(`- src dir: \\`${cwd}\\``)\n if (!cwdExists) {\n logger.warn(\n `Warning: It appears the src dir could not be found. An error might be printed later because of that.`,\n )\n }\n logger.groupEnd()\n\n try {\n // Run gradlew with the init script we provide which should yield zero or more\n // pom files. We have to figure out where to store those pom files such that\n // we can upload them and predict them through the GitHub API. We could do a\n // .socket folder. We could do a socket.pom.gz with all the poms, although\n // I'd prefer something plain-text if it is to be committed.\n // Note: init.gradle will be exported by .config/rollup.dist.config.mjs\n const initLocation = path.join(constants.distPath, 'init.gradle')\n const commandArgs = ['--init-script', initLocation, ...gradleOpts, 'pom']\n if (verbose) {\n logger.log('[VERBOSE] Executing:', [bin], ', args:', commandArgs)\n }\n logger.log(`Converting gradle to maven from \\`${bin}\\` on \\`${cwd}\\` ...`)\n const output = await execGradleWithSpinner(rBin, commandArgs, cwd)\n if (verbose) {\n logger.group('[VERBOSE] gradle stdout:')\n logger.log(output)\n logger.groupEnd()\n }\n if (output.code) {\n process.exitCode = 1\n logger.fail(`Gradle exited with exit code ${output.code}`)\n // (In verbose mode, stderr was printed above, no need to repeat it)\n if (!verbose) {\n logger.group('stderr:')\n logger.error(output.stderr)\n logger.groupEnd()\n }\n return\n }\n logger.success('Executed gradle successfully')\n logger.log('Reported exports:')\n output.stdout.replace(\n /^POM file copied to: (.*)/gm,\n (_all: string, fn: string) => {\n logger.log('- ', fn)\n return fn\n },\n )\n logger.log('')\n logger.log(\n 'Next step is to generate a Scan by running the `socket scan create` command on the same directory',\n )\n } catch (e) {\n process.exitCode = 1\n logger.fail(\n 'There was an unexpected error while generating manifests' +\n (verbose ? '' : ' (use --verbose for details)'),\n )\n if (verbose) {\n logger.group('[VERBOSE] error:')\n logger.log(e)\n logger.groupEnd()\n }\n }\n}\n\nasync function execGradleWithSpinner(\n bin: string,\n commandArgs: string[],\n cwd: string,\n): Promise<{ code: number; stdout: string; stderr: string }> {\n const { spinner } = constants\n\n let pass = false\n try {\n logger.info(\n '(Running gradle can take a while, it depends on how long gradlew has to run)',\n )\n logger.info(\n '(It will show no output, you can use --verbose to see its output)',\n )\n spinner.start(`Running gradlew...`)\n\n const output = await spawn(bin, commandArgs, {\n // We can pipe the output through to have the user see the result\n // of running gradlew, but then we can't (easily) gather the output\n // to discover the generated files... probably a flag we should allow?\n // stdio: isDebug() ? 'inherit' : undefined,\n cwd,\n })\n\n pass = true\n const { code, stderr, stdout } = output\n return { code, stdout, stderr }\n } finally {\n if (pass) {\n spinner.successAndStop('Gracefully completed gradlew execution.')\n } else {\n spinner.failAndStop('There was an error while trying to run gradlew.')\n }\n }\n}\n","import { safeReadFile } from '@socketsecurity/registry/lib/fs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants.mts'\n\nexport async function convertSbtToMaven({\n bin,\n cwd,\n out,\n sbtOpts,\n verbose,\n}: {\n bin: string\n cwd: string\n out: string\n sbtOpts: string[]\n verbose: boolean\n}) {\n // TODO: Implement json/md.\n\n const { spinner } = constants\n\n logger.group('sbt2maven:')\n logger.info(`- executing: \\`${bin}\\``)\n logger.info(`- src dir: \\`${cwd}\\``)\n logger.groupEnd()\n\n try {\n spinner.start(`Converting sbt to maven from \\`${bin}\\` on \\`${cwd}\\`...`)\n\n // Run sbt with the init script we provide which should yield zero or more\n // pom files. We have to figure out where to store those pom files such that\n // we can upload them and predict them through the GitHub API. We could do a\n // .socket folder. We could do a socket.pom.gz with all the poms, although\n // I'd prefer something plain-text if it is to be committed.\n const output = await spawn(bin, ['makePom', ...sbtOpts], { cwd })\n\n spinner.stop()\n\n if (verbose) {\n logger.group('[VERBOSE] sbt stdout:')\n logger.log(output)\n logger.groupEnd()\n }\n if (output.stderr) {\n process.exitCode = 1\n logger.fail('There were errors while running sbt')\n // (In verbose mode, stderr was printed above, no need to repeat it)\n if (!verbose) {\n logger.group('[VERBOSE] stderr:')\n logger.error(output.stderr)\n logger.groupEnd()\n }\n return\n }\n const poms: string[] = []\n output.stdout.replace(/Wrote (.*?.pom)\\n/g, (_all: string, fn: string) => {\n poms.push(fn)\n return fn\n })\n if (!poms.length) {\n process.exitCode = 1\n logger.fail(\n 'There were no errors from sbt but it seems to not have generated any poms either',\n )\n return\n }\n // Move the pom file to ...? initial cwd? loc will be an absolute path, or dump to stdout\n // TODO: What do we do with multiple output files? Do we want to dump them to stdout? Raw or with separators or ?\n // TODO: Maybe we can add an option to target a specific file to dump to stdout.\n if (out === '-' && poms.length === 1) {\n logger.log('Result:\\n```')\n logger.log(await safeReadFile(poms[0]!))\n logger.log('```')\n logger.success(`OK`)\n } else if (out === '-') {\n process.exitCode = 1\n logger.error('')\n logger.fail(\n 'Requested output target was stdout but there are multiple generated files',\n )\n logger.error('')\n poms.forEach(fn => logger.info('-', fn))\n if (poms.length > 10) {\n logger.error('')\n logger.fail(\n 'Requested output target was stdout but there are multiple generated files',\n )\n }\n logger.error('')\n logger.info('Exiting now...')\n return\n } else {\n // if (verbose) {\n // logger.log(\n // `Moving manifest file from \\`${loc.replace(/^\\/home\\/[^/]*?\\//, '~/')}\\` to \\`${out}\\``\n // )\n // } else {\n // logger.log('Moving output pom file')\n // }\n // TODO: Do we prefer fs-extra? Renaming can be gnarly on windows and fs-extra's version is better.\n // await renamep(loc, out)\n logger.success(`Generated ${poms.length} pom files`)\n poms.forEach(fn => logger.log('-', fn))\n logger.success(`OK`)\n }\n } catch (e) {\n process.exitCode = 1\n spinner.stop()\n logger.fail(\n 'There was an unexpected error while running this' +\n (verbose ? '' : ' (use --verbose for details)'),\n )\n if (verbose) {\n logger.group('[VERBOSE] error:')\n logger.log(e)\n logger.groupEnd()\n }\n }\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { stripAnsi } from '@socketsecurity/registry/lib/strings'\n\nimport type { CResult } from '../../types.mts'\n\nfunction prepareContent(content: string): string {\n return stripAnsi(content.trim())\n}\n\nexport async function convertCondaToRequirements(\n filename: string,\n cwd: string,\n verbose: boolean,\n): Promise<CResult<{ content: string; pip: string }>> {\n let content: string\n if (filename === '-') {\n if (verbose) {\n logger.info(`[VERBOSE] reading input from stdin`)\n }\n\n const strings: string[] = []\n content = await new Promise((resolve, reject) => {\n process.stdin.on('data', chunk => {\n const input = chunk.toString()\n strings.push(input)\n })\n process.stdin.on('end', () => {\n resolve(prepareContent(strings.join('')))\n })\n process.stdin.on('error', e => {\n if (verbose) {\n logger.error('Unexpected error while reading from stdin:', e)\n }\n reject(e)\n })\n process.stdin.on('close', () => {\n if (strings.length) {\n if (verbose) {\n logger.error(\n 'warning: stdin closed explicitly with some data received',\n )\n }\n resolve(prepareContent(strings.join('')))\n } else {\n if (verbose) {\n logger.error('stdin closed explicitly without data received')\n }\n reject(new Error('No data received from stdin'))\n }\n })\n })\n\n if (!content) {\n return {\n ok: false,\n message: 'Manifest Generation Failed',\n cause: 'No data received from stdin',\n }\n }\n } else {\n const filepath = path.join(cwd, filename)\n\n if (verbose) {\n logger.info(`[VERBOSE] target: ${filepath}`)\n }\n\n if (!existsSync(filepath)) {\n return {\n ok: false,\n message: 'Manifest Generation Failed',\n cause: `The file was not found at ${filepath}`,\n }\n }\n\n content = readFileSync(filepath, 'utf8')\n\n if (!content) {\n return {\n ok: false,\n message: 'Manifest Generation Failed',\n cause: `File at ${filepath} is empty`,\n }\n }\n }\n\n return {\n ok: true,\n data: {\n content,\n pip: convertCondaToRequirementsFromInput(content),\n },\n }\n}\n\n// Just extract the first pip block, if one exists at all.\nexport function convertCondaToRequirementsFromInput(input: string): string {\n let collecting = false\n let delim = '-'\n let indent = ''\n const keeping: string[] = []\n for (const line of input.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed) {\n // Ignore empty lines.\n continue\n }\n if (collecting) {\n if (line.startsWith('#')) {\n // Ignore comment lines (keep?).\n continue\n }\n if (line.startsWith(delim)) {\n // In this case we have a line with the same indentation as the\n // `- pip:` line, so we have reached the end of the pip block.\n break\n }\n if (!indent) {\n // Store the indentation of the block.\n if (trimmed.startsWith('-')) {\n indent = line.split('-')[0] + '-'\n if (indent.length <= delim.length) {\n // The first line after the `pip:` line does not indent further\n // than that so the block is empty?\n break\n }\n }\n }\n if (line.startsWith(indent)) {\n keeping.push(line.slice(indent.length).trim())\n } else {\n // Unexpected input. bail.\n break\n }\n }\n // Note: the line may end with a line comment so don't === it.\n else if (trimmed.startsWith('- pip:')) {\n delim = line.split('-')[0] + '-'\n collecting = true\n }\n }\n\n return prepareContent(keeping.join('\\n'))\n}\n","import fs from 'node:fs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputRequirements(\n result: CResult<{ content: string; pip: string }>,\n outputKind: OutputKind,\n out: string,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (!result.ok) {\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'json') {\n const json = serializeResultJson(result)\n\n if (out === '-') {\n logger.log(json)\n } else {\n fs.writeFileSync(out, json, 'utf8')\n }\n\n return\n }\n\n if (outputKind === 'markdown') {\n const arr = []\n arr.push('# Converted Conda file')\n arr.push('')\n arr.push(\n 'This is the Conda `environment.yml` file converted to python `requirements.txt`:',\n )\n arr.push('')\n arr.push('```file=requirements.txt')\n arr.push(result.data.pip)\n arr.push('```')\n arr.push('')\n const md = arr.join('\\n')\n\n if (out === '-') {\n logger.log(md)\n } else {\n fs.writeFileSync(out, md, 'utf8')\n }\n return\n }\n\n if (out === '-') {\n logger.log(result.data.pip)\n logger.log('')\n } else {\n fs.writeFileSync(out, result.data.pip, 'utf8')\n }\n}\n","import { convertCondaToRequirements } from './convert-conda-to-requirements.mts'\nimport { outputRequirements } from './output-requirements.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleManifestConda({\n cwd,\n filename,\n out,\n outputKind,\n verbose,\n}: {\n cwd: string\n filename: string\n out: string\n outputKind: OutputKind\n verbose: boolean\n}): Promise<void> {\n const data = await convertCondaToRequirements(filename, cwd, verbose)\n\n await outputRequirements(data, outputKind, out)\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { convertGradleToMaven } from './convert_gradle_to_maven.mts'\nimport { convertSbtToMaven } from './convert_sbt_to_maven.mts'\nimport { handleManifestConda } from './handle-manifest-conda.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\n\nimport type { GeneratableManifests } from './detect-manifest-actions.mts'\nimport type { OutputKind } from '../../types.mts'\n\nexport async function generateAutoManifest({\n cwd,\n detected,\n outputKind,\n verbose,\n}: {\n detected: GeneratableManifests\n cwd: string\n outputKind: OutputKind\n verbose: boolean\n}) {\n const sockJson = readOrDefaultSocketJson(cwd)\n\n if (verbose) {\n logger.info('Using this socket.json for defaults:', sockJson)\n }\n\n if (!sockJson?.defaults?.manifest?.sbt?.disabled && detected.sbt) {\n logger.log('Detected a Scala sbt build, generating pom files with sbt...')\n await convertSbtToMaven({\n // Note: `sbt` is more likely to be resolved against PATH env\n bin: sockJson.defaults?.manifest?.sbt?.bin ?? 'sbt',\n cwd,\n out: sockJson.defaults?.manifest?.sbt?.outfile ?? './socket.sbt.pom.xml',\n sbtOpts:\n sockJson.defaults?.manifest?.sbt?.sbtOpts\n ?.split(' ')\n .map(s => s.trim())\n .filter(Boolean) ?? [],\n verbose: Boolean(sockJson.defaults?.manifest?.sbt?.verbose),\n })\n }\n\n if (!sockJson?.defaults?.manifest?.gradle?.disabled && detected.gradle) {\n logger.log(\n 'Detected a gradle build (Gradle, Kotlin, Scala), running default gradle generator...',\n )\n await convertGradleToMaven({\n // Note: `gradlew` is more likely to be resolved against cwd.\n // Note: .resolve() won't butcher an absolute path.\n // TODO: `gradlew` (or anything else given) may want to resolve against PATH.\n bin: sockJson.defaults?.manifest?.gradle?.bin\n ? path.resolve(cwd, sockJson.defaults.manifest.gradle.bin)\n : path.join(cwd, 'gradlew'),\n cwd,\n verbose: Boolean(sockJson.defaults?.manifest?.gradle?.verbose),\n gradleOpts:\n sockJson.defaults?.manifest?.gradle?.gradleOpts\n ?.split(' ')\n .map(s => s.trim())\n .filter(Boolean) ?? [],\n })\n }\n\n if (!sockJson?.defaults?.manifest?.conda?.disabled && detected.conda) {\n logger.log(\n 'Detected an environment.yml file, running default Conda generator...',\n )\n await handleManifestConda({\n cwd,\n filename: sockJson.defaults?.manifest?.conda?.infile ?? 'environment.yml',\n outputKind,\n out: sockJson.defaults?.manifest?.conda?.outfile ?? 'requirements.txt',\n verbose: Boolean(sockJson.defaults?.manifest?.conda?.verbose),\n })\n }\n}\n","import path from 'node:path'\n\nimport terminalLink from 'terminal-link'\n\nimport { debugDir } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { fetchCreateOrgFullScan } from './fetch-create-org-full-scan.mts'\nimport { fetchSupportedScanFileNames } from './fetch-supported-scan-file-names.mts'\nimport { finalizeTier1Scan } from './finalize-tier1-scan.mts'\nimport { handleScanReport } from './handle-scan-report.mts'\nimport { outputCreateNewScan } from './output-create-new-scan.mts'\nimport { performReachabilityAnalysis } from './perform-reachability-analysis.mts'\nimport constants from '../../constants.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getPackageFilesForScan } from '../../utils/path-resolve.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\nimport { detectManifestActions } from '../manifest/detect-manifest-actions.mts'\nimport { generateAutoManifest } from '../manifest/generate_auto_manifest.mts'\n\nimport type { ReachabilityOptions } from './perform-reachability-analysis.mts'\nimport type { REPORT_LEVEL } from './types.mts'\nimport type { OutputKind } from '../../types.mts'\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\n\nexport type HandleCreateNewScanConfig = {\n autoManifest: boolean\n branchName: string\n commitHash: string\n commitMessage: string\n committers: string\n cwd: string\n defaultBranch: boolean\n interactive: boolean\n orgSlug: string\n pendingHead: boolean\n pullRequest: number\n outputKind: OutputKind\n reach: Remap<\n ReachabilityOptions & {\n runReachabilityAnalysis: boolean\n }\n >\n readOnly: boolean\n repoName: string\n report: boolean\n reportLevel: REPORT_LEVEL\n targets: string[]\n tmp: boolean\n}\n\nexport async function handleCreateNewScan({\n autoManifest,\n branchName,\n commitHash,\n commitMessage,\n committers,\n cwd,\n defaultBranch,\n interactive,\n orgSlug,\n outputKind,\n pendingHead,\n pullRequest,\n reach,\n readOnly,\n repoName,\n report,\n reportLevel,\n targets,\n tmp,\n}: HandleCreateNewScanConfig): Promise<void> {\n if (autoManifest) {\n logger.info('Auto-generating manifest files ...')\n const sockJson = readOrDefaultSocketJson(cwd)\n const detected = await detectManifestActions(sockJson, cwd)\n await generateAutoManifest({\n detected,\n cwd,\n outputKind,\n verbose: false,\n })\n logger.info('Auto-generation finished. Proceeding with Scan creation.')\n }\n\n const { spinner } = constants\n\n const supportedFilesCResult = await fetchSupportedScanFileNames({ spinner })\n if (!supportedFilesCResult.ok) {\n await outputCreateNewScan(supportedFilesCResult, {\n interactive,\n outputKind,\n })\n return\n }\n\n spinner.start('Searching for local files to include in scan...')\n\n const supportedFiles = supportedFilesCResult.data\n const packagePaths = await getPackageFilesForScan(targets, supportedFiles, {\n cwd,\n })\n\n spinner.successAndStop(\n `Found ${packagePaths.length} ${pluralize('file', packagePaths.length)} to include in scan.`,\n )\n\n const wasValidInput = checkCommandInput(outputKind, {\n nook: true,\n test: packagePaths.length > 0,\n fail: `found no eligible files to scan. See supported manifest files at ${terminalLink('docs.socket.dev', 'https://docs.socket.dev/docs/manifest-file-detection-in-socket')}`,\n message:\n 'TARGET (file/dir) must contain matching / supported file types for a scan',\n })\n if (!wasValidInput) {\n return\n }\n\n logger.success(\n `Found ${packagePaths.length} local ${pluralize('file', packagePaths.length)}`,\n )\n\n debugDir('inspect', { packagePaths })\n\n if (readOnly) {\n logger.log('[ReadOnly] Bailing now')\n return\n }\n\n let scanPaths: string[] = packagePaths\n let tier1ReachabilityScanId: string | undefined\n\n // If reachability is enabled, perform reachability analysis.\n if (reach.runReachabilityAnalysis) {\n logger.error('')\n logger.info('Starting reachability analysis...')\n\n spinner.start()\n\n const reachResult = await performReachabilityAnalysis({\n branchName,\n cwd,\n orgSlug,\n packagePaths,\n reachabilityOptions: reach,\n repoName,\n spinner,\n })\n\n spinner.stop()\n\n if (!reachResult.ok) {\n await outputCreateNewScan(reachResult, { interactive, outputKind })\n return\n }\n\n logger.success('Reachability analysis completed successfully')\n\n const reachabilityReport = reachResult.data?.reachabilityReport\n\n scanPaths = [\n ...packagePaths.filter(\n // Ensure the .socket.facts.json isn't duplicated in case it happened\n // to be in the scan folder before the analysis was run.\n p =>\n path.basename(p).toLowerCase() !==\n constants.DOT_SOCKET_DOT_FACTS_JSON,\n ),\n ...(reachabilityReport ? [reachabilityReport] : []),\n ]\n\n tier1ReachabilityScanId = reachResult.data?.tier1ReachabilityScanId\n }\n\n const fullScanCResult = await fetchCreateOrgFullScan(\n scanPaths,\n orgSlug,\n {\n commitHash,\n commitMessage,\n committers,\n pullRequest,\n repoName,\n branchName,\n },\n {\n cwd,\n defaultBranch,\n pendingHead,\n tmp,\n },\n )\n\n const scanId = fullScanCResult.ok ? fullScanCResult.data?.id : undefined\n\n if (reach && scanId && tier1ReachabilityScanId) {\n await finalizeTier1Scan(tier1ReachabilityScanId, scanId)\n }\n\n if (report && fullScanCResult.ok) {\n if (scanId) {\n await handleScanReport({\n filepath: '-',\n fold: constants.FOLD_SETTING_VERSION,\n includeLicensePolicy: true,\n orgSlug,\n outputKind,\n reportLevel,\n scanId,\n short: false,\n })\n } else {\n await outputCreateNewScan(\n {\n ok: false,\n message: 'Missing Scan ID',\n cause: 'Server did not respond with a scan ID',\n data: fullScanCResult.data,\n },\n {\n interactive,\n outputKind,\n },\n )\n }\n } else {\n spinner.stop()\n\n await outputCreateNewScan(fullScanCResult, { interactive, outputKind })\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getDefaultOrgSlug } from './fetch-default-org-slug.mts'\nimport constants from '../../constants.mts'\nimport {\n detectDefaultBranch,\n getRepoName,\n gitBranch,\n} from '../../utils/git.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\nimport { handleCreateNewScan } from '../scan/handle-create-new-scan.mts'\n\nexport async function handleCi(autoManifest: boolean): Promise<void> {\n const orgSlugCResult = await getDefaultOrgSlug()\n if (!orgSlugCResult.ok) {\n process.exitCode = orgSlugCResult.code ?? 1\n // Always assume json mode.\n logger.log(serializeResultJson(orgSlugCResult))\n return\n }\n\n const orgSlug = orgSlugCResult.data\n const cwd = process.cwd()\n const branchName = (await gitBranch(cwd)) || (await detectDefaultBranch(cwd))\n const repoName = await getRepoName(cwd)\n\n await handleCreateNewScan({\n autoManifest,\n branchName,\n commitMessage: '',\n commitHash: '',\n committers: '',\n cwd,\n defaultBranch: false,\n interactive: false,\n orgSlug,\n outputKind: 'json',\n // When 'pendingHead' is true, it requires 'branchName' set and 'tmp' false.\n pendingHead: true,\n pullRequest: 0,\n reach: {\n reachAnalysisTimeout: 0,\n reachAnalysisMemoryLimit: 0,\n reachDisableAnalytics: false,\n reachEcosystems: [],\n reachExcludePaths: [],\n reachSkipCache: false,\n runReachabilityAnalysis: false,\n },\n repoName,\n readOnly: false,\n report: true,\n reportLevel: constants.REPORT_LEVEL_ERROR,\n targets: ['.'],\n // Don't set 'tmp' when 'pendingHead' is true.\n tmp: false,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleCi } from './handle-ci.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'ci',\n description: 'Shorthand for `socket scan create --report --no-interactive`',\n hidden: false,\n flags: {\n ...commonFlags,\n autoManifest: {\n type: 'boolean',\n // Dev tools in CI environments are not likely to be set up, so this is safer.\n default: false,\n description:\n 'Auto generate manifest files where detected? See autoManifest flag in `socket scan create`',\n },\n },\n help: (command, _config) => `\n Usage\n $ ${command} [options]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n This command is intended to use in CI runs to allow automated systems to\n accept or reject a current build. It will use the default org of the\n Socket API token. The exit code will be non-zero when the scan does not pass\n your security policy.\n\n The --auto-manifest flag does the same as the one from \\`socket scan create\\`\n but is not enabled by default since the CI is less likely to be set up with\n all the necessary dev tooling. Enable it if you want the scan to include\n locally generated manifests like for gradle and sbt.\n\n Examples\n $ ${command}\n $ ${command} --auto-manifest\n `,\n}\n\nexport const cmdCI = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleCi(Boolean(cli.flags['autoManifest']))\n}\n","import { isSupportedConfigKey } from '../../utils/config.mts'\nimport { getOrgSlugs } from '../../utils/organization.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\nimport { fetchOrganization } from '../organization/fetch-organization-list.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport async function discoverConfigValue(\n key: string,\n): Promise<CResult<unknown>> {\n // This will have to be a specific implementation per key because certain\n // keys should request information from particular API endpoints while\n // others should simply return their default value, like endpoint URL.\n\n if (key !== 'test' && !isSupportedConfigKey(key)) {\n return {\n ok: false,\n message: 'Auto discover failed',\n cause: 'Requested key is not a valid config key.',\n }\n }\n\n if (key === 'apiBaseUrl') {\n // Return the default value\n return {\n ok: false,\n message: 'Auto discover failed',\n cause:\n \"If you're unsure about the base endpoint URL then simply unset it.\",\n }\n }\n\n if (key === 'apiProxy') {\n // I don't think we can auto-discover this with any order of reliability..?\n return {\n ok: false,\n message: 'Auto discover failed',\n cause:\n 'When uncertain, unset this key. Otherwise ask your network administrator',\n }\n }\n\n if (key === 'apiToken') {\n return {\n ok: false,\n message: 'Auto discover failed',\n cause:\n 'You can find/create your API token in your Socket dashboard > settings > API tokens.\\nYou should then use `socket login` to login instead of this command.',\n }\n }\n\n if (key === 'defaultOrg') {\n const hasApiToken = hasDefaultApiToken()\n if (!hasApiToken) {\n return {\n ok: false,\n message: 'Auto discover failed',\n cause:\n 'No API token set, must have a token to resolve its default org.',\n }\n }\n\n const org = await getDefaultOrgFromToken()\n if (!org?.length) {\n return {\n ok: false,\n message: 'Auto discover failed',\n cause: 'Was unable to determine default org for the current API token.',\n }\n }\n\n if (Array.isArray(org)) {\n return {\n ok: true,\n data: org,\n message: 'These are the orgs that the current API token can access.',\n }\n }\n\n return {\n ok: true,\n data: org,\n message: 'This is the org that belongs to the current API token.',\n }\n }\n\n if (key === 'enforcedOrgs') {\n const hasApiToken = hasDefaultApiToken()\n if (!hasApiToken) {\n return {\n ok: false,\n message: 'Auto discover failed',\n cause:\n 'No API token set, must have a token to resolve orgs to enforce.',\n }\n }\n\n const orgs = await getEnforceableOrgsFromToken()\n if (!orgs?.length) {\n return {\n ok: false,\n message: 'Auto discover failed',\n cause:\n 'Was unable to determine any orgs to enforce for the current API token.',\n }\n }\n\n return {\n ok: true,\n data: orgs,\n message: 'These are the orgs whose security policy you can enforce.',\n }\n }\n\n if (key === 'test') {\n return {\n ok: false,\n message: 'Auto discover failed',\n cause: 'congrats, you found the test key',\n }\n }\n\n // Mostly to please TS, because we're not telling it `key` is keyof LocalConfig\n return {\n ok: false,\n message: 'Auto discover failed',\n cause: 'unreachable?',\n }\n}\n\nasync function getDefaultOrgFromToken(): Promise<\n string[] | string | undefined\n> {\n const orgsCResult = await fetchOrganization()\n if (!orgsCResult.ok) {\n return undefined\n }\n\n const { organizations } = orgsCResult.data\n if (organizations.length === 0) {\n return undefined\n }\n const slugs = getOrgSlugs(organizations)\n if (slugs.length === 1) {\n return slugs[0]\n }\n return slugs\n}\n\nasync function getEnforceableOrgsFromToken(): Promise<string[] | undefined> {\n const orgsCResult = await fetchOrganization()\n if (!orgsCResult.ok) {\n return undefined\n }\n\n const { organizations } = orgsCResult.data\n return organizations.length ? getOrgSlugs(organizations) : undefined\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\nimport { select } from '@socketsecurity/registry/lib/prompts'\n\nimport { isReadOnlyConfig, updateConfigValue } from '../../utils/config.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { LocalConfig } from '../../utils/config.mts'\n\nexport async function outputConfigAuto(\n key: keyof LocalConfig,\n result: CResult<unknown>,\n outputKind: OutputKind,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'markdown') {\n logger.log(`# Auto discover config value`)\n logger.log('')\n logger.log(\n `Attempted to automatically discover the value for config key: \"${key}\"`,\n )\n logger.log('')\n if (result.ok) {\n logger.log(`The discovered value is: \"${result.data}\"`)\n if (result.message) {\n logger.log('')\n logger.log(result.message)\n }\n }\n logger.log('')\n } else {\n if (result.message) {\n logger.log(result.message)\n logger.log('')\n }\n logger.log(`- ${key}: ${result.data}`)\n logger.log('')\n\n if (isReadOnlyConfig()) {\n logger.log(\n '(Unable to persist this value because the config is in read-only mode, meaning it was overridden through env or flag.)',\n )\n } else if (key === 'defaultOrg') {\n const proceed = await select<string>({\n message:\n 'Would you like to update the default org in local config to this value?',\n choices: (Array.isArray(result.data) ? result.data : [result.data])\n .map(slug => ({\n name: 'Yes [' + slug + ']',\n value: slug,\n description: `Use \"${slug}\" as the default organization`,\n }))\n .concat({\n name: 'No',\n value: '',\n description: 'Do not use any of these organizations',\n }),\n })\n if (proceed) {\n logger.log(`Setting defaultOrg to \"${proceed}\"...`)\n const updateResult = updateConfigValue('defaultOrg', proceed)\n if (updateResult.ok) {\n logger.log(\n `OK. Updated defaultOrg to \"${proceed}\".\\nYou should no longer need to add the org to commands that normally require it.`,\n )\n } else {\n logger.log(failMsgWithBadge(updateResult.message, updateResult.cause))\n }\n } else {\n logger.log('OK. No changes made.')\n }\n } else if (key === 'enforcedOrgs') {\n const proceed = await select<string>({\n message:\n 'Would you like to update the enforced orgs in local config to this value?',\n choices: (Array.isArray(result.data) ? result.data : [result.data])\n .map(slug => ({\n name: 'Yes [' + slug + ']',\n value: slug,\n description: `Enforce the security policy of \"${slug}\" on this machine`,\n }))\n .concat({\n name: 'No',\n value: '',\n description: 'Do not use any of these organizations',\n }),\n })\n if (proceed) {\n logger.log(`Setting enforcedOrgs key to \"${proceed}\"...`)\n const updateResult = updateConfigValue('defaultOrg', proceed)\n if (updateResult.ok) {\n logger.log(`OK. Updated enforcedOrgs to \"${proceed}\".`)\n } else {\n logger.log(failMsgWithBadge(updateResult.message, updateResult.cause))\n }\n } else {\n logger.log('OK. No changes made.')\n }\n }\n }\n}\n","import { discoverConfigValue } from './discover-config-value.mts'\nimport { outputConfigAuto } from './output-config-auto.mts'\n\nimport type { OutputKind } from '../../types.mts'\nimport type { LocalConfig } from '../../utils/config.mts'\n\nexport async function handleConfigAuto({\n key,\n outputKind,\n}: {\n key: keyof LocalConfig\n outputKind: OutputKind\n}) {\n const result = await discoverConfigValue(key)\n\n await outputConfigAuto(key, result, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleConfigAuto } from './handle-config-auto.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport {\n getSupportedConfigEntries,\n isSupportedConfigKey,\n} from '../../utils/config.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { LocalConfig } from '../../utils/config.mts'\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'auto'\n\nconst description =\n 'Automatically discover and set the correct value config item'\n\nconst hidden = false\n\nexport const cmdConfigAuto = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] KEY\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Attempt to automatically discover the correct value for a given config KEY.\n\n Examples\n $ ${command} defaultOrg\n\n Keys:\n${getSupportedConfigEntries()\n .map(([key, desc]) => ` - ${key} -- ${desc}`)\n .join('\\n')}\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const [key = ''] = cli.input\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: key !== 'test' && isSupportedConfigKey(key),\n message: 'Config key should be the first arg',\n fail: key ? 'invalid config key' : 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleConfigAuto({\n key: key as keyof LocalConfig,\n outputKind,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { isReadOnlyConfig } from '../../utils/config.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { LocalConfig } from '../../utils/config.mts'\n\nexport async function outputConfigGet(\n key: keyof LocalConfig,\n result: CResult<LocalConfig[keyof LocalConfig]>,\n outputKind: OutputKind,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const readOnly = isReadOnlyConfig()\n\n if (outputKind === 'markdown') {\n logger.log(`# Config Value`)\n logger.log('')\n logger.log(`Config key '${key}' has value '${result.data}`)\n if (readOnly) {\n logger.log('')\n logger.log(\n 'Note: the config is in read-only mode, meaning at least one key was temporarily\\n overridden from an env var or command flag.',\n )\n }\n } else {\n logger.log(`${key}: ${result.data}`)\n if (readOnly) {\n logger.log('')\n logger.log(\n 'Note: the config is in read-only mode, meaning at least one key was temporarily overridden from an env var or command flag.',\n )\n }\n }\n}\n","import { outputConfigGet } from './output-config-get.mts'\nimport { getConfigValue } from '../../utils/config.mts'\n\nimport type { OutputKind } from '../../types.mts'\nimport type { LocalConfig } from '../../utils/config.mts'\n\nexport async function handleConfigGet({\n key,\n outputKind,\n}: {\n key: keyof LocalConfig\n outputKind: OutputKind\n}) {\n const result = getConfigValue(key)\n\n await outputConfigGet(key, result, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleConfigGet } from './handle-config-get.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport {\n getSupportedConfigEntries,\n isSupportedConfigKey,\n} from '../../utils/config.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { LocalConfig } from '../../utils/config.mts'\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'get',\n description: 'Get the value of a local CLI config item',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] KEY\n\n Retrieve the value for given KEY at this time. If you have overridden the\n config then the value will come from that override.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n KEY is an enum. Valid keys:\n\n${getSupportedConfigEntries()\n .map(([key, desc]) => ` - ${key} -- ${desc}`)\n .join('\\n')}\n\n Examples\n $ ${command} defaultOrg\n `,\n}\n\nexport const cmdConfigGet = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const [key = ''] = cli.input\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: key === 'test' || isSupportedConfigKey(key),\n message: 'Config key should be the first arg',\n fail: key ? 'invalid config key' : 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleConfigGet({\n key: key as keyof LocalConfig,\n outputKind,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport {\n getConfigValue,\n getSupportedConfigKeys,\n isReadOnlyConfig,\n isSensitiveConfigKey,\n} from '../../utils/config.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function outputConfigList({\n full,\n outputKind,\n}: {\n full: boolean\n outputKind: OutputKind\n}) {\n const readOnly = isReadOnlyConfig()\n const supportedConfigKeys = getSupportedConfigKeys()\n if (outputKind === 'json') {\n let failed = false\n const obj: Record<string, unknown> = {}\n for (const key of supportedConfigKeys) {\n const result = getConfigValue(key)\n let value = result.data\n if (!result.ok) {\n value = `Failed to retrieve: ${result.message}`\n failed = true\n } else if (!full && isSensitiveConfigKey(key)) {\n value = '********'\n }\n if (full || value !== undefined) {\n obj[key as any] = value ?? '<none>'\n }\n }\n if (failed) {\n process.exitCode = 1\n }\n logger.log(\n serializeResultJson(\n failed\n ? {\n ok: false,\n message: 'At least one config key failed to be fetched...',\n data: JSON.stringify({\n full,\n config: obj,\n readOnly,\n }),\n }\n : {\n ok: true,\n data: {\n full,\n config: obj,\n readOnly,\n },\n },\n ),\n )\n } else {\n const maxWidth = supportedConfigKeys.reduce(\n (a, b) => Math.max(a, b.length),\n 0,\n )\n\n logger.log('# Local CLI Config')\n logger.log('')\n logger.log(`This is the local CLI config (full=${!!full}):`)\n logger.log('')\n for (const key of supportedConfigKeys) {\n const result = getConfigValue(key)\n if (!result.ok) {\n logger.log(`- ${key}: failed to read: ${result.message}`)\n } else {\n let value = result.data\n if (!full && isSensitiveConfigKey(key)) {\n value = '********'\n }\n if (full || value !== undefined) {\n logger.log(\n `- ${key}:${' '.repeat(Math.max(0, maxWidth - key.length + 3))} ${Array.isArray(value) ? value.join(', ') || '<none>' : (value ?? '<none>')}`,\n )\n }\n }\n }\n if (readOnly) {\n logger.log('')\n logger.log(\n 'Note: the config is in read-only mode, meaning at least one key was temporarily\\n overridden from an env var or command flag.',\n )\n }\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { outputConfigList } from './output-config-list.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'list',\n description: 'Show all local CLI config items and their values',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n full: {\n type: 'boolean',\n default: false,\n description: 'Show full tokens in plaintext (unsafe)',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n `,\n}\n\nexport const cmdConfigList = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { full, json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(outputKind, {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n })\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await outputConfigList({\n full: !!full,\n outputKind,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputConfigSet(\n result: CResult<undefined | string>,\n outputKind: OutputKind,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'markdown') {\n logger.log(`# Update config`)\n logger.log('')\n logger.log(result.message)\n if (result.data) {\n logger.log('')\n logger.log(result.data)\n }\n } else {\n logger.log(`OK`)\n logger.log(result.message)\n if (result.data) {\n logger.log('')\n logger.log(result.data)\n }\n }\n}\n","import { outputConfigSet } from './output-config-set.mts'\nimport { updateConfigValue } from '../../utils/config.mts'\n\nimport type { OutputKind } from '../../types.mts'\nimport type { LocalConfig } from '../../utils/config.mts'\n\nexport async function handleConfigSet({\n key,\n outputKind,\n value,\n}: {\n key: keyof LocalConfig\n outputKind: OutputKind\n value: string\n}) {\n const result = updateConfigValue(key, value)\n\n await outputConfigSet(result, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleConfigSet } from './handle-config-set.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport {\n getSupportedConfigEntries,\n isSupportedConfigKey,\n} from '../../utils/config.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { LocalConfig } from '../../utils/config.mts'\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'set'\n\nconst description = 'Update the value of a local CLI config item'\n\nconst hidden = false\n\nexport const cmdConfigSet = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <KEY> <VALUE>\n\n Options\n ${getFlagListOutput(config.flags)}\n\n This is a crude way of updating the local configuration for this CLI tool.\n\n Note that updating a value here is nothing more than updating a key/value\n store entry. No validation is happening. The server may reject your values\n in some cases. Use at your own risk.\n\n Note: use \\`socket config unset\\` to restore to defaults. Setting a key\n to \\`undefined\\` will not allow default values to be set on it.\n\n Keys:\n\n${getSupportedConfigEntries()\n .map(([key, desc]) => ` - ${key} -- ${desc}`)\n .join('\\n')}\n\n Examples\n $ ${command} apiProxy https://example.com\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const [key = '', ...rest] = cli.input\n\n const value = rest.join(' ')\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: key === 'test' || isSupportedConfigKey(key),\n message: 'Config key should be the first arg',\n fail: key ? 'invalid config key' : 'missing',\n },\n {\n test: !!value, // This is a string, empty string is not ok\n message:\n 'Key value should be the remaining args (use `unset` to unset a value)',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleConfigSet({\n key: key as keyof LocalConfig,\n outputKind,\n value,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputConfigUnset(\n updateResult: CResult<undefined | string>,\n outputKind: OutputKind,\n) {\n if (!updateResult.ok) {\n process.exitCode = updateResult.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(updateResult))\n return\n }\n if (!updateResult.ok) {\n logger.fail(failMsgWithBadge(updateResult.message, updateResult.cause))\n return\n }\n\n if (outputKind === 'markdown') {\n logger.log(`# Update config`)\n logger.log('')\n logger.log(updateResult.message)\n if (updateResult.data) {\n logger.log('')\n logger.log(updateResult.data)\n }\n } else {\n logger.log(`OK`)\n logger.log(updateResult.message)\n if (updateResult.data) {\n logger.log('')\n logger.log(updateResult.data)\n }\n }\n}\n","import { outputConfigUnset } from './output-config-unset.mts'\nimport { updateConfigValue } from '../../utils/config.mts'\n\nimport type { OutputKind } from '../../types.mts'\nimport type { LocalConfig } from '../../utils/config.mts'\n\nexport async function handleConfigUnset({\n key,\n outputKind,\n}: {\n key: keyof LocalConfig\n outputKind: OutputKind\n}) {\n const updateResult = updateConfigValue(key, undefined)\n\n await outputConfigUnset(updateResult, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleConfigUnset } from './handle-config-unset.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport {\n getSupportedConfigEntries,\n isSupportedConfigKey,\n} from '../../utils/config.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { LocalConfig } from '../../utils/config.mts'\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'unset'\n\nconst description = 'Clear the value of a local CLI config item'\n\nconst hidden = false\n\nexport const cmdConfigUnset = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <KEY> <VALUE>\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Removes a value from a config key, allowing the default value to be used\n for it instead.\n\n Keys:\n\n${getSupportedConfigEntries()\n .map(([key, desc]) => ` - ${key} -- ${desc}`)\n .join('\\n')}\n\n Examples\n $ ${command} defaultOrg\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const [key = ''] = cli.input\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: key === 'test' || isSupportedConfigKey(key),\n message: 'Config key should be the first arg',\n fail: key ? 'invalid config key' : 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleConfigUnset({\n key: key as keyof LocalConfig,\n outputKind,\n })\n}\n","import { cmdConfigAuto } from './cmd-config-auto.mts'\nimport { cmdConfigGet } from './cmd-config-get.mts'\nimport { cmdConfigList } from './cmd-config-list.mts'\nimport { cmdConfigSet } from './cmd-config-set.mts'\nimport { cmdConfigUnset } from './cmd-config-unset.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Manage Socket CLI configuration'\n\nexport const cmdConfig: CliSubcommand = {\n description,\n hidden: false,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n auto: cmdConfigAuto,\n get: cmdConfigGet,\n list: cmdConfigList,\n set: cmdConfigSet,\n unset: cmdConfigUnset,\n },\n {\n argv,\n description,\n importMeta,\n name: `${parentName} config`,\n },\n )\n },\n}\n","import { joinAnd } from '@socketsecurity/registry/lib/arrays'\n\nimport constants from '../../constants.mts'\n\nimport type { GhsaDetails } from '../../utils/github.mts'\n\nconst GITHUB_ADVISORIES_URL = 'https://github.com/advisories'\n\nexport type SocketFixBranchParser = (\n branch: string,\n) => SocketFixBranchParseResult | null\n\nexport type SocketFixBranchParseResult = {\n ghsaId: string\n}\n\nexport function createSocketFixBranchParser(\n ghsaId?: string | undefined,\n): SocketFixBranchParser {\n const pattern = getSocketFixBranchPattern(ghsaId)\n return function parse(branch: string): SocketFixBranchParseResult | null {\n const match = pattern.exec(branch) as [string, string] | null\n if (!match) {\n return null\n }\n const { 1: ghsaId } = match\n return { ghsaId } as SocketFixBranchParseResult\n }\n}\n\nexport const genericSocketFixBranchParser = createSocketFixBranchParser()\n\nexport function getSocketFixBranchName(ghsaId: string): string {\n return `socket/fix/${ghsaId}`\n}\n\nexport function getSocketFixBranchPattern(ghsaId?: string | undefined): RegExp {\n return new RegExp(`^socket/fix/(${ghsaId ?? '.+'})$`)\n}\n\nexport function getSocketFixCommitMessage(\n ghsaId: string,\n details?: GhsaDetails | undefined,\n): string {\n const summary = details?.summary\n return `fix: ${ghsaId}${summary ? ` - ${summary}` : ''}`\n}\n\nexport function getSocketFixPullRequestBody(\n ghsaIds: string[],\n ghsaDetails?: Map<string, GhsaDetails>,\n): string {\n const vulnCount = ghsaIds.length\n if (vulnCount === 1) {\n const ghsaId = ghsaIds[0]!\n const details = ghsaDetails?.get(ghsaId)\n const body = `[Socket](${constants.SOCKET_WEBSITE_URL}) fix for [${ghsaId}](${GITHUB_ADVISORIES_URL}/${ghsaId}).`\n if (!details) {\n return body\n }\n const packages = details.vulnerabilities.nodes.map(\n v => `${v.package.name} (${v.package.ecosystem})`,\n )\n return [\n body,\n '',\n '',\n `**Vulnerability Summary:** ${details.summary}`,\n '',\n `**Severity:** ${details.severity}`,\n '',\n `**Affected Packages:** ${joinAnd(packages)}`,\n ].join('\\n')\n }\n return [\n `[Socket](${constants.SOCKET_WEBSITE_URL}) fixes for ${vulnCount} GHSAs.`,\n '',\n '**Fixed Vulnerabilities:**',\n ...ghsaIds.map(id => {\n const details = ghsaDetails?.get(id)\n const item = `- [${id}](${GITHUB_ADVISORIES_URL}/${id})`\n if (details) {\n const packages = details.vulnerabilities.nodes.map(\n v => `${v.package.name}`,\n )\n return `${item} - ${details.summary} (${joinAnd(packages)})`\n }\n return item\n }),\n ].join('\\n')\n}\n\nexport function getSocketFixPullRequestTitle(ghsaIds: string[]): string {\n const vulnCount = ghsaIds.length\n return vulnCount === 1\n ? `Fix for ${ghsaIds[0]}`\n : `Fixes for ${vulnCount} GHSAs`\n}\n","import { RequestError } from '@octokit/request-error'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport {\n getSocketFixBranchPattern,\n getSocketFixPullRequestBody,\n getSocketFixPullRequestTitle,\n} from './git.mts'\nimport {\n type GhsaDetails,\n type Pr,\n cacheFetch,\n getOctokit,\n getOctokitGraphql,\n writeCache,\n} from '../../utils/github.mts'\n\nimport type { OctokitResponse } from '@octokit/types'\nimport type { JsonContent } from '@socketsecurity/registry/lib/fs'\n\nexport type OpenSocketFixPrOptions = {\n baseBranch?: string | undefined\n cwd?: string | undefined\n ghsaDetails?: Map<string, GhsaDetails> | undefined\n}\n\nexport async function openSocketFixPr(\n owner: string,\n repo: string,\n branch: string,\n ghsaIds: string[],\n options?: OpenSocketFixPrOptions | undefined,\n): Promise<OctokitResponse<Pr> | null> {\n const { baseBranch = 'main', ghsaDetails } = {\n __proto__: null,\n ...options,\n } as OpenSocketFixPrOptions\n\n const octokit = getOctokit()\n\n try {\n const octokitPullsCreateParams = {\n owner,\n repo,\n title: getSocketFixPullRequestTitle(ghsaIds),\n head: branch,\n base: baseBranch,\n body: getSocketFixPullRequestBody(ghsaIds, ghsaDetails),\n }\n debugDir('inspect', { octokitPullsCreateParams })\n return await octokit.pulls.create(octokitPullsCreateParams)\n } catch (e) {\n let message = `Failed to open pull request`\n const errors =\n e instanceof RequestError\n ? (e.response?.data as any)?.['errors']\n : undefined\n if (Array.isArray(errors) && errors.length) {\n const details = errors\n .map(\n d =>\n `- ${d.message?.trim() ?? `${d.resource}.${d.field} (${d.code})`}`,\n )\n .join('\\n')\n message += `:\\n${details}`\n }\n debugFn('error', message)\n }\n return null\n}\n\nexport type GQL_MERGE_STATE_STATUS =\n | 'BEHIND'\n | 'BLOCKED'\n | 'CLEAN'\n | 'DIRTY'\n | 'DRAFT'\n | 'HAS_HOOKS'\n | 'UNKNOWN'\n | 'UNSTABLE'\n\nexport type GQL_PR_STATE = 'OPEN' | 'CLOSED' | 'MERGED'\n\nexport type PrMatch = {\n author: string\n baseRefName: string\n headRefName: string\n mergeStateStatus: GQL_MERGE_STATE_STATUS\n number: number\n state: GQL_PR_STATE\n title: string\n}\n\nexport async function cleanupPrs(\n owner: string,\n repo: string,\n ghsaId: string,\n): Promise<PrMatch[]> {\n const contextualMatches = await getSocketPrsWithContext(owner, repo, {\n ghsaId,\n })\n\n if (!contextualMatches.length) {\n return []\n }\n\n const cachesToSave = new Map<string, JsonContent>()\n const octokit = getOctokit()\n\n const settledMatches = await Promise.allSettled(\n contextualMatches.map(async ({ context, match }) => {\n // Update stale PRs.\n // https://docs.github.com/en/graphql/reference/enums#mergestatestatus\n if (match.mergeStateStatus === 'BEHIND') {\n const { number: prNum } = match\n const prRef = `PR #${prNum}`\n try {\n await octokit.repos.merge({\n owner,\n repo,\n base: match.headRefName,\n head: match.baseRefName,\n })\n debugFn('notice', `pr: updating stale ${prRef}`)\n // Update entry entry.\n if (context.apiType === 'graphql') {\n context.entry.mergeStateStatus = 'CLEAN'\n } else if (context.apiType === 'rest') {\n context.entry.mergeable_state = 'clean'\n }\n // Mark cache to be saved.\n cachesToSave.set(context.cacheKey, context.data)\n } catch (e) {\n const message = (e as Error)?.message || 'Unknown error'\n debugFn('error', `pr: failed to update ${prRef} - ${message}`)\n }\n }\n return match\n }),\n )\n\n if (cachesToSave.size) {\n await Promise.allSettled(\n Array.from(cachesToSave).map(({ 0: key, 1: data }) =>\n writeCache(key, data),\n ),\n )\n }\n\n const fulfilledMatches = settledMatches.filter(\n r => r.status === 'fulfilled' && r.value,\n ) as unknown as Array<PromiseFulfilledResult<ContextualPrMatch>>\n\n return fulfilledMatches.map(r => r.value.match)\n}\n\nexport type PrAutoMergeState = {\n enabled: boolean\n details?: string[]\n}\n\nexport type SocketPrsOptions = {\n author?: string | undefined\n ghsaId?: string | undefined\n states?: 'all' | GQL_PR_STATE | GQL_PR_STATE[]\n}\n\nexport async function getSocketPrs(\n owner: string,\n repo: string,\n options?: SocketPrsOptions | undefined,\n): Promise<PrMatch[]> {\n return (await getSocketPrsWithContext(owner, repo, options)).map(d => d.match)\n}\n\ntype ContextualPrMatch = {\n context: {\n apiType: 'graphql' | 'rest'\n cacheKey: string\n data: any\n entry: any\n index: number\n parent: any[]\n }\n match: PrMatch\n}\n\nasync function getSocketPrsWithContext(\n owner: string,\n repo: string,\n options?: SocketPrsOptions | undefined,\n): Promise<ContextualPrMatch[]> {\n const {\n author,\n ghsaId,\n states: statesValue = 'all',\n } = {\n __proto__: null,\n ...options,\n } as SocketPrsOptions\n const branchPattern = getSocketFixBranchPattern(ghsaId)\n const checkAuthor = isNonEmptyString(author)\n const octokit = getOctokit()\n const octokitGraphql = getOctokitGraphql()\n const contextualMatches: ContextualPrMatch[] = []\n const states = (\n typeof statesValue === 'string'\n ? statesValue.toLowerCase() === 'all'\n ? ['OPEN', 'CLOSED', 'MERGED']\n : [statesValue]\n : statesValue\n ).map(s => s.toUpperCase())\n try {\n // Optimistically fetch only the first 50 open PRs using GraphQL to minimize\n // API quota usage. Fallback to REST if no matching PRs are found.\n const gqlCacheKey = `${repo}-pr-graphql-snapshot`\n const gqlResp = await cacheFetch(gqlCacheKey, () =>\n octokitGraphql(\n `\n query($owner: String!, $repo: String!, $states: [PullRequestState!]) {\n repository(owner: $owner, name: $repo) {\n pullRequests(first: 50, states: $states, orderBy: {field: CREATED_AT, direction: DESC}) {\n nodes {\n author {\n login\n }\n baseRefName\n headRefName\n mergeStateStatus\n number\n state\n title\n }\n }\n }\n }\n `,\n {\n owner,\n repo,\n states,\n },\n ),\n )\n\n type GqlPrNode = {\n author?: {\n login: string\n }\n baseRefName: string\n headRefName: string\n mergeStateStatus: GQL_MERGE_STATE_STATUS\n number: number\n state: GQL_PR_STATE\n title: string\n }\n\n const nodes: GqlPrNode[] =\n (gqlResp as any)?.repository?.pullRequests?.nodes ?? []\n for (let i = 0, { length } = nodes; i < length; i += 1) {\n const node = nodes[i]!\n const login = node.author?.login\n const matchesAuthor = checkAuthor ? login === author : true\n const matchesBranch = branchPattern.test(node.headRefName)\n if (matchesAuthor && matchesBranch) {\n contextualMatches.push({\n context: {\n apiType: 'graphql',\n cacheKey: gqlCacheKey,\n data: gqlResp,\n entry: node,\n index: i,\n parent: nodes,\n },\n match: {\n ...node,\n author: login ?? '<unknown>',\n },\n })\n }\n }\n } catch {}\n\n if (contextualMatches.length) {\n return contextualMatches\n }\n\n // Fallback to REST if GraphQL found no matching PRs.\n let allPrs: Pr[] | undefined\n const cacheKey = `${repo}-pull-requests`\n try {\n allPrs = await cacheFetch(\n cacheKey,\n async () =>\n (await octokit.paginate(octokit.pulls.list, {\n owner,\n repo,\n state: 'all',\n per_page: 100,\n })) as Pr[],\n )\n } catch {}\n\n if (!allPrs) {\n return contextualMatches\n }\n\n for (let i = 0, { length } = allPrs; i < length; i += 1) {\n const pr = allPrs[i]!\n const login = pr.user?.login\n const headRefName = pr.head.ref\n const matchesAuthor = checkAuthor ? login === author : true\n const matchesBranch = branchPattern.test(headRefName)\n if (matchesAuthor && matchesBranch) {\n // Upper cased mergeable_state is equivalent to mergeStateStatus.\n // https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request\n const mergeStateStatus = (pr.mergeable_state?.toUpperCase?.() ??\n 'UNKNOWN') as GQL_MERGE_STATE_STATUS\n // The REST API does not have a distinct merged state for pull requests.\n // Instead, a merged pull request is represented as a closed pull request\n // with a non-null merged_at timestamp.\n const state = (\n pr.merged_at ? 'MERGED' : pr.state.toUpperCase()\n ) as GQL_PR_STATE\n contextualMatches.push({\n context: {\n apiType: 'rest',\n cacheKey,\n data: allPrs,\n entry: pr,\n index: i,\n parent: allPrs,\n },\n match: {\n author: login ?? '<unknown>',\n baseRefName: pr.base.ref,\n headRefName,\n mergeStateStatus,\n number: pr.number,\n state,\n title: pr.title,\n },\n })\n }\n }\n return contextualMatches\n}\n","import { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { debugFn, isDebug } from '@socketsecurity/registry/lib/debug'\n\nimport { getSocketPrs } from './pull-request.mts'\nimport constants from '../../constants.mts'\nimport { getBaseBranch, getRepoInfo } from '../../utils/git.mts'\n\nimport type { PrMatch } from './pull-request.mts'\nimport type { RepoInfo } from '../../utils/git.mts'\n\nfunction ciRepoInfo(): RepoInfo | null {\n const { GITHUB_REPOSITORY } = constants.ENV\n if (!GITHUB_REPOSITORY) {\n debugFn('notice', 'miss: GITHUB_REPOSITORY env var')\n }\n const ownerSlashRepo = GITHUB_REPOSITORY\n const slashIndex = ownerSlashRepo.indexOf('/')\n if (slashIndex === -1) {\n return null\n }\n return {\n owner: ownerSlashRepo.slice(0, slashIndex),\n repo: ownerSlashRepo.slice(slashIndex + 1),\n }\n}\n\nexport interface FixEnv {\n baseBranch: string\n gitEmail: string\n githubToken: string\n gitUser: string\n isCi: boolean\n prs: PrMatch[]\n repoInfo: RepoInfo | null\n}\n\nexport async function getFixEnv(): Promise<FixEnv> {\n const baseBranch = await getBaseBranch()\n const gitEmail = constants.ENV.SOCKET_CLI_GIT_USER_EMAIL\n const gitUser = constants.ENV.SOCKET_CLI_GIT_USER_NAME\n const githubToken = constants.ENV.SOCKET_CLI_GITHUB_TOKEN\n const isCi = !!(constants.ENV.CI && gitEmail && gitUser && githubToken)\n\n if (\n // If isCi is false,\n !isCi &&\n // but some CI checks are passing,\n (constants.ENV.CI || gitEmail || gitUser || githubToken) &&\n // then log about it when in debug mode.\n isDebug('notice')\n ) {\n const envVars = [\n ...(constants.ENV.CI ? [] : ['process.env.CI']),\n ...(gitEmail ? [] : ['process.env.SOCKET_CLI_GIT_USER_EMAIL']),\n ...(gitUser ? [] : ['process.env.SOCKET_CLI_GIT_USER_NAME']),\n ...(githubToken ? [] : ['process.env.GITHUB_TOKEN']),\n ]\n debugFn(\n 'notice',\n `miss: fixEnv.isCi is false, expected ${joinAnd(envVars)} to be set`,\n )\n }\n\n let repoInfo: RepoInfo | null = null\n if (isCi) {\n repoInfo = ciRepoInfo()\n }\n if (!repoInfo) {\n if (isCi) {\n debugFn('notice', 'falling back to `git remote get-url origin`')\n }\n repoInfo = await getRepoInfo()\n }\n\n const prs =\n isCi && repoInfo\n ? await getSocketPrs(repoInfo.owner, repoInfo.repo, {\n author: gitUser,\n states: 'all',\n })\n : []\n\n return {\n baseBranch,\n gitEmail,\n githubToken,\n gitUser,\n isCi,\n prs,\n repoInfo,\n }\n}\n","import path from 'node:path'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getFixEnv } from './env-helpers.mts'\nimport { getSocketFixBranchName, getSocketFixCommitMessage } from './git.mts'\nimport { openSocketFixPr } from './pull-request.mts'\nimport { handleApiCall } from '../../utils/api.mts'\nimport { cmdFlagValueToArray } from '../../utils/cmd.mts'\nimport { spawnCoana } from '../../utils/coana.mts'\nimport {\n gitCheckoutBranch,\n gitCommit,\n gitCreateBranch,\n gitDeleteBranch,\n gitPushBranch,\n gitRemoteBranchExists,\n gitResetAndClean,\n gitUnstagedModifiedFiles,\n} from '../../utils/git.mts'\nimport {\n enablePrAutoMerge,\n fetchGhsaDetails,\n setGitRemoteGithubRepoUrl,\n} from '../../utils/github.mts'\nimport { getPackageFilesForScan } from '../../utils/path-resolve.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\nimport { fetchSupportedScanFileNames } from '../scan/fetch-supported-scan-file-names.mts'\n\nimport type { FixConfig } from './types.mts'\nimport type { CResult } from '../../types.mts'\n\nexport async function coanaFix(\n fixConfig: FixConfig,\n): Promise<CResult<{ fixed: boolean }>> {\n const { autoMerge, cwd, ghsas, limit, orgSlug, spinner } = fixConfig\n\n const fixEnv = await getFixEnv()\n debugDir('inspect', { fixEnv })\n\n spinner?.start()\n\n const sockSdkCResult = await setupSdk()\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n\n const sockSdk = sockSdkCResult.data\n\n const supportedFilesCResult = await fetchSupportedScanFileNames({ spinner })\n if (!supportedFilesCResult.ok) {\n return supportedFilesCResult\n }\n\n const supportedFiles = supportedFilesCResult.data\n const scanFilepaths = await getPackageFilesForScan(['.'], supportedFiles, {\n cwd,\n })\n const uploadCResult = await handleApiCall(\n sockSdk.uploadManifestFiles(orgSlug, scanFilepaths),\n {\n desc: 'upload manifests',\n spinner,\n },\n )\n\n if (!uploadCResult.ok) {\n return uploadCResult\n }\n\n const tarHash: string = (uploadCResult as any).data.tarHash\n if (!tarHash) {\n spinner?.stop()\n return {\n ok: false,\n message:\n 'No tar hash returned from Socket API upload-manifest-files endpoint',\n data: uploadCResult.data,\n }\n }\n\n const isAll =\n !ghsas.length ||\n (ghsas.length === 1 && (ghsas[0] === 'all' || ghsas[0] === 'auto'))\n\n const shouldOpenPrs = fixEnv.isCi && fixEnv.repoInfo\n\n if (!shouldOpenPrs) {\n const ids = isAll ? ['all'] : ghsas.slice(0, limit)\n if (!ids.length) {\n spinner?.stop()\n return { ok: true, data: { fixed: false } }\n }\n\n const fixCResult = await spawnCoana(\n [\n 'compute-fixes-and-upgrade-purls',\n cwd,\n '--manifests-tar-hash',\n tarHash,\n '--apply-fixes-to',\n ...(isAll ? ['all'] : ghsas),\n ...(fixConfig.rangeStyle\n ? ['--range-style', fixConfig.rangeStyle]\n : []),\n ...fixConfig.unknownFlags,\n ],\n fixConfig.orgSlug,\n { cwd, spinner, stdio: 'inherit' },\n )\n\n spinner?.stop()\n\n return fixCResult.ok ? { ok: true, data: { fixed: true } } : fixCResult\n }\n\n let ids: string[] | undefined\n if (isAll) {\n const foundCResult = await spawnCoana(\n [\n 'compute-fixes-and-upgrade-purls',\n cwd,\n '--manifests-tar-hash',\n tarHash,\n ...(fixConfig.rangeStyle\n ? ['--range-style', fixConfig.rangeStyle]\n : []),\n ...fixConfig.unknownFlags,\n ],\n fixConfig.orgSlug,\n { cwd, spinner },\n )\n if (foundCResult.ok) {\n const foundIds = cmdFlagValueToArray(\n /(?<=Vulnerabilities found:).*/.exec(foundCResult.data),\n )\n ids = foundIds.slice(0, limit)\n }\n } else {\n ids = ghsas.slice(0, limit)\n }\n\n if (!ids?.length) {\n debugFn('notice', 'miss: no GHSA IDs to process')\n }\n\n if (!fixEnv.repoInfo) {\n debugFn('notice', 'miss: no repo info detected')\n }\n\n if (!ids?.length || !fixEnv.repoInfo) {\n spinner?.stop()\n return { ok: true, data: { fixed: false } }\n }\n\n debugFn('notice', `fetch: ${ids.length} GHSA details for ${joinAnd(ids)}`)\n\n const ghsaDetails = await fetchGhsaDetails(ids)\n const scanBaseNames = new Set(scanFilepaths.map(p => path.basename(p)))\n\n debugFn('notice', `found: ${ghsaDetails.size} GHSA details`)\n\n let count = 0\n let overallFixed = false\n\n // Process each GHSA ID individually, similar to npm-fix/pnpm-fix.\n ghsaLoop: for (let i = 0, { length } = ids; i < length; i += 1) {\n const ghsaId = ids[i]!\n debugFn('notice', `check: ${ghsaId}`)\n\n // Apply fix for single GHSA ID.\n // eslint-disable-next-line no-await-in-loop\n const fixCResult = await spawnCoana(\n [\n 'compute-fixes-and-upgrade-purls',\n cwd,\n '--manifests-tar-hash',\n tarHash,\n '--apply-fixes-to',\n ghsaId,\n ...(fixConfig.rangeStyle\n ? ['--range-style', fixConfig.rangeStyle]\n : []),\n ...fixConfig.unknownFlags,\n ],\n fixConfig.orgSlug,\n { cwd, spinner, stdio: 'inherit' },\n )\n\n if (!fixCResult.ok) {\n logger.error(\n `Update failed for ${ghsaId}: ${fixCResult.message || 'Unknown error'}`,\n )\n continue ghsaLoop\n }\n\n // Check for modified files after applying the fix.\n // eslint-disable-next-line no-await-in-loop\n const unstagedCResult = await gitUnstagedModifiedFiles(cwd)\n const modifiedFiles = unstagedCResult.ok\n ? unstagedCResult.data.filter(relPath =>\n scanBaseNames.has(path.basename(relPath)),\n )\n : []\n\n if (!modifiedFiles.length) {\n debugFn('notice', `skip: no changes for ${ghsaId}`)\n continue ghsaLoop\n }\n\n overallFixed = true\n\n const branch = getSocketFixBranchName(ghsaId)\n\n try {\n // Check if branch already exists.\n // eslint-disable-next-line no-await-in-loop\n if (await gitRemoteBranchExists(branch, cwd)) {\n debugFn('notice', `skip: remote branch \"${branch}\" exists`)\n continue ghsaLoop\n }\n\n debugFn('notice', `pr: creating for ${ghsaId}`)\n\n const details = ghsaDetails.get(ghsaId)\n debugFn(\n 'notice',\n `ghsa: ${ghsaId} details ${details ? 'found' : 'missing'}`,\n )\n\n const pushed =\n // eslint-disable-next-line no-await-in-loop\n (await gitCreateBranch(branch, cwd)) &&\n // eslint-disable-next-line no-await-in-loop\n (await gitCheckoutBranch(branch, cwd)) &&\n // eslint-disable-next-line no-await-in-loop\n (await gitCommit(\n getSocketFixCommitMessage(ghsaId, details),\n modifiedFiles,\n {\n cwd,\n email: fixEnv.gitEmail,\n user: fixEnv.gitUser,\n },\n )) &&\n // eslint-disable-next-line no-await-in-loop\n (await gitPushBranch(branch, cwd))\n\n if (!pushed) {\n logger.warn(`Push failed for ${ghsaId}, skipping PR creation.`)\n // eslint-disable-next-line no-await-in-loop\n await gitResetAndClean(fixEnv.baseBranch, cwd)\n // eslint-disable-next-line no-await-in-loop\n await gitCheckoutBranch(fixEnv.baseBranch, cwd)\n // eslint-disable-next-line no-await-in-loop\n await gitDeleteBranch(branch, cwd)\n continue ghsaLoop\n }\n\n // Set up git remote.\n // eslint-disable-next-line no-await-in-loop\n await setGitRemoteGithubRepoUrl(\n fixEnv.repoInfo.owner,\n fixEnv.repoInfo.repo,\n fixEnv.githubToken!,\n cwd,\n )\n\n // eslint-disable-next-line no-await-in-loop\n const prResponse = await openSocketFixPr(\n fixEnv.repoInfo.owner,\n fixEnv.repoInfo.repo,\n branch,\n // Single GHSA ID.\n [ghsaId],\n {\n baseBranch: fixEnv.baseBranch,\n cwd,\n ghsaDetails,\n },\n )\n\n if (prResponse) {\n const { data } = prResponse\n const prRef = `PR #${data.number}`\n\n logger.success(`Opened ${prRef} for ${ghsaId}.`)\n\n if (autoMerge) {\n logger.indent()\n spinner?.indent()\n // eslint-disable-next-line no-await-in-loop\n const { details, enabled } = await enablePrAutoMerge(data)\n if (enabled) {\n logger.info(`Auto-merge enabled for ${prRef}.`)\n } else {\n const message = `Failed to enable auto-merge for ${prRef}${\n details ? `:\\n${details.map(d => ` - ${d}`).join('\\n')}` : '.'\n }`\n logger.error(message)\n }\n logger.dedent()\n spinner?.dedent()\n }\n }\n\n // Reset back to base branch for next iteration.\n // eslint-disable-next-line no-await-in-loop\n await gitResetAndClean(branch, cwd)\n // eslint-disable-next-line no-await-in-loop\n await gitCheckoutBranch(fixEnv.baseBranch, cwd)\n } catch (e) {\n logger.warn(\n `Unexpected condition: Push failed for ${ghsaId}, skipping PR creation.`,\n )\n debugDir('inspect', { error: e })\n // eslint-disable-next-line no-await-in-loop\n await gitResetAndClean(fixEnv.baseBranch, cwd)\n // eslint-disable-next-line no-await-in-loop\n await gitCheckoutBranch(fixEnv.baseBranch, cwd)\n }\n\n count += 1\n debugFn(\n 'notice',\n `increment: count ${count}/${Math.min(limit, ids.length)}`,\n )\n if (count >= limit) {\n break ghsaLoop\n }\n }\n\n spinner?.stop()\n\n return {\n ok: true,\n data: { fixed: overallFixed },\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputFixResult(\n result: CResult<unknown>,\n outputKind: OutputKind,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log('')\n logger.success('Finished!')\n}\n","import { coanaFix } from './coana-fix.mts'\nimport { outputFixResult } from './output-fix-result.mts'\n\nimport type { FixConfig } from './types.mts'\nimport type { OutputKind } from '../../types.mts'\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\n\nexport type HandleFixConfig = Remap<\n FixConfig & {\n ghsas: string[]\n orgSlug: string\n outputKind: OutputKind\n unknownFlags: string[]\n }\n>\n\nexport async function handleFix({\n autoMerge,\n cwd,\n ghsas,\n limit,\n minSatisfying,\n orgSlug,\n outputKind,\n prCheck,\n purls,\n rangeStyle,\n spinner,\n test,\n testScript,\n unknownFlags,\n}: HandleFixConfig) {\n await outputFixResult(\n await coanaFix({\n autoMerge,\n cwd,\n ghsas,\n limit,\n minSatisfying,\n orgSlug,\n prCheck,\n purls,\n rangeStyle,\n spinner,\n test,\n testScript,\n unknownFlags,\n }),\n outputKind,\n )\n}\n","import path from 'node:path'\n\nimport terminalLink from 'terminal-link'\n\nimport { arrayUnique, joinOr } from '@socketsecurity/registry/lib/arrays'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleFix } from './handle-fix.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { cmdFlagValueToArray } from '../../utils/cmd.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { getPurlObject } from '../../utils/purl.mts'\nimport { RangeStyles } from '../../utils/semver.mts'\nimport { getDefaultOrgSlug } from '../ci/fetch-default-org-slug.mts'\n\nimport type { MeowFlag, MeowFlags } from '../../flags.mts'\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\nimport type { RangeStyle } from '../../utils/semver.mts'\n\nexport const CMD_NAME = 'fix'\n\nconst DEFAULT_LIMIT = 10\n\nconst description = 'Update dependencies with \"fixable\" Socket alerts'\n\nconst hidden = false\n\nexport const cmdFix = {\n description,\n hidden,\n run,\n}\n\nconst generalFlags: MeowFlags = {\n autoMerge: {\n type: 'boolean',\n default: false,\n description: `Enable auto-merge for pull requests that Socket opens.\\nSee ${terminalLink(\n 'GitHub documentation',\n 'https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-auto-merge-for-pull-requests-in-your-repository',\n )} for managing auto-merge for pull requests in your repository.`,\n },\n id: {\n type: 'string',\n default: [],\n description: `Provide a list of ${terminalLink(\n 'GHSA IDs',\n 'https://docs.github.com/en/code-security/security-advisories/working-with-global-security-advisories-from-the-github-advisory-database/about-the-github-advisory-database#about-ghsa-ids',\n )} to compute fixes for, as either a comma separated value or as multiple flags`,\n isMultiple: true,\n },\n limit: {\n type: 'number',\n default: DEFAULT_LIMIT,\n description: `The number of fixes to attempt at a time (default ${DEFAULT_LIMIT})`,\n },\n rangeStyle: {\n type: 'string',\n default: 'preserve',\n description: `\nDefine how dependency version ranges are updated in package.json (default 'preserve').\nAvailable styles:\n * caret - Use ^ range for compatible updates (e.g. ^1.2.3)\n * gt - Use > to allow any newer version (e.g. >1.2.3)\n * gte - Use >= to allow any newer version (e.g. >=1.2.3)\n * lt - Use < to allow only lower versions (e.g. <1.2.3)\n * lte - Use <= to allow only lower versions (e.g. <=1.2.3)\n * pin - Use the exact version (e.g. 1.2.3)\n * preserve - Retain the existing version range style as-is\n * tilde - Use ~ range for patch/minor updates (e.g. ~1.2.3)\n `.trim(),\n },\n}\n\nconst hiddenFlags: MeowFlags = {\n autopilot: {\n type: 'boolean',\n default: false,\n description: `Shorthand for --auto-merge --test`,\n hidden: true,\n },\n ghsa: {\n ...generalFlags['id'],\n hidden: true,\n } as MeowFlag,\n maxSatisfying: {\n type: 'boolean',\n default: true,\n description: 'Use the maximum satisfying version for dependency updates',\n hidden: true,\n },\n minSatisfying: {\n type: 'boolean',\n default: false,\n description:\n 'Constrain dependency updates to the minimum satisfying version',\n hidden: true,\n },\n prCheck: {\n type: 'boolean',\n default: true,\n description: 'Check for an existing PR before attempting a fix',\n hidden: true,\n },\n purl: {\n type: 'string',\n default: [],\n description: `Provide a list of ${terminalLink(\n 'PURLs',\n 'https://github.com/package-url/purl-spec?tab=readme-ov-file#purl',\n )} to compute fixes for, as either a comma separated value or as\\nmultiple flags`,\n isMultiple: true,\n shortFlag: 'p',\n hidden: true,\n },\n test: {\n type: 'boolean',\n default: false,\n description: 'Verify the fix by running unit tests',\n hidden: true,\n },\n testScript: {\n type: 'string',\n default: 'test',\n description: \"The test script to run for fix attempts (default 'test')\",\n hidden: true,\n },\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n ...generalFlags,\n ...hiddenFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} ./proj/tree --auto-merge\n `,\n }\n\n const cli = meowOrExit({\n allowUnknownFlags: false,\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n let rangeStyle = cli.flags['rangeStyle'] as RangeStyle\n if (!rangeStyle) {\n rangeStyle = 'preserve'\n }\n\n const rawPurls = cmdFlagValueToArray(cli.flags['purl'])\n const purls = []\n for (const purl of rawPurls) {\n const version = getPurlObject(purl, { throws: false })?.version\n if (version) {\n purls.push(purl)\n } else {\n logger.warn(`--purl ${purl} is missing a version and will be ignored.`)\n }\n }\n if (rawPurls.length !== purls.length && !purls.length) {\n process.exitCode = 1\n logger.fail('No valid --purl values provided.')\n return\n }\n\n const outputKind = getOutputKind(cli.flags['json'], cli.flags['markdown'])\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: RangeStyles.includes(rangeStyle),\n message: `Expecting range style of ${joinOr(RangeStyles)}`,\n fail: 'invalid',\n },\n {\n nook: true,\n test: !cli.flags['json'] || !cli.flags['markdown'],\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_NOT_SAVING)\n return\n }\n\n const orgSlugCResult = await getDefaultOrgSlug()\n if (!orgSlugCResult.ok) {\n process.exitCode = orgSlugCResult.code ?? 1\n logger.fail(\n 'Unable to resolve a Socket account organization.\\nEnsure a Socket API token is specified for the organization using the SOCKET_CLI_API_TOKEN environment variable.',\n )\n return\n }\n\n const orgSlug = orgSlugCResult.data\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n let autoMerge = Boolean(cli.flags['autoMerge'])\n let test = Boolean(cli.flags['test'])\n if (cli.flags['autopilot']) {\n autoMerge = true\n test = true\n }\n\n const { spinner } = constants\n // We patched in this feature with `npx custompatch meow` at\n // socket-cli/patches/meow#13.2.0.patch.\n const unknownFlags = cli.unknownFlags ?? []\n const ghsas = arrayUnique([\n ...cmdFlagValueToArray(cli.flags['id']),\n ...cmdFlagValueToArray(cli.flags['ghsa']),\n ])\n const limit = Number(cli.flags['limit']) || DEFAULT_LIMIT\n const maxSatisfying = Boolean(cli.flags['maxSatisfying'])\n const minSatisfying = Boolean(cli.flags['minSatisfying']) || !maxSatisfying\n const prCheck = Boolean(cli.flags['prCheck'])\n const testScript = String(cli.flags['testScript'] || 'test')\n\n await handleFix({\n autoMerge,\n cwd,\n ghsas,\n limit,\n minSatisfying,\n prCheck,\n orgSlug,\n outputKind,\n purls,\n rangeStyle,\n spinner,\n test,\n testScript,\n unknownFlags,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport async function outputInstallCompletion(\n result: CResult<{\n actions: string[]\n bashrcPath: string\n completionCommand: string\n bashrcUpdated: boolean\n foundBashrc: boolean\n sourcingCommand: string\n targetName: string\n targetPath: string\n }>,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log('')\n logger.log(\n `Installation of tab completion for \"${result.data.targetName}\" finished!`,\n )\n logger.log('')\n\n result.data.actions.forEach(action => {\n logger.log(` - ${action}`)\n })\n logger.log('')\n logger.log('Socket tab completion works automatically in new terminals.')\n logger.log('')\n logger.log(\n 'Due to a bash limitation, tab completion cannot be enabled in the',\n )\n logger.log('current shell (bash instance) through NodeJS. You must either:')\n logger.log('')\n logger.log('1. Reload your .bashrc script (best):')\n logger.log('')\n logger.log(` source ~/.bashrc`)\n logger.log('')\n logger.log('2. Run these commands to load the completion script:')\n logger.log('')\n logger.log(` source ${result.data.targetPath}`)\n logger.log(` ${result.data.completionCommand}`)\n logger.log('')\n logger.log('3. Or restart bash somehow (restart terminal or run `bash`)')\n logger.log('')\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport constants from '../../constants.mts'\nimport { getBashrcDetails } from '../../utils/completion.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport async function setupTabCompletion(targetName: string): Promise<\n CResult<{\n actions: string[]\n bashrcPath: string\n bashrcUpdated: boolean\n completionCommand: string\n foundBashrc: boolean\n sourcingCommand: string\n targetName: string\n targetPath: string\n }>\n> {\n const result = getBashrcDetails(targetName)\n if (!result.ok) {\n return result\n }\n\n const { completionCommand, sourcingCommand, targetPath, toAddToBashrc } =\n result.data\n\n // Target dir is something like ~/.local/share/socket/settings/completion (linux)\n const targetDir = path.dirname(targetPath)\n debugFn('notice', 'target: path + dir', targetPath, targetDir)\n\n if (!fs.existsSync(targetDir)) {\n debugFn('notice', 'create: target dir')\n fs.mkdirSync(targetDir, { recursive: true })\n }\n\n updateInstalledTabCompletionScript(targetPath)\n\n let bashrcUpdated = false\n\n // Add to ~/.bashrc if not already there\n const bashrcPath = constants.homePath\n ? path.join(constants.homePath, '.bashrc')\n : ''\n\n const foundBashrc = Boolean(bashrcPath && fs.existsSync(bashrcPath))\n\n if (foundBashrc) {\n const content = fs.readFileSync(bashrcPath, 'utf8')\n if (!content.includes(sourcingCommand)) {\n fs.appendFileSync(bashrcPath, toAddToBashrc)\n bashrcUpdated = true\n }\n }\n\n return {\n ok: true,\n data: {\n actions: [\n `Installed the tab completion script in ${targetPath}`,\n bashrcUpdated\n ? 'Added tab completion loader to ~/.bashrc'\n : foundBashrc\n ? 'Tab completion already found in ~/.bashrc'\n : 'No ~/.bashrc found so tab completion was not completely installed',\n ],\n bashrcPath,\n bashrcUpdated,\n completionCommand,\n foundBashrc,\n sourcingCommand,\n targetName,\n targetPath,\n },\n }\n}\n\nfunction getTabCompletionScriptRaw(): CResult<string> {\n const sourceDir = path.dirname(fileURLToPath(import.meta.url))\n const sourcePath = path.join(sourceDir, 'socket-completion.bash')\n\n if (!fs.existsSync(sourcePath)) {\n return {\n ok: false,\n message: 'Source not found.',\n cause: `Unable to find the source tab completion bash script that Socket should ship. Expected to find it in \\`${sourcePath}\\` but it was not there.`,\n }\n }\n\n return { ok: true, data: fs.readFileSync(sourcePath, 'utf8') }\n}\n\nexport function updateInstalledTabCompletionScript(\n targetPath: string,\n): CResult<undefined> {\n const content = getTabCompletionScriptRaw()\n if (!content.ok) {\n return content\n }\n\n // When installing set the current package.json version.\n // Later, we can call _socket_completion_version to get the installed version.\n fs.writeFileSync(\n targetPath,\n content.data.replaceAll(\n '%SOCKET_VERSION_TOKEN%',\n constants.ENV.INLINED_SOCKET_CLI_VERSION_HASH,\n ),\n 'utf8',\n )\n\n return { ok: true, data: undefined }\n}\n","import { outputInstallCompletion } from './output-install-completion.mts'\nimport { setupTabCompletion } from './setup-tab-completion.mts'\n\nexport async function handleInstallCompletion(targetName: string) {\n const result = await setupTabCompletion(targetName)\n await outputInstallCompletion(result)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleInstallCompletion } from './handle-install-completion.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'completion',\n description: 'Install bash completion for Socket CLI',\n hidden: false,\n flags: {\n ...commonFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [NAME=socket]\n\n Installs bash completion for the Socket CLI. This will:\n 1. Source the completion script in your current shell\n 2. Add the source command to your ~/.bashrc if it's not already there\n\n This command will only setup tab completion, nothing else.\n\n Afterwards you should be able to type \\`socket \\` and then press tab to\n have bash auto-complete/suggest the sub/command or flags.\n\n Currently only supports bash.\n\n The optional name argument allows you to enable tab completion on a command\n name other than \"socket\". Mostly for debugging but also useful if you use a\n different alias for socket on your system.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n\n $ ${command}\n $ ${command} sd\n $ ${command} ./sd\n `,\n}\n\nexport const cmdInstallCompletion = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n const targetName = cli.input[0] || 'socket'\n\n await handleInstallCompletion(String(targetName))\n}\n","import { cmdInstallCompletion } from './cmd-install-completion.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Install Socket CLI tab completion'\n\nexport const cmdInstall: CliSubcommand = {\n description,\n hidden: false,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n completion: cmdInstallCompletion,\n },\n {\n argv,\n description,\n importMeta,\n name: `${parentName} install`,\n },\n )\n },\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport {\n safeReadFileSync,\n safeStatsSync,\n} from '@socketsecurity/registry/lib/fs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { tildify } from '../../utils/tildify.mts'\n\nexport async function outputCmdJson(cwd: string) {\n logger.info('Target cwd:', constants.ENV.VITEST ? '<redacted>' : tildify(cwd))\n\n const sockJsonPath = path.join(cwd, 'socket.json')\n const tildeSockJsonPath = constants.ENV.VITEST\n ? '<redacted>'\n : tildify(sockJsonPath)\n\n if (!existsSync(sockJsonPath)) {\n logger.fail(`Not found: ${tildeSockJsonPath}`)\n process.exitCode = 1\n return\n }\n\n if (!safeStatsSync(sockJsonPath)?.isFile()) {\n logger.fail(\n `This is not a regular file (maybe a directory?): ${tildeSockJsonPath}`,\n )\n process.exitCode = 1\n return\n }\n\n logger.success(`This is the contents of ${tildeSockJsonPath}:`)\n logger.error('')\n\n const data = safeReadFileSync(sockJsonPath)\n logger.log(data)\n}\n","import { outputCmdJson } from './output-cmd-json.mts'\n\nexport async function handleCmdJson(cwd: string) {\n await outputCmdJson(cwd)\n}\n","import path from 'node:path'\n\nimport { handleCmdJson } from './handle-cmd-json.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'json',\n description:\n 'Display the `socket.json` that would be applied for target folder',\n hidden: true,\n flags: {\n ...commonFlags,\n },\n help: command => `\n Usage\n $ ${command} [options] [CWD=.]\n\n Display the \\`socket.json\\` file that would apply when running relevant commands\n in the target directory.\n\n Examples\n $ ${command}\n `,\n}\n\nexport const cmdJson = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n await handleCmdJson(cwd)\n}\n","import { updateConfigValue } from '../../utils/config.mts'\n\nexport function applyLogin(\n apiToken: string,\n enforcedOrgs: string[],\n apiBaseUrl: string | undefined,\n apiProxy: string | undefined,\n) {\n updateConfigValue('enforcedOrgs', enforcedOrgs)\n updateConfigValue('apiToken', apiToken)\n updateConfigValue('apiBaseUrl', apiBaseUrl)\n updateConfigValue('apiProxy', apiProxy)\n}\n","import terminalLink from 'terminal-link'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { confirm, password, select } from '@socketsecurity/registry/lib/prompts'\n\nimport { applyLogin } from './apply-login.mts'\nimport constants from '../../constants.mts'\nimport {\n getConfigValueOrUndef,\n isReadOnlyConfig,\n updateConfigValue,\n} from '../../utils/config.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { getEnterpriseOrgs, getOrgSlugs } from '../../utils/organization.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\nimport { setupTabCompletion } from '../install/setup-tab-completion.mts'\nimport { fetchOrganization } from '../organization/fetch-organization-list.mts'\n\nimport type { Choice, Separator } from '@socketsecurity/registry/lib/prompts'\n\ntype OrgChoice = Choice<string>\ntype OrgChoices = Array<Separator | OrgChoice>\n\nexport async function attemptLogin(\n apiBaseUrl: string | undefined,\n apiProxy: string | undefined,\n) {\n apiBaseUrl ??= getConfigValueOrUndef('apiBaseUrl') ?? undefined\n apiProxy ??= getConfigValueOrUndef('apiProxy') ?? undefined\n const apiTokenInput = await password({\n message: `Enter your ${terminalLink(\n 'Socket.dev API token',\n 'https://docs.socket.dev/docs/api-keys',\n )} (leave blank to use a limited public token)`,\n })\n\n if (apiTokenInput === undefined) {\n logger.fail('Canceled by user')\n return { ok: false, message: 'Canceled', cause: 'Canceled by user' }\n }\n\n const apiToken = apiTokenInput || constants.SOCKET_PUBLIC_API_TOKEN\n\n const sockSdkCResult = await setupSdk({ apiBaseUrl, apiProxy, apiToken })\n if (!sockSdkCResult.ok) {\n process.exitCode = 1\n logger.fail(failMsgWithBadge(sockSdkCResult.message, sockSdkCResult.cause))\n return\n }\n\n const sockSdk = sockSdkCResult.data\n\n const orgsCResult = await fetchOrganization({\n desc: 'token verification',\n sdk: sockSdk,\n })\n if (!orgsCResult.ok) {\n process.exitCode = 1\n logger.fail(failMsgWithBadge(orgsCResult.message, orgsCResult.cause))\n return\n }\n\n const { organizations } = orgsCResult.data\n\n const orgSlugs = getOrgSlugs(organizations)\n\n logger.success(`API token verified: ${joinAnd(orgSlugs)}`)\n\n const enterpriseOrgs = getEnterpriseOrgs(organizations)\n\n const enforcedChoices: OrgChoices = enterpriseOrgs.map(org => ({\n name: org.name ?? 'undefined',\n value: org.id,\n }))\n\n let enforcedOrgs: string[] = []\n if (enforcedChoices.length > 1) {\n const id = await select({\n message:\n \"Which organization's policies should Socket enforce system-wide?\",\n choices: [\n ...enforcedChoices,\n {\n name: 'None',\n value: '',\n description: 'Pick \"None\" if this is a personal device',\n },\n ],\n })\n if (id === undefined) {\n logger.fail('Canceled by user')\n return { ok: false, message: 'Canceled', cause: 'Canceled by user' }\n }\n if (id) {\n enforcedOrgs = [id]\n }\n } else if (enforcedChoices.length) {\n const shouldEnforce = await confirm({\n message: `Should Socket enforce ${(enforcedChoices[0] as OrgChoice)?.name}'s security policies system-wide?`,\n default: true,\n })\n if (shouldEnforce === undefined) {\n logger.fail('Canceled by user')\n return { ok: false, message: 'Canceled', cause: 'Canceled by user' }\n }\n if (shouldEnforce) {\n const existing = enforcedChoices[0] as OrgChoice\n if (existing) {\n enforcedOrgs = [existing.value]\n }\n }\n }\n\n const wantToComplete = await select({\n message: 'Would you like to install bash tab completion?',\n choices: [\n {\n name: 'Yes',\n value: true,\n description:\n 'Sets up tab completion for \"socket\" in your bash env. If you\\'re unsure, this is probably what you want.',\n },\n {\n name: 'No',\n value: false,\n description:\n 'Will skip tab completion setup. Does not change how Socket works.',\n },\n ],\n })\n if (wantToComplete === undefined) {\n logger.fail('Canceled by user')\n return { ok: false, message: 'Canceled', cause: 'Canceled by user' }\n }\n if (wantToComplete) {\n logger.log('')\n logger.log('Setting up tab completion...')\n const setupCResult = await setupTabCompletion('socket')\n if (setupCResult.ok) {\n logger.success(\n 'Tab completion will be enabled after restarting your terminal',\n )\n } else {\n logger.fail(\n 'Failed to install tab completion script. Try `socket install completion` later.',\n )\n }\n }\n\n updateConfigValue('defaultOrg', orgSlugs[0])\n\n const previousPersistedToken = getConfigValueOrUndef('apiToken')\n try {\n applyLogin(apiToken, enforcedOrgs, apiBaseUrl, apiProxy)\n logger.success(\n `API credentials ${previousPersistedToken === apiToken ? 'refreshed' : previousPersistedToken ? 'updated' : 'set'}`,\n )\n if (isReadOnlyConfig()) {\n logger.log('')\n logger.warn(\n 'Note: config is in read-only mode, at least one key was overridden through flag/env, so the login was not persisted!',\n )\n }\n } catch {\n process.exitCode = 1\n logger.fail(`API login failed`)\n }\n}\n","import isInteractive from '@socketregistry/is-interactive/index.cjs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { attemptLogin } from './attempt-login.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { InputError } from '../../utils/errors.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'login'\n\nconst description = 'Setup Socket CLI with an API token and defaults'\n\nconst hidden = false\n\nexport const cmdLogin = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n apiBaseUrl: {\n type: 'string',\n description: 'API server to connect to for login',\n },\n apiProxy: {\n type: 'string',\n description: 'Proxy to use when making connection to API server',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Logs into the Socket API by prompting for an API token\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} --api-proxy=http://localhost:1234\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n if (!isInteractive()) {\n throw new InputError(\n 'Cannot prompt for credentials in a non-interactive shell. Use SOCKET_CLI_API_TOKEN environment variable instead',\n )\n }\n\n const apiBaseUrl = cli.flags['apiBaseUrl'] as string | undefined\n\n const apiProxy = cli.flags['apiProxy'] as string | undefined\n\n await attemptLogin(apiBaseUrl, apiProxy)\n}\n","import { updateConfigValue } from '../../utils/config.mts'\n\nexport function applyLogout() {\n updateConfigValue('apiToken', null)\n updateConfigValue('apiBaseUrl', null)\n updateConfigValue('apiProxy', null)\n updateConfigValue('enforcedOrgs', null)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { applyLogout } from './apply-logout.mts'\nimport { isReadOnlyConfig } from '../../utils/config.mts'\n\nexport function attemptLogout() {\n try {\n applyLogout()\n logger.success('Successfully logged out')\n if (isReadOnlyConfig()) {\n logger.log('')\n logger.warn(\n 'Note: config is in read-only mode, at least one key was overridden through flag/env, so the logout was not persisted!',\n )\n }\n } catch {\n logger.fail('Failed to complete logout steps')\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { attemptLogout } from './attempt-logout.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'logout',\n description: 'Socket API logout',\n hidden: false,\n flags: {\n ...commonFlags,\n },\n help: (command, _config) => `\n Usage\n $ ${command} [options]\n\n Logs out of the Socket API and clears all Socket credentials from disk\n\n Examples\n $ ${command}\n `,\n}\n\nexport const cmdLogout = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n attemptLogout()\n}\n","import { existsSync, rmSync } from 'node:fs'\nimport path from 'node:path'\n\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants, { NPM, PNPM } from '../../constants.mts'\nimport shadowBin from '../../shadow/npm/bin.mts'\n\nimport type {\n ShadowBinOptions,\n ShadowBinResult,\n} from '../../shadow/npm/bin.mts'\n\nconst { PACKAGE_LOCK_JSON, YARN, YARN_LOCK } = constants\n\nconst nodejsPlatformTypes = new Set([\n 'javascript',\n 'js',\n 'nodejs',\n NPM,\n PNPM,\n 'ts',\n 'tsx',\n 'typescript',\n])\n\nexport type ArgvObject = {\n [key: string]: boolean | null | number | string | Array<string | number>\n}\n\nfunction argvToArray(argvObj: ArgvObject): string[] {\n if (argvObj['help']) {\n return ['--help']\n }\n const result = []\n for (const { 0: key, 1: value } of Object.entries(argvObj)) {\n if (key === '_' || key === '--') {\n continue\n }\n if (key === 'babel' || key === 'install-deps' || key === 'validate') {\n // cdxgen documents no-babel, no-install-deps, and no-validate flags so\n // use them when relevant.\n result.push(`--${value ? key : `no-${key}`}`)\n } else if (value === true) {\n result.push(`--${key}`)\n } else if (typeof value === 'string') {\n result.push(`--${key}`, String(value))\n } else if (Array.isArray(value)) {\n result.push(`--${key}`, ...value.map(String))\n }\n }\n const pathArgs = argvObj['_'] as string[]\n if (Array.isArray(pathArgs)) {\n result.push(...pathArgs)\n }\n const argsAfterDoubleHyphen = argvObj['--'] as string[]\n if (Array.isArray(argsAfterDoubleHyphen)) {\n result.push('--', ...argsAfterDoubleHyphen)\n }\n return result\n}\n\nexport async function runCdxgen(argvObj: ArgvObject): Promise<ShadowBinResult> {\n let cleanupPackageLock = false\n const argvMutable = { __proto__: null, ...argvObj } as ArgvObject\n const shadowOpts: ShadowBinOptions = {\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 },\n stdio: 'inherit',\n }\n if (\n argvMutable['type'] !== YARN &&\n nodejsPlatformTypes.has(argvMutable['type'] as string) &&\n existsSync(`./${YARN_LOCK}`)\n ) {\n if (existsSync(`./${PACKAGE_LOCK_JSON}`)) {\n argvMutable['type'] = NPM\n } else {\n // Use synp to create a package-lock.json from the yarn.lock,\n // based on the node_modules folder, for a more accurate SBOM.\n try {\n const { spawnPromise: synpPromise } = await shadowBin(\n 'npx',\n [\n '--yes',\n `synp@${constants.ENV.INLINED_SOCKET_CLI_SYNP_VERSION}`,\n '--source-file',\n `./${YARN_LOCK}`,\n ],\n shadowOpts,\n )\n await synpPromise\n argvMutable['type'] = NPM\n cleanupPackageLock = true\n } catch {}\n }\n }\n\n const shadowResult = await shadowBin(\n 'npx',\n [\n '--yes',\n `@cyclonedx/cdxgen@${constants.ENV.INLINED_SOCKET_CLI_CYCLONEDX_CDXGEN_VERSION}`,\n ...argvToArray(argvMutable),\n ],\n shadowOpts,\n )\n\n shadowResult.spawnPromise.process.on('exit', () => {\n if (cleanupPackageLock) {\n try {\n rmSync(`./${PACKAGE_LOCK_JSON}`)\n } catch {}\n }\n\n const outputPath = argvMutable['output'] as string\n if (outputPath) {\n const fullOutputPath = path.join(process.cwd(), outputPath)\n if (existsSync(fullOutputPath)) {\n logger.log(colors.cyanBright(`${outputPath} created!`))\n }\n }\n })\n\n return shadowResult\n}\n","import terminalLink from 'terminal-link'\nimport yargsParse from 'yargs-parser'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { isPath } from '@socketsecurity/registry/lib/path'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { runCdxgen } from './run-cdxgen.mts'\nimport constants from '../../constants.mts'\nimport { isHelpFlag } from '../../utils/cmd.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\n// TODO: Convert yargs to meow.\nconst toLower = (arg: string) => arg.toLowerCase()\nconst arrayToLower = (arg: string[]) => arg.map(toLower)\n\n// npx @cyclonedx/cdxgen@11.2.7 --help\n//\n// Options:\n// -o, --output Output file. Default bom.json [default: \"bom.json\"]\n// -t, --type Project type. Please refer to https://cyclonedx.github.io/cdxgen/#/PROJECT_TYPES for supp\n// orted languages/platforms. [array]\n// --exclude-type Project types to exclude. Please refer to https://cyclonedx.github.io/cdxgen/#/PROJECT_TY\n// PES for supported languages/platforms.\n// -r, --recurse Recurse mode suitable for mono-repos. Defaults to true. Pass --no-recurse to disable.\n// [boolean] [default: true]\n// -p, --print Print the SBOM as a table with tree. [boolean]\n// -c, --resolve-class Resolve class names for packages. jars only for now. [boolean]\n// --deep Perform deep searches for components. Useful while scanning C/C++ apps, live OS and oci i\n// mages. [boolean]\n// --server-url Dependency track url. Eg: https://deptrack.cyclonedx.io\n// --skip-dt-tls-check Skip TLS certificate check when calling Dependency-Track. [boolean] [default: false]\n// --api-key Dependency track api key\n// --project-group Dependency track project group\n// --project-name Dependency track project name. Default use the directory name\n// --project-version Dependency track project version [string] [default: \"\"]\n// --project-id Dependency track project id. Either provide the id or the project name and version togeth\n// er [string]\n// --parent-project-id Dependency track parent project id [string]\n// --required-only Include only the packages with required scope on the SBOM. Would set compositions.aggrega\n// te to incomplete unless --no-auto-compositions is passed. [boolean]\n// --fail-on-error Fail if any dependency extractor fails. [boolean]\n// --no-babel Do not use babel to perform usage analysis for JavaScript/TypeScript projects. [boolean]\n// --generate-key-and-sign Generate an RSA public/private key pair and then sign the generated SBOM using JSON Web S\n// ignatures. [boolean]\n// --server Run cdxgen as a server [boolean]\n// --server-host Listen address [default: \"127.0.0.1\"]\n// --server-port Listen port [default: \"9090\"]\n// --install-deps Install dependencies automatically for some projects. Defaults to true but disabled for c\n// ontainers and oci scans. Use --no-install-deps to disable this feature.\n// [boolean] [default: true]\n// --validate Validate the generated SBOM using json schema. Defaults to true. Pass --no-validate to di\n// sable. [boolean] [default: true]\n// --evidence Generate SBOM with evidence for supported languages. [boolean] [default: false]\n// --spec-version CycloneDX Specification version to use. Defaults to 1.6\n// [number] [choices: 1.4, 1.5, 1.6, 1.7] [default: 1.6]\n// --filter Filter components containing this word in purl or component.properties.value. Multiple va\n// lues allowed. [array]\n// --only Include components only containing this word in purl. Useful to generate BOM with first p\n// arty components alone. Multiple values allowed. [array]\n// --author The person(s) who created the BOM. Set this value if you're intending the modify the BOM\n// and claim authorship. [array] [default: \"OWASP Foundation\"]\n// --profile BOM profile to use for generation. Default generic.\n// [choices: \"appsec\", \"research\", \"operational\", \"threat-modeling\", \"license-compliance\", \"generic\", \"machine-learning\",\n// \"ml\", \"deep-learning\", \"ml-deep\", \"ml-tiny\"] [default: \"generic\"]\n// --exclude Additional glob pattern(s) to ignore [array]\n// --export-proto Serialize and export BOM as protobuf binary. [boolean] [default: false]\n// --proto-bin-file Path for the serialized protobuf binary. [default: \"bom.cdx\"]\n// --include-formulation Generate formulation section with git metadata and build tools. Defaults to false.\n// [boolean] [default: false]\n// --include-crypto Include crypto libraries as components. [boolean] [default: false]\n// --standard The list of standards which may consist of regulations, industry or organizational-specif\n// ic standards, maturity models, best practices, or any other requirements which can be eva\n// luated against or attested to.\n// [array] [choices: \"asvs-5.0\", \"asvs-4.0.3\", \"bsimm-v13\", \"masvs-2.0.0\", \"nist_ssdf-1.1\", \"pcissc-secure-slc-1.1\", \"scv\n// s-1.0.0\", \"ssaf-DRAFT-2023-11\"]\n// --json-pretty Pretty-print the generated BOM json. [boolean] [default: false]\n// --min-confidence Minimum confidence needed for the identity of a component from 0 - 1, where 1 is 100% con\n// fidence. [number] [default: 0]\n// --technique Analysis technique to use\n// [array] [choices: \"auto\", \"source-code-analysis\", \"binary-analysis\", \"manifest-analysis\", \"hash-comparison\", \"instrume\n// ntation\", \"filename\"]\n// --auto-compositions Automatically set compositions when the BOM was filtered. Defaults to true\n// [boolean] [default: true]\n// -h, --help Show help [boolean]\n// -v, --version Show version number [boolean]\n\n// isSecureMode defined at:\n// https://github.com/CycloneDX/cdxgen/blob/v11.2.7/lib/helpers/utils.js#L66\n// const isSecureMode =\n// ['true', '1'].includes(process.env?.CDXGEN_SECURE_MODE) ||\n// process.env?.NODE_OPTIONS?.includes('--permission')\n\n// Yargs CDXGEN configuration defined at:\n// https://github.com/CycloneDX/cdxgen/blob/v11.2.7/bin/cdxgen.js#L64\nconst yargsConfig = {\n configuration: {\n 'camel-case-expansion': false,\n 'greedy-arrays': false,\n 'parse-numbers': false,\n 'populate--': true,\n 'short-option-groups': false,\n 'strip-aliased': true,\n 'unknown-options-as-args': true,\n },\n coerce: {\n 'exclude-type': arrayToLower,\n 'feature-flags': arrayToLower,\n filter: arrayToLower,\n only: arrayToLower,\n profile: toLower,\n standard: arrayToLower,\n technique: arrayToLower,\n type: arrayToLower,\n },\n default: {\n //author: ['OWASP Foundation'],\n //'auto-compositions': true,\n //babel: true,\n //banner: false, // hidden\n //'deps-slices-file': 'deps.slices.json', // hidden\n //evidence: false,\n //'exclude-type': [],\n //'export-proto': false,\n //'fail-on-error': isSecureMode,\n //'feature-flags': [], // hidden\n //'include-crypto': false,\n //'include-formulation': false,\n //'install-deps': !isSecureMode\n //lifecycle: 'build', // hidden\n //'min-confidence': '0',\n //output: 'bom.json',\n //profile: 'generic',\n //'project-version': '',\n //'proto-bin-file': 'bom.cdx',\n //recurse: true,\n //'skip-dt-tls-check': false,\n //'semantics-slices-file': 'semantics.slices.json',\n //'server-host': '127.0.0.1',\n //'server-port': '9090',\n //'spec-version': '1.6',\n type: ['js'],\n //validate: true,\n },\n alias: {\n help: ['h'],\n output: ['o'],\n print: ['p'],\n recurse: ['r'],\n 'resolve-class': ['c'],\n type: ['t'],\n version: ['v'],\n },\n array: [\n { key: 'author', type: 'string' },\n { key: 'exclude', type: 'string' },\n { key: 'exclude-type', type: 'string' },\n { key: 'feature-flags', type: 'string' }, // hidden\n { key: 'filter', type: 'string' },\n { key: 'only', type: 'string' },\n { key: 'standard', type: 'string' },\n { key: 'technique', type: 'string' },\n { key: 'type', type: 'string' },\n ],\n boolean: [\n 'auto-compositions',\n 'babel',\n 'banner', // hidden\n 'deep',\n 'evidence',\n 'export-proto',\n 'fail-on-error',\n 'generate-key-and-sign',\n 'help',\n 'include-crypto',\n 'include-formulation',\n 'install-deps',\n 'json-pretty',\n 'print',\n 'recurse',\n 'required-only',\n 'resolve-class',\n 'skip-dt-tls-check',\n 'server',\n 'validate',\n 'version',\n ],\n string: [\n 'api-key',\n 'data-flow-slices-file', // hidden\n 'deps-slices-file', // hidden\n 'evinse-output', // hidden\n 'lifecycle',\n 'min-confidence', // number\n 'openapi-spec-file', // hidden\n 'output',\n 'parent-project-id',\n 'profile',\n 'project-group',\n 'project-name',\n 'project-version',\n 'project-id',\n 'proto-bin-file',\n 'reachables-slices-file', // hidden\n 'semantics-slices-file', // hidden\n 'server-host',\n 'server-port',\n 'server-url',\n 'spec-version', // number\n 'usages-slices-file', // hidden\n ],\n}\n\nconst config: CliCommandConfig = {\n commandName: 'cdxgen',\n description: 'Create an SBOM with CycloneDX generator (cdxgen)',\n hidden: false,\n // Stub out flags and help.\n // TODO: Convert yargs to meow.\n flags: {},\n help: () => '',\n}\n\nexport const cmdManifestCdxgen = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n // Don't let meow take over --help.\n argv: argv.filter(a => !isHelpFlag(a)),\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n // TODO: Convert yargs to meow.\n const yargv = {\n ...yargsParse(argv as string[], yargsConfig),\n } as any\n\n const pathArgs: string[] = []\n const unknowns: string[] = []\n for (const a of yargv._) {\n if (isPath(a)) {\n pathArgs.push(a)\n } else {\n unknowns.push(a)\n }\n }\n\n yargv._ = pathArgs\n\n const { length: unknownsCount } = unknowns\n if (unknownsCount) {\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 logger.fail(\n `Unknown ${pluralize('argument', unknownsCount)}: ${unknowns.join(', ')}`,\n )\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n // Change defaults when not passing the --help flag.\n if (!yargv.help) {\n // Make 'lifecycle' default to 'pre-build', which also sets 'install-deps' to `false`,\n // to avoid arbitrary code execution on the cdxgen scan.\n // https://github.com/CycloneDX/cdxgen/issues/1328\n if (yargv.lifecycle === undefined) {\n yargv.lifecycle = 'pre-build'\n yargv['install-deps'] = false\n logger.info(\n `Setting cdxgen --lifecycle to \"${yargv.lifecycle}\" to avoid arbitrary code execution on this scan.\\n Pass \"--lifecycle build\" to generate a BOM consisting of information obtained during the build process.\\n See cdxgen ${terminalLink(\n 'BOM lifecycles documentation',\n 'https://cyclonedx.github.io/cdxgen/#/ADVANCED?id=bom-lifecycles',\n )} for more details.\\n`,\n )\n }\n if (yargv.output === undefined) {\n yargv.output = 'socket-cdx.json'\n }\n }\n\n process.exitCode = 1\n\n const { spawnPromise } = await runCdxgen(yargv)\n\n // See https://nodejs.org/api/child_process.html#event-exit.\n spawnPromise.process.on('exit', (code, signalName) => {\n if (signalName) {\n process.kill(process.pid, signalName)\n } else if (typeof code === 'number') {\n // eslint-disable-next-line n/no-process-exit\n process.exit(code)\n }\n })\n\n await spawnPromise\n}\n","import path from 'node:path'\n\nimport { debugDir } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { detectManifestActions } from './detect-manifest-actions.mts'\nimport { generateAutoManifest } from './generate_auto_manifest.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'auto',\n description: 'Auto-detect build and attempt to generate manifest file',\n hidden: false,\n flags: {\n ...commonFlags,\n verbose: {\n type: 'boolean',\n default: false,\n description:\n 'Enable debug output (only for auto itself; sub-steps need to have it pre-configured), may help when running into errors',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Tries to figure out what language your target repo uses. If it finds a\n supported case then it will try to generate the manifest file for that\n language with the default or detected settings.\n\n Note: you can exclude languages from being auto-generated if you don't want\n them to. Run \\`socket manifest setup\\` in the same dir to disable it.\n\n Examples\n\n $ ${command}\n $ ${command} ./project/foo\n `,\n}\n\nexport const cmdManifestAuto = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n // TODO: Implement json/md further.\n const { json, markdown, verbose: verboseFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const verbose = !!verboseFlag\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n const outputKind = getOutputKind(json, markdown)\n\n if (verbose) {\n logger.group('- ', parentName, config.commandName, ':')\n logger.group('- flags:', cli.flags)\n logger.groupEnd()\n logger.log('- input:', cli.input)\n logger.log('- cwd:', cwd)\n logger.groupEnd()\n }\n\n const sockJson = readOrDefaultSocketJson(cwd)\n\n const detected = await detectManifestActions(sockJson, cwd)\n debugDir('inspect', { detected })\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n if (!detected.count) {\n logger.fail(\n 'Was unable to discover any targets for which we can generate manifest files...',\n )\n logger.log('')\n logger.log(\n '- Make sure this script would work with your target build (see `socket manifest --help` for your target).',\n )\n logger.log(\n '- Make sure to run it from the correct dir (use --cwd to target another dir)',\n )\n logger.log('- Make sure the necessary build tools are available (`PATH`)')\n process.exitCode = 1\n return\n }\n\n await generateAutoManifest({\n detected,\n cwd,\n outputKind,\n verbose,\n })\n\n logger.success(\n `Finished. Should have attempted to generate manifest files for ${detected.count} targets.`,\n )\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleManifestConda } from './handle-manifest-conda.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'conda',\n description:\n '[beta] Convert a Conda environment.yml file to a python requirements.txt',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n file: {\n type: 'string',\n description:\n 'Input file name (by default for Conda this is \"environment.yml\"), relative to cwd',\n },\n stdin: {\n type: 'boolean',\n description: 'Read the input from stdin (supersedes --file)',\n },\n out: {\n type: 'string',\n description: 'Output path (relative to cwd)',\n },\n stdout: {\n type: 'boolean',\n description:\n 'Print resulting requirements.txt to stdout (supersedes --out)',\n },\n verbose: {\n type: 'boolean',\n description: 'Print debug messages',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n Warning: While we don't support Conda necessarily, this tool extracts the pip\n block from an environment.yml and outputs it as a requirements.txt\n which you can scan as if it were a pypi package.\n\n USE AT YOUR OWN RISK\n\n Note: FILE can be a dash (-) to indicate stdin. This way you can pipe the\n contents of a file to have it processed.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n\n $ ${command}\n $ ${command} ./project/foo --file environment.yaml\n `,\n}\n\nexport const cmdManifestConda = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json = false, markdown = false } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n const sockJson = readOrDefaultSocketJson(cwd)\n\n let { file: filename, out, stdin, stdout, verbose } = cli.flags\n\n // Set defaults for any flag/arg that is not given. Check socket.json first.\n if (\n stdin === undefined &&\n sockJson.defaults?.manifest?.conda?.stdin !== undefined\n ) {\n stdin = sockJson.defaults?.manifest?.conda?.stdin\n logger.info('Using default --stdin from socket.json:', stdin)\n }\n if (stdin) {\n filename = '-'\n } else if (!filename) {\n if (sockJson.defaults?.manifest?.conda?.infile) {\n filename = sockJson.defaults?.manifest?.conda?.infile\n logger.info('Using default --file from socket.json:', filename)\n } else {\n filename = 'environment.yml'\n }\n }\n if (\n stdout === undefined &&\n sockJson.defaults?.manifest?.conda?.stdout !== undefined\n ) {\n stdout = sockJson.defaults?.manifest?.conda?.stdout\n logger.info('Using default --stdout from socket.json:', stdout)\n }\n if (stdout) {\n out = '-'\n } else if (!out) {\n if (sockJson.defaults?.manifest?.conda?.outfile) {\n out = sockJson.defaults?.manifest?.conda?.outfile\n logger.info('Using default --out from socket.json:', out)\n } else {\n out = 'requirements.txt'\n }\n }\n if (\n verbose === undefined &&\n sockJson.defaults?.manifest?.conda?.verbose !== undefined\n ) {\n verbose = sockJson.defaults?.manifest?.conda?.verbose\n logger.info('Using default --verbose from socket.json:', verbose)\n } else if (verbose === undefined) {\n verbose = false\n }\n\n if (verbose) {\n logger.group('- ', parentName, config.commandName, ':')\n logger.group('- flags:', cli.flags)\n logger.groupEnd()\n logger.log('- target:', cwd)\n logger.log('- output:', out)\n logger.groupEnd()\n }\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: cli.input.length <= 1,\n message: 'Can only accept one DIR (make sure to escape spaces!)',\n fail: `received ${cli.input.length}`,\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n logger.warn(\n 'Warning: This will approximate your Conda dependencies using PyPI. We do not yet officially support Conda. Use at your own risk.',\n )\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleManifestConda({\n cwd,\n filename: String(filename),\n out: String(out || ''),\n outputKind,\n verbose: Boolean(verbose),\n })\n}\n","import path from 'node:path'\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { convertGradleToMaven } from './convert_gradle_to_maven.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'gradle',\n description:\n '[beta] Use Gradle to generate a manifest file (`pom.xml`) for a Gradle/Java/Kotlin/etc project',\n hidden: false,\n flags: {\n ...commonFlags,\n bin: {\n type: 'string',\n description: 'Location of gradlew binary to use, default: CWD/gradlew',\n },\n gradleOpts: {\n type: 'string',\n description:\n 'Additional options to pass on to ./gradlew, see `./gradlew --help`',\n },\n verbose: {\n type: 'boolean',\n description: 'Print debug messages',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Uses gradle, preferably through your local project \\`gradlew\\`, to generate a\n \\`pom.xml\\` file for each task. If you have no \\`gradlew\\` you can try the\n global \\`gradle\\` binary but that may not work (hard to predict).\n\n The \\`pom.xml\\` is a manifest file similar to \\`package.json\\` for npm or\n or requirements.txt for PyPi), but specifically for Maven, which is Java's\n dependency repository. Languages like Kotlin and Scala piggy back on it too.\n\n There are some caveats with the gradle to \\`pom.xml\\` conversion:\n\n - each task will generate its own xml file and by default it generates one xml\n for every task. (This may be a good thing!)\n\n - it's possible certain features don't translate well into the xml. If you\n think something is missing that could be supported please reach out.\n\n - it works with your \\`gradlew\\` from your repo and local settings and config\n\n Support is beta. Please report issues or give us feedback on what's missing.\n\n Examples\n\n $ ${command} .\n $ ${command} --bin=../gradlew .\n `,\n}\n\nexport const cmdManifestGradle = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json = false, markdown = false } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n // TODO: Implement json/md further.\n const outputKind = getOutputKind(json, markdown)\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n const sockJson = readOrDefaultSocketJson(cwd)\n\n debugFn(\n 'inspect',\n 'override: socket.json gradle',\n sockJson?.defaults?.manifest?.gradle,\n )\n\n let { bin, gradleOpts, verbose } = cli.flags\n\n // Set defaults for any flag/arg that is not given. Check socket.json first.\n if (!bin) {\n if (sockJson.defaults?.manifest?.gradle?.bin) {\n bin = sockJson.defaults?.manifest?.gradle?.bin\n logger.info('Using default --bin from socket.json:', bin)\n } else {\n bin = path.join(cwd, 'gradlew')\n }\n }\n if (!gradleOpts) {\n if (sockJson.defaults?.manifest?.gradle?.gradleOpts) {\n gradleOpts = sockJson.defaults?.manifest?.gradle?.gradleOpts\n logger.info('Using default --gradle-opts from socket.json:', gradleOpts)\n } else {\n gradleOpts = ''\n }\n }\n if (verbose === undefined) {\n if (sockJson.defaults?.manifest?.gradle?.verbose !== undefined) {\n verbose = sockJson.defaults?.manifest?.gradle?.verbose\n logger.info('Using default --verbose from socket.json:', verbose)\n } else {\n verbose = false\n }\n }\n\n if (verbose) {\n logger.group('- ', parentName, config.commandName, ':')\n logger.group('- flags:', cli.flags)\n logger.groupEnd()\n logger.log('- input:', cli.input)\n logger.groupEnd()\n }\n\n // TODO: We're not sure it's feasible to parse source file from stdin. We could\n // try, store contents in a file in some folder, target that folder... what\n // would the file name be?\n\n const wasValidInput = checkCommandInput(outputKind, {\n nook: true,\n test: cli.input.length <= 1,\n message: 'Can only accept one DIR (make sure to escape spaces!)',\n fail: 'received ' + cli.input.length,\n })\n if (!wasValidInput) {\n return\n }\n\n if (verbose) {\n logger.group()\n logger.info('- cwd:', cwd)\n logger.info('- gradle bin:', bin)\n logger.groupEnd()\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await convertGradleToMaven({\n bin: String(bin),\n cwd,\n gradleOpts: String(gradleOpts || '')\n .split(' ')\n .map(s => s.trim())\n .filter(Boolean),\n verbose: Boolean(verbose),\n })\n}\n","import path from 'node:path'\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { convertGradleToMaven } from './convert_gradle_to_maven.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\n// TODO: We may want to dedupe some pieces for all gradle languages. I think it\n// makes sense to have separate commands for them and I think it makes\n// sense for the help panels to note the requested language, rather than\n// `socket manifest kotlin` to print help screens with `gradle` as the\n// command. Room for improvement.\nconst config: CliCommandConfig = {\n commandName: 'kotlin',\n description:\n '[beta] Use Gradle to generate a manifest file (`pom.xml`) for a Kotlin project',\n hidden: false,\n flags: {\n ...commonFlags,\n bin: {\n type: 'string',\n description: 'Location of gradlew binary to use, default: CWD/gradlew',\n },\n gradleOpts: {\n type: 'string',\n description:\n 'Additional options to pass on to ./gradlew, see `./gradlew --help`',\n },\n verbose: {\n type: 'boolean',\n description: 'Print debug messages',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Uses gradle, preferably through your local project \\`gradlew\\`, to generate a\n \\`pom.xml\\` file for each task. If you have no \\`gradlew\\` you can try the\n global \\`gradle\\` binary but that may not work (hard to predict).\n\n The \\`pom.xml\\` is a manifest file similar to \\`package.json\\` for npm or\n or requirements.txt for PyPi), but specifically for Maven, which is Java's\n dependency repository. Languages like Kotlin and Scala piggy back on it too.\n\n There are some caveats with the gradle to \\`pom.xml\\` conversion:\n\n - each task will generate its own xml file and by default it generates one xml\n for every task. (This may be a good thing!)\n\n - it's possible certain features don't translate well into the xml. If you\n think something is missing that could be supported please reach out.\n\n - it works with your \\`gradlew\\` from your repo and local settings and config\n\n Support is beta. Please report issues or give us feedback on what's missing.\n\n Examples\n\n $ ${command} .\n $ ${command} --bin=../gradlew .\n `,\n}\n\nexport const cmdManifestKotlin = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json = false, markdown = false } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n // TODO: Implement json/md further.\n const outputKind = getOutputKind(json, markdown)\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n const sockJson = readOrDefaultSocketJson(cwd)\n\n debugFn(\n 'inspect',\n 'override: socket.json gradle',\n sockJson?.defaults?.manifest?.gradle,\n )\n\n let { bin, gradleOpts, verbose } = cli.flags\n\n // Set defaults for any flag/arg that is not given. Check socket.json first.\n if (!bin) {\n if (sockJson.defaults?.manifest?.gradle?.bin) {\n bin = sockJson.defaults?.manifest?.gradle?.bin\n logger.info('Using default --bin from socket.json:', bin)\n } else {\n bin = path.join(cwd, 'gradlew')\n }\n }\n if (!gradleOpts) {\n if (sockJson.defaults?.manifest?.gradle?.gradleOpts) {\n gradleOpts = sockJson.defaults?.manifest?.gradle?.gradleOpts\n logger.info('Using default --gradle-opts from socket.json:', gradleOpts)\n } else {\n gradleOpts = ''\n }\n }\n if (verbose === undefined) {\n if (sockJson.defaults?.manifest?.gradle?.verbose !== undefined) {\n verbose = sockJson.defaults?.manifest?.gradle?.verbose\n logger.info('Using default --verbose from socket.json:', verbose)\n } else {\n verbose = false\n }\n }\n\n if (verbose) {\n logger.group('- ', parentName, config.commandName, ':')\n logger.group('- flags:', cli.flags)\n logger.groupEnd()\n logger.log('- input:', cli.input)\n logger.groupEnd()\n }\n\n // TODO: We're not sure it's feasible to parse source file from stdin. We could\n // try, store contents in a file in some folder, target that folder... what\n // would the file name be?\n\n const wasValidInput = checkCommandInput(outputKind, {\n nook: true,\n test: cli.input.length <= 1,\n message: 'Can only accept one DIR (make sure to escape spaces!)',\n fail: 'received ' + cli.input.length,\n })\n if (!wasValidInput) {\n return\n }\n\n if (verbose) {\n logger.group()\n logger.info('- cwd:', cwd)\n logger.info('- gradle bin:', bin)\n logger.groupEnd()\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await convertGradleToMaven({\n bin: String(bin),\n cwd,\n gradleOpts: String(gradleOpts || '')\n .split(' ')\n .map(s => s.trim())\n .filter(Boolean),\n verbose: Boolean(verbose),\n })\n}\n","import path from 'node:path'\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { convertSbtToMaven } from './convert_sbt_to_maven.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'scala',\n description:\n \"[beta] Generate a manifest file (`pom.xml`) from Scala's `build.sbt` file\",\n hidden: false,\n flags: {\n ...commonFlags,\n bin: {\n type: 'string',\n description: 'Location of sbt binary to use',\n },\n out: {\n type: 'string',\n description:\n 'Path of output file; where to store the resulting manifest, see also --stdout',\n },\n stdout: {\n type: 'boolean',\n description: 'Print resulting pom.xml to stdout (supersedes --out)',\n },\n sbtOpts: {\n type: 'string',\n description: 'Additional options to pass on to sbt, as per `sbt --help`',\n },\n verbose: {\n type: 'boolean',\n description: 'Print debug messages',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Uses \\`sbt makePom\\` to generate a \\`pom.xml\\` from your \\`build.sbt\\` file.\n This xml file is the dependency manifest (like a package.json\n for Node.js or requirements.txt for PyPi), but specifically for Scala.\n\n There are some caveats with \\`build.sbt\\` to \\`pom.xml\\` conversion:\n\n - the xml is exported as socket.pom.xml as to not confuse existing build tools\n but it will first hit your /target/sbt<version> folder (as a different name)\n\n - the pom.xml format (standard by Scala) does not support certain sbt features\n - \\`excludeAll()\\`, \\`dependencyOverrides\\`, \\`force()\\`, \\`relativePath\\`\n - For details: https://www.scala-sbt.org/1.x/docs/Library-Management.html\n\n - it uses your sbt settings and local configuration verbatim\n\n - it can only export one target per run, so if you have multiple targets like\n development and production, you must run them separately.\n\n You can specify --bin to override the path to the \\`sbt\\` binary to invoke.\n\n Support is beta. Please report issues or give us feedback on what's missing.\n\n This is only for SBT. If your Scala setup uses gradle, please see the help\n sections for \\`socket manifest gradle\\` or \\`socket cdxgen\\`.\n\n Examples\n\n $ ${command}\n $ ${command} ./proj --bin=/usr/bin/sbt --file=boot.sbt\n `,\n}\n\nexport const cmdManifestScala = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json = false, markdown = false } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n // TODO: Implement json/md further.\n const outputKind = getOutputKind(json, markdown)\n\n const sockJson = readOrDefaultSocketJson(cwd)\n\n debugFn(\n 'inspect',\n 'override: socket.json sbt',\n sockJson?.defaults?.manifest?.sbt,\n )\n\n let { bin, out, sbtOpts, stdout, verbose } = cli.flags\n\n // Set defaults for any flag/arg that is not given. Check socket.json first.\n if (!bin) {\n if (sockJson.defaults?.manifest?.sbt?.bin) {\n bin = sockJson.defaults?.manifest?.sbt?.bin\n logger.info('Using default --bin from socket.json:', bin)\n } else {\n bin = 'sbt'\n }\n }\n if (\n stdout === undefined &&\n sockJson.defaults?.manifest?.sbt?.stdout !== undefined\n ) {\n stdout = sockJson.defaults?.manifest?.sbt?.stdout\n logger.info('Using default --stdout from socket.json:', stdout)\n }\n if (stdout) {\n out = '-'\n } else if (!out) {\n if (sockJson.defaults?.manifest?.sbt?.outfile) {\n out = sockJson.defaults?.manifest?.sbt?.outfile\n logger.info('Using default --out from socket.json:', out)\n } else {\n out = './socket.pom.xml'\n }\n }\n if (!sbtOpts) {\n if (sockJson.defaults?.manifest?.sbt?.sbtOpts) {\n sbtOpts = sockJson.defaults?.manifest?.sbt?.sbtOpts\n logger.info('Using default --sbt-opts from socket.json:', sbtOpts)\n } else {\n sbtOpts = ''\n }\n }\n if (\n verbose === undefined &&\n sockJson.defaults?.manifest?.sbt?.verbose !== undefined\n ) {\n verbose = sockJson.defaults?.manifest?.sbt?.verbose\n logger.info('Using default --verbose from socket.json:', verbose)\n } else if (verbose === undefined) {\n verbose = false\n }\n\n if (verbose) {\n logger.group('- ', parentName, config.commandName, ':')\n logger.group('- flags:', cli.flags)\n logger.groupEnd()\n logger.log('- input:', cli.input)\n logger.groupEnd()\n }\n\n // TODO: We're not sure it's feasible to parse source file from stdin. We could\n // try, store contents in a file in some folder, target that folder... what\n // would the file name be?\n\n const wasValidInput = checkCommandInput(outputKind, {\n nook: true,\n test: cli.input.length <= 1,\n message: 'Can only accept one DIR (make sure to escape spaces!)',\n fail: 'received ' + cli.input.length,\n })\n if (!wasValidInput) {\n return\n }\n\n if (verbose) {\n logger.group()\n logger.log('- target:', cwd)\n logger.log('- sbt bin:', bin)\n logger.log('- out:', out)\n logger.groupEnd()\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await convertSbtToMaven({\n bin: String(bin),\n cwd: cwd,\n out: String(out),\n sbtOpts: String(sbtOpts)\n .split(' ')\n .map(s => s.trim())\n .filter(Boolean),\n verbose: Boolean(verbose),\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport async function outputManifestSetup(result: CResult<unknown>) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.success('Setup complete')\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nimport { debugDir } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { input, select } from '@socketsecurity/registry/lib/prompts'\n\nimport { detectManifestActions } from './detect-manifest-actions.mts'\nimport {\n readSocketJsonSync,\n writeSocketJson,\n} from '../../utils/socket-json.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { SocketJson } from '../../utils/socket-json.mts'\n\nexport async function setupManifestConfig(\n cwd: string,\n defaultOnReadError = false,\n): Promise<CResult<unknown>> {\n const detected = await detectManifestActions(null, cwd)\n debugDir('inspect', { detected })\n\n // - repeat\n // - give the user an option to configure one of the supported targets\n // - run through an interactive prompt for selected target\n // - each target will have its own specific options\n // - record them to the socket.yml (or socket-cli.yml ? or just socket.json ?)\n\n const jsonPath = path.join(cwd, `socket.json`)\n if (fs.existsSync(jsonPath)) {\n logger.info(`Found socket.json at ${jsonPath}`)\n } else {\n logger.info(`No socket.json found at ${cwd}, will generate a new one`)\n }\n\n logger.log('')\n logger.log(\n 'Note: This tool will set up flag and argument defaults for certain',\n )\n logger.log(' CLI commands. You can still override them by explicitly')\n logger.log(' setting the flag. It is meant to be a convenience tool.')\n logger.log('')\n logger.log('This command will generate a socket.json file in the target cwd.')\n logger.log(\n 'You can choose to add this file to your repo (handy for collaboration)',\n )\n logger.log('or to add it to the ignored files, or neither. This file is only')\n logger.log('used in CLI workflows.')\n logger.log('')\n\n const choices = [\n {\n name: 'Conda'.padEnd(30, ' '),\n value: 'conda',\n description: 'Generate requirements.txt from a Conda environment.yml',\n },\n {\n name: 'Gradle'.padEnd(30, ' '),\n value: 'gradle',\n description: 'Generate pom.xml files through gradle',\n },\n {\n name: 'Kotlin (gradle)'.padEnd(30, ' '),\n value: 'gradle',\n description: 'Generate pom.xml files (for Kotlin) through gradle',\n },\n {\n name: 'Scala (gradle)'.padEnd(30, ' '),\n value: 'gradle',\n description: 'Generate pom.xml files (for Scala) through gradle',\n },\n {\n name: 'Scala (sbt)'.padEnd(30, ' '),\n value: 'sbt',\n description: 'Generate pom.xml files through sbt',\n },\n ]\n\n choices.forEach(obj => {\n if (detected[obj.value as keyof typeof detected]) {\n obj.name += ' [detected]'\n }\n })\n\n // Surface detected language first, then by alphabet\n choices.sort((a, b) => {\n if (\n detected[a.value as keyof typeof detected] &&\n !detected[b.value as keyof typeof detected]\n ) {\n return -1\n }\n if (\n !detected[a.value as keyof typeof detected] &&\n detected[b.value as keyof typeof detected]\n ) {\n return 1\n }\n return a.value < b.value ? -1 : a.value > b.value ? 1 : 0\n })\n\n // Make exit the last entry...\n choices.push({\n name: 'None, exit configurator',\n value: '',\n description: 'Exit setup',\n })\n\n // TODO: Use detected to list those first.\n const targetEco = (await select({\n message: 'Select ecosystem manifest generator to configure',\n choices,\n })) as string | null\n\n const sockJsonCResult = readSocketJsonSync(cwd, defaultOnReadError)\n if (!sockJsonCResult.ok) {\n return sockJsonCResult\n }\n const sockJson = sockJsonCResult.data\n\n if (!sockJson.defaults) {\n sockJson.defaults = {}\n }\n if (!sockJson.defaults.manifest) {\n sockJson.defaults.manifest = {}\n }\n\n let result: CResult<{ canceled: boolean }>\n switch (targetEco) {\n case 'conda': {\n if (!sockJson.defaults.manifest.conda) {\n sockJson.defaults.manifest.conda = {}\n }\n result = await setupConda(sockJson.defaults.manifest.conda)\n break\n }\n case 'gradle': {\n if (!sockJson.defaults.manifest.gradle) {\n sockJson.defaults.manifest.gradle = {}\n }\n result = await setupGradle(sockJson.defaults.manifest.gradle)\n break\n }\n case 'sbt': {\n if (!sockJson.defaults.manifest.sbt) {\n sockJson.defaults.manifest.sbt = {}\n }\n result = await setupSbt(sockJson.defaults.manifest.sbt)\n break\n }\n default: {\n result = canceledByUser()\n }\n }\n\n if (!result.ok || result.data.canceled) {\n return result\n }\n\n logger.log('')\n logger.log('Setup complete. Writing socket.json')\n logger.log('')\n\n if (\n await select({\n message: `Do you want to write the new config to ${jsonPath} ?`,\n choices: [\n {\n name: 'yes',\n value: true,\n description: 'Update config',\n },\n {\n name: 'no',\n value: false,\n description: 'Do not update the config',\n },\n ],\n })\n ) {\n return await writeSocketJson(cwd, sockJson)\n }\n\n return canceledByUser()\n}\n\nasync function setupConda(\n config: NonNullable<\n NonNullable<NonNullable<SocketJson['defaults']>['manifest']>['conda']\n >,\n): Promise<CResult<{ canceled: boolean }>> {\n const on = await askForEnabled(!config.disabled)\n if (on === undefined) {\n return canceledByUser()\n } else if (on) {\n delete config.disabled\n } else {\n config.disabled = true\n }\n\n const infile = await askForInputFile(config.infile || 'environment.yml')\n if (infile === undefined) {\n return canceledByUser()\n } else if (infile === '-') {\n config.stdin = true\n } else {\n delete config.stdin\n if (infile) {\n config.infile = infile\n } else {\n delete config.infile\n }\n }\n\n const stdout = await askForStdout(config.stdout)\n if (stdout === undefined) {\n return canceledByUser()\n } else if (stdout === 'yes') {\n config.stdout = true\n } else if (stdout === 'no') {\n config.stdout = false\n } else {\n delete config.stdout\n }\n\n if (!config.stdout) {\n const out = await askForOutputFile(config.outfile || 'requirements.txt')\n if (out === undefined) {\n return canceledByUser()\n } else if (out === '-') {\n config.stdout = true\n } else {\n delete config.stdout\n if (out) {\n config.outfile = out\n } else {\n delete config.outfile\n }\n }\n }\n\n const verbose = await askForVerboseFlag(config.verbose)\n if (verbose === undefined) {\n return canceledByUser()\n } else if (verbose === 'yes' || verbose === 'no') {\n config.verbose = verbose === 'yes'\n } else {\n delete config.verbose\n }\n\n return notCanceled()\n}\n\nasync function setupGradle(\n config: NonNullable<\n NonNullable<NonNullable<SocketJson['defaults']>['manifest']>['gradle']\n >,\n): Promise<CResult<{ canceled: boolean }>> {\n const bin = await askForBin(config.bin || './gradlew')\n if (bin === undefined) {\n return canceledByUser()\n } else if (bin) {\n config.bin = bin\n } else {\n delete config.bin\n }\n\n const opts = await input({\n message: '(--gradle-opts) Enter gradle options to pass through',\n default: config.gradleOpts || '',\n required: false,\n // validate: async string => bool\n })\n if (opts === undefined) {\n return canceledByUser()\n } else if (opts) {\n config.gradleOpts = opts\n } else {\n delete config.gradleOpts\n }\n\n const verbose = await askForVerboseFlag(config.verbose)\n if (verbose === undefined) {\n return canceledByUser()\n } else if (verbose === 'yes' || verbose === 'no') {\n config.verbose = verbose === 'yes'\n } else {\n delete config.verbose\n }\n\n return notCanceled()\n}\n\nasync function setupSbt(\n config: NonNullable<\n NonNullable<NonNullable<SocketJson['defaults']>['manifest']>['sbt']\n >,\n): Promise<CResult<{ canceled: boolean }>> {\n const bin = await askForBin(config.bin || 'sbt')\n if (bin === undefined) {\n return canceledByUser()\n } else if (bin) {\n config.bin = bin\n } else {\n delete config.bin\n }\n\n const opts = await input({\n message: '(--sbt-opts) Enter sbt options to pass through',\n default: config.sbtOpts || '',\n required: false,\n // validate: async string => bool\n })\n if (opts === undefined) {\n return canceledByUser()\n } else if (opts) {\n config.sbtOpts = opts\n } else {\n delete config.sbtOpts\n }\n\n const stdout = await askForStdout(config.stdout)\n if (stdout === undefined) {\n return canceledByUser()\n } else if (stdout === 'yes') {\n config.stdout = true\n } else if (stdout === 'no') {\n config.stdout = false\n } else {\n delete config.stdout\n }\n\n if (config.stdout !== true) {\n const out = await askForOutputFile(config.outfile || 'sbt.pom.xml')\n if (out === undefined) {\n return canceledByUser()\n } else if (out === '-') {\n config.stdout = true\n } else {\n delete config.stdout\n if (out) {\n config.outfile = out\n } else {\n delete config.outfile\n }\n }\n }\n\n const verbose = await askForVerboseFlag(config.verbose)\n if (verbose === undefined) {\n return canceledByUser()\n } else if (verbose === 'yes' || verbose === 'no') {\n config.verbose = verbose === 'yes'\n } else {\n delete config.verbose\n }\n\n return notCanceled()\n}\n\nasync function askForStdout(\n defaultValue: boolean | undefined,\n): Promise<string | undefined> {\n return await select({\n message: '(--stdout) Print the resulting pom.xml to stdout?',\n choices: [\n {\n name: 'no',\n value: 'no',\n description: 'Write output to a file, not stdout',\n },\n {\n name: 'yes',\n value: 'yes',\n description: 'Print in stdout (this will supersede --out)',\n },\n {\n name: '(leave default)',\n value: '',\n description: 'Do not store a setting for this',\n },\n ],\n default: defaultValue === true ? 'yes' : defaultValue === false ? 'no' : '',\n })\n}\n\nasync function askForEnabled(\n defaultValue: boolean | undefined,\n): Promise<boolean | undefined> {\n return await select({\n message:\n 'Do you want to enable or disable auto generating manifest files for this language in this dir?',\n choices: [\n {\n name: 'Enable',\n value: true,\n description: 'Generate manifest files for this language when detected',\n },\n {\n name: 'Disable',\n value: false,\n description:\n 'Do not generate manifest files for this language when detected, unless explicitly asking for it',\n },\n {\n name: 'Cancel',\n value: undefined,\n description: 'Exit configurator',\n },\n ],\n default:\n defaultValue === true\n ? 'enable'\n : defaultValue === false\n ? 'disable'\n : '',\n })\n}\n\nasync function askForInputFile(defaultName = ''): Promise<string | undefined> {\n return await input({\n message:\n '(--file) What should be the default file name to read? Should be an absolute path or relative to the cwd. Use `-` to read from stdin instead.' +\n (defaultName ? ' (Backspace to leave default)' : ''),\n default: defaultName,\n required: false,\n // validate: async string => bool\n })\n}\n\nasync function askForOutputFile(defaultName = ''): Promise<string | undefined> {\n return await input({\n message:\n '(--out) What should be the default output file? Should be absolute path or relative to cwd.' +\n (defaultName ? ' (Backspace to leave default)' : ''),\n default: defaultName,\n required: false,\n // validate: async string => bool\n })\n}\n\nasync function askForBin(defaultName = ''): Promise<string | undefined> {\n return await input({\n message:\n '(--bin) What should be the command to execute? Usually your build binary.' +\n (defaultName ? ' (Backspace to leave default)' : ''),\n default: defaultName,\n required: false,\n // validate: async string => bool\n })\n}\n\nasync function askForVerboseFlag(\n current: boolean | undefined,\n): Promise<string | undefined> {\n return await select({\n message: '(--verbose) Should this run in verbose mode by default?',\n choices: [\n {\n name: 'no',\n value: 'no',\n description: 'Do not run this manifest in verbose mode',\n },\n {\n name: 'yes',\n value: 'yes',\n description: 'Run this manifest in verbose mode',\n },\n {\n name: '(leave default)',\n value: '',\n description: 'Do not store a setting for this',\n },\n ],\n default: current === true ? 'yes' : current === false ? 'no' : '',\n })\n}\n\nfunction canceledByUser(): CResult<{ canceled: boolean }> {\n logger.log('')\n logger.info('User canceled')\n logger.log('')\n return { ok: true, data: { canceled: true } }\n}\n\nfunction notCanceled(): CResult<{ canceled: boolean }> {\n return { ok: true, data: { canceled: false } }\n}\n","import { outputManifestSetup } from './output-manifest-setup.mts'\nimport { setupManifestConfig } from './setup-manifest-config.mts'\n\nexport async function handleManifestSetup(\n cwd: string,\n defaultOnReadError: boolean,\n): Promise<void> {\n const result = await setupManifestConfig(cwd, defaultOnReadError)\n\n await outputManifestSetup(result)\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleManifestSetup } from './handle-manifest-setup.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'setup',\n description:\n 'Start interactive configurator to customize default flag values for `socket manifest` in this dir',\n hidden: false,\n flags: {\n ...commonFlags,\n defaultOnReadError: {\n type: 'boolean',\n description:\n 'If reading the socket.json fails, just use a default config? Warning: This might override the existing json file!',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [CWD=.]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n This command will try to detect all supported ecosystems in given CWD. Then\n it starts a configurator where you can setup default values for certain flags\n when creating manifest files in that dir. These configuration details are\n then stored in a local \\`socket.json\\` file (which you may or may not commit\n to the repo). Next time you run \\`socket manifest ...\\` it will load this\n json file and any flags which are not explicitly set in the command but which\n have been registered in the json file will get the default value set to that\n value you stored rather than the hardcoded defaults.\n\n This helps with for example when your build binary is in a particular path\n or when your build tool needs specific opts and you don't want to specify\n them when running the command every time.\n\n You can also disable manifest generation for certain ecosystems.\n\n This generated configuration file will only be used locally by the CLI. You\n can commit it to the repo (useful for collaboration) or choose to add it to\n your .gitignore all the same. Only this CLI will use it.\n\n Examples\n $ ${command}\n $ ${command} ./proj\n `,\n}\n\nexport const cmdManifestSetup = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { defaultOnReadError = false } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleManifestSetup(cwd, Boolean(defaultOnReadError))\n}\n","import { cmdManifestAuto } from './cmd-manifest-auto.mts'\nimport { cmdManifestCdxgen } from './cmd-manifest-cdxgen.mts'\nimport { cmdManifestConda } from './cmd-manifest-conda.mts'\nimport { cmdManifestGradle } from './cmd-manifest-gradle.mts'\nimport { cmdManifestKotlin } from './cmd-manifest-kotlin.mts'\nimport { cmdManifestScala } from './cmd-manifest-scala.mts'\nimport { cmdManifestSetup } from './cmd-manifest-setup.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'manifest',\n description: 'Generate a dependency manifest for certain ecosystems',\n hidden: false,\n flags: {\n ...commonFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <LANGUAGE> <TARGET>\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Generates a declarative dependency manifest (like a package.json for Node.JS\n or requirements.txt for PyPi), but for certain supported ecosystems\n where it's common to use a dynamic manifest, like Scala's sbt.\n\n Only certain languages are supported and there may be language specific\n configurations available. See \\`manifest <language> --help\\` for usage details\n per language.\n\n Currently supported language: scala [beta], gradle [beta], kotlin (through\n gradle) [beta].\n\n Examples\n\n $ ${command} scala .\n\n To have it auto-detect and attempt to run:\n\n $ ${command} auto\n `,\n}\n\nexport const cmdManifest = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n await meowWithSubcommands(\n {\n auto: cmdManifestAuto,\n cdxgen: cmdManifestCdxgen,\n conda: cmdManifestConda,\n gradle: cmdManifestGradle,\n kotlin: cmdManifestKotlin,\n scala: cmdManifestScala,\n setup: cmdManifestSetup,\n },\n {\n argv,\n aliases: {\n yolo: {\n description: config.description,\n hidden: true,\n argv: ['auto'],\n },\n },\n description: config.description,\n importMeta,\n flags: config.flags,\n name: `${parentName} ${config.commandName}`,\n },\n )\n}\n","import { createRequire } from 'node:module'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants, { NPM } from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagApiRequirementsOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst require = createRequire(import.meta.url)\n\nexport const CMD_NAME = NPM\n\nconst description = 'Run npm with the Socket wrapper'\n\nconst hidden = false\n\nexport const cmdNpm = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n },\n help: command => `\n Usage\n $ ${command} ...\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Note: Everything after \"npm\" is passed to the npm command.\n Only the \\`--dry-run\\` and \\`--help\\` flags are caught here.\n\n Use \\`socket wrapper on\\` to alias this command as \\`npm\\`.\n\n Examples\n $ ${command}\n $ ${command} install -g cowsay\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n const shadowBin = /*@__PURE__*/ require(constants.shadowNpmBinPath)\n\n process.exitCode = 1\n\n const { spawnPromise } = await shadowBin(NPM, argv, { stdio: 'inherit' })\n\n // See https://nodejs.org/api/child_process.html#event-exit.\n spawnPromise.process.on(\n 'exit',\n (code: string | null, signalName: NodeJS.Signals | null) => {\n if (signalName) {\n process.kill(process.pid, signalName)\n } else if (typeof code === 'number') {\n // eslint-disable-next-line n/no-process-exit\n process.exit(code)\n }\n },\n )\n\n await spawnPromise\n}\n","import { createRequire } from 'node:module'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants, { NPX } from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagApiRequirementsOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst require = createRequire(import.meta.url)\n\nconst CMD_NAME = NPX\n\nconst description = 'Run npx with the Socket wrapper'\n\nconst hidden = false\n\nexport const cmdNpx = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n },\n help: (command, _config) => `\n Usage\n $ ${command} ...\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Note: Everything after \"npx\" is passed to the npx command.\n Only the \\`--dry-run\\` and \\`--help\\` flags are caught here.\n\n Use \\`socket wrapper on\\` to alias this command as \\`npx\\`.\n\n Examples\n $ ${command} cowsay\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n const shadowBin = /*@__PURE__*/ require(constants.shadowNpmBinPath)\n\n process.exitCode = 1\n\n const { spawnPromise } = await shadowBin(NPX, argv, { stdio: 'inherit' })\n\n // See https://nodejs.org/api/child_process.html#event-exit.\n spawnPromise.process.on(\n 'exit',\n (code: string | null, signalName: NodeJS.Signals | null) => {\n if (signalName) {\n process.kill(process.pid, signalName)\n } else if (typeof code === 'number') {\n // eslint-disable-next-line n/no-process-exit\n process.exit(code)\n }\n },\n )\n\n await spawnPromise\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'oops',\n description: 'Trigger an intentional error (for development)',\n hidden: true,\n flags: {\n ...commonFlags,\n ...outputFlags,\n throw: {\n type: 'boolean',\n default: false,\n description:\n 'Throw an explicit error even if --json or --markdown are set',\n },\n },\n help: (parentName, config) => `\n Usage\n $ ${parentName} ${config.commandName}\n\n Don't run me.\n `,\n}\n\nexport const cmdOops = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, throw: justThrow } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n if (json && !justThrow) {\n process.exitCode = 1\n logger.log(\n serializeResultJson({\n ok: false,\n message: 'Oops',\n cause: 'This error was intentionally left blank',\n }),\n )\n }\n\n if (markdown && !justThrow) {\n process.exitCode = 1\n logger.fail(\n failMsgWithBadge('Oops', 'This error was intentionally left blank'),\n )\n return\n }\n\n throw new Error('This error was intentionally left blank')\n}\n","import constants from '../../constants.mts'\n\nimport type { EnvDetails } from '../../utils/package-environment.mts'\n\nconst { BUN, NPM, PNPM, VLT, YARN_BERRY, YARN_CLASSIC } = constants\n\nexport function matchLsCmdViewHumanStdout(stdout: string, name: string) {\n return stdout.includes(` ${name}@`)\n}\n\nexport function matchQueryCmdStdout(stdout: string, name: string) {\n return stdout.includes(`\"${name}\"`)\n}\n\nexport function lsStdoutIncludes(\n pkgEnvDetails: EnvDetails,\n stdout: string,\n name: string,\n): boolean {\n switch (pkgEnvDetails.agent) {\n case BUN:\n case YARN_BERRY:\n case YARN_CLASSIC:\n return matchLsCmdViewHumanStdout(stdout, name)\n case PNPM:\n case VLT:\n case NPM:\n default:\n return matchQueryCmdStdout(stdout, name)\n }\n}\n","import type { EnvDetails } from '../../utils/package-environment.mts'\n\nexport function getDependencyEntries(pkgEnvDetails: EnvDetails) {\n const {\n dependencies,\n devDependencies,\n optionalDependencies,\n peerDependencies,\n } = pkgEnvDetails.editablePkgJson.content\n return [\n [\n 'dependencies',\n dependencies ? { __proto__: null, ...dependencies } : undefined,\n ],\n [\n 'devDependencies',\n devDependencies ? { __proto__: null, ...devDependencies } : undefined,\n ],\n [\n 'peerDependencies',\n peerDependencies ? { __proto__: null, ...peerDependencies } : undefined,\n ],\n [\n 'optionalDependencies',\n optionalDependencies\n ? { __proto__: null, ...optionalDependencies }\n : undefined,\n ],\n ].filter(({ 1: o }) => o) as Array<[string, NonNullable<typeof dependencies>]>\n}\n","import constants from '../../constants.mts'\n\nimport type { NpmOverrides, Overrides, PnpmOrYarnOverrides } from './types.mts'\nimport type { Agent, EnvDetails } from '../../utils/package-environment.mts'\nimport type { PackageJson } from '@socketsecurity/registry/lib/packages'\n\nconst {\n BUN,\n NPM,\n OVERRIDES,\n PNPM,\n RESOLUTIONS,\n VLT,\n YARN_BERRY,\n YARN_CLASSIC,\n} = constants\n\nexport function getOverridesDataBun(\n pkgEnvDetails: EnvDetails,\n pkgJson = pkgEnvDetails.editablePkgJson.content,\n) {\n const overrides = (pkgJson?.[RESOLUTIONS] ?? {}) as PnpmOrYarnOverrides\n return { type: YARN_BERRY, overrides }\n}\n\n// npm overrides documentation:\n// https://docs.npmjs.com/cli/v10/configuring-npm/package-json#overrides\nexport function getOverridesDataNpm(\n pkgEnvDetails: EnvDetails,\n pkgJson = pkgEnvDetails.editablePkgJson.content,\n) {\n const overrides = (pkgJson?.[OVERRIDES] ?? {}) as NpmOverrides\n return { type: NPM, overrides }\n}\n\n// pnpm overrides documentation:\n// https://pnpm.io/package_json#pnpmoverrides\nexport function getOverridesDataPnpm(\n pkgEnvDetails: EnvDetails,\n pkgJson = pkgEnvDetails.editablePkgJson.content,\n) {\n const overrides = ((pkgJson as any)?.[PNPM]?.[OVERRIDES] ??\n {}) as PnpmOrYarnOverrides\n return { type: PNPM, overrides }\n}\n\nexport function getOverridesDataVlt(\n pkgEnvDetails: EnvDetails,\n pkgJson = pkgEnvDetails.editablePkgJson.content,\n) {\n const overrides = (pkgJson?.[OVERRIDES] ?? {}) as NpmOverrides\n return { type: VLT, overrides }\n}\n\n// Yarn resolutions documentation:\n// https://yarnpkg.com/configuration/manifest#resolutions\nexport function getOverridesDataYarn(\n pkgEnvDetails: EnvDetails,\n pkgJson = pkgEnvDetails.editablePkgJson.content,\n) {\n const overrides = (pkgJson?.[RESOLUTIONS] ?? {}) as PnpmOrYarnOverrides\n return { type: YARN_BERRY, overrides }\n}\n\n// Yarn resolutions documentation:\n// https://classic.yarnpkg.com/en/docs/selective-version-resolutions\nexport function getOverridesDataYarnClassic(\n pkgEnvDetails: EnvDetails,\n pkgJson = pkgEnvDetails.editablePkgJson.content,\n) {\n const overrides = (pkgJson?.[RESOLUTIONS] ?? {}) as PnpmOrYarnOverrides\n return { type: YARN_CLASSIC, overrides }\n}\n\nexport type GetOverrides = (\n pkgEnvDetails: EnvDetails,\n pkgJson?: PackageJson | undefined,\n) => GetOverridesResult\n\nexport type GetOverridesResult = { type: Agent; overrides: Overrides }\n\nexport function getOverridesData(\n pkgEnvDetails: EnvDetails,\n pkgJson?: PackageJson | undefined,\n): GetOverridesResult {\n switch (pkgEnvDetails.agent) {\n case BUN:\n return getOverridesDataBun(pkgEnvDetails, pkgJson)\n case PNPM:\n return getOverridesDataPnpm(pkgEnvDetails, pkgJson)\n case VLT:\n return getOverridesDataVlt(pkgEnvDetails, pkgJson)\n case YARN_BERRY:\n return getOverridesDataYarn(pkgEnvDetails, pkgJson)\n case YARN_CLASSIC:\n return getOverridesDataYarnClassic(pkgEnvDetails, pkgJson)\n case NPM:\n default:\n return getOverridesDataNpm(pkgEnvDetails, pkgJson)\n }\n}\n","import { escapeRegExp } from '@socketsecurity/registry/lib/regexps'\n\nimport constants from '../../constants.mts'\n\nimport type { EnvDetails } from '../../utils/package-environment.mts'\n\nconst { BUN, EXT_LOCK, NPM, PNPM, VLT, YARN_BERRY, YARN_CLASSIC } = constants\n\nexport function npmLockSrcIncludes(lockSrc: string, name: string) {\n // Detects the package name in the following cases:\n // \"name\":\n return lockSrc.includes(`\"${name}\":`)\n}\n\nexport function bunLockSrcIncludes(\n lockSrc: string,\n name: string,\n lockName?: string | undefined,\n) {\n // This is a bit counterintuitive. When lockName ends with a .lockb\n // we treat it as a yarn.lock. When lockName ends with a .lock we\n // treat it as a package-lock.json. The bun.lock format is not identical\n // package-lock.json, however it close enough for npmLockIncludes to work.\n const lockfileScanner = lockName?.endsWith(EXT_LOCK)\n ? npmLockSrcIncludes\n : yarnLockSrcIncludes\n return lockfileScanner(lockSrc, name)\n}\n\nexport function pnpmLockSrcIncludes(lockSrc: string, name: string) {\n const escapedName = escapeRegExp(name)\n return new RegExp(\n // Detects the package name.\n // v9.0 and v6.0 lockfile patterns:\n // 'name'\n // name:\n // name@\n // v6.0 lockfile patterns:\n // /name@\n `(?<=^\\\\s*)(?:'${escapedName}'|/?${escapedName}(?=[:@]))`,\n 'm',\n ).test(lockSrc)\n}\n\nexport function vltLockSrcIncludes(lockSrc: string, name: string) {\n // Detects the package name in the following cases:\n // \"name\"\n return lockSrc.includes(`\"${name}\"`)\n}\n\nexport function yarnLockSrcIncludes(lockSrc: string, name: string) {\n const escapedName = escapeRegExp(name)\n return new RegExp(\n // Detects the package name in the following cases:\n // \"name@\n // , \"name@\n // name@\n // , name@\n `(?<=(?:^\\\\s*|,\\\\s*)\"?)${escapedName}(?=@)`,\n 'm',\n ).test(lockSrc)\n}\n\nexport function lockSrcIncludes(\n pkgEnvDetails: EnvDetails,\n lockSrc: string,\n name: string,\n lockName?: string | undefined,\n): boolean {\n switch (pkgEnvDetails.agent) {\n case BUN:\n return bunLockSrcIncludes(lockSrc, name, lockName)\n case PNPM:\n return pnpmLockSrcIncludes(lockSrc, name)\n case VLT:\n return vltLockSrcIncludes(lockSrc, name)\n case YARN_BERRY:\n return yarnLockSrcIncludes(lockSrc, name)\n case YARN_CLASSIC:\n return yarnLockSrcIncludes(lockSrc, name)\n case NPM:\n default:\n return npmLockSrcIncludes(lockSrc, name)\n }\n}\n","import { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants.mts'\n\nimport type { EnvDetails } from '../../utils/package-environment.mts'\n\nconst { BUN, NPM, PNPM, VLT, YARN_BERRY, YARN_CLASSIC } = constants\n\nfunction cleanupQueryStdout(stdout: string): string {\n if (stdout === '') {\n return ''\n }\n let pkgs\n try {\n pkgs = JSON.parse(stdout)\n } catch {}\n if (!Array.isArray(pkgs) || !pkgs.length) {\n return ''\n }\n const names = new Set<string>()\n for (const { _id, name, pkgid } of pkgs) {\n // `npm query` results may not have a \"name\" property, in which case we\n // fallback to \"_id\" and then \"pkgid\".\n // `vlt ls --view json` results always have a \"name\" property.\n const fallback = _id ?? pkgid ?? ''\n const resolvedName = name ?? fallback.slice(0, fallback.indexOf('@', 1))\n // Add package names, except for those under the `@types` scope as those\n // are known to only be dev dependencies.\n if (resolvedName && !resolvedName.startsWith('@types/')) {\n names.add(resolvedName)\n }\n }\n return JSON.stringify(Array.from(names), null, 2)\n}\n\nfunction parsableToQueryStdout(stdout: string) {\n if (stdout === '') {\n return ''\n }\n // Convert the parsable stdout into a json array of unique names.\n // The matchAll regexp looks for a forward (posix) or backward (win32) slash\n // and matches one or more non-slashes until the newline.\n const names = new Set(stdout.matchAll(/(?<=[/\\\\])[^/\\\\]+(?=\\n)/g))\n return JSON.stringify(Array.from(names), null, 2)\n}\n\nasync function npmQuery(npmExecPath: string, cwd: string): Promise<string> {\n let stdout = ''\n try {\n stdout = (\n await spawn(npmExecPath, ['query', ':not(.dev)'], {\n cwd,\n shell: constants.WIN32,\n })\n ).stdout\n } catch {}\n return cleanupQueryStdout(stdout)\n}\n\nexport async function lsBun(\n pkgEnvDetails: EnvDetails,\n options?: AgentListDepsOptions | undefined,\n): Promise<string> {\n const { cwd = process.cwd() } = {\n __proto__: null,\n ...options,\n } as AgentListDepsOptions\n try {\n // Bun does not support filtering by production packages yet.\n // https://github.com/oven-sh/bun/issues/8283\n return (\n await spawn(pkgEnvDetails.agentExecPath, ['pm', 'ls', '--all'], {\n cwd,\n shell: constants.WIN32,\n })\n ).stdout\n } catch {}\n return ''\n}\n\nexport async function lsNpm(\n pkgEnvDetails: EnvDetails,\n options?: AgentListDepsOptions | undefined,\n): Promise<string> {\n const { cwd = process.cwd() } = {\n __proto__: null,\n ...options,\n } as AgentListDepsOptions\n return await npmQuery(pkgEnvDetails.agentExecPath, cwd)\n}\n\nexport async function lsPnpm(\n pkgEnvDetails: EnvDetails,\n options?: AgentListDepsOptions | undefined,\n): Promise<string> {\n const { cwd = process.cwd(), npmExecPath } = {\n __proto__: null,\n ...options,\n } as AgentListDepsOptions\n if (npmExecPath && npmExecPath !== NPM) {\n const result = await npmQuery(npmExecPath, cwd)\n if (result) {\n return result\n }\n }\n let stdout = ''\n try {\n stdout = (\n await spawn(\n pkgEnvDetails.agentExecPath,\n // Pnpm uses the alternative spelling of parsable.\n // https://en.wiktionary.org/wiki/parsable\n ['ls', '--parseable', '--prod', '--depth', 'Infinity'],\n {\n cwd,\n shell: constants.WIN32,\n },\n )\n ).stdout\n } catch {}\n return parsableToQueryStdout(stdout)\n}\n\nexport async function lsVlt(\n pkgEnvDetails: EnvDetails,\n options?: AgentListDepsOptions | undefined,\n): Promise<string> {\n const { cwd = process.cwd() } = {\n __proto__: null,\n ...options,\n } as AgentListDepsOptions\n let stdout = ''\n try {\n // See https://docs.vlt.sh/cli/commands/list#options.\n stdout = (\n await spawn(\n pkgEnvDetails.agentExecPath,\n ['ls', '--view', 'human', ':not(.dev)'],\n {\n cwd,\n shell: constants.WIN32,\n },\n )\n ).stdout\n } catch {}\n return cleanupQueryStdout(stdout)\n}\n\nexport async function lsYarnBerry(\n pkgEnvDetails: EnvDetails,\n options?: AgentListDepsOptions | undefined,\n): Promise<string> {\n const { cwd = process.cwd() } = {\n __proto__: null,\n ...options,\n } as AgentListDepsOptions\n try {\n // Yarn Berry does not support filtering by production packages yet.\n // https://github.com/yarnpkg/berry/issues/5117\n return (\n await spawn(\n pkgEnvDetails.agentExecPath,\n ['info', '--recursive', '--name-only'],\n {\n cwd,\n shell: constants.WIN32,\n },\n )\n ).stdout\n } catch {}\n return ''\n}\n\nexport async function lsYarnClassic(\n pkgEnvDetails: EnvDetails,\n options?: AgentListDepsOptions | undefined,\n): Promise<string> {\n const { cwd = process.cwd() } = {\n __proto__: null,\n ...options,\n } as AgentListDepsOptions\n try {\n // However, Yarn Classic does support it.\n // https://github.com/yarnpkg/yarn/releases/tag/v1.0.0\n // > Fix: Excludes dev dependencies from the yarn list output when the\n // environment is production\n return (\n await spawn(pkgEnvDetails.agentExecPath, ['list', '--prod'], {\n cwd,\n shell: constants.WIN32,\n })\n ).stdout\n } catch {}\n return ''\n}\n\nexport type AgentListDepsOptions = {\n cwd?: string | undefined\n npmExecPath?: string | undefined\n}\n\nexport async function listPackages(\n pkgEnvDetails: EnvDetails,\n options?: AgentListDepsOptions | undefined,\n): Promise<string> {\n switch (pkgEnvDetails.agent) {\n case BUN:\n return await lsBun(pkgEnvDetails, options)\n case PNPM:\n return await lsPnpm(pkgEnvDetails, options)\n case VLT:\n return await lsVlt(pkgEnvDetails, options)\n case YARN_BERRY:\n return await lsYarnBerry(pkgEnvDetails, options)\n case YARN_CLASSIC:\n return await lsYarnClassic(pkgEnvDetails, options)\n case NPM:\n default:\n return await lsNpm(pkgEnvDetails, options)\n }\n}\n","export const CMD_NAME = 'socket optimize'\n","import { hasKeys, isObject } from '@socketsecurity/registry/lib/objects'\n\nimport constants from '../../constants.mts'\n\nimport type { Overrides } from './types.mts'\nimport type { Agent } from '../../utils/package-environment.mts'\nimport type { EditablePackageJson } from '@socketsecurity/registry/lib/packages'\n\nconst {\n BUN,\n NPM,\n OVERRIDES,\n PNPM,\n RESOLUTIONS,\n VLT,\n YARN_BERRY,\n YARN_CLASSIC,\n} = constants\n\nconst depFields = [\n 'dependencies',\n 'devDependencies',\n 'peerDependencies',\n 'peerDependenciesMeta',\n 'optionalDependencies',\n 'bundleDependencies',\n]\n\nfunction getEntryIndexes(\n entries: Array<[string | symbol, any]>,\n keys: Array<string | symbol>,\n): number[] {\n return keys\n .map(n => entries.findIndex(p => p[0] === n))\n .filter(n => n !== -1)\n .sort((a, b) => a - b)\n}\n\nfunction getLowestEntryIndex(\n entries: Array<[string | symbol, any]>,\n keys: Array<string | symbol>,\n) {\n return getEntryIndexes(entries, keys)?.[0] ?? -1\n}\n\nfunction getHighestEntryIndex(\n entries: Array<[string | symbol, any]>,\n keys: Array<string | symbol>,\n) {\n return getEntryIndexes(entries, keys).at(-1) ?? -1\n}\n\nfunction updatePkgJsonField(\n editablePkgJson: EditablePackageJson,\n field: string,\n value: any,\n) {\n const oldValue = editablePkgJson.content[field]\n if (oldValue) {\n // The field already exists so we simply update the field value.\n if (field === PNPM) {\n const isPnpmObj = isObject(oldValue)\n if (hasKeys(value)) {\n editablePkgJson.update({\n [field]: {\n ...(isPnpmObj ? oldValue : {}),\n overrides: {\n ...(isPnpmObj ? (oldValue as any)[OVERRIDES] : {}),\n ...value,\n },\n },\n })\n } else {\n // Properties with undefined values are deleted when saved as JSON.\n editablePkgJson.update(\n (hasKeys(oldValue)\n ? {\n [field]: {\n ...(isPnpmObj ? oldValue : {}),\n overrides: undefined,\n },\n }\n : { [field]: undefined }) as typeof editablePkgJson.content,\n )\n }\n } else if (field === OVERRIDES || field === RESOLUTIONS) {\n // Properties with undefined values are deleted when saved as JSON.\n editablePkgJson.update({\n [field]: hasKeys(value) ? value : undefined,\n } as typeof editablePkgJson.content)\n } else {\n editablePkgJson.update({ [field]: value })\n }\n return\n }\n if (\n (field === OVERRIDES || field === PNPM || field === RESOLUTIONS) &&\n !hasKeys(value)\n ) {\n return\n }\n // Since the field doesn't exist we want to insert it into the package.json\n // in a place that makes sense, e.g. close to the \"dependencies\" field. If\n // we can't find a place to insert the field we'll add it to the bottom.\n const entries = Object.entries(editablePkgJson.content)\n let insertIndex = -1\n let isPlacingHigher = false\n if (field === OVERRIDES) {\n insertIndex = getLowestEntryIndex(entries, [RESOLUTIONS])\n if (insertIndex === -1) {\n isPlacingHigher = true\n insertIndex = getHighestEntryIndex(entries, [...depFields, PNPM])\n }\n } else if (field === RESOLUTIONS) {\n isPlacingHigher = true\n insertIndex = getHighestEntryIndex(entries, [...depFields, OVERRIDES, PNPM])\n } else if (field === PNPM) {\n insertIndex = getLowestEntryIndex(entries, [OVERRIDES, RESOLUTIONS])\n if (insertIndex === -1) {\n isPlacingHigher = true\n insertIndex = getHighestEntryIndex(entries, depFields)\n }\n }\n if (insertIndex === -1) {\n insertIndex = getLowestEntryIndex(entries, ['engines', 'files'])\n }\n if (insertIndex === -1) {\n isPlacingHigher = true\n insertIndex = getHighestEntryIndex(entries, ['exports', 'imports', 'main'])\n }\n if (insertIndex === -1) {\n insertIndex = entries.length\n } else if (isPlacingHigher) {\n insertIndex += 1\n }\n entries.splice(insertIndex, 0, [\n field,\n field === PNPM ? { [OVERRIDES]: value } : value,\n ])\n editablePkgJson.fromJSON(\n `${JSON.stringify(Object.fromEntries(entries), null, 2)}\\n`,\n )\n}\n\nexport function updateOverridesField(\n editablePkgJson: EditablePackageJson,\n overrides: Overrides,\n) {\n updatePkgJsonField(editablePkgJson, OVERRIDES, overrides)\n}\n\nexport function updateResolutionsField(\n editablePkgJson: EditablePackageJson,\n overrides: Overrides,\n) {\n updatePkgJsonField(editablePkgJson, RESOLUTIONS, overrides)\n}\n\nexport function updatePnpmField(\n editablePkgJson: EditablePackageJson,\n overrides: Overrides,\n) {\n updatePkgJsonField(editablePkgJson, PNPM, overrides)\n}\n\nexport function updateManifest(\n agent: Agent,\n editablePkgJson: EditablePackageJson,\n overrides: Overrides,\n): void {\n switch (agent) {\n case BUN:\n updateResolutionsField(editablePkgJson, overrides)\n return\n case PNPM:\n updatePnpmField(editablePkgJson, overrides)\n return\n case VLT:\n updateOverridesField(editablePkgJson, overrides)\n return\n case YARN_BERRY:\n updateResolutionsField(editablePkgJson, overrides)\n return\n case YARN_CLASSIC:\n updateResolutionsField(editablePkgJson, overrides)\n return\n case NPM:\n default:\n updateOverridesField(editablePkgJson, overrides)\n return\n }\n}\n","import path from 'node:path'\n\nimport semver from 'semver'\n\nimport { getManifestData } from '@socketsecurity/registry'\nimport { hasOwn, toSortedObject } from '@socketsecurity/registry/lib/objects'\nimport { fetchPackageManifest } from '@socketsecurity/registry/lib/packages'\nimport { pEach } from '@socketsecurity/registry/lib/promises'\nimport { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nimport { lsStdoutIncludes } from './deps-includes-by-agent.mts'\nimport { getDependencyEntries } from './get-dependency-entries.mts'\nimport {\n getOverridesData,\n getOverridesDataNpm,\n getOverridesDataYarnClassic,\n} from './get-overrides-by-agent.mts'\nimport { lockSrcIncludes } from './lockfile-includes-by-agent.mts'\nimport { listPackages } from './ls-by-agent.mts'\nimport { CMD_NAME } from './shared.mts'\nimport { updateManifest } from './update-manifest-by-agent.mts'\nimport { NPM, PNPM } from '../../constants.mts'\nimport { cmdPrefixMessage } from '../../utils/cmd.mts'\nimport { globWorkspace } from '../../utils/glob.mts'\nimport { npa } from '../../utils/npm-package-arg.mts'\nimport { getMajor } from '../../utils/semver.mts'\n\nimport type { GetOverridesResult } from './get-overrides-by-agent.mts'\nimport type { AliasResult } from '../../utils/npm-package-arg.mts'\nimport type { EnvDetails } from '../../utils/package-environment.mts'\nimport type { Logger } from '@socketsecurity/registry/lib/logger'\nimport type { PackageJson } from '@socketsecurity/registry/lib/packages'\n\ntype AddOverridesOptions = {\n logger?: Logger | undefined\n pin?: boolean | undefined\n prod?: boolean | undefined\n spinner?: Spinner | undefined\n state?: AddOverridesState | undefined\n}\ntype AddOverridesState = {\n added: Set<string>\n addedInWorkspaces: Set<string>\n updated: Set<string>\n updatedInWorkspaces: Set<string>\n warnedPnpmWorkspaceRequiresNpm: boolean\n}\n\nconst manifestNpmOverrides = getManifestData(NPM)\n\nexport async function addOverrides(\n pkgEnvDetails: EnvDetails,\n pkgPath: string,\n options?: AddOverridesOptions | undefined,\n): Promise<AddOverridesState> {\n const {\n agent,\n lockName,\n lockSrc,\n npmExecPath,\n pkgPath: rootPath,\n } = pkgEnvDetails\n const {\n logger,\n pin,\n prod,\n spinner,\n state = {\n added: new Set(),\n addedInWorkspaces: new Set(),\n updated: new Set(),\n updatedInWorkspaces: new Set(),\n warnedPnpmWorkspaceRequiresNpm: false,\n },\n } = { __proto__: null, ...options } as AddOverridesOptions\n const workspacePkgJsonPaths = await globWorkspace(agent, pkgPath)\n const isPnpm = agent === PNPM\n const isWorkspace = workspacePkgJsonPaths.length > 0\n const isWorkspaceRoot = pkgPath === rootPath\n const isLockScanned = isWorkspaceRoot && !prod\n const workspace = isWorkspaceRoot ? 'root' : path.relative(rootPath, pkgPath)\n if (\n isWorkspace &&\n isPnpm &&\n // npmExecPath will === the agent name IF it CANNOT be resolved.\n npmExecPath === NPM &&\n !state.warnedPnpmWorkspaceRequiresNpm\n ) {\n state.warnedPnpmWorkspaceRequiresNpm = true\n spinner?.stop()\n logger?.warn(\n cmdPrefixMessage(\n CMD_NAME,\n `${agent} workspace support requires \\`npm ls\\`, falling back to \\`${agent} list\\``,\n ),\n )\n spinner?.start()\n }\n\n const overridesDataObjects = [] as GetOverridesResult[]\n if (isWorkspace || pkgEnvDetails.editablePkgJson.content['private']) {\n overridesDataObjects.push(getOverridesData(pkgEnvDetails))\n } else {\n overridesDataObjects.push(\n getOverridesDataNpm(pkgEnvDetails),\n getOverridesDataYarnClassic(pkgEnvDetails),\n )\n }\n\n const depAliasMap = new Map<string, string>()\n const depEntries = getDependencyEntries(pkgEnvDetails)\n const manifestEntries = manifestNpmOverrides.filter(({ 1: data }) =>\n semver.satisfies(\n // Roughly check Node range as semver.coerce will strip leading\n // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).\n semver.coerce(data.engines.node)!,\n pkgEnvDetails.pkgRequirements.node,\n ),\n )\n\n const addingText = `Adding overrides to ${workspace}...`\n let loggedAddingText = false\n\n // Chunk package names to process them in parallel 3 at a time.\n await pEach(\n manifestEntries,\n async ({ 1: data }) => {\n const { name: sockRegPkgName, package: origPkgName, version } = data\n const major = getMajor(version)!\n const sockOverridePrefix = `npm:${sockRegPkgName}@`\n const sockOverrideSpec = `${sockOverridePrefix}${pin ? version : `^${major}`}`\n for (const { 1: depObj } of depEntries) {\n const sockSpec = hasOwn(depObj, sockRegPkgName)\n ? depObj[sockRegPkgName]\n : undefined\n if (sockSpec) {\n depAliasMap.set(sockRegPkgName, sockSpec)\n }\n const origSpec = hasOwn(depObj, origPkgName)\n ? depObj[origPkgName]\n : undefined\n if (origSpec) {\n let thisSpec = origSpec\n // Add package aliases for direct dependencies to avoid npm EOVERRIDE\n // errors...\n // https://docs.npmjs.com/cli/v8/using-npm/package-spec#aliases\n if (\n // ...if the spec doesn't start with a valid Socket override.\n !(\n thisSpec.startsWith(sockOverridePrefix) &&\n // Check the validity of the spec by passing it through npa and\n // seeing if it will coerce to a version.\n semver.coerce((npa(thisSpec) as AliasResult).subSpec.rawSpec)\n ?.version\n )\n ) {\n thisSpec = sockOverrideSpec\n depObj[origPkgName] = thisSpec\n state.added.add(sockRegPkgName)\n if (!isWorkspaceRoot) {\n state.addedInWorkspaces.add(workspace)\n }\n if (!loggedAddingText) {\n spinner?.setText(addingText)\n loggedAddingText = true\n }\n }\n depAliasMap.set(origPkgName, thisSpec)\n }\n }\n if (isWorkspaceRoot) {\n // The lockSrcIncludes and lsStdoutIncludes functions overlap in their\n // first two parameters. lockSrcIncludes accepts an optional third parameter\n // which lsStdoutIncludes will ignore.\n const thingScanner = (\n isLockScanned ? lockSrcIncludes : lsStdoutIncludes\n ) as typeof lockSrcIncludes\n\n const thingToScan = isLockScanned\n ? lockSrc\n : await listPackages(pkgEnvDetails, { cwd: pkgPath, npmExecPath })\n // Chunk package names to process them in parallel 3 at a time.\n await pEach(\n overridesDataObjects,\n async ({ overrides, type }) => {\n const overrideExists = hasOwn(overrides, origPkgName)\n if (\n overrideExists ||\n thingScanner(pkgEnvDetails, thingToScan, origPkgName, lockName)\n ) {\n const oldSpec = overrideExists\n ? overrides[origPkgName]!\n : undefined\n const origDepAlias = depAliasMap.get(origPkgName)\n const sockRegDepAlias = depAliasMap.get(sockRegPkgName)\n const depAlias = sockRegDepAlias ?? origDepAlias\n let newSpec = sockOverrideSpec\n if (type === NPM && depAlias) {\n // With npm one may not set an override for a package that one directly\n // depends on unless both the dependency and the override itself share\n // the exact same spec. To make this limitation easier to deal with,\n // overrides may also be defined as a reference to a spec for a direct\n // dependency by prefixing the name of the package to match the version\n // of with a $.\n // https://docs.npmjs.com/cli/v8/configuring-npm/package-json#overrides\n newSpec = `$${sockRegDepAlias ? sockRegPkgName : origPkgName}`\n } else if (typeof oldSpec === 'string') {\n const thisSpec = oldSpec.startsWith('$')\n ? depAlias || newSpec\n : oldSpec || newSpec\n if (thisSpec.startsWith(sockOverridePrefix)) {\n if (\n pin &&\n getMajor(\n // Check the validity of the spec by passing it through npa\n // and seeing if it will coerce to a version. semver.coerce\n // will strip leading v's, carets (^), comparators (<,<=,>,>=,=),\n // and tildes (~). If not coerced to a valid version then\n // default to the manifest entry version.\n semver.coerce(\n (npa(thisSpec) as AliasResult).subSpec.rawSpec,\n )?.version ?? version,\n ) !== major\n ) {\n const otherVersion = (await fetchPackageManifest(thisSpec))\n ?.version\n if (otherVersion && otherVersion !== version) {\n newSpec = `${sockOverridePrefix}${pin ? otherVersion : `^${getMajor(otherVersion)!}`}`\n }\n }\n } else {\n newSpec = oldSpec\n }\n }\n if (newSpec !== oldSpec) {\n overrides[origPkgName] = newSpec\n const addedOrUpdated = overrideExists ? 'updated' : 'added'\n state[addedOrUpdated].add(sockRegPkgName)\n if (!loggedAddingText) {\n spinner?.setText(addingText)\n loggedAddingText = true\n }\n }\n }\n },\n { concurrency: 3 },\n )\n }\n },\n { concurrency: 3 },\n )\n\n if (isWorkspace) {\n // Chunk package names to process them in parallel 3 at a time.\n await pEach(\n workspacePkgJsonPaths,\n async workspacePkgJsonPath => {\n const otherState = await addOverrides(\n pkgEnvDetails,\n path.dirname(workspacePkgJsonPath),\n {\n logger,\n pin,\n prod,\n spinner,\n },\n )\n for (const key of [\n 'added',\n 'addedInWorkspaces',\n 'updated',\n 'updatedInWorkspaces',\n ] satisfies\n // Here we're just telling TS that we're looping over key names\n // of the type and that they're all Set<string> props.\n Array<\n keyof Pick<\n AddOverridesState,\n 'added' | 'addedInWorkspaces' | 'updated' | 'updatedInWorkspaces'\n >\n >) {\n for (const value of otherState[key]) {\n state[key].add(value)\n }\n }\n },\n { concurrency: 3 },\n )\n }\n\n if (state.added.size > 0 || state.updated.size > 0) {\n pkgEnvDetails.editablePkgJson.update(\n Object.fromEntries(depEntries) as PackageJson,\n )\n if (isWorkspaceRoot) {\n for (const { overrides, type } of overridesDataObjects) {\n updateManifest(\n type,\n pkgEnvDetails.editablePkgJson,\n toSortedObject(overrides),\n )\n }\n }\n await pkgEnvDetails.editablePkgJson.save()\n }\n\n return state\n}\n","import { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nimport constants from '../../constants.mts'\nimport { runAgentInstall } from '../../utils/agent.mts'\nimport { cmdPrefixMessage } from '../../utils/cmd.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { EnvDetails } from '../../utils/package-environment.mts'\nimport type { Logger } from '@socketsecurity/registry/lib/logger'\n\nconst { NPM_BUGGY_OVERRIDES_PATCHED_VERSION } = constants\n\nexport type UpdateLockfileOptions = {\n cmdName?: string | undefined\n logger?: Logger | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function updateLockfile(\n pkgEnvDetails: EnvDetails,\n options: UpdateLockfileOptions,\n): Promise<CResult<unknown>> {\n const {\n cmdName = '',\n logger,\n spinner,\n } = {\n __proto__: null,\n ...options,\n } as UpdateLockfileOptions\n\n const wasSpinning = !!spinner?.isSpinning\n\n spinner?.start(`Updating ${pkgEnvDetails.lockName}...`)\n\n try {\n await runAgentInstall(pkgEnvDetails, { spinner })\n if (pkgEnvDetails.features.npmBuggyOverrides) {\n spinner?.stop()\n logger?.log(\n `💡 Re-run ${cmdName ? `${cmdName} ` : ''}whenever ${pkgEnvDetails.lockName} changes.\\n This can be skipped for ${pkgEnvDetails.agent} >=${NPM_BUGGY_OVERRIDES_PATCHED_VERSION}.`,\n )\n }\n } catch (e) {\n spinner?.stop()\n\n debugFn('error', 'fail: update')\n debugDir('inspect', { error: e })\n\n if (wasSpinning) {\n spinner.start()\n }\n\n return {\n ok: false,\n message: 'Update failed',\n cause: cmdPrefixMessage(\n cmdName,\n `${pkgEnvDetails.agent} install failed to update ${pkgEnvDetails.lockName}`,\n ),\n }\n }\n\n spinner?.stop()\n\n if (wasSpinning) {\n spinner.start()\n }\n\n return { ok: true, data: undefined }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { addOverrides } from './add-overrides.mts'\nimport { CMD_NAME } from './shared.mts'\nimport { updateLockfile } from './update-lockfile.mts'\nimport constants from '../../constants.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { EnvDetails } from '../../utils/package-environment.mts'\n\nexport type OptimizeConfig = {\n pin: boolean\n prod: boolean\n}\n\nexport async function applyOptimization(\n pkgEnvDetails: EnvDetails,\n { pin, prod }: OptimizeConfig,\n): Promise<\n CResult<{\n addedCount: number\n updatedCount: number\n pkgJsonChanged: boolean\n updatedInWorkspaces: number\n addedInWorkspaces: number\n }>\n> {\n const { spinner } = constants\n\n spinner.start()\n\n const state = await addOverrides(pkgEnvDetails, pkgEnvDetails.pkgPath, {\n logger,\n pin,\n prod,\n spinner,\n })\n\n const addedCount = state.added.size\n const updatedCount = state.updated.size\n const pkgJsonChanged = addedCount > 0 || updatedCount > 0\n\n if (pkgJsonChanged || pkgEnvDetails.features.npmBuggyOverrides) {\n const result = await updateLockfile(pkgEnvDetails, {\n cmdName: CMD_NAME,\n logger,\n spinner,\n })\n if (!result.ok) {\n spinner.stop()\n return result\n }\n }\n\n spinner.stop()\n return {\n ok: true,\n data: {\n addedCount,\n addedInWorkspaces: state.addedInWorkspaces.size,\n pkgJsonChanged,\n updatedCount,\n updatedInWorkspaces: state.updatedInWorkspaces.size,\n },\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputOptimizeResult(\n result: CResult<{\n addedCount: number\n updatedCount: number\n pkgJsonChanged: boolean\n updatedInWorkspaces: number\n addedInWorkspaces: number\n }>,\n outputKind: OutputKind,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const data = result.data\n\n if (data.updatedCount > 0) {\n logger?.log(\n `${createActionMessage('Updated', data.updatedCount, data.updatedInWorkspaces)}${data.addedCount ? '.' : '🚀'}`,\n )\n }\n if (data.addedCount > 0) {\n logger?.log(\n `${createActionMessage('Added', data.addedCount, data.addedInWorkspaces)} 🚀`,\n )\n }\n if (!data.pkgJsonChanged) {\n logger?.log('Scan complete. No Socket.dev optimized overrides applied.')\n }\n\n logger.log('')\n logger.success('Finished!')\n logger.log('')\n}\n\nfunction createActionMessage(\n verb: string,\n overrideCount: number,\n workspaceCount: number,\n): string {\n return `${verb} ${overrideCount} Socket.dev optimized ${pluralize('override', overrideCount)}${workspaceCount ? ` in ${workspaceCount} ${pluralize('workspace', workspaceCount)}` : ''}`\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { applyOptimization } from './apply-optimization.mts'\nimport { outputOptimizeResult } from './output-optimize-result.mts'\nimport { CMD_NAME } from './shared.mts'\nimport constants from '../../constants.mts'\nimport { cmdPrefixMessage } from '../../utils/cmd.mts'\nimport { detectAndValidatePackageEnvironment } from '../../utils/package-environment.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nconst { VLT } = constants\n\nexport async function handleOptimize({\n cwd,\n outputKind,\n pin,\n prod,\n}: {\n cwd: string\n outputKind: OutputKind\n pin: boolean\n prod: boolean\n}) {\n const pkgEnvCResult = await detectAndValidatePackageEnvironment(cwd, {\n cmdName: CMD_NAME,\n logger,\n prod,\n })\n if (!pkgEnvCResult.ok) {\n await outputOptimizeResult(pkgEnvCResult, outputKind)\n return\n }\n\n const pkgEnvDetails = pkgEnvCResult.data\n if (!pkgEnvDetails) {\n await outputOptimizeResult(\n {\n ok: false,\n message: 'No package found.',\n cause: `No valid package environment found for project path: ${cwd}`,\n },\n outputKind,\n )\n return\n }\n\n const { agent, agentVersion } = pkgEnvDetails\n if (agent === VLT) {\n await outputOptimizeResult(\n {\n ok: false,\n message: 'Unsupported',\n cause: cmdPrefixMessage(\n CMD_NAME,\n `${agent} v${agentVersion} does not support overrides.`,\n ),\n },\n outputKind,\n )\n return\n }\n\n logger.info(`Optimizing packages for ${agent} v${agentVersion}.\\n`)\n\n await outputOptimizeResult(\n await applyOptimization(pkgEnvDetails, { pin, prod }),\n outputKind,\n )\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleOptimize } from './handle-optimize.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'optimize'\n\nconst description = 'Optimize dependencies with @socketregistry overrides'\n\nconst hidden = false\n\nexport const cmdOptimize = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n pin: {\n type: 'boolean',\n default: false,\n description: 'Pin overrides to their latest version',\n },\n prod: {\n type: 'boolean',\n default: false,\n description: 'Only add overrides for production dependencies',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} ./proj/tree --pin\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n const { json, markdown, pin, prod } = cli.flags\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n const outputKind = getOutputKind(json, markdown)\n\n await handleOptimize({\n cwd,\n pin: Boolean(pin),\n outputKind,\n prod: Boolean(prod),\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchDependenciesConfig = {\n limit: number\n offset: number\n}\n\nexport type FetchDependenciesOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchDependencies(\n config: FetchDependenciesConfig,\n options?: FetchDependenciesOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'searchDependencies'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchDependenciesOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n const { limit, offset } = {\n __proto__: null,\n ...config,\n } as FetchDependenciesConfig\n\n return await handleApiCall(sockSdk.searchDependencies({ limit, offset }), {\n desc: 'organization dependencies',\n })\n}\n","// @ts-ignore\nimport chalkTable from 'chalk-table'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputDependencies(\n result: CResult<SocketSdkSuccessResult<'searchDependencies'>['data']>,\n {\n limit,\n offset,\n outputKind,\n }: {\n limit: number\n offset: number\n outputKind: OutputKind\n },\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n outputMarkdown(result.data, { limit, offset })\n}\n\nfunction outputMarkdown(\n result: SocketSdkSuccessResult<'searchDependencies'>['data'],\n {\n limit,\n offset,\n }: {\n limit: number\n offset: number\n },\n) {\n logger.log('# Organization dependencies')\n logger.log('')\n logger.log('Request details:')\n logger.log('- Offset:', offset)\n logger.log('- Limit:', limit)\n logger.log('- Is there more data after this?', result.end ? 'no' : 'yes')\n logger.log('')\n\n const options = {\n columns: [\n { field: 'type', name: colors.cyan('Ecosystem') },\n { field: 'namespace', name: colors.cyan('Namespace') },\n { field: 'name', name: colors.cyan('Name') },\n { field: 'version', name: colors.cyan('Version') },\n { field: 'repository', name: colors.cyan('Repository') },\n { field: 'branch', name: colors.cyan('Branch') },\n { field: 'direct', name: colors.cyan('Direct') },\n ],\n }\n\n logger.log(chalkTable(options, result.rows))\n}\n","import { fetchDependencies } from './fetch-dependencies.mts'\nimport { outputDependencies } from './output-dependencies.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleDependencies({\n limit,\n offset,\n outputKind,\n}: {\n limit: number\n offset: number\n outputKind: OutputKind\n}): Promise<void> {\n const result = await fetchDependencies({ limit, offset })\n\n await outputDependencies(result, { limit, offset, outputKind })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleDependencies } from './handle-dependencies.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'dependencies'\n\nconst description =\n 'Search for any dependency that is being used in your organization'\n\nconst hidden = false\n\nexport const cmdOrganizationDependencies = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n limit: {\n type: 'number',\n default: 50,\n description: 'Maximum number of dependencies returned',\n },\n offset: {\n type: 'number',\n default: 0,\n description: 'Page number',\n },\n ...outputFlags,\n },\n help: (command, config) => `\n Usage\n ${command} [options]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n ${command}\n ${command} --limit 20 --offset 10\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, limit, markdown, offset } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleDependencies({\n limit: Number(limit || 0) || 0,\n offset: Number(offset || 0) || 0,\n outputKind,\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchLicensePolicyOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchLicensePolicy(\n orgSlug: string,\n options?: FetchLicensePolicyOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgLicensePolicy'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchLicensePolicyOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getOrgLicensePolicy(orgSlug), {\n desc: 'organization license policy',\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mdTableOfPairs } from '../../utils/markdown.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputLicensePolicy(\n result: CResult<SocketSdkSuccessResult<'getOrgLicensePolicy'>['data']>,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.info('Use --json to get the full result')\n logger.log('# License policy')\n logger.log('')\n logger.log('This is the license policy for your organization:')\n logger.log('')\n const rules = result.data['license_policy']!\n const entries = rules ? Object.entries(rules) : []\n const mapped: Array<[string, string]> = entries.map(\n ({ 0: key, 1: value }) =>\n [key, (value as any)?.['allowed'] ? ' yes' : ' no'] as const,\n )\n mapped.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n logger.log(mdTableOfPairs(mapped, ['License Name', 'Allowed']))\n logger.log('')\n}\n","import { fetchLicensePolicy } from './fetch-license-policy.mts'\nimport { outputLicensePolicy } from './output-license-policy.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleLicensePolicy(\n orgSlug: string,\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchLicensePolicy(orgSlug)\n\n await outputLicensePolicy(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleLicensePolicy } from './handle-license-policy.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'license'\n\nconst description = 'Retrieve the license policy of an organization'\n\nconst hidden = false\n\nexport const cmdOrganizationPolicyLicense = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: command => `\n Usage\n $ ${command} [options]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Your API token will need the \\`license-policy:read\\` permission otherwise\n the request will fail with an authentication error.\n\n Examples\n $ ${command}\n $ ${command} --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleLicensePolicy(orgSlug, outputKind)\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchSecurityPolicyOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchSecurityPolicy(\n orgSlug: string,\n options?: FetchSecurityPolicyOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgSecurityPolicy'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchSecurityPolicyOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getOrgSecurityPolicy(orgSlug), {\n desc: 'organization security policy',\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mdTableOfPairs } from '../../utils/markdown.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputSecurityPolicy(\n result: CResult<SocketSdkSuccessResult<'getOrgSecurityPolicy'>['data']>,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log('# Security policy')\n logger.log('')\n logger.log(\n `The default security policy setting is: \"${result.data.securityPolicyDefault}\"`,\n )\n logger.log('')\n logger.log(\n 'These are the security policies per setting for your organization:',\n )\n logger.log('')\n const rules = result.data.securityPolicyRules\n const entries: Array<\n [string, { action: 'defer' | 'error' | 'warn' | 'monitor' | 'ignore' }]\n > = rules ? Object.entries(rules) : []\n const mapped: Array<[string, string]> = entries.map(\n ({ 0: key, 1: value }) => [key, value.action],\n )\n mapped.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n logger.log(mdTableOfPairs(mapped, ['name', 'action']))\n logger.log('')\n}\n","import { fetchSecurityPolicy } from './fetch-security-policy.mts'\nimport { outputSecurityPolicy } from './output-security-policy.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleSecurityPolicy(\n orgSlug: string,\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchSecurityPolicy(orgSlug)\n\n await outputSecurityPolicy(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleSecurityPolicy } from './handle-security-policy.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'security'\n\nconst description = 'Retrieve the security policy of an organization'\n\nconst hidden = true\n\nexport const cmdOrganizationPolicySecurity = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: (command, _config) => `\n Usage\n $ ${command} [options]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Your API token will need the \\`security-policy:read\\` permission otherwise\n the request will fail with an authentication error.\n\n Examples\n $ ${command}\n $ ${command} --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleSecurityPolicy(orgSlug, outputKind)\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { getVisibleTokenPrefix } from '../../utils/sdk.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { OrganizationsCResult } from './fetch-organization-list.mts'\nimport type { OutputKind } from '../../types.mts'\n\nexport async function outputOrganizationList(\n orgsCResult: OrganizationsCResult,\n outputKind: OutputKind = 'text',\n): Promise<void> {\n if (!orgsCResult.ok) {\n process.exitCode = orgsCResult.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(orgsCResult))\n return\n }\n\n if (!orgsCResult.ok) {\n logger.fail(failMsgWithBadge(orgsCResult.message, orgsCResult.cause))\n return\n }\n\n const { organizations } = orgsCResult.data\n const visibleTokenPrefix = getVisibleTokenPrefix()\n\n if (outputKind !== 'markdown') {\n logger.log(\n `List of organizations associated with your API token, starting with: ${colors.italic(visibleTokenPrefix)}\\n`,\n )\n // Just dump.\n for (const o of organizations) {\n logger.log(\n `- Name: ${colors.bold(o.name ?? 'undefined')}, ID: ${colors.bold(o.id)}, Plan: ${colors.bold(o.plan)}`,\n )\n }\n return\n }\n\n // | Syntax | Description |\n // | ----------- | ----------- |\n // | Header | Title |\n // | Paragraph | Text |\n let mw1 = 4\n let mw2 = 2\n let mw3 = 4\n for (const o of organizations) {\n mw1 = Math.max(mw1, o.name?.length ?? 0)\n mw2 = Math.max(mw2, o.id.length)\n mw3 = Math.max(mw3, o.plan.length)\n }\n logger.log('# Organizations\\n')\n logger.log(\n `List of organizations associated with your API token, starting with: ${colors.italic(visibleTokenPrefix)}\\n`,\n )\n logger.log(\n `| Name${' '.repeat(mw1 - 4)} | ID${' '.repeat(mw2 - 2)} | Plan${' '.repeat(mw3 - 4)} |`,\n )\n logger.log(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} | ${'-'.repeat(mw3)} |`)\n for (const o of organizations) {\n logger.log(\n `| ${(o.name || '').padEnd(mw1, ' ')} | ${(o.id || '').padEnd(mw2, ' ')} | ${(o.plan || '').padEnd(mw3, ' ')} |`,\n )\n }\n logger.log(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} | ${'-'.repeat(mw3)} |`)\n}\n","import { fetchOrganization } from './fetch-organization-list.mts'\nimport { outputOrganizationList } from './output-organization-list.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleOrganizationList(\n outputKind: OutputKind = 'text',\n): Promise<void> {\n const data = await fetchOrganization()\n\n await outputOrganizationList(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleOrganizationList } from './handle-organization-list.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'list'\n\nconst description = 'List organizations associated with the Socket API token'\n\nconst hidden = false\n\nexport const cmdOrganizationList = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, _config) => `\n Usage\n $ ${command} [options]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleOrganizationList(outputKind)\n}\n","import { cmdOrganizationPolicyLicense } from './cmd-organization-policy-license.mts'\nimport { cmdOrganizationPolicySecurity } from './cmd-organization-policy-security.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Organization policy details'\n\nexport const cmdOrganizationPolicy: CliSubcommand = {\n description,\n // Hidden because it was broken all this time (nobody could be using it)\n // and we're not sure if it's useful to anyone in its current state.\n // Until we do, we'll hide this to keep the help tidier.\n // And later, we may simply move this under `scan`, anyways.\n hidden: false,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n security: cmdOrganizationPolicySecurity,\n license: cmdOrganizationPolicyLicense,\n },\n {\n argv,\n description,\n defaultSub: 'list', // Backwards compat\n importMeta,\n name: `${parentName} policy`,\n },\n )\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchQuotaOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchQuota(\n options?: FetchQuotaOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getQuota'>['data']>> {\n const { sdkOpts } = { __proto__: null, ...options } as FetchQuotaOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getQuota(), { desc: 'token quota' })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputQuota(\n result: CResult<SocketSdkSuccessResult<'getQuota'>['data']>,\n outputKind: OutputKind = 'text',\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'markdown') {\n logger.log('# Quota')\n logger.log('')\n logger.log(`Quota left on the current API token: ${result.data.quota}`)\n logger.log('')\n return\n }\n\n logger.log(`Quota left on the current API token: ${result.data.quota}`)\n logger.log('')\n}\n","import { fetchQuota } from './fetch-quota.mts'\nimport { outputQuota } from './output-quota.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleQuota(\n outputKind: OutputKind = 'text',\n): Promise<void> {\n const data = await fetchQuota()\n\n await outputQuota(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleQuota } from './handle-quota.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'quota',\n description: 'List organizations associated with the Socket API token',\n hidden: true,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, _config) => `\n Usage\n $ ${command} [options]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} --json\n `,\n}\n\nexport const cmdOrganizationQuota = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n const json = Boolean(cli.flags['json'])\n\n const markdown = Boolean(cli.flags['markdown'])\n\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleQuota(outputKind)\n}\n","import { cmdOrganizationDependencies } from './cmd-organization-dependencies.mts'\nimport { cmdOrganizationList } from './cmd-organization-list.mts'\nimport { cmdOrganizationPolicyLicense } from './cmd-organization-policy-license.mts'\nimport { cmdOrganizationPolicySecurity } from './cmd-organization-policy-security.mts'\nimport { cmdOrganizationPolicy } from './cmd-organization-policy.mts'\nimport { cmdOrganizationQuota } from './cmd-organization-quota.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Manage Socket organization account details'\n\nexport const cmdOrganization: CliSubcommand = {\n description,\n hidden: false,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n dependencies: cmdOrganizationDependencies,\n list: cmdOrganizationList,\n quota: cmdOrganizationQuota,\n policy: cmdOrganizationPolicy,\n },\n {\n aliases: {\n deps: {\n description: cmdOrganizationDependencies.description,\n hidden: true,\n argv: ['dependencies'],\n },\n license: {\n description: cmdOrganizationPolicyLicense.description,\n hidden: true,\n argv: ['policy', 'license'],\n },\n security: {\n description: cmdOrganizationPolicySecurity.description,\n hidden: true,\n argv: ['policy', 'security'],\n },\n },\n argv,\n description,\n importMeta,\n name: `${parentName} organization`,\n },\n )\n },\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { queryApiSafeJson } from '../../utils/api.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport interface PurlDataResponse {\n purl: string\n self: {\n purl: string\n score: {\n license: number\n maintenance: number\n overall: number\n quality: number\n supplyChain: number\n vulnerability: number\n }\n capabilities: string[]\n alerts: Array<{\n name: string\n severity: string\n category: string\n example: string\n }>\n }\n transitively: {\n dependencyCount: number\n func: string\n score: {\n license: number\n maintenance: number\n overall: number\n quality: number\n supplyChain: number\n vulnerability: number\n }\n lowest: {\n license: string\n maintenance: string\n overall: string\n quality: string\n supplyChain: string\n vulnerability: string\n }\n capabilities: string[]\n alerts: Array<{\n name: string\n severity: string\n category: string\n example: string\n }>\n }\n}\n\nexport async function fetchPurlDeepScore(\n purl: string,\n): Promise<CResult<PurlDataResponse>> {\n logger.info(`Requesting deep score data for this purl: ${purl}`)\n\n return await queryApiSafeJson<PurlDataResponse>(\n `purl/score/${encodeURIComponent(purl)}`,\n 'the deep package scores',\n )\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mdTable } from '../../utils/markdown.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { PurlDataResponse } from './fetch-purl-deep-score.mts'\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputPurlsDeepScore(\n purl: string,\n result: CResult<PurlDataResponse>,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'markdown') {\n const md = createMarkdownReport(result.data)\n logger.success(`Score report for \"${result.data.purl}\" (\"${purl}\"):\\n`)\n logger.log(md)\n return\n }\n\n logger.log(\n `Score report for \"${purl}\" (use --json for raw and --markdown for formatted reports):`,\n )\n logger.log(result.data)\n logger.log('')\n}\n\nexport function createMarkdownReport(data: PurlDataResponse): string {\n const {\n self: {\n alerts: selfAlerts,\n capabilities: selfCaps,\n purl,\n score: selfScore,\n },\n transitively: {\n alerts,\n capabilities,\n dependencyCount,\n func,\n lowest,\n score,\n },\n } = data\n\n const o: string[] = ['# Complete Package Score', '']\n if (dependencyCount) {\n o.push(\n `This is a Socket report for the package *\"${purl}\"* and its *${dependencyCount}* direct/transitive dependencies.`,\n )\n } else {\n o.push(\n `This is a Socket report for the package *\"${purl}\"*. It has *no dependencies*.`,\n )\n }\n o.push('')\n if (dependencyCount) {\n o.push(\n `It will show you the shallow score for just the package itself and a deep score for all the transitives combined. Additionally you can see which capabilities were found and the top alerts as well as a package that was responsible for it.`,\n )\n } else {\n o.push(\n `It will show you the shallow score for the package itself, which capabilities were found, and its top alerts.`,\n )\n o.push('')\n o.push(\n 'Since it has no dependencies, the shallow score is also the deep score.',\n )\n }\n o.push('')\n if (dependencyCount) {\n // This doesn't make much sense if there are no dependencies. Better to omit it.\n o.push(\n 'The report should give you a good insight into the status of this package.',\n )\n o.push('')\n o.push('## Package itself')\n o.push('')\n o.push(\n 'Here are results for the package itself (excluding data from dependencies).',\n )\n } else {\n o.push('## Report')\n o.push('')\n o.push(\n 'The report should give you a good insight into the status of this package.',\n )\n }\n o.push('')\n o.push('### Shallow Score')\n o.push('')\n o.push('This score is just for the package itself:')\n o.push('')\n o.push(`- Overall: ${selfScore.overall}`)\n o.push(`- Maintenance: ${selfScore.maintenance}`)\n o.push(`- Quality: ${selfScore.quality}`)\n o.push(`- Supply Chain: ${selfScore.supplyChain}`)\n o.push(`- Vulnerability: ${selfScore.vulnerability}`)\n o.push(`- License: ${selfScore.license}`)\n o.push('')\n o.push('### Capabilities')\n o.push('')\n if (selfCaps.length) {\n o.push('These are the capabilities detected in the package itself:')\n o.push('')\n for (const cap of selfCaps) {\n o.push(`- ${cap}`)\n }\n } else {\n o.push('No capabilities were found in the package.')\n }\n o.push('')\n o.push('### Alerts for this package')\n o.push('')\n if (selfAlerts.length) {\n if (dependencyCount) {\n o.push('These are the alerts found for the package itself:')\n } else {\n o.push('These are the alerts found for this package:')\n }\n o.push('')\n o.push(\n mdTable(selfAlerts, ['severity', 'name'], ['Severity', 'Alert Name']),\n )\n } else {\n o.push('There are currently no alerts for this package.')\n }\n o.push('')\n if (dependencyCount) {\n o.push('## Transitive Package Results')\n o.push('')\n o.push(\n 'Here are results for the package and its direct/transitive dependencies.',\n )\n o.push('')\n o.push('### Deep Score')\n o.push('')\n o.push(\n 'This score represents the package and and its direct/transitive dependencies:',\n )\n o.push(\n `The function used to calculate the values in aggregate is: *\"${func}\"*`,\n )\n o.push('')\n o.push(`- Overall: ${score.overall}`)\n o.push(`- Maintenance: ${score.maintenance}`)\n o.push(`- Quality: ${score.quality}`)\n o.push(`- Supply Chain: ${score.supplyChain}`)\n o.push(`- Vulnerability: ${score.vulnerability}`)\n o.push(`- License: ${score.license}`)\n o.push('')\n o.push('### Capabilities')\n o.push('')\n o.push(\n 'These are the packages with the lowest recorded score. If there is more than one with the lowest score, just one is shown here. This may help you figure out the source of low scores.',\n )\n o.push('')\n o.push(`- Overall: ${lowest.overall}`)\n o.push(`- Maintenance: ${lowest.maintenance}`)\n o.push(`- Quality: ${lowest.quality}`)\n o.push(`- Supply Chain: ${lowest.supplyChain}`)\n o.push(`- Vulnerability: ${lowest.vulnerability}`)\n o.push(`- License: ${lowest.license}`)\n o.push('')\n o.push('### Capabilities')\n o.push('')\n if (capabilities.length) {\n o.push('These are the capabilities detected in at least one package:')\n o.push('')\n for (const cap of capabilities) {\n o.push(`- ${cap}`)\n }\n } else {\n o.push(\n 'This package had no capabilities and neither did any of its direct/transitive dependencies.',\n )\n }\n o.push('')\n o.push('### Alerts')\n o.push('')\n if (alerts.length) {\n o.push('These are the alerts found:')\n o.push('')\n\n o.push(\n mdTable(\n alerts,\n ['severity', 'name', 'example'],\n ['Severity', 'Alert Name', 'Example package reporting it'],\n ),\n )\n } else {\n o.push(\n 'This package had no alerts and neither did any of its direct/transitive dependencies',\n )\n }\n o.push('')\n }\n return o.join('\\n')\n}\n","import { fetchPurlDeepScore } from './fetch-purl-deep-score.mts'\nimport { outputPurlsDeepScore } from './output-purls-deep-score.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handlePurlDeepScore(\n purl: string,\n outputKind: OutputKind,\n) {\n const result = await fetchPurlDeepScore(purl)\n\n await outputPurlsDeepScore(purl, result, outputKind)\n}\n","// Either an ecosystem was given or all args must be (namespaced) purls\n// The `pkg:` part is optional here. We'll scan for `eco/name@version`.\n// Not hardcoding the namespace since we don't know what the server accepts.\n// The ecosystem is considered as the first package if it is not an a-z string.\nexport function parsePackageSpecifiers(\n ecosystem: string,\n pkgs: string[],\n): { purls: string[]; valid: boolean } {\n let valid = true\n const purls = []\n if (!ecosystem) {\n valid = false\n } else if (/^[a-zA-Z]+$/.test(ecosystem)) {\n for (let i = 0; i < pkgs.length; ++i) {\n const pkg = pkgs[i] ?? ''\n if (!pkg) {\n valid = false\n break\n } else if (pkg.startsWith('pkg:')) {\n // keep\n purls.push(pkg)\n } else {\n purls.push('pkg:' + ecosystem + '/' + pkg)\n }\n }\n if (!purls.length) {\n valid = false\n }\n } else {\n // Assume ecosystem is a purl, too.\n pkgs.unshift(ecosystem)\n\n for (let i = 0; i < pkgs.length; ++i) {\n const pkg = pkgs[i] ?? ''\n if (!/^(?:pkg:)?[a-zA-Z]+\\/./.test(pkg)) {\n // At least one purl did not start with `pkg:eco/x` or `eco/x`.\n valid = false\n break\n } else if (pkg.startsWith('pkg:')) {\n purls.push(pkg)\n } else {\n purls.push('pkg:' + pkg)\n }\n }\n\n if (!purls.length) {\n valid = false\n }\n }\n\n return { purls, valid }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handlePurlDeepScore } from './handle-purl-deep-score.mts'\nimport { parsePackageSpecifiers } from './parse-package-specifiers.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'score'\n\nconst description =\n 'Look up score for one package which reflects all of its transitive dependencies as well'\n\nconst hidden = false\n\nexport const cmdPackageScore = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <<ECOSYSTEM> <NAME> | <PURL>>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Show deep scoring details for one package. The score will reflect the package\n itself, any of its dependencies, and any of its transitive dependencies.\n\n When you want to know whether to trust a package, this is the command to run.\n\n See also the \\`socket package shallow\\` command, which returns the shallow\n score for any number of packages. That will not reflect the dependency scores.\n\n Only a few ecosystems are supported like npm, pypi, nuget, gem, golang, and maven.\n\n A \"purl\" is a standard package name formatting: \\`pkg:eco/name@version\\`\n This command will automatically prepend \"pkg:\" when not present.\n\n The version is optional but when given should be a direct match. The \\`pkg:\\`\n prefix is optional.\n\n Note: if a package cannot be found it may be too old or perhaps was removed\n before we had the opportunity to process it.\n\n Examples\n $ ${command} npm babel-cli\n $ ${command} npm eslint@1.0.0 --json\n $ ${command} pkg:golang/github.com/steelpoor/tlsproxy@v0.0.0-20250304082521-29051ed19c60\n $ ${command} nuget/needpluscommonlibrary@1.0.0 --markdown\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const [ecosystem = '', purl] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n const { purls, valid } = parsePackageSpecifiers(ecosystem, purl ? [purl] : [])\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: valid,\n message: 'First parameter must be an ecosystem or the whole purl',\n fail: 'bad',\n },\n {\n test: purls.length === 1,\n message: 'Expecting at least one package',\n fail: purls.length === 0 ? 'missing' : 'too many',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handlePurlDeepScore(purls[0] || '', outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { 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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchPurlsShallowScoreOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchPurlsShallowScore(\n purls: string[],\n options?: FetchPurlsShallowScoreOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'batchPackageFetch'>>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchPurlsShallowScoreOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n logger.info(\n `Requesting shallow score data for ${purls.length} package urls (purl): ${purls.join(', ')}`,\n )\n\n const batchPackageCResult = await handleApiCall(\n sockSdk.batchPackageFetch(\n { components: purls.map(purl => ({ purl })) },\n {\n alerts: 'true',\n },\n ),\n { desc: 'looking up package' },\n )\n if (!batchPackageCResult.ok) {\n return batchPackageCResult\n }\n\n // TODO: Seems like there's a bug in the typing since we absolutely have to\n // return the .data here.\n return {\n ok: true,\n data: batchPackageCResult.data as SocketSdkSuccessResult<'batchPackageFetch'>,\n }\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketArtifact } from '../../utils/alert/artifact.mts'\n\n// This is a simplified view of an artifact. Potentially merged with other artifacts.\ninterface DedupedArtifact {\n ecosystem: string // artifact.type\n namespace: string\n name: string\n version: string\n score: {\n supplyChain: number\n maintenance: number\n quality: number\n vulnerability: number\n license: number\n }\n alerts: Map<\n string,\n {\n type: string\n severity: string\n }\n >\n}\n\nexport function outputPurlsShallowScore(\n purls: string[],\n result: CResult<SocketArtifact[]>,\n outputKind: OutputKind,\n): void {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const { missing, rows } = preProcess(result.data, purls)\n\n if (outputKind === 'markdown') {\n const md = generateMarkdownReport(rows, missing)\n logger.log(md)\n return\n }\n\n const txt = generateTextReport(rows, missing)\n logger.log(txt)\n}\n\nfunction formatReportCard(\n artifact: DedupedArtifact,\n colorize: boolean,\n): string {\n const scoreResult = {\n 'Supply Chain Risk': Math.floor((artifact.score?.supplyChain ?? 0) * 100),\n Maintenance: Math.floor((artifact.score?.maintenance ?? 0) * 100),\n Quality: Math.floor((artifact.score?.quality ?? 0) * 100),\n Vulnerabilities: Math.floor((artifact.score?.vulnerability ?? 0) * 100),\n License: Math.floor((artifact.score?.license ?? 0) * 100),\n }\n const alertString = getAlertString(artifact.alerts, { colorize })\n if (!artifact.ecosystem) {\n debugFn('notice', 'miss: artifact ecosystem', artifact)\n }\n const purl = `pkg:${artifact.ecosystem}/${artifact.name}${artifact.version ? '@' + artifact.version : ''}`\n\n // Calculate proper padding based on longest label.\n const maxLabelLength = Math.max(\n ...Object.keys(scoreResult).map(label => label.length),\n )\n const labelPadding = maxLabelLength + 2 // +2 for \": \"\n\n return [\n 'Package: ' + (colorize ? colors.bold(purl) : purl),\n '',\n ...Object.entries(scoreResult).map(\n score =>\n `- ${score[0]}:`.padEnd(labelPadding, ' ') +\n ` ${formatScore(score[1], { colorize })}`,\n ),\n alertString,\n ].join('\\n')\n}\n\ntype FormatScoreOptions = {\n colorize?: boolean | undefined\n padding?: number | undefined\n}\n\nfunction formatScore(\n score: number,\n options?: FormatScoreOptions | undefined,\n): string {\n const { colorize, padding = 3 } = {\n __proto__: null,\n ...options,\n } as FormatScoreOptions\n const padded = String(score).padStart(padding, ' ')\n if (!colorize) {\n return padded\n }\n if (score >= 80) {\n return colors.green(padded)\n }\n if (score >= 60) {\n return colors.yellow(padded)\n }\n return colors.red(padded)\n}\n\ntype AlertStringOptions = {\n colorize?: boolean | undefined\n}\n\nfunction getAlertString(\n alerts: DedupedArtifact['alerts'],\n options?: AlertStringOptions | undefined,\n): string {\n const { colorize } = { __proto__: null, ...options } as AlertStringOptions\n\n if (!alerts.size) {\n return `- Alerts: ${colorize ? colors.green('none') : 'none'}!`\n }\n\n const o = Array.from(alerts.values())\n\n const bad = o\n .filter(alert => alert.severity !== 'low' && alert.severity !== 'middle')\n .sort((a, b) => (a.type < b.type ? -1 : a.type > b.type ? 1 : 0))\n\n const mid = o\n .filter(alert => alert.severity === 'middle')\n .sort((a, b) => (a.type < b.type ? -1 : a.type > b.type ? 1 : 0))\n\n const low = o\n .filter(alert => alert.severity === 'low')\n .sort((a, b) => (a.type < b.type ? -1 : a.type > b.type ? 1 : 0))\n\n // We need to create the no-color string regardless because the actual string\n // contains a bunch of invisible ANSI chars which would screw up length checks.\n const colorless = `- Alerts (${bad.length}/${mid.length.toString()}/${low.length}):`\n const padding = ` ${' '.repeat(Math.max(0, 20 - colorless.length))}`\n\n if (colorize) {\n return (\n `- Alerts (${colors.red(bad.length.toString())}/${colors.yellow(mid.length.toString())}/${low.length}):` +\n padding +\n [\n bad\n .map(a => colors.red(`${colors.dim(`[${a.severity}] `)}${a.type}`))\n .join(', '),\n mid\n .map(a => colors.yellow(`${colors.dim(`[${a.severity}] `)}${a.type}`))\n .join(', '),\n low.map(a => `${colors.dim(`[${a.severity}] `)}${a.type}`).join(', '),\n ]\n .filter(Boolean)\n .join(', ')\n )\n }\n return (\n colorless +\n padding +\n [\n bad.map(a => `[${a.severity}] ${a.type}`).join(', '),\n mid.map(a => `[${a.severity}] ${a.type}`).join(', '),\n low.map(a => `[${a.severity}] ${a.type}`).join(', '),\n ]\n .filter(Boolean)\n .join(', ')\n )\n}\n\nexport function preProcess(\n artifacts: SocketArtifact[],\n requestedPurls: string[],\n): { rows: Map<string, DedupedArtifact>; missing: string[] } {\n // Dedupe results (for example, pypi will emit one package for each system release (win/mac/cpu) even if it's\n // the same package version with same results. The duplication is irrelevant and annoying to the user.\n\n // Make some effort to match the requested data with the response\n // Dedupe and merge results when only the .release value is different\n\n // API does not tell us which purls were not found.\n // Generate all purls to try so we can try to match search request.\n const purls: Set<string> = new Set()\n for (const data of artifacts) {\n purls.add(\n `pkg:${data.type}/${data.namespace ? `${data.namespace}/` : ''}${data.name}@${data.version}`,\n )\n purls.add(`pkg:${data.type}/${data.name}@${data.version}`)\n purls.add(`pkg:${data.type}/${data.name}`)\n purls.add(\n `pkg:${data.type}/${data.namespace ? `${data.namespace}/` : ''}${data.name}`,\n )\n }\n // Try to match the searched purls against this list\n const missing = requestedPurls.filter(purl => {\n if (purls.has(purl)) {\n return false\n }\n if (\n purl.endsWith('@latest') &&\n purls.has(purl.slice(0, -'@latest'.length))\n ) {\n return false\n }\n // Not found.\n return true\n })\n\n // Create a unique set of rows which represents each artifact that is returned\n // while deduping when the artifact (main) meta data only differs due to the\n // .release field (observed with python, at least).\n // Merge the alerts for duped packages. Use lowest score between all of them.\n const rows: Map<string, DedupedArtifact> = new Map()\n for (const artifact of artifacts) {\n const purl = `pkg:${artifact.type}/${artifact.namespace ? `${artifact.namespace}/` : ''}${artifact.name}${artifact.version ? `@${artifact.version}` : ''}`\n if (rows.has(purl)) {\n const row = rows.get(purl)\n if (!row) {\n // Unreachable; Satisfy TS.\n continue\n }\n if ((artifact.score?.supplyChain || 100) < row.score.supplyChain) {\n row.score.supplyChain = artifact.score?.supplyChain || 100\n }\n if ((artifact.score?.maintenance || 100) < row.score.maintenance) {\n row.score.maintenance = artifact.score?.maintenance || 100\n }\n if ((artifact.score?.quality || 100) < row.score.quality) {\n row.score.quality = artifact.score?.quality || 100\n }\n if ((artifact.score?.vulnerability || 100) < row.score.vulnerability) {\n row.score.vulnerability = artifact.score?.vulnerability || 100\n }\n if ((artifact.score?.license || 100) < row.score.license) {\n row.score.license = artifact.score?.license || 100\n }\n\n artifact.alerts?.forEach(({ severity, type }) => {\n row.alerts.set(`${type}:${severity}`, {\n type: (type as string) ?? 'unknown',\n severity: (severity as string) ?? 'none',\n })\n })\n } else {\n const alerts = new Map<string, { type: string; severity: string }>()\n artifact.alerts?.forEach(({ severity, type }) => {\n alerts.set(`${type}:${severity}`, {\n type: (type as string) ?? 'unknown',\n severity: (severity as string) ?? 'none',\n })\n })\n\n rows.set(purl, {\n ecosystem: artifact.type,\n namespace: artifact.namespace || '',\n name: artifact.name!,\n version: artifact.version || '',\n score: {\n supplyChain: artifact.score?.supplyChain || 100,\n maintenance: artifact.score?.maintenance || 100,\n quality: artifact.score?.quality || 100,\n vulnerability: artifact.score?.vulnerability || 100,\n license: artifact.score?.license || 100,\n },\n alerts,\n })\n }\n }\n\n return { rows, missing }\n}\n\nexport function generateMarkdownReport(\n artifacts: Map<string, DedupedArtifact>,\n missing: string[],\n): string {\n const blocks: string[] = []\n const dupes: Set<string> = new Set()\n for (const artifact of artifacts.values()) {\n const block = `## ${formatReportCard(artifact, false)}`\n if (dupes.has(block)) {\n // Omit duplicate blocks.\n continue\n }\n dupes.add(block)\n blocks.push(block)\n }\n return `\n# Shallow Package Report\n\nThis report contains the response for requesting data on some package url(s).\n\nPlease note: The listed scores are ONLY for the package itself. It does NOT\n reflect the scores of any dependencies, transitive or otherwise.\n\n${missing.length ? `\\n## Missing response\\n\\nAt least one package had no response or the purl was not canonical:\\n\\n${missing.map(purl => `- ${purl}\\n`).join('')}` : ''}\n\n${blocks.join('\\n\\n\\n')}\n `.trim()\n}\n\nexport function generateTextReport(\n artifacts: Map<string, DedupedArtifact>,\n missing: string[],\n): string {\n const o: string[] = []\n o.push(`\\n${colors.bold('Shallow Package Score')}\\n`)\n o.push(\n 'Please note: The listed scores are ONLY for the package itself. It does NOT\\n' +\n ' reflect the scores of any dependencies, transitive or otherwise.',\n )\n if (missing.length) {\n o.push(\n `\\nAt least one package had no response or the purl was not canonical:\\n${missing.map(purl => `\\n- ${colors.bold(purl)}`).join('')}`,\n )\n }\n const dupes: Set<string> = new Set()\n for (const artifact of artifacts.values()) {\n const block = formatReportCard(artifact, true)\n if (dupes.has(block)) {\n // Omit duplicate blocks.\n continue\n }\n dupes.add(block)\n o.push('\\n')\n o.push(block)\n }\n o.push('')\n\n return o.join('\\n')\n}\n","import { fetchPurlsShallowScore } from './fetch-purls-shallow-score.mts'\nimport { outputPurlsShallowScore } from './output-purls-shallow-score.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketArtifact } from '../../utils/alert/artifact.mts'\n\nexport async function handlePurlsShallowScore({\n outputKind,\n purls,\n}: {\n outputKind: OutputKind\n purls: string[]\n}) {\n const packageData = await fetchPurlsShallowScore(purls)\n\n outputPurlsShallowScore(\n purls,\n packageData as CResult<SocketArtifact[]>,\n outputKind,\n )\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handlePurlsShallowScore } from './handle-purls-shallow-score.mts'\nimport { parsePackageSpecifiers } from './parse-package-specifiers.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'shallow'\n\nconst description =\n 'Look up info regarding one or more packages but not their transitives'\n\nconst hidden = false\n\nexport const cmdPackageShallow = {\n description,\n hidden,\n alias: {\n shallowScore: {\n description,\n hidden: true,\n argv: [],\n },\n },\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <<ECOSYSTEM> <PKGNAME> [<PKGNAME> ...] | <PURL> [<PURL> ...]>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Show scoring details for one or more packages purely based on their own package.\n This means that any dependency scores are not reflected by the score. You can\n use the \\`socket package score <pkg>\\` command to get its full transitive score.\n\n Only a few ecosystems are supported like npm, pypi, nuget, gem, golang, and maven.\n\n A \"purl\" is a standard package name formatting: \\`pkg:eco/name@version\\`\n This command will automatically prepend \"pkg:\" when not present.\n\n If the first arg is an ecosystem, remaining args that are not a purl are\n assumed to be scoped to that ecosystem. The \\`pkg:\\` prefix is optional.\n\n Note: if a package cannot be found, it may be too old or perhaps was removed\n before we had the opportunity to process it.\n\n Examples\n $ ${command} npm webtorrent\n $ ${command} npm webtorrent@1.9.1\n $ ${command} npm/webtorrent@1.9.1\n $ ${command} pkg:npm/webtorrent@1.9.1\n $ ${command} maven webtorrent babel\n $ ${command} npm/webtorrent golang/babel\n $ ${command} npm npm/webtorrent@1.0.1 babel\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const [ecosystem = '', ...pkgs] = cli.input\n\n const outputKind = getOutputKind(json, markdown)\n\n const { purls, valid } = parsePackageSpecifiers(ecosystem, pkgs)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: valid,\n message:\n 'First parameter should be an ecosystem or all args must be purls',\n fail: 'bad',\n },\n {\n test: purls.length > 0,\n message: 'Expecting at least one package',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handlePurlsShallowScore({\n outputKind,\n purls,\n })\n}\n","import { cmdPackageScore } from './cmd-package-score.mts'\nimport { cmdPackageShallow } from './cmd-package-shallow.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Look up published package details'\n\nexport const cmdPackage: CliSubcommand = {\n description,\n hidden: false,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n score: cmdPackageScore,\n shallow: cmdPackageShallow,\n },\n {\n aliases: {\n deep: {\n description,\n hidden: true,\n argv: ['score'],\n },\n },\n argv,\n description,\n importMeta,\n name: `${parentName} package`,\n },\n )\n },\n}\n","import { z } from 'zod'\n\nexport type PatchManifest = z.infer<typeof PatchManifestSchema>\n\nexport type PatchRecord = z.infer<typeof PatchRecordSchema>\n\nexport const PatchRecordSchema = z.object({\n exportedAt: z.string(),\n files: z.record(\n z.string(), // File path\n z.object({\n beforeHash: z.string(),\n afterHash: z.string(),\n }),\n ),\n vulnerabilities: z.record(\n z.string(), // Vulnerability ID like \"GHSA-jrhj-2j3q-xf3v\"\n z.object({\n cves: z.array(z.string()),\n summary: z.string(),\n severity: z.string(),\n description: z.string(),\n patchExplanation: z.string(),\n }),\n ),\n})\n\nexport const PatchManifestSchema = z.object({\n patches: z.record(\n // Package identifier like \"npm:simplehttpserver@0.0.6\".\n z.string(),\n PatchRecordSchema,\n ),\n})\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputPatchResult(\n result: CResult<{ patched: string[] }>,\n outputKind: OutputKind,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const { patched } = result.data\n\n if (patched.length) {\n logger.group(\n `Successfully processed patches for ${patched.length} package(s):`,\n )\n for (const pkg of patched) {\n logger.success(pkg)\n }\n logger.groupEnd()\n } else {\n logger.info('No packages found requiring patches')\n }\n\n logger.log('')\n logger.success('Patch command completed!')\n}\n","import crypto from 'node:crypto'\nimport { existsSync, promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nimport { glob } from 'fast-glob'\n\nimport { PackageURL } from '@socketregistry/packageurl-js'\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { readDirNames } from '@socketsecurity/registry/lib/fs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { readPackageJson } from '@socketsecurity/registry/lib/packages'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { PatchManifestSchema } from './manifest-schema.mts'\nimport { outputPatchResult } from './output-patch-result.mts'\nimport constants, { NODE_MODULES, NPM } from '../../constants.mts'\nimport { findUp } from '../../utils/fs.mts'\nimport { getPurlObject } from '../../utils/purl.mts'\n\nimport type { PatchRecord } from './manifest-schema.mts'\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport type PatchEntry = {\n key: string\n patch: PatchRecord\n purlObj: PackageURL\n}\n\nasync function applyNPMPatches(\n patches: PatchEntry[],\n purlObjs: PackageURL[],\n socketDir: string,\n dryRun: boolean,\n) {\n const patchLookup = new Map<string, PatchEntry>()\n for (const patchInfo of patches) {\n const key = getLookupKey(patchInfo.purlObj)\n patchLookup.set(key, patchInfo)\n }\n\n const nmPaths = await findNodeModulesPaths(process.cwd())\n logger.log(\n `Found ${nmPaths.length} node_modules ${pluralize('folder', nmPaths.length)}`,\n )\n\n for (const nmPath of nmPaths) {\n // eslint-disable-next-line no-await-in-loop\n const dirNames = await readDirNames(nmPath)\n for (const dirName of dirNames) {\n const isScoped = dirName.startsWith('@')\n const pkgPath = path.join(nmPath, dirName)\n const pkgSubNames = isScoped\n ? // eslint-disable-next-line no-await-in-loop\n await readDirNames(pkgPath)\n : [dirName]\n\n try {\n for (const pkgSubName of pkgSubNames) {\n const dirFullName = isScoped ? `${dirName}/${pkgSubName}` : pkgSubName\n const pkgPath = path.join(nmPath, dirFullName)\n // eslint-disable-next-line no-await-in-loop\n const pkgJson = await readPackageJson(pkgPath, { throws: false })\n if (\n !isNonEmptyString(pkgJson?.name) ||\n !isNonEmptyString(pkgJson?.version)\n ) {\n continue\n }\n const pkgFullName = pkgJson.name\n const purlObj = getPurlObject(`pkg:npm/${pkgFullName}`)\n // Skip if specific packages requested and this isn't one of them\n if (\n purlObjs.findIndex(\n p =>\n p.type === 'npm' &&\n p.namespace === purlObj.namespace &&\n p.name === purlObj.name,\n ) === -1\n ) {\n continue\n }\n\n const patchInfo = patchLookup.get(getLookupKey(purlObj))\n if (!patchInfo) {\n continue\n }\n\n logger.log(\n `Found match: ${pkgFullName}@${pkgJson.version} at ${pkgPath}`,\n )\n logger.log(`Patch key: ${patchInfo.key}`)\n logger.group(`Processing files:`)\n\n for (const { 0: fileName, 1: fileInfo } of Object.entries(\n patchInfo.patch.files,\n )) {\n // eslint-disable-next-line no-await-in-loop\n await processFilePatch(\n pkgPath,\n fileName,\n fileInfo,\n dryRun,\n socketDir,\n )\n }\n logger.groupEnd()\n }\n } catch (error) {\n logger.error(`Error processing ${nmPath}:`, error)\n }\n }\n }\n}\n\nasync function computeSHA256(filepath: string): Promise<string | null> {\n try {\n const content = await fs.readFile(filepath)\n const hash = crypto.createHash('sha256')\n hash.update(content)\n return hash.digest('hex')\n } catch {}\n return null\n}\n\nasync function findNodeModulesPaths(cwd: string): Promise<string[]> {\n const rootNmPath = await findUp(NODE_MODULES, { cwd, onlyDirectories: true })\n if (!rootNmPath) {\n return []\n }\n return await glob([`**/${NODE_MODULES}`], {\n absolute: true,\n cwd: path.dirname(rootNmPath),\n onlyDirectories: true,\n })\n}\n\nfunction getLookupKey(purlObj: PackageURL): string {\n const fullName = purlObj.namespace\n ? `${purlObj.namespace}/${purlObj.name}`\n : purlObj.name\n return `${fullName}@${purlObj.version}`\n}\n\nasync function processFilePatch(\n pkgPath: string,\n fileName: string,\n fileInfo: { beforeHash: string; afterHash: string },\n dryRun: boolean,\n socketDir: string,\n): Promise<void> {\n const filepath = path.join(pkgPath, fileName)\n if (!existsSync(filepath)) {\n logger.log(`File not found: ${fileName}`)\n return\n }\n\n const currentHash = await computeSHA256(filepath)\n if (!currentHash) {\n logger.log(`Failed to compute hash for: ${fileName}`)\n return\n }\n\n if (currentHash === fileInfo.beforeHash) {\n logger.success(`File matches expected hash: ${fileName}`)\n logger.log(`Current hash: ${currentHash}`)\n logger.log(`Ready to patch to: ${fileInfo.afterHash}`)\n\n if (dryRun) {\n logger.log(`(dry run - no changes made)`)\n } else {\n const blobPath = path.join(socketDir, 'blobs', fileInfo.afterHash)\n if (!existsSync(blobPath)) {\n logger.fail(`Error: Patch file not found at ${blobPath}`)\n return\n }\n try {\n await fs.copyFile(blobPath, filepath)\n logger.success(`Patch applied successfully`)\n } catch (error) {\n logger.error('Error applying patch:', error)\n }\n }\n } else if (currentHash === fileInfo.afterHash) {\n logger.success(`File already patched: ${fileName}`)\n logger.log(`Current hash: ${currentHash}`)\n } else {\n logger.fail(`File hash mismatch: ${fileName}`)\n logger.log(`Expected: ${fileInfo.beforeHash}`)\n logger.log(`Current: ${currentHash}`)\n logger.log(`Target: ${fileInfo.afterHash}`)\n }\n}\n\nexport interface HandlePatchConfig {\n cwd: string\n dryRun: boolean\n outputKind: OutputKind\n purlObjs: PackageURL[]\n spinner: typeof constants.spinner\n}\n\nexport async function handlePatch({\n cwd,\n dryRun,\n outputKind,\n purlObjs,\n spinner,\n}: HandlePatchConfig): Promise<void> {\n try {\n const dotSocketDirPath = path.join(cwd, '.socket')\n const manifestPath = path.join(dotSocketDirPath, 'manifest.json')\n const manifestContent = await fs.readFile(manifestPath, 'utf-8')\n const manifestData = JSON.parse(manifestContent)\n const purls = purlObjs.map(String)\n const validated = PatchManifestSchema.parse(manifestData)\n\n // Parse PURLs and group by ecosystem.\n const patchesByEcosystem = new Map<string, PatchEntry[]>()\n for (const { 0: key, 1: patch } of Object.entries(validated.patches)) {\n const purlObj = getPurlObject(key, { throws: false })\n if (!purlObj) {\n continue\n }\n let patches = patchesByEcosystem.get(purlObj.type)\n if (!Array.isArray(patches)) {\n patches = []\n patchesByEcosystem.set(purlObj.type, patches)\n }\n patches.push({\n key,\n patch,\n purlObj,\n })\n }\n\n spinner.stop()\n\n logger.log('')\n if (purlObjs.length) {\n logger.info(`Checking patches for: ${joinAnd(purls)}`)\n } else {\n logger.info('Scanning all dependencies for available patches')\n }\n logger.log('')\n\n const npmPatches = patchesByEcosystem.get(NPM)\n if (npmPatches) {\n await applyNPMPatches(npmPatches, purlObjs, dotSocketDirPath, dryRun)\n }\n const result: CResult<{ patched: string[] }> = {\n ok: true,\n data: {\n patched: purls.length ? purls : ['patched successfully'],\n },\n }\n\n await outputPatchResult(result, outputKind)\n } catch (e) {\n spinner.stop()\n\n let message = 'Failed to apply patches'\n let cause = (e as Error)?.message || 'Unknown error'\n\n if (e instanceof SyntaxError) {\n message = 'Invalid JSON in manifest.json'\n cause = e.message\n } else if (e instanceof Error && 'issues' in e) {\n message = 'Schema validation failed'\n cause = String(e)\n }\n\n const result: CResult<never> = {\n ok: false,\n code: 1,\n message,\n cause,\n }\n\n await outputPatchResult(result, outputKind)\n }\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport { arrayUnique } from '@socketsecurity/registry/lib/arrays'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handlePatch } from './handle-patch.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { cmdFlagValueToArray } from '../../utils/cmd.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { getPurlObject } from '../../utils/purl.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\nimport type { PurlObject } from '../../utils/purl.mts'\nimport type { PackageURL } from '@socketregistry/packageurl-js'\n\nexport const CMD_NAME = 'patch'\n\nconst description = 'Apply CVE patches to dependencies'\n\nconst hidden = true\n\nexport const cmdPatch = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n purl: {\n type: 'string',\n default: [],\n description:\n 'Specify purls to patch, as either a comma separated value or as multiple flags',\n isMultiple: true,\n shortFlag: 'p',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} --package lodash\n $ ${command} ./proj/tree --package lodash,react\n `,\n }\n\n const cli = meowOrExit({\n allowUnknownFlags: false,\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n const outputKind = getOutputKind(cli.flags['json'], cli.flags['markdown'])\n\n const wasValidInput = checkCommandInput(outputKind, {\n nook: true,\n test: !cli.flags['json'] || !cli.flags['markdown'],\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n })\n if (!wasValidInput) {\n return\n }\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n const dotSocketDirPath = path.join(cwd, '.socket')\n if (!existsSync(dotSocketDirPath)) {\n logger.error('Error: No .socket directory found in current directory')\n return\n }\n\n const manifestPath = path.join(dotSocketDirPath, 'manifest.json')\n if (!existsSync(manifestPath)) {\n logger.error('Error: No manifest.json found in .socket directory')\n }\n\n const { spinner } = constants\n\n const purlObjs = arrayUnique(cmdFlagValueToArray(cli.flags['purl']))\n .map(p => getPurlObject(p, { throws: false }))\n .filter(Boolean) as Array<PurlObject<PackageURL>>\n\n await handlePatch({\n cwd,\n dryRun,\n outputKind,\n purlObjs,\n spinner,\n })\n}\n","import { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants.mts'\nimport { getNpmBinPath } from '../../utils/npm-paths.mts'\n\nexport async function runRawNpm(\n argv: string[] | readonly string[],\n): Promise<void> {\n process.exitCode = 1\n\n const spawnPromise = spawn(getNpmBinPath(), argv as string[], {\n shell: constants.WIN32,\n stdio: 'inherit',\n })\n\n // See https://nodejs.org/api/child_process.html#event-exit.\n spawnPromise.process.on('exit', (code, signalName) => {\n if (signalName) {\n process.kill(process.pid, signalName)\n } else if (typeof code === 'number') {\n // eslint-disable-next-line n/no-process-exit\n process.exit(code)\n }\n })\n\n await spawnPromise\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { runRawNpm } from './run-raw-npm.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'raw-npm',\n description: 'Run npm without the Socket wrapper',\n hidden: false,\n flags: {\n ...commonFlags,\n },\n help: command => `\n Usage\n $ ${command} ...\n\n Execute \\`npm\\` without gating installs through the Socket API.\n Useful when \\`socket wrapper on\\` is enabled and you want to bypass\n the Socket wrapper. Use at your own risk.\n\n Note: Everything after \"raw-npm\" is passed to the npm command.\n Only the \\`--dry-run\\` and \\`--help\\` flags are caught here.\n\n Examples\n $ ${command} install -g cowsay\n `,\n}\n\nexport const cmdRawNpm = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await runRawNpm(argv)\n}\n","import { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants.mts'\nimport { getNpxBinPath } from '../../utils/npm-paths.mts'\n\nexport async function runRawNpx(\n argv: string[] | readonly string[],\n): Promise<void> {\n process.exitCode = 1\n\n const spawnPromise = spawn(getNpxBinPath(), argv as string[], {\n shell: constants.WIN32,\n stdio: 'inherit',\n })\n\n // See https://nodejs.org/api/child_process.html#event-exit.\n spawnPromise.process.on('exit', (code, signalName) => {\n if (signalName) {\n process.kill(process.pid, signalName)\n } else if (typeof code === 'number') {\n // eslint-disable-next-line n/no-process-exit\n process.exit(code)\n }\n })\n\n await spawnPromise\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { runRawNpx } from './run-raw-npx.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'raw-npx',\n description: 'Run npx without the Socket wrapper',\n hidden: false,\n flags: {\n ...commonFlags,\n },\n help: command => `\n Usage\n $ ${command} ...\n\n Execute \\`npx\\` without gating installs through the Socket API.\n Useful when \\`socket wrapper on\\` is enabled and you want to bypass\n the Socket wrapper. Use at your own risk.\n\n Note: Everything after \"raw-npx\" is passed to the npx command.\n Only the \\`--dry-run\\` and \\`--help\\` flags are caught here.\n\n Examples\n $ ${command} cowsay\n `,\n}\n\nexport const cmdRawNpx = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await runRawNpx(argv)\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchCreateRepoConfig = {\n defaultBranch: string\n description: string\n homepage: string\n orgSlug: string\n repoName: string\n visibility: string\n}\n\nexport type FetchCreateRepoOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchCreateRepo(\n config: FetchCreateRepoConfig,\n options?: FetchCreateRepoOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'createOrgRepo'>['data']>> {\n const {\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n } = config\n\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchCreateRepoOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(\n sockSdk.createOrgRepo(orgSlug, {\n default_branch: defaultBranch,\n description,\n homepage,\n name: repoName,\n visibility,\n }),\n { desc: 'to create a repository' },\n )\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport function outputCreateRepo(\n result: CResult<SocketSdkSuccessResult<'createOrgRepo'>['data']>,\n requestedName: string,\n outputKind: OutputKind,\n): void {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n const { slug } = result.data\n logger.success(\n `OK. Repository created successfully, slug: \\`${slug}\\`${slug !== requestedName ? ' (Warning: slug is not the same as name that was requested!)' : ''}`,\n )\n}\n","import { fetchCreateRepo } from './fetch-create-repo.mts'\nimport { outputCreateRepo } from './output-create-repo.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleCreateRepo(\n {\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n }: {\n orgSlug: string\n repoName: string\n description: string\n homepage: string\n defaultBranch: string\n visibility: string\n },\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchCreateRepo({\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n })\n outputCreateRepo(data, repoName, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleCreateRepo } from './handle-create-repo.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'create'\n\nconst description = 'Create a repository in an organization'\n\nconst hidden = false\n\nexport const cmdRepositoryCreate = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n defaultBranch: {\n type: 'string',\n default: 'main',\n description: 'Repository default branch. Defaults to \"main\"',\n },\n homepage: {\n type: 'string',\n default: '',\n description: 'Repository url',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n repoDescription: {\n type: 'string',\n default: '',\n description: 'Repository description',\n },\n visibility: {\n type: 'string',\n default: 'private',\n description: 'Repository visibility (Default Private)',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <REPO>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n The REPO name should be a \"slug\". Follows the same naming convention as GitHub.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} test-repo\n $ ${command} our-repo --homepage=socket.dev --default-branch=trunk\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const noLegacy = !cli.flags['repoName']\n\n const [repoName = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n nook: true,\n test: noLegacy,\n message: 'Legacy flags are no longer supported. See v1 migration guide.',\n fail: `received legacy flags`,\n },\n {\n test: !!repoName,\n message: 'Repository name as first argument',\n fail: 'missing',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleCreateRepo(\n {\n orgSlug,\n repoName: String(repoName),\n description: String(cli.flags['repoDescription'] || ''),\n homepage: String(cli.flags['homepage'] || ''),\n defaultBranch: String(cli.flags['defaultBranch'] || ''),\n visibility: String(cli.flags['visibility'] || 'private'),\n },\n outputKind,\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchDeleteRepoOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchDeleteRepo(\n orgSlug: string,\n repoName: string,\n options?: FetchDeleteRepoOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'deleteOrgRepo'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchDeleteRepoOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.deleteOrgRepo(orgSlug, repoName), {\n desc: 'to delete a repository',\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputDeleteRepo(\n result: CResult<SocketSdkSuccessResult<'deleteOrgRepo'>['data']>,\n repoName: string,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.success(`OK. Repository \\`${repoName}\\` deleted successfully`)\n}\n","import { fetchDeleteRepo } from './fetch-delete-repo.mts'\nimport { outputDeleteRepo } from './output-delete-repo.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleDeleteRepo(\n orgSlug: string,\n repoName: string,\n outputKind: OutputKind,\n) {\n const data = await fetchDeleteRepo(orgSlug, repoName)\n\n await outputDeleteRepo(data, repoName, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleDeleteRepo } from './handle-delete-repo.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'del'\n\nconst description = 'Delete a repository in an organization'\n\nconst hidden = false\n\nexport const cmdRepositoryDel = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <REPO>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} test-repo\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const noLegacy = !cli.flags['repoName']\n\n const [repoName = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: noLegacy,\n message: 'Legacy flags are no longer supported. See v1 migration guide.',\n fail: `received legacy flags`,\n },\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n test: !!repoName,\n message: 'Repository name as first argument',\n fail: 'missing',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleDeleteRepo(orgSlug, repoName, outputKind)\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchListAllReposOptions = {\n direction?: string | undefined\n sdkOpts?: SetupSdkOptions | undefined\n sort?: string | undefined\n}\n\nexport async function fetchListAllRepos(\n orgSlug: string,\n options?: FetchListAllReposOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgRepoList'>['data']>> {\n const { direction, sdkOpts, sort } = {\n __proto__: null,\n ...options,\n } as FetchListAllReposOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n const rows: SocketSdkSuccessResult<'getOrgRepoList'>['data']['results'] = []\n let protection = 0\n let nextPage = 0\n while (nextPage >= 0) {\n if (++protection > 100) {\n return {\n ok: false,\n message: 'Infinite loop detected',\n cause: `Either there are over 100 pages of results or the fetch has run into an infinite loop. Breaking it off now. nextPage=${nextPage}`,\n }\n }\n // eslint-disable-next-line no-await-in-loop\n const orgRepoListCResult = await handleApiCall(\n sockSdk.getOrgRepoList(orgSlug, {\n sort,\n direction,\n per_page: String(100), // max\n page: String(nextPage),\n }),\n { desc: 'list of repositories' },\n )\n if (!orgRepoListCResult.ok) {\n return orgRepoListCResult\n }\n\n rows.push(...orgRepoListCResult.data.results)\n nextPage = orgRepoListCResult.data.nextPage ?? -1\n }\n\n return {\n ok: true,\n data: {\n results: rows,\n nextPage: null,\n },\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchListReposConfig = {\n direction: string\n orgSlug: string\n page: number\n perPage: number\n sort: string\n}\n\nexport type FetchListReposOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchListRepos(\n config: FetchListReposConfig,\n options?: FetchListReposOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgRepoList'>['data']>> {\n const { direction, orgSlug, page, perPage, sort } = {\n __proto__: null,\n ...config,\n } as FetchListReposConfig\n\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchListReposOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(\n sockSdk.getOrgRepoList(orgSlug, {\n sort,\n direction,\n per_page: String(perPage),\n page: String(page),\n }),\n { desc: 'list of repositories' },\n )\n}\n","// @ts-ignore\nimport chalkTable from 'chalk-table'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputListRepos(\n result: CResult<SocketSdkSuccessResult<'getOrgRepoList'>['data']>,\n outputKind: OutputKind,\n page: number,\n nextPage: number | null,\n sort: string,\n perPage: number,\n direction: 'asc' | 'desc',\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n if (result.ok) {\n logger.log(\n serializeResultJson({\n ok: true,\n data: {\n data: result.data,\n direction,\n nextPage: nextPage ?? 0,\n page,\n perPage,\n sort,\n },\n }),\n )\n } else {\n logger.log(serializeResultJson(result))\n }\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log(\n `Result page: ${page}, results per page: ${perPage === Infinity ? 'all' : perPage}, sorted by: ${sort}, direction: ${direction}`,\n )\n\n const options = {\n columns: [\n { field: 'id', name: colors.magenta('ID') },\n { field: 'name', name: colors.magenta('Name') },\n { field: 'visibility', name: colors.magenta('Visibility') },\n { field: 'default_branch', name: colors.magenta('Default branch') },\n { field: 'archived', name: colors.magenta('Archived') },\n ],\n }\n\n logger.log(chalkTable(options, result.data.results))\n if (nextPage) {\n logger.info(\n `This is page ${page}. Server indicated there are more results available on page ${nextPage}...`,\n )\n logger.info(\n `(Hint: you can use \\`socket repository list --page ${nextPage}\\`)`,\n )\n } else if (perPage === Infinity) {\n logger.info(`This should be the entire list available on the server.`)\n } else {\n logger.info(\n `This is page ${page}. Server indicated this is the last page with results.`,\n )\n }\n}\n","import { fetchListAllRepos } from './fetch-list-all-repos.mts'\nimport { fetchListRepos } from './fetch-list-repos.mts'\nimport { outputListRepos } from './output-list-repos.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleListRepos({\n all,\n direction,\n orgSlug,\n outputKind,\n page,\n perPage,\n sort,\n}: {\n all: boolean\n direction: 'asc' | 'desc'\n orgSlug: string\n outputKind: OutputKind\n page: number\n perPage: number\n sort: string\n}): Promise<void> {\n if (all) {\n const data = await fetchListAllRepos(orgSlug, { direction, sort })\n\n await outputListRepos(data, outputKind, 0, 0, sort, Infinity, direction)\n } else {\n const data = await fetchListRepos({\n direction,\n orgSlug,\n page,\n perPage,\n sort,\n })\n\n if (!data.ok) {\n await outputListRepos(data, outputKind, 0, 0, '', 0, direction)\n } else {\n // Note: nextPage defaults to 0, is null when there's no next page\n await outputListRepos(\n data,\n outputKind,\n page,\n data.data.nextPage,\n sort,\n perPage,\n direction,\n )\n }\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleListRepos } from './handle-list-repos.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'list'\n\nconst description = 'List repositories in an organization'\n\nconst hidden = false\n\nexport const cmdRepositoryList = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n all: {\n type: 'boolean',\n default: false,\n description:\n 'By default view shows the last n repos. This flag allows you to fetch the entire list. Will ignore --page and --per-page.',\n },\n direction: {\n type: 'string',\n default: 'desc',\n description: 'Direction option',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n perPage: {\n type: 'number',\n shortFlag: 'pp',\n default: 30,\n description: 'Number of results per page',\n },\n page: {\n type: 'number',\n shortFlag: 'p',\n default: 1,\n description: 'Page number',\n },\n sort: {\n type: 'string',\n shortFlag: 's',\n default: 'created_at',\n description: 'Sorting option',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { all, direction = 'desc', json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n {\n nook: true,\n test: direction === 'asc' || direction === 'desc',\n message: 'The --direction value must be \"asc\" or \"desc\"',\n fail: 'unexpected value',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleListRepos({\n all: Boolean(all),\n direction: direction === 'asc' ? 'asc' : 'desc',\n orgSlug,\n outputKind,\n page: Number(cli.flags['page']) || 1,\n perPage: Number(cli.flags['perPage']) || 30,\n sort: String(cli.flags['sort'] || 'created_at'),\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchUpdateRepoConfig = {\n defaultBranch: string\n description: string\n homepage: string\n orgSlug: string\n repoName: string\n visibility: string\n}\n\nexport type FetchUpdateRepoOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchUpdateRepo(\n config: FetchUpdateRepoConfig,\n options?: FetchUpdateRepoOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'updateOrgRepo'>['data']>> {\n const {\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n } = { __proto__: null, ...config } as FetchUpdateRepoConfig\n\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchUpdateRepoOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(\n sockSdk.updateOrgRepo(orgSlug, repoName, {\n default_branch: defaultBranch,\n description,\n homepage,\n name: repoName,\n orgSlug,\n visibility,\n }),\n { desc: 'to update a repository' },\n )\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputUpdateRepo(\n result: CResult<SocketSdkSuccessResult<'updateOrgRepo'>['data']>,\n repoName: string,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.success(`Repository \\`${repoName}\\` updated successfully`)\n}\n","import { fetchUpdateRepo } from './fetch-update-repo.mts'\nimport { outputUpdateRepo } from './output-update-repo.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleUpdateRepo(\n {\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n }: {\n orgSlug: string\n repoName: string\n description: string\n homepage: string\n defaultBranch: string\n visibility: string\n },\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchUpdateRepo({\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n })\n\n await outputUpdateRepo(data, repoName, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleUpdateRepo } from './handle-update-repo.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'update'\n\nconst description = 'Update a repository in an organization'\n\nconst hidden = false\n\nexport const cmdRepositoryUpdate = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n defaultBranch: {\n type: 'string',\n shortFlag: 'b',\n default: 'main',\n description: 'Repository default branch',\n },\n homepage: {\n type: 'string',\n shortFlag: 'h',\n default: '',\n description: 'Repository url',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n repoDescription: {\n type: 'string',\n shortFlag: 'd',\n default: '',\n description: 'Repository description',\n },\n visibility: {\n type: 'string',\n shortFlag: 'v',\n default: 'private',\n description: 'Repository visibility (Default Private)',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <REPO>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} test-repo\n $ ${command} test-repo --homepage https://example.com\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const noLegacy = !cli.flags['repoName']\n\n const [repoName = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: noLegacy,\n message: 'Legacy flags are no longer supported. See v1 migration guide.',\n fail: `received legacy flags`,\n },\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n test: !!repoName,\n message: 'Repository name as first argument',\n fail: 'missing',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleUpdateRepo(\n {\n orgSlug,\n repoName: String(repoName),\n description: String(cli.flags['repoDescription'] || ''),\n homepage: String(cli.flags['homepage'] || ''),\n defaultBranch: String(cli.flags['defaultBranch'] || ''),\n visibility: String(cli.flags['visibility'] || 'private'),\n },\n outputKind,\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchViewRepoOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchViewRepo(\n orgSlug: string,\n repoName: string,\n options?: FetchViewRepoOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgRepo'>['data']>> {\n const { sdkOpts } = { __proto__: null, ...options } as FetchViewRepoOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getOrgRepo(orgSlug, repoName), {\n desc: 'repository data',\n })\n}\n","// @ts-ignore\nimport chalkTable from 'chalk-table'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputViewRepo(\n result: CResult<SocketSdkSuccessResult<'createOrgRepo'>['data']>,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const options = {\n columns: [\n { field: 'id', name: colors.magenta('ID') },\n { field: 'name', name: colors.magenta('Name') },\n { field: 'visibility', name: colors.magenta('Visibility') },\n { field: 'default_branch', name: colors.magenta('Default branch') },\n { field: 'homepage', name: colors.magenta('Homepage') },\n { field: 'archived', name: colors.magenta('Archived') },\n { field: 'created_at', name: colors.magenta('Created at') },\n ],\n }\n\n logger.log(chalkTable(options, [result.data]))\n}\n","import { fetchViewRepo } from './fetch-view-repo.mts'\nimport { outputViewRepo } from './output-view-repo.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleViewRepo(\n orgSlug: string,\n repoName: string,\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchViewRepo(orgSlug, repoName)\n\n await outputViewRepo(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleViewRepo } from './handle-view-repo.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'view'\n\nconst description = 'View repositories in an organization'\n\nconst hidden = false\n\nexport const cmdRepositoryView = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <REPO>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} test-repo\n $ ${command} test-repo --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const noLegacy = !cli.flags['repoName']\n\n const [repoName = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: noLegacy,\n message: 'Legacy flags are no longer supported. See v1 migration guide.',\n fail: `received legacy flags`,\n },\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n test: !!repoName,\n message: 'Repository name as first argument',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleViewRepo(orgSlug, String(repoName), outputKind)\n}\n","import { cmdRepositoryCreate } from './cmd-repository-create.mts'\nimport { cmdRepositoryDel } from './cmd-repository-del.mts'\nimport { cmdRepositoryList } from './cmd-repository-list.mts'\nimport { cmdRepositoryUpdate } from './cmd-repository-update.mts'\nimport { cmdRepositoryView } from './cmd-repository-view.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Manage registered repositories'\n\nexport const cmdRepository: CliSubcommand = {\n description,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n create: cmdRepositoryCreate,\n view: cmdRepositoryView,\n list: cmdRepositoryList,\n del: cmdRepositoryDel,\n update: cmdRepositoryUpdate,\n },\n {\n argv,\n description,\n importMeta,\n name: `${parentName} repository`,\n },\n )\n },\n}\n","import type { MeowFlags } from '../../flags.mts'\n\nexport const reachabilityFlags: MeowFlags = {\n reachAnalysisMemoryLimit: {\n type: 'number',\n default: 8192,\n description:\n 'The maximum memory in MB to use for the reachability analysis. The default is 8192MB.',\n },\n reachAnalysisTimeout: {\n type: 'number',\n default: 0,\n description:\n 'Set timeout for the reachability analysis. Split analysis runs may cause the total scan time to exceed this timeout significantly.',\n },\n reachDisableAnalytics: {\n type: 'boolean',\n default: false,\n description:\n 'Disable reachability analytics sharing with Socket. Also disables caching-based optimizations.',\n },\n reachEcosystems: {\n type: 'string',\n isMultiple: true,\n description:\n 'List of ecosystems to conduct reachability analysis on, as either a comma separated value or as multiple flags. Defaults to all ecosystems.',\n },\n reachExcludePaths: {\n type: 'string',\n isMultiple: true,\n description:\n 'List of paths to exclude from reachability analysis, as either a comma separated value or as multiple flags.',\n },\n reachSkipCache: {\n type: 'boolean',\n default: false,\n description:\n 'Skip caching-based optimizations. By default, the reachability analysis will use cached configurations from previous runs to speed up the analysis.',\n },\n}\n","import { select } from '@socketsecurity/registry/lib/prompts'\n\nexport async function suggestTarget(): Promise<string[]> {\n // We could prefill this with sub-dirs of the current\n // dir ... but is that going to be useful?\n const proceed = await select<boolean>({\n message: 'No TARGET given. Do you want to use the current directory?',\n choices: [\n {\n name: 'Yes',\n value: true,\n description: 'Target the current directory',\n },\n {\n name: 'No',\n value: false,\n description:\n 'Do not use the current directory (this will end in a no-op)',\n },\n ],\n })\n return proceed ? ['.'] : []\n}\n","import path from 'node:path'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleCreateNewScan } from './handle-create-new-scan.mts'\nimport { outputCreateNewScan } from './output-create-new-scan.mts'\nimport { reachabilityFlags } from './reachability-flags.mts'\nimport { suggestOrgSlug } from './suggest-org-slug.mts'\nimport { suggestTarget } from './suggest_target.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { cmdFlagValueToArray } from '../../utils/cmd.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getEcosystemChoicesForMeow } from '../../utils/ecosystem.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport {\n detectDefaultBranch,\n getRepoName,\n gitBranch,\n} from '../../utils/git.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\nimport { detectManifestActions } from '../manifest/detect-manifest-actions.mts'\n\nimport type { REPORT_LEVEL } from './types.mts'\nimport type { MeowFlags } from '../../flags.mts'\nimport type { PURL_Type } from '../../utils/ecosystem.mts'\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'create'\n\nconst description = 'Create a new Socket scan and report'\n\nconst hidden = false\n\nconst generalFlags: MeowFlags = {\n ...commonFlags,\n ...outputFlags,\n autoManifest: {\n type: 'boolean',\n description:\n 'Run `socket manifest auto` before collecting manifest files. This is necessary for languages like Scala, Gradle, and Kotlin, See `socket manifest auto --help`.',\n },\n branch: {\n type: 'string',\n shortFlag: 'b',\n description: 'Branch name',\n },\n commitHash: {\n type: 'string',\n shortFlag: 'ch',\n default: '',\n description: 'Commit hash',\n },\n commitMessage: {\n type: 'string',\n shortFlag: 'm',\n default: '',\n description: 'Commit message',\n },\n committers: {\n type: 'string',\n shortFlag: 'c',\n default: '',\n description: 'Committers',\n },\n cwd: {\n type: 'string',\n description: 'working directory, defaults to process.cwd()',\n },\n defaultBranch: {\n type: 'boolean',\n default: false,\n description:\n 'Set the default branch of the repository to the branch of this full-scan. Should only need to be done once, for example for the \"main\" or \"master\" branch.',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n pullRequest: {\n type: 'number',\n shortFlag: 'pr',\n description: 'Pull request number',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n reach: {\n type: 'boolean',\n default: false,\n description: 'Run tier 1 full application reachability analysis',\n },\n readOnly: {\n type: 'boolean',\n default: false,\n description:\n 'Similar to --dry-run except it can read from remote, stops before it would create an actual report',\n },\n repo: {\n type: 'string',\n shortFlag: 'r',\n description: 'Repository name',\n },\n report: {\n type: 'boolean',\n description:\n 'Wait for the scan creation to complete, then basically run `socket scan report` on it',\n },\n reportLevel: {\n type: 'string',\n default: constants.REPORT_LEVEL_ERROR,\n description: `Which policy level alerts should be reported (default '${constants.REPORT_LEVEL_ERROR}')`,\n },\n setAsAlertsPage: {\n type: 'boolean',\n default: true,\n aliases: ['pendingHead'],\n description:\n 'When true and if this is the \"default branch\" then this Scan will be the one reflected on your alerts page. See help for details. Defaults to true.',\n },\n tmp: {\n type: 'boolean',\n shortFlag: 't',\n default: false,\n description:\n 'Set the visibility (true/false) of the scan in your dashboard.',\n },\n}\n\nexport const cmdScanCreate = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...generalFlags,\n ...reachabilityFlags,\n },\n // TODO: Your project's \"socket.yml\" file's \"projectIgnorePaths\".\n help: command => `\n Usage\n $ ${command} [options] [TARGET...]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(generalFlags)}\n\n Reachability Options (when --reach is used)\n ${getFlagListOutput(reachabilityFlags)}\n\n Uploads the specified dependency manifest files for Go, Gradle, JavaScript,\n Kotlin, Python, and Scala. Files like \"package.json\" and \"requirements.txt\".\n If any folder is specified, the ones found in there recursively are uploaded.\n\n Details on TARGET:\n\n - Defaults to the current dir (cwd) if none given\n - Multiple targets can be specified\n - If a target is a file, only that file is checked\n - If it is a dir, the dir is scanned for any supported manifest files\n - Dirs MUST be within the current dir (cwd), you can use --cwd to change it\n - Supports globbing such as \"**/package.json\", \"**/requirements.txt\", etc.\n - Ignores any file specified in your project's \".gitignore\"\n - Also a sensible set of default ignores from the \"ignore-by-default\" module\n\n The --repo and --branch flags tell Socket to associate this Scan with that\n repo/branch. The names will show up on your dashboard on the Socket website.\n\n Note: for a first run you probably want to set --default-branch to indicate\n the default branch name, like \"main\" or \"master\".\n\n The \"alerts page\" (https://socket.dev/dashboard/org/YOURORG/alerts) will show\n the results from the last scan designated as the \"pending head\" on the branch\n configured on Socket to be the \"default branch\". When creating a scan the\n --set-as-alerts-page flag will default to true to update this. You can prevent\n this by using --no-set-as-alerts-page. This flag is ignored for any branch that\n is not designated as the \"default branch\". It is disabled when using --tmp.\n\n You can use \\`socket scan setup\\` to configure certain repo flag defaults.\n\n Examples\n $ ${command}\n $ ${command} ./proj --json\n $ ${command} --repo=test-repo --branch=main ./package.json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const {\n commitHash,\n commitMessage,\n committers,\n cwd: cwdOverride,\n defaultBranch,\n interactive = true,\n json,\n markdown,\n org: orgFlag,\n pullRequest,\n reach,\n reachAnalysisMemoryLimit,\n reachAnalysisTimeout,\n reachDisableAnalytics,\n reachSkipCache,\n readOnly,\n reportLevel,\n setAsAlertsPage: pendingHeadFlag,\n tmp,\n } = cli.flags as {\n cwd: string\n commitHash: string\n commitMessage: string\n committers: string\n defaultBranch: boolean\n interactive: boolean\n json: boolean\n markdown: boolean\n org: string\n pullRequest: number\n readOnly: boolean\n reportLevel: REPORT_LEVEL\n setAsAlertsPage: boolean\n tmp: boolean\n // Reachability flags.\n reach: boolean\n reachAnalysisTimeout: number\n reachAnalysisMemoryLimit: number\n reachDisableAnalytics: boolean\n reachSkipCache: boolean\n }\n\n // Validate ecosystem values.\n const reachEcosystems: PURL_Type[] = []\n const reachEcosystemsRaw = cmdFlagValueToArray(cli.flags['reachEcosystems'])\n const validEcosystems = getEcosystemChoicesForMeow()\n for (const ecosystem of reachEcosystemsRaw) {\n if (!validEcosystems.includes(ecosystem)) {\n throw new Error(\n `Invalid ecosystem: \"${ecosystem}\". Valid values are: ${joinAnd(validEcosystems)}`,\n )\n }\n reachEcosystems.push(ecosystem as PURL_Type)\n }\n\n const dryRun = !!cli.flags['dryRun']\n\n let {\n autoManifest,\n branch: branchName,\n repo: repoName,\n report,\n } = cli.flags as {\n autoManifest?: boolean | undefined\n branch: string\n repo: string\n report?: boolean | undefined\n }\n\n let [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const processCwd = process.cwd()\n const cwd =\n cwdOverride && cwdOverride !== processCwd\n ? path.resolve(processCwd, String(cwdOverride))\n : processCwd\n\n const sockJson = readOrDefaultSocketJson(cwd)\n\n // Note: This needs meow booleanDefault=undefined.\n if (typeof autoManifest !== 'boolean') {\n if (sockJson.defaults?.scan?.create?.autoManifest !== undefined) {\n autoManifest = sockJson.defaults.scan.create.autoManifest\n logger.info(\n 'Using default --auto-manifest from socket.json:',\n autoManifest,\n )\n } else {\n autoManifest = false\n }\n }\n if (!branchName) {\n if (sockJson.defaults?.scan?.create?.branch) {\n branchName = sockJson.defaults.scan.create.branch\n logger.info('Using default --branch from socket.json:', branchName)\n } else {\n branchName = (await gitBranch(cwd)) || (await detectDefaultBranch(cwd))\n }\n }\n if (!repoName) {\n if (sockJson.defaults?.scan?.create?.repo) {\n repoName = sockJson.defaults.scan.create.repo\n logger.info('Using default --repo from socket.json:', repoName)\n } else {\n repoName = await getRepoName(cwd)\n }\n }\n if (typeof report !== 'boolean') {\n if (sockJson.defaults?.scan?.create?.report !== undefined) {\n report = sockJson.defaults.scan.create.report\n logger.info('Using default --report from socket.json:', report)\n } else {\n report = false\n }\n }\n\n // If we updated any inputs then we should print the command line to repeat\n // the command without requiring user input, as a suggestion.\n let updatedInput = false\n\n // Accept zero or more paths. Default to cwd() if none given.\n let targets = cli.input || [cwd]\n\n if (!targets.length && !dryRun && interactive) {\n targets = await suggestTarget()\n updatedInput = true\n }\n\n // We're going to need an api token to suggest data because those suggestions\n // must come from data we already know. Don't error on missing api token yet.\n // If the api-token is not set, ignore it for the sake of suggestions.\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n const pendingHead = tmp ? false : pendingHeadFlag\n\n // If the current cwd is unknown and is used as a repo slug anyways, we will\n // first need to register the slug before we can use it.\n // Only do suggestions with an apiToken and when not in dryRun mode\n if (hasApiToken && !dryRun && interactive) {\n if (!orgSlug) {\n const suggestion = await suggestOrgSlug()\n if (suggestion === undefined) {\n await outputCreateNewScan(\n {\n ok: false,\n message: 'Canceled by user',\n cause: 'Org selector was canceled by user',\n },\n {\n interactive: false,\n outputKind,\n },\n )\n return\n }\n if (suggestion) {\n orgSlug = suggestion\n }\n updatedInput = true\n }\n }\n\n const detected = await detectManifestActions(sockJson, cwd)\n if (detected.count > 0 && !autoManifest) {\n logger.info(\n `Detected ${detected.count} manifest targets we could try to generate. Please set the --auto-manifest flag if you want to include languages covered by \\`socket manifest auto\\` in the Scan.`,\n )\n }\n\n if (updatedInput && orgSlug && targets.length) {\n logger.info(\n 'Note: You can invoke this command next time to skip the interactive questions:',\n )\n logger.error('```')\n logger.error(\n ` socket scan create [other flags...] ${orgSlug} ${targets.join(' ')}`,\n )\n logger.error('```')\n logger.error('')\n logger.info(\n 'You can also run `socket scan setup` to persist these flag defaults to a socket.json file.',\n )\n logger.error('')\n }\n\n const reachExcludePaths = cmdFlagValueToArray(cli.flags['reachExcludePaths'])\n\n // Validation helpers for better readability.\n const hasReachEcosystems = reachEcosystems.length > 0\n\n const hasReachExcludePaths = reachExcludePaths.length > 0\n\n const isUsingNonDefaultMemoryLimit =\n reachAnalysisMemoryLimit !==\n reachabilityFlags['reachAnalysisMemoryLimit']?.default\n\n const isUsingNonDefaultTimeout =\n reachAnalysisTimeout !== reachabilityFlags['reachAnalysisTimeout']?.default\n\n const isUsingNonDefaultAnalytics =\n reachDisableAnalytics !==\n reachabilityFlags['reachDisableAnalytics']?.default\n\n const isUsingAnyReachabilityFlags =\n isUsingNonDefaultMemoryLimit ||\n isUsingNonDefaultTimeout ||\n isUsingNonDefaultAnalytics ||\n hasReachEcosystems ||\n hasReachExcludePaths ||\n reachSkipCache\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n test: !!targets.length,\n message: 'At least one TARGET (e.g. `.` or `./package.json`)',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n {\n nook: true,\n test: !defaultBranch || !!branchName,\n message: 'When --default-branch is set, --branch is mandatory',\n fail: 'missing branch name',\n },\n {\n nook: true,\n test: !pendingHead || !!branchName,\n message: 'When --pending-head is set, --branch is mandatory',\n fail: 'missing branch name',\n },\n {\n nook: true,\n test: reach || !isUsingAnyReachabilityFlags,\n message: 'Reachability analysis flags require --reach to be enabled',\n fail: 'add --reach flag to use --reach-* options',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleCreateNewScan({\n autoManifest: Boolean(autoManifest),\n branchName: branchName as string,\n commitHash: (commitHash && String(commitHash)) || '',\n commitMessage: (commitMessage && String(commitMessage)) || '',\n committers: (committers && String(committers)) || '',\n cwd,\n defaultBranch: Boolean(defaultBranch),\n interactive: Boolean(interactive),\n orgSlug,\n outputKind,\n pendingHead: Boolean(pendingHead),\n pullRequest: Number(pullRequest),\n reach: {\n runReachabilityAnalysis: Boolean(reach),\n reachDisableAnalytics: Boolean(reachDisableAnalytics),\n reachAnalysisTimeout: Number(reachAnalysisTimeout),\n reachAnalysisMemoryLimit: Number(reachAnalysisMemoryLimit),\n reachEcosystems,\n reachExcludePaths,\n reachSkipCache: Boolean(reachSkipCache),\n },\n readOnly: Boolean(readOnly),\n repoName,\n report,\n reportLevel,\n targets,\n tmp: Boolean(tmp),\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchDeleteOrgFullScanOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchDeleteOrgFullScan(\n orgSlug: string,\n scanId: string,\n options?: FetchDeleteOrgFullScanOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'deleteOrgFullScan'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchDeleteOrgFullScanOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.deleteOrgFullScan(orgSlug, scanId), {\n desc: 'to delete a scan',\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputDeleteScan(\n result: CResult<SocketSdkSuccessResult<'deleteOrgFullScan'>['data']>,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.success('Scan deleted successfully')\n}\n","import { fetchDeleteOrgFullScan } from './fetch-delete-org-full-scan.mts'\nimport { outputDeleteScan } from './output-delete-scan.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleDeleteScan(\n orgSlug: string,\n scanId: string,\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchDeleteOrgFullScan(orgSlug, scanId)\n\n await outputDeleteScan(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleDeleteScan } from './handle-delete-scan.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'del'\n\nconst description = 'Delete a scan'\n\nconst hidden = false\n\nexport const cmdScanDel = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <SCAN_ID>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0 --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const [scanId = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug, defaultOrgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: !!defaultOrgSlug,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n test: !!scanId,\n message: 'Scan ID to delete',\n fail: 'missing',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleDeleteScan(orgSlug, scanId, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { queryApiSafeJson } from '../../utils/api.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function fetchDiffScan({\n id1,\n id2,\n orgSlug,\n}: {\n id1: string\n id2: string\n orgSlug: string\n}): Promise<CResult<SocketSdkSuccessResult<'GetOrgDiffScan'>['data']>> {\n logger.info('Scan ID 1:', id1)\n logger.info('Scan ID 2:', id2)\n logger.info('Note: this request may take some time if the scans are big')\n\n return await queryApiSafeJson<\n SocketSdkSuccessResult<'GetOrgDiffScan'>['data']\n >(\n `orgs/${orgSlug}/full-scans/diff?before=${encodeURIComponent(id1)}&after=${encodeURIComponent(id2)}`,\n 'a scan diff',\n )\n}\n","import fs from 'node:fs'\nimport util from 'node:util'\n\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputDiffScan(\n result: CResult<SocketSdkSuccessResult<'GetOrgDiffScan'>['data']>,\n {\n depth,\n file,\n outputKind,\n }: {\n depth: number\n file: string\n outputKind: OutputKind\n },\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (!result.ok) {\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const dashboardUrl = result.data.diff_report_url\n const dashboardMessage = dashboardUrl\n ? `\\n View this diff scan in the Socket dashboard: ${colors.cyan(dashboardUrl)}`\n : ''\n\n // When forcing json, or dumping to file, serialize to string such that it\n // won't get truncated. The only way to dump the full raw JSON to stdout is\n // to use `--json --file -` (the dash is a standard notation for stdout)\n if (outputKind === 'json' || file) {\n await handleJson(result, file, dashboardMessage)\n return\n }\n\n if (outputKind === 'markdown') {\n await handleMarkdown(result.data)\n return\n }\n\n // In this case neither the --json nor the --file flag was passed\n // Dump the JSON to CLI and let NodeJS deal with truncation\n\n logger.log('Diff scan result:')\n logger.log(\n util.inspect(result.data, {\n showHidden: false,\n depth: depth > 0 ? depth : null,\n colors: true,\n maxArrayLength: null,\n }),\n )\n logger.info(\n `\\n 📝 To display the detailed report in the terminal, use the --json flag. For a friendlier report, use the --markdown flag.\\n`,\n )\n logger.info(dashboardMessage)\n}\n\nasync function handleJson(\n data: CResult<SocketSdkSuccessResult<'GetOrgDiffScan'>['data']>,\n file: string,\n dashboardMessage: string,\n) {\n const json = serializeResultJson(data)\n\n if (file && file !== '-') {\n logger.log(`Writing json to \\`${file}\\``)\n fs.writeFile(file, json, err => {\n if (err) {\n logger.fail(`Writing to \\`${file}\\` failed...`)\n logger.error(err)\n } else {\n logger.success(`Data successfully written to \\`${file}\\``)\n }\n logger.error(dashboardMessage)\n })\n } else {\n // only .log goes to stdout\n logger.info(`\\n Diff scan result: \\n`)\n logger.log(json)\n logger.info(dashboardMessage)\n }\n}\n\nasync function handleMarkdown(\n data: SocketSdkSuccessResult<'GetOrgDiffScan'>['data'],\n) {\n const SOCKET_SBOM_URL_PREFIX = `${constants.SOCKET_WEBSITE_URL}/dashboard/org/SocketDev/sbom/`\n\n logger.log('# Scan diff result')\n logger.log('')\n logger.log('This Socket.dev report shows the changes between two scans:')\n logger.log(\n `- [${data.before.id}](${SOCKET_SBOM_URL_PREFIX}${data.before.id})`,\n )\n logger.log(`- [${data.after.id}](${SOCKET_SBOM_URL_PREFIX}${data.after.id})`)\n logger.log('')\n logger.log(\n `You can [view this report in your dashboard](${data.diff_report_url})`,\n )\n logger.log('')\n logger.log('## Changes')\n logger.log('')\n logger.log(`- directDependenciesChanged: ${data.directDependenciesChanged}`)\n logger.log(`- Added packages: ${data.artifacts.added.length}`)\n\n if (data.artifacts.added.length > 0) {\n data.artifacts.added.slice(0, 10).forEach(artifact => {\n logger.log(` - ${artifact.type} ${artifact.name}@${artifact.version}`)\n })\n if (data.artifacts.added.length > 10) {\n logger.log(` ... and ${data.artifacts.added.length - 10} more`)\n }\n }\n\n logger.log(`- Removed packages: ${data.artifacts.removed.length}`)\n if (data.artifacts.removed.length > 0) {\n data.artifacts.removed.slice(0, 10).forEach(artifact => {\n logger.log(` - ${artifact.type} ${artifact.name}@${artifact.version}`)\n })\n if (data.artifacts.removed.length > 10) {\n logger.log(` ... and ${data.artifacts.removed.length - 10} more`)\n }\n }\n\n logger.log(`- Replaced packages: ${data.artifacts.replaced.length}`)\n if (data.artifacts.replaced.length > 0) {\n data.artifacts.replaced.slice(0, 10).forEach(artifact => {\n logger.log(` - ${artifact.type} ${artifact.name}@${artifact.version}`)\n })\n if (data.artifacts.replaced.length > 10) {\n logger.log(` ... and ${data.artifacts.replaced.length - 10} more`)\n }\n }\n\n logger.log(`- Updated packages: ${data.artifacts.updated.length}`)\n if (data.artifacts.updated.length > 0) {\n data.artifacts.updated.slice(0, 10).forEach(artifact => {\n logger.log(` - ${artifact.type} ${artifact.name}@${artifact.version}`)\n })\n if (data.artifacts.updated.length > 10) {\n logger.log(` ... and ${data.artifacts.updated.length - 10} more`)\n }\n }\n\n const unchanged = data.artifacts.unchanged ?? []\n logger.log(`- Unchanged packages: ${unchanged.length}`)\n if (unchanged.length > 0) {\n const firstUpToTen = unchanged.slice(0, 10)\n for (const artifact of firstUpToTen) {\n logger.log(` - ${artifact.type} ${artifact.name}@${artifact.version}`)\n }\n if (unchanged.length > 10) {\n logger.log(` ... and ${unchanged.length - 10} more`)\n }\n }\n\n logger.log('')\n logger.log(`## Scan ${data.before.id}`)\n logger.log('')\n logger.log(\n 'This Scan was considered to be the \"base\" / \"from\" / \"before\" Scan.',\n )\n logger.log('')\n for (const [key, value] of Object.entries(data.before)) {\n if (key === 'pull_request' && !value) {\n continue\n }\n if (!['id', 'organization_id', 'repository_id'].includes(key)) {\n logger.group(\n `- ${key === 'repository_slug' ? 'repo' : key === 'organization_slug' ? 'org' : key}: ${value}`,\n )\n logger.groupEnd()\n }\n }\n\n logger.log('')\n logger.log(`## Scan ${data.after.id}`)\n logger.log('')\n logger.log('This Scan was considered to be the \"head\" / \"to\" / \"after\" Scan.')\n logger.log('')\n for (const [key, value] of Object.entries(data.after)) {\n if (key === 'pull_request' && !value) {\n continue\n }\n if (!['id', 'organization_id', 'repository_id'].includes(key)) {\n logger.group(\n `- ${key === 'repository_slug' ? 'repo' : key === 'organization_slug' ? 'org' : key}: ${value}`,\n )\n logger.groupEnd()\n }\n }\n\n logger.log('')\n}\n","import { fetchDiffScan } from './fetch-diff-scan.mts'\nimport { outputDiffScan } from './output-diff-scan.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleDiffScan({\n depth,\n file,\n id1,\n id2,\n orgSlug,\n outputKind,\n}: {\n depth: number\n file: string\n id1: string\n id2: string\n orgSlug: string\n outputKind: OutputKind\n}): Promise<void> {\n const data = await fetchDiffScan({\n id1,\n id2,\n orgSlug,\n })\n\n await outputDiffScan(data, {\n depth,\n file,\n outputKind,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleDiffScan } from './handle-diff-scan.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'diff'\n\nconst description = 'See what changed between two Scans'\n\nconst hidden = false\n\nexport const cmdScanDiff = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n depth: {\n type: 'number',\n default: 2,\n description:\n 'Max depth of JSON to display before truncating, use zero for no limit (without --json/--file)',\n },\n file: {\n type: 'string',\n shortFlag: 'f',\n default: '',\n description:\n 'Path to a local file where the output should be saved. Use `-` to force stdout.',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <SCAN_ID1> <SCAN_ID2>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n This command displays the package changes between two scans. The full output\n can be pretty large depending on the size of your repo and time range. It is\n best stored to disk (with --json) to be further analyzed by other tools.\n\n Note: While it will work in any order, the first Scan ID is assumed to be the\n older ID, even if it is a newer Scan. This is only relevant for the\n added/removed list (similar to diffing two files with git).\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} aaa0aa0a-aaaa-0000-0a0a-0000000a00a0 aaa1aa1a-aaaa-1111-1a1a-1111111a11a1\n $ ${command} aaa0aa0a-aaaa-0000-0a0a-0000000a00a0 aaa1aa1a-aaaa-1111-1a1a-1111111a11a1 --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const SOCKET_SBOM_URL_PREFIX = `${constants.SOCKET_WEBSITE_URL}/dashboard/org/SocketDev/sbom/`\n\n const SOCKET_SBOM_URL_PREFIX_LENGTH = SOCKET_SBOM_URL_PREFIX.length\n\n const { depth, file, json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n let [id1 = '', id2 = ''] = cli.input\n // Support dropping in full socket urls to an sbom.\n if (id1.startsWith(SOCKET_SBOM_URL_PREFIX)) {\n id1 = id1.slice(SOCKET_SBOM_URL_PREFIX_LENGTH)\n }\n if (id2.startsWith(SOCKET_SBOM_URL_PREFIX)) {\n id2 = id2.slice(SOCKET_SBOM_URL_PREFIX_LENGTH)\n }\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: !!(id1 && id2),\n message:\n 'Specify two Scan IDs.\\nA Scan ID looks like `aaa0aa0a-aaaa-0000-0a0a-0000000a00a0`.',\n fail:\n !id1 && !id2\n ? 'missing both Scan IDs'\n : !id2\n ? 'missing second Scan ID'\n : 'missing first Scan ID', // Not sure how this can happen but ok.\n },\n {\n test: !!orgSlug,\n nook: true,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleDiffScan({\n id1: String(id1 || ''),\n id2: String(id2 || ''),\n depth: Number(depth),\n orgSlug,\n outputKind,\n file: String(file || ''),\n })\n}\n","import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport { pipeline } from 'node:stream/promises'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { confirm, select } from '@socketsecurity/registry/lib/prompts'\n\nimport { fetchSupportedScanFileNames } from './fetch-supported-scan-file-names.mts'\nimport { handleCreateNewScan } from './handle-create-new-scan.mts'\nimport constants from '../../constants.mts'\nimport { isReportSupportedFile } from '../../utils/glob.mts'\nimport { fetchListAllRepos } from '../repository/fetch-list-all-repos.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function createScanFromGithub({\n all,\n githubApiUrl,\n githubToken,\n interactive,\n orgGithub,\n orgSlug,\n outputKind,\n repos,\n}: {\n all: boolean\n githubApiUrl: string\n githubToken: string\n interactive: boolean\n orgSlug: string\n orgGithub: string\n outputKind: OutputKind\n repos: string\n}): Promise<CResult<undefined>> {\n let targetRepos: string[] = repos\n .trim()\n .split(',')\n .map(r => r.trim())\n .filter(Boolean)\n if (all || targetRepos.length === 0) {\n // Fetch from Socket API\n const result = await fetchListAllRepos(orgSlug, {\n direction: 'asc',\n sort: 'name',\n })\n if (!result.ok) {\n return result\n }\n targetRepos = result.data.results.map(obj => obj.slug || '')\n }\n\n targetRepos = targetRepos.map(s => s.trim()).filter(Boolean)\n\n logger.info(`Have ${targetRepos.length} repo names to Scan!`)\n logger.log('')\n\n if (!targetRepos.filter(Boolean).length) {\n return {\n ok: false,\n message: 'No repo found',\n cause:\n 'You did not set the --repos value and/or the server responded with zero repos when asked for some. Unable to proceed.',\n }\n }\n\n // Non-interactive or explicitly requested; just do it.\n if (interactive && targetRepos.length > 1 && !all && !repos) {\n const which = await selectFocus(targetRepos)\n if (!which.ok) {\n return which\n }\n targetRepos = which.data\n }\n\n // 10 is an arbitrary number. Maybe confirm whenever count>1 ?\n // Do not ask to confirm when the list was given explicit.\n if (interactive && (all || !repos) && targetRepos.length > 10) {\n const sure = await makeSure(targetRepos.length)\n if (!sure.ok) {\n return sure\n }\n }\n\n let scansCreated = 0\n for (const repoSlug of targetRepos) {\n // eslint-disable-next-line no-await-in-loop\n const scanCResult = await scanRepo(repoSlug, {\n githubApiUrl,\n githubToken,\n orgSlug,\n orgGithub,\n outputKind,\n repos,\n })\n if (scanCResult.ok) {\n const { scanCreated } = scanCResult.data\n if (scanCreated) {\n scansCreated += 1\n }\n }\n }\n\n logger.success(targetRepos.length, 'GitHub repos detected')\n logger.success(scansCreated, 'with supported Manifest files')\n\n return {\n ok: true,\n data: undefined,\n }\n}\n\nasync function scanRepo(\n repoSlug: string,\n {\n githubApiUrl,\n githubToken,\n orgGithub,\n orgSlug,\n outputKind,\n repos,\n }: {\n githubApiUrl: string\n githubToken: string\n orgSlug: string\n orgGithub: string\n outputKind: OutputKind\n repos: string\n },\n): Promise<CResult<{ scanCreated: boolean }>> {\n logger.info(\n `Requesting repo details from GitHub API for: \\`${orgGithub}/${repoSlug}\\`...`,\n )\n logger.group()\n const result = await scanOneRepo(repoSlug, {\n githubApiUrl,\n githubToken,\n orgSlug,\n orgGithub,\n outputKind,\n repos,\n })\n logger.groupEnd()\n logger.log('')\n return result\n}\n\nasync function scanOneRepo(\n repoSlug: string,\n {\n githubApiUrl,\n githubToken,\n orgGithub,\n orgSlug,\n outputKind,\n }: {\n githubApiUrl: string\n githubToken: string\n orgSlug: string\n orgGithub: string\n outputKind: OutputKind\n repos: string\n },\n): Promise<CResult<{ scanCreated: boolean }>> {\n const repoResult = await getRepoDetails({\n orgGithub,\n repoSlug,\n githubApiUrl,\n githubToken,\n })\n if (!repoResult.ok) {\n return repoResult\n }\n const { defaultBranch, repoApiUrl } = repoResult.data\n\n logger.info(`Default branch: \\`${defaultBranch}\\``)\n\n const treeResult = await getRepoBranchTree({\n defaultBranch,\n githubToken,\n orgGithub,\n repoSlug,\n repoApiUrl,\n })\n if (!treeResult.ok) {\n return treeResult\n }\n const files = treeResult.data\n\n if (!files.length) {\n logger.warn(\n 'No files were reported for the default branch. Moving on to next repo.',\n )\n return { ok: true, data: { scanCreated: false } }\n }\n\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), repoSlug))\n debugFn('notice', 'init: temp dir for scan root', tmpDir)\n\n const downloadResult = await testAndDownloadManifestFiles({\n files,\n tmpDir,\n repoSlug,\n defaultBranch,\n orgGithub,\n repoApiUrl,\n githubToken,\n })\n if (!downloadResult.ok) {\n return downloadResult\n }\n\n const commitResult = await getLastCommitDetails({\n orgGithub,\n repoSlug,\n defaultBranch,\n repoApiUrl,\n githubToken,\n })\n if (!commitResult.ok) {\n return commitResult\n }\n\n const { lastCommitMessage, lastCommitSha, lastCommitter } = commitResult.data\n\n // Make request for full scan\n // I think we can just kick off the socket scan create command now...\n\n await handleCreateNewScan({\n autoManifest: false,\n branchName: defaultBranch,\n commitHash: lastCommitSha,\n commitMessage: lastCommitMessage || '',\n committers: lastCommitter || '',\n cwd: tmpDir,\n defaultBranch: true,\n interactive: false,\n orgSlug,\n outputKind,\n pendingHead: true,\n pullRequest: 0,\n reach: {\n runReachabilityAnalysis: false,\n reachDisableAnalytics: false,\n reachAnalysisTimeout: 0,\n reachAnalysisMemoryLimit: 0,\n reachEcosystems: [],\n reachExcludePaths: [],\n reachSkipCache: false,\n },\n readOnly: false,\n repoName: repoSlug,\n report: false,\n reportLevel: constants.REPORT_LEVEL_ERROR,\n targets: ['.'],\n tmp: false,\n })\n\n return { ok: true, data: { scanCreated: true } }\n}\n\nasync function testAndDownloadManifestFiles({\n defaultBranch,\n files,\n githubToken,\n orgGithub,\n repoApiUrl,\n repoSlug,\n tmpDir,\n}: {\n files: string[]\n tmpDir: string\n repoSlug: string\n defaultBranch: string\n orgGithub: string\n repoApiUrl: string\n githubToken: string\n}): Promise<CResult<unknown>> {\n logger.info(\n `File tree for ${defaultBranch} contains`,\n files.length,\n `entries. Searching for supported manifest files...`,\n )\n logger.group()\n let fileCount = 0\n let firstFailureResult\n for (const file of files) {\n // eslint-disable-next-line no-await-in-loop\n const result = await testAndDownloadManifestFile({\n file,\n tmpDir,\n defaultBranch,\n repoApiUrl,\n githubToken,\n })\n if (result.ok) {\n if (result.data.isManifest) {\n fileCount += 1\n }\n } else if (!firstFailureResult) {\n firstFailureResult = result\n }\n }\n logger.groupEnd()\n logger.info('Found and downloaded', fileCount, 'manifest files')\n\n if (!fileCount) {\n if (firstFailureResult) {\n logger.fail(\n 'While no supported manifest files were downloaded, at least one error encountered trying to do so. Showing the first error.',\n )\n return firstFailureResult\n }\n return {\n ok: false,\n message: 'No manifest files found',\n cause: `No supported manifest files were found in the latest commit on the branch ${defaultBranch} for repo ${orgGithub}/${repoSlug}. Skipping full scan.`,\n }\n }\n\n return { ok: true, data: undefined }\n}\n\nasync function testAndDownloadManifestFile({\n defaultBranch,\n file,\n githubToken,\n repoApiUrl,\n tmpDir,\n}: {\n file: string\n tmpDir: string\n defaultBranch: string\n repoApiUrl: string\n githubToken: string\n}): Promise<CResult<{ isManifest: boolean }>> {\n debugFn('notice', 'testing: file', file)\n\n const supportedFilesCResult = await fetchSupportedScanFileNames()\n const supportedFiles = supportedFilesCResult.ok\n ? supportedFilesCResult.data\n : undefined\n\n if (!supportedFiles || !isReportSupportedFile(file, supportedFiles)) {\n debugFn('notice', 'skip: not a known pattern')\n // Not an error.\n return { ok: true, data: { isManifest: false } }\n }\n\n debugFn(\n 'notice',\n 'found: manifest file, going to attempt to download it;',\n file,\n )\n\n const result = await downloadManifestFile({\n file,\n tmpDir,\n defaultBranch,\n repoApiUrl,\n githubToken,\n })\n\n return result.ok ? { ok: true, data: { isManifest: true } } : result\n}\n\nasync function downloadManifestFile({\n defaultBranch,\n file,\n githubToken,\n repoApiUrl,\n tmpDir,\n}: {\n file: string\n tmpDir: string\n defaultBranch: string\n repoApiUrl: string\n githubToken: string\n}): Promise<CResult<undefined>> {\n debugFn('notice', 'request: download url from GitHub')\n\n const fileUrl = `${repoApiUrl}/contents/${file}?ref=${defaultBranch}`\n debugDir('inspect', { fileUrl })\n\n const downloadUrlResponse = await fetch(fileUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n debugFn('notice', 'complete: request')\n\n const downloadUrlText = await downloadUrlResponse.text()\n debugFn('inspect', 'response: raw download url', downloadUrlText)\n\n let downloadUrl\n try {\n downloadUrl = JSON.parse(downloadUrlText).download_url\n } catch {\n logger.fail(\n `GitHub response contained invalid JSON for download url for: ${file}`,\n )\n\n return {\n ok: false,\n message: 'Invalid JSON response',\n cause: `Server responded with invalid JSON for download url ${downloadUrl}`,\n }\n }\n\n const localPath = path.join(tmpDir, file)\n debugFn(\n 'notice',\n 'download: manifest file started',\n downloadUrl,\n '->',\n localPath,\n )\n\n // Now stream the file to that file...\n const result = await streamDownloadWithFetch(localPath, downloadUrl)\n if (!result.ok) {\n // Do we proceed? Bail? Hrm...\n logger.fail(\n `Failed to download manifest file, skipping to next file. File: ${file}`,\n )\n return result\n }\n\n debugFn('notice', 'download: manifest file completed')\n\n return { ok: true, data: undefined }\n}\n\n// Courtesy of gemini:\nasync function streamDownloadWithFetch(\n localPath: string,\n downloadUrl: string,\n): Promise<CResult<string>> {\n let response // Declare response here to access it in catch if needed\n\n try {\n response = await fetch(downloadUrl)\n\n if (!response.ok) {\n const errorMsg = `Download failed due to bad server response: ${response.status} ${response.statusText} for ${downloadUrl}`\n logger.fail(errorMsg)\n return { ok: false, message: 'Download Failed', cause: errorMsg }\n }\n\n if (!response.body) {\n logger.fail(\n `Download failed because the server response was empty, for ${downloadUrl}`,\n )\n return {\n ok: false,\n message: 'Download Failed',\n cause: 'Response body is null or undefined.',\n }\n }\n\n // Make sure the dir exists. It may be nested and we need to construct that\n // before starting the download.\n const dir = path.dirname(localPath)\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n\n const fileStream = fs.createWriteStream(localPath)\n\n // Using stream.pipeline for better error handling and cleanup\n\n await pipeline(response.body, fileStream)\n // 'pipeline' will automatically handle closing streams and propagating errors.\n // It resolves when the piping is fully complete and fileStream is closed.\n return { ok: true, data: localPath }\n } catch (error) {\n logger.fail(\n 'An error was thrown while trying to download a manifest file... url:',\n downloadUrl,\n )\n debugDir('inspect', { error })\n\n // If an error occurs and fileStream was created, attempt to clean up.\n if (fs.existsSync(localPath)) {\n // Check if fileStream was even opened before trying to delete\n // This check might be too simplistic depending on when error occurs\n fs.unlink(localPath, unlinkErr => {\n if (unlinkErr) {\n logger.fail(\n `Error deleting partial file ${localPath}: ${unlinkErr.message}`,\n )\n }\n })\n }\n // Construct a more informative error message\n let detailedError = `Error during download of ${downloadUrl}: ${(error as { message: string }).message}`\n if ((error as { cause: string }).cause) {\n // Include cause if available (e.g., from network errors)\n detailedError += `\\nCause: ${(error as { cause: string }).cause}`\n }\n if (response && !response.ok) {\n // If error was due to bad HTTP status\n detailedError += ` (HTTP Status: ${response.status} ${response.statusText})`\n }\n debugFn('error', detailedError)\n return { ok: false, message: 'Download Failed', cause: detailedError }\n }\n}\n\nasync function getLastCommitDetails({\n defaultBranch,\n githubToken,\n orgGithub,\n repoApiUrl,\n repoSlug,\n}: {\n orgGithub: string\n repoSlug: string\n defaultBranch: string\n repoApiUrl: string\n githubToken: string\n}): Promise<\n CResult<{\n lastCommitSha: string\n lastCommitter: string | undefined\n lastCommitMessage: string\n }>\n> {\n logger.info(\n `Requesting last commit for default branch ${defaultBranch} for ${orgGithub}/${repoSlug}...`,\n )\n\n const commitApiUrl = `${repoApiUrl}/commits?sha=${defaultBranch}&per_page=1`\n debugFn('inspect', 'url: commit', commitApiUrl)\n\n const commitResponse = await fetch(commitApiUrl, {\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n\n const commitText = await commitResponse.text()\n debugFn('inspect', 'response: commit', commitText)\n\n let lastCommit\n try {\n lastCommit = JSON.parse(commitText)?.[0]\n } catch {\n logger.fail(`GitHub response contained invalid JSON for last commit`)\n logger.error(commitText)\n return {\n ok: false,\n message: 'Invalid JSON response',\n cause: `Server responded with invalid JSON for last commit of repo ${repoSlug}`,\n }\n }\n\n const lastCommitSha = lastCommit.sha\n const lastCommitter = Array.from(\n new Set([lastCommit.commit.author.name, lastCommit.commit.committer.name]),\n )[0]\n const lastCommitMessage = lastCommit.message\n\n if (!lastCommitSha) {\n return {\n ok: false,\n message: 'Missing commit SHA',\n cause: 'Unable to get last commit for repo',\n }\n }\n\n if (!lastCommitter) {\n return {\n ok: false,\n message: 'Missing committer',\n cause: 'Last commit does not have information about who made the commit',\n }\n }\n\n return { ok: true, data: { lastCommitSha, lastCommitter, lastCommitMessage } }\n}\n\nasync function selectFocus(repos: string[]): Promise<CResult<string[]>> {\n const proceed = await select<string>({\n message: 'Please select the repo to process:',\n choices: repos\n .map(slug => ({\n name: slug,\n value: slug,\n description: `Create scan for the ${slug} repo through GitHub`,\n }))\n .concat({\n name: '(Exit)',\n value: '',\n description: 'Cancel this action and exit',\n }),\n })\n if (!proceed) {\n return {\n ok: false,\n message: 'Canceled by user',\n cause: 'User chose to cancel the action',\n }\n }\n return { ok: true, data: [proceed] }\n}\n\nasync function makeSure(count: number): Promise<CResult<undefined>> {\n if (\n !(await confirm({\n message: `Are you sure you want to run this for ${count} repos?`,\n default: false,\n }))\n ) {\n return {\n ok: false,\n message: 'User canceled',\n cause: 'Action canceled by user',\n }\n }\n return { ok: true, data: undefined }\n}\n\nasync function getRepoDetails({\n githubApiUrl,\n githubToken,\n orgGithub,\n repoSlug,\n}: {\n orgGithub: string\n repoSlug: string\n githubApiUrl: string\n githubToken: string\n}): Promise<\n CResult<{ defaultBranch: string; repoDetails: unknown; repoApiUrl: string }>\n> {\n const repoApiUrl = `${githubApiUrl}/repos/${orgGithub}/${repoSlug}`\n debugDir('inspect', { repoApiUrl })\n\n const repoDetailsResponse = await fetch(repoApiUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n logger.success(`Request completed.`)\n\n const repoDetailsText = await repoDetailsResponse.text()\n debugFn('inspect', 'response: repo', repoDetailsText)\n\n let repoDetails\n try {\n repoDetails = JSON.parse(repoDetailsText)\n } catch {\n logger.fail(`GitHub response contained invalid JSON for repo ${repoSlug}`)\n logger.error(repoDetailsText)\n return {\n ok: false,\n message: 'Invalid JSON response',\n cause: `Server responded with invalid JSON for repo ${repoSlug}`,\n }\n }\n\n const defaultBranch = repoDetails.default_branch\n if (!defaultBranch) {\n return {\n ok: false,\n message: 'Default Branch Not Found',\n cause: `Repo ${repoSlug} does not have a default branch set or it was not reported`,\n }\n }\n\n return { ok: true, data: { defaultBranch, repoDetails, repoApiUrl } }\n}\n\nasync function getRepoBranchTree({\n defaultBranch,\n githubToken,\n orgGithub,\n repoApiUrl,\n repoSlug,\n}: {\n defaultBranch: string\n githubToken: string\n orgGithub: string\n repoApiUrl: string\n repoSlug: string\n}): Promise<CResult<string[]>> {\n logger.info(\n `Requesting default branch file tree; branch \\`${defaultBranch}\\`, repo \\`${orgGithub}/${repoSlug}\\`...`,\n )\n\n const treeApiUrl = `${repoApiUrl}/git/trees/${defaultBranch}?recursive=1`\n debugFn('inspect', 'url: tree', treeApiUrl)\n\n const treeResponse = await fetch(treeApiUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n\n const treeText = await treeResponse.text()\n debugFn('inspect', 'response: tree', treeText)\n\n let treeDetails\n try {\n treeDetails = JSON.parse(treeText)\n } catch {\n logger.fail(\n `GitHub response contained invalid JSON for default branch of repo ${repoSlug}`,\n )\n logger.error(treeText)\n return {\n ok: false,\n message: 'Invalid JSON response',\n cause: `Server responded with invalid JSON for repo ${repoSlug}`,\n }\n }\n\n if (treeDetails.message) {\n if (treeDetails.message === 'Git Repository is empty.') {\n logger.warn(\n `GitHub reports the default branch of repo ${repoSlug} to be empty. Moving on to next repo.`,\n )\n return { ok: true, data: [] }\n }\n\n logger.fail('Negative response from GitHub:', treeDetails.message)\n return {\n ok: false,\n message: 'Unexpected error response',\n cause: `GitHub responded with an unexpected error while asking for details on the default branch: ${treeDetails.message}`,\n }\n }\n\n if (!treeDetails.tree || !Array.isArray(treeDetails.tree)) {\n debugDir('inspect', { treeDetails: { tree: treeDetails.tree } })\n\n return {\n ok: false,\n message: `Tree response for default branch ${defaultBranch} for ${orgGithub}/${repoSlug} was not a list`,\n }\n }\n\n const files = (treeDetails.tree as Array<{ type: string; path: string }>)\n .filter(obj => obj.type === 'blob')\n .map(obj => obj.path)\n\n return { ok: true, data: files }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputScanGithub(\n result: CResult<unknown>,\n outputKind: OutputKind,\n) {\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log('')\n logger.success('Finished!')\n}\n","import { createScanFromGithub } from './create-scan-from-github.mts'\nimport { outputScanGithub } from './output-scan-github.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleCreateGithubScan({\n all,\n githubApiUrl,\n githubToken,\n interactive,\n orgGithub,\n orgSlug,\n outputKind,\n repos,\n}: {\n all: boolean\n githubApiUrl: string\n githubToken: string\n interactive: boolean\n orgSlug: string\n orgGithub: string\n outputKind: OutputKind\n repos: string\n}) {\n const ghScanCResult = await createScanFromGithub({\n all: Boolean(all),\n githubApiUrl,\n githubToken,\n interactive: Boolean(interactive),\n orgSlug,\n orgGithub,\n outputKind,\n repos: String(repos || ''),\n })\n\n await outputScanGithub(ghScanCResult, outputKind)\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleCreateGithubScan } from './handle-create-github-scan.mts'\nimport { outputScanGithub } from './output-scan-github.mts'\nimport { suggestOrgSlug } from './suggest-org-slug.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\nimport { readOrDefaultSocketJson } from '../../utils/socket-json.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'github'\n\nconst DEFAULT_GITHUB_URL = 'https://api.github.com'\n\nconst description = 'Create a scan for given GitHub repo'\n\nconst hidden = true\n\nexport const cmdScanGithub = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n all: {\n type: 'boolean',\n description:\n 'Apply for all known repositories reported by the Socket API. Supersedes `repos`.',\n },\n githubToken: {\n type: 'string',\n description:\n 'Required GitHub token for authentication.\\nMay set environment variable GITHUB_TOKEN or SOCKET_CLI_GITHUB_TOKEN instead.',\n },\n githubApiUrl: {\n type: 'string',\n description: `Base URL of the GitHub API (default: ${DEFAULT_GITHUB_URL})`,\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n orgGithub: {\n type: 'string',\n description:\n 'Alternate GitHub Org if the name is different than the Socket Org',\n },\n repos: {\n type: 'string',\n description:\n 'List of repos to target in a comma-separated format (e.g., repo1,repo2). If not specified, the script will pull the list from Socket and ask you to pick one. Use --all to use them all.',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n This is similar to the \\`socket scan create\\` command except it pulls the files\n from GitHub. See the help for that command for more details.\n\n A GitHub Personal Access Token (PAT) will at least need read access to the repo\n (\"contents\", read-only) for this command to work.\n\n Note: This command cannot run the \\`socket manifest auto\\` things because that\n requires local access to the repo while this command runs entirely through the\n GitHub for file access.\n\n You can use \\`socket scan setup\\` to configure certain repo flag defaults.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} ./proj\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const {\n githubToken = constants.ENV.SOCKET_CLI_GITHUB_TOKEN,\n interactive = true,\n json,\n markdown,\n org: orgFlag,\n } = cli.flags as {\n githubToken: string\n interactive: boolean\n json: boolean\n markdown: boolean\n org: string\n orgGithub: string\n }\n\n const dryRun = !!cli.flags['dryRun']\n\n let { all, githubApiUrl, orgGithub, repos } = cli.flags as {\n all: boolean\n githubApiUrl: string\n orgGithub: string\n repos: string\n }\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n let [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n const sockJson = readOrDefaultSocketJson(cwd)\n\n if (all === undefined) {\n if (sockJson.defaults?.scan?.github?.all !== undefined) {\n all = sockJson.defaults?.scan?.github?.all\n } else {\n all = false\n }\n }\n if (!githubApiUrl) {\n if (sockJson.defaults?.scan?.github?.githubApiUrl !== undefined) {\n githubApiUrl = sockJson.defaults.scan.github.githubApiUrl\n } else {\n githubApiUrl = DEFAULT_GITHUB_URL\n }\n }\n if (!orgGithub) {\n if (sockJson.defaults?.scan?.github?.orgGithub !== undefined) {\n orgGithub = sockJson.defaults.scan.github.orgGithub\n } else {\n // Default to Socket org slug. Often that's fine. Vanity and all that.\n orgGithub = orgSlug\n }\n }\n if (!all && !repos) {\n if (sockJson.defaults?.scan?.github?.repos !== undefined) {\n repos = sockJson.defaults.scan.github.repos\n } else {\n repos = ''\n }\n }\n\n // We will also be needing that GitHub token.\n const hasGithubApiToken = !!githubToken\n\n // We're going to need an api token to suggest data because those suggestions\n // must come from data we already know. Don't error on missing api token yet.\n // If the api-token is not set, ignore it for the sake of suggestions.\n const hasSocketApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n // If the current cwd is unknown and is used as a repo slug anyways, we will\n // first need to register the slug before we can use it.\n // Only do suggestions with an apiToken and when not in dryRun mode\n if (hasSocketApiToken && !dryRun && interactive) {\n if (!orgSlug) {\n const suggestion = await suggestOrgSlug()\n if (suggestion === undefined) {\n await outputScanGithub(\n {\n ok: false,\n message: 'Canceled by user',\n cause: 'Org selector was canceled by user',\n },\n outputKind,\n )\n return\n }\n if (suggestion) {\n orgSlug = suggestion\n }\n }\n }\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasSocketApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n {\n test: hasGithubApiToken,\n message: 'This command requires a GitHub API token for access',\n fail: 'missing',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n // Note exiting earlier to skirt a hidden auth requirement\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleCreateGithubScan({\n all: Boolean(all),\n githubApiUrl,\n githubToken,\n interactive: Boolean(interactive),\n orgSlug,\n orgGithub,\n outputKind,\n repos,\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchOrgFullScanListConfig = {\n branch: string\n direction: string\n from_time: string\n orgSlug: string\n page: number\n perPage: number\n repo: string\n sort: string\n}\n\nexport type FetchOrgFullScanListOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchOrgFullScanList(\n config: FetchOrgFullScanListConfig,\n options?: FetchOrgFullScanListOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgFullScanList'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchOrgFullScanListOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n const { branch, direction, from_time, orgSlug, page, perPage, repo, sort } = {\n __proto__: null,\n ...config,\n } as FetchOrgFullScanListConfig\n\n return await handleApiCall(\n sockSdk.getOrgFullScanList(orgSlug, {\n ...(branch ? { branch } : {}),\n ...(repo ? { repo } : {}),\n sort,\n direction,\n from: from_time,\n page: String(page),\n per_page: String(perPage),\n }),\n { desc: 'list of scans' },\n )\n}\n","// @ts-ignore\nimport chalkTable from 'chalk-table'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputListScans(\n result: CResult<SocketSdkSuccessResult<'getOrgFullScanList'>['data']>,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n const options = {\n columns: [\n { field: 'id', name: colors.magenta('ID') },\n { field: 'report_url', name: colors.magenta('Scan URL') },\n { field: 'repo', name: colors.magenta('Repo') },\n { field: 'branch', name: colors.magenta('Branch') },\n { field: 'created_at', name: colors.magenta('Created at') },\n ],\n }\n\n const formattedResults = result.data.results.map(d => {\n return {\n id: d.id,\n report_url: colors.underline(`${d.html_report_url}`),\n created_at: d.created_at\n ? new Date(d.created_at).toLocaleDateString('en-us', {\n year: 'numeric',\n month: 'numeric',\n day: 'numeric',\n })\n : '',\n repo: d.repo,\n branch: d.branch,\n }\n })\n\n logger.log(chalkTable(options, formattedResults))\n}\n","import { fetchOrgFullScanList } from './fetch-list-scans.mts'\nimport { outputListScans } from './output-list-scans.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleListScans({\n branch,\n direction,\n from_time,\n orgSlug,\n outputKind,\n page,\n perPage,\n repo,\n sort,\n}: {\n branch: string\n direction: string\n from_time: string\n orgSlug: string\n outputKind: OutputKind\n page: number\n perPage: number\n repo: string\n sort: string\n}): Promise<void> {\n const data = await fetchOrgFullScanList({\n branch,\n direction,\n from_time,\n orgSlug,\n page,\n perPage,\n repo,\n sort,\n })\n\n await outputListScans(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleListScans } from './handle-list-scans.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type {\n CliCommandConfig,\n CliSubcommand,\n} from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'list'\n\nconst description = 'List the scans for an organization'\n\nconst hidden = false\n\nexport const cmdScanList: CliSubcommand = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n branch: {\n type: 'string',\n description: 'Filter to show only scans with this branch name',\n },\n direction: {\n type: 'string',\n shortFlag: 'd',\n default: 'desc',\n description: 'Direction option (`desc` or `asc`) - Default is `desc`',\n },\n fromTime: {\n type: 'string',\n shortFlag: 'f',\n default: '',\n description: 'From time - as a unix timestamp',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n page: {\n type: 'number',\n shortFlag: 'p',\n default: 1,\n description: 'Page number - Default is 1',\n },\n perPage: {\n type: 'number',\n shortFlag: 'pp',\n default: 30,\n description: 'Results per page - Default is 30',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n sort: {\n type: 'string',\n shortFlag: 's',\n default: 'created_at',\n description:\n 'Sorting option (`name` or `created_at`) - default is `created_at`',\n },\n untilTime: {\n type: 'string',\n shortFlag: 'u',\n default: '',\n description: 'Until time - as a unix timestamp',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [REPO [BRANCH]]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Optionally filter by REPO. If you specify a repo, you can also specify a\n branch to filter by. (Note: If you don't specify a repo then you must use\n \\`--branch\\` to filter by branch across all repos).\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command}\n $ ${command} webtools badbranch --markdown\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { branch: branchFlag, json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const noLegacy = !cli.flags['repo']\n\n const [repo = '', branchArg = ''] = cli.input\n\n const branch = String(branchFlag || branchArg || '')\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: noLegacy,\n message: 'Legacy flags are no longer supported. See v1 migration guide.',\n fail: `received legacy flags`,\n },\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'dot is an invalid org, most likely you forgot the org name here?',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n {\n nook: true,\n test: !branchFlag || !branchArg,\n message:\n 'You should not set --branch and also give a second arg for branch name',\n fail: 'received flag and second arg',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleListScans({\n branch: branch ? String(branch) : '',\n direction: String(cli.flags['direction'] || ''),\n from_time: String(cli.flags['fromTime'] || ''),\n orgSlug,\n outputKind,\n page: Number(cli.flags['page'] || 1),\n perPage: Number(cli.flags['perPage'] || 30),\n repo: repo ? String(repo) : '',\n sort: String(cli.flags['sort'] || ''),\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 { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchScanMetadataOptions = {\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function fetchScanMetadata(\n orgSlug: string,\n scanId: string,\n options?: FetchScanMetadataOptions | undefined,\n): Promise<CResult<SocketSdkSuccessResult<'getOrgFullScanMetadata'>['data']>> {\n const { sdkOpts } = {\n __proto__: null,\n ...options,\n } as FetchScanMetadataOptions\n\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n return await handleApiCall(sockSdk.getOrgFullScanMetadata(orgSlug, scanId), {\n desc: 'meta data for a full scan',\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport async function outputScanMetadata(\n result: CResult<SocketSdkSuccessResult<'getOrgFullScanMetadata'>['data']>,\n scanId: string,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (outputKind === 'markdown') {\n logger.log('# Scan meta data\\n')\n }\n logger.log(`Scan ID: ${scanId}\\n`)\n for (const [key, value] of Object.entries(result.data)) {\n if (\n [\n 'id',\n 'updated_at',\n 'organization_id',\n 'repository_id',\n 'commit_hash',\n 'html_report_url',\n ].includes(key)\n ) {\n continue\n }\n logger.log(`- ${key}:`, value)\n }\n if (outputKind === 'markdown') {\n logger.log(\n `\\nYou can view this report at: [${result.data.html_report_url}](${result.data.html_report_url})\\n`,\n )\n } else {\n logger.log(\n `\\nYou can view this report at: ${result.data.html_report_url}]\\n`,\n )\n }\n}\n","import { fetchScanMetadata } from './fetch-scan-metadata.mts'\nimport { outputScanMetadata } from './output-scan-metadata.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleOrgScanMetadata(\n orgSlug: string,\n scanId: string,\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchScanMetadata(orgSlug, scanId)\n\n await outputScanMetadata(data, scanId, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleOrgScanMetadata } from './handle-scan-metadata.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type {\n CliCommandConfig,\n CliSubcommand,\n} from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'metadata'\n\nconst description = \"Get a scan's metadata\"\n\nconst hidden = false\n\nexport const cmdScanMetadata: CliSubcommand = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <SCAN_ID>\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0 --json\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const [scanId = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail:\n orgSlug === '.'\n ? 'dot is an invalid org, most likely you forgot the org name here?'\n : 'missing',\n },\n {\n test: !!scanId,\n message: 'Scan ID to inspect as argument',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleOrgScanMetadata(orgSlug, scanId, outputKind)\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { ReachabilityAnalysisResult } from './perform-reachability-analysis.mts'\nimport type { CResult, OutputKind } from '../../types.mts'\n\nexport async function outputScanReach(\n result: CResult<ReachabilityAnalysisResult>,\n { cwd, outputKind }: { cwd: string; outputKind: OutputKind },\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log('')\n logger.success('Reachability analysis completed successfully!')\n logger.info(\n `Reachability report has been written to: ${path.join(cwd, constants.DOT_SOCKET_DOT_FACTS_JSON)}`,\n )\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { fetchSupportedScanFileNames } from './fetch-supported-scan-file-names.mts'\nimport { outputScanReach } from './output-scan-reach.mts'\nimport { performReachabilityAnalysis } from './perform-reachability-analysis.mts'\nimport constants from '../../constants.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getPackageFilesForScan } from '../../utils/path-resolve.mts'\n\nimport type { ReachabilityOptions } from './perform-reachability-analysis.mts'\nimport type { OutputKind } from '../../types.mts'\n\nexport type HandleScanReachConfig = {\n cwd: string\n interactive: boolean\n orgSlug: string\n outputKind: OutputKind\n reachabilityOptions: ReachabilityOptions\n targets: string[]\n}\n\nexport async function handleScanReach({\n cwd,\n interactive: _interactive,\n orgSlug,\n outputKind,\n reachabilityOptions,\n targets,\n}: HandleScanReachConfig) {\n const { spinner } = constants\n\n // Get supported file names\n const supportedFilesCResult = await fetchSupportedScanFileNames({ spinner })\n if (!supportedFilesCResult.ok) {\n await outputScanReach(supportedFilesCResult, { cwd, outputKind })\n return\n }\n\n spinner.start(\n 'Searching for local manifest files to include in reachability analysis...',\n )\n\n const supportedFiles = supportedFilesCResult.data\n const packagePaths = await getPackageFilesForScan(targets, supportedFiles, {\n cwd,\n })\n\n spinner.successAndStop(\n `Found ${packagePaths.length} ${pluralize('manifest file', packagePaths.length)} for reachability analysis.`,\n )\n\n const wasValidInput = checkCommandInput(outputKind, {\n nook: true,\n test: packagePaths.length > 0,\n fail: 'found no eligible files to analyze',\n message:\n 'TARGET (file/dir) must contain matching / supported file types for reachability analysis',\n })\n if (!wasValidInput) {\n return\n }\n\n logger.success(\n `Found ${packagePaths.length} local ${pluralize('file', packagePaths.length)}`,\n )\n\n spinner.start('Running reachability analysis...')\n\n const result = await performReachabilityAnalysis({\n cwd,\n orgSlug,\n packagePaths,\n reachabilityOptions,\n spinner,\n uploadManifests: true,\n })\n\n spinner.stop()\n\n await outputScanReach(result, { cwd, outputKind })\n}\n","import path from 'node:path'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleScanReach } from './handle-scan-reach.mts'\nimport { reachabilityFlags } from './reachability-flags.mts'\nimport { suggestTarget } from './suggest_target.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { cmdFlagValueToArray } from '../../utils/cmd.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getEcosystemChoicesForMeow } from '../../utils/ecosystem.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { MeowFlags } from '../../flags.mts'\nimport type { PURL_Type } from '../../utils/ecosystem.mts'\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'reach'\n\nconst description = 'Compute tier 1 reachability'\n\nconst hidden = true\n\nconst generalFlags: MeowFlags = {\n ...commonFlags,\n ...outputFlags,\n cwd: {\n type: 'string',\n description: 'working directory, defaults to process.cwd()',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n}\n\nexport const cmdScanReach = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...generalFlags,\n ...reachabilityFlags,\n },\n help: command =>\n `\n Usage\n $ ${command} [options] [CWD=.]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(generalFlags)}\n\n Reachability Options\n ${getFlagListOutput(reachabilityFlags)}\n\n Runs the Socket reachability analysis without creating a scan in Socket.\n The output is written to .socket.facts.json in the current working directory.\n\n Note: Manifest files are uploaded to Socket's backend services because the\n reachability analysis requires creating a Software Bill of Materials (SBOM)\n from these files before the analysis can run.\n\n Examples\n $ ${command}\n $ ${command} ./proj\n $ ${command} ./proj --reach-ecosystems npm,pypi\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const {\n cwd: cwdOverride,\n interactive = true,\n json,\n markdown,\n org: orgFlag,\n reachAnalysisMemoryLimit,\n reachAnalysisTimeout,\n reachDisableAnalytics,\n reachSkipCache,\n } = cli.flags as {\n cwd: string\n interactive: boolean\n json: boolean\n markdown: boolean\n org: string\n reachAnalysisTimeout: number\n reachAnalysisMemoryLimit: number\n reachDisableAnalytics: boolean\n reachSkipCache: boolean\n }\n\n const dryRun = !!cli.flags['dryRun']\n\n // Process comma-separated values for isMultiple flags.\n const reachEcosystemsRaw = cmdFlagValueToArray(cli.flags['reachEcosystems'])\n const reachExcludePaths = cmdFlagValueToArray(cli.flags['reachExcludePaths'])\n\n // Validate ecosystem values.\n const reachEcosystems: PURL_Type[] = []\n const validEcosystems = getEcosystemChoicesForMeow()\n for (const ecosystem of reachEcosystemsRaw) {\n if (!validEcosystems.includes(ecosystem)) {\n throw new Error(\n `Invalid ecosystem: \"${ecosystem}\". Valid values are: ${joinAnd(validEcosystems)}`,\n )\n }\n reachEcosystems.push(ecosystem as PURL_Type)\n }\n\n const processCwd = process.cwd()\n const cwd =\n cwdOverride && cwdOverride !== processCwd\n ? path.resolve(processCwd, String(cwdOverride))\n : processCwd\n\n // Accept zero or more paths. Default to cwd() if none given.\n let targets = cli.input || [cwd]\n\n // Use suggestTarget if no targets specified and in interactive mode\n if (!targets.length && !dryRun && interactive) {\n targets = await suggestTarget()\n }\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires an API token for access',\n fail: 'try `socket login`',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleScanReach({\n cwd,\n orgSlug,\n outputKind,\n targets,\n interactive,\n reachabilityOptions: {\n reachAnalysisTimeout: Number(reachAnalysisTimeout),\n reachAnalysisMemoryLimit: Number(reachAnalysisMemoryLimit),\n reachDisableAnalytics: Boolean(reachDisableAnalytics),\n reachEcosystems,\n reachExcludePaths,\n reachSkipCache: Boolean(reachSkipCache),\n },\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleScanReport } from './handle-scan-report.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { FOLD_SETTING, REPORT_LEVEL } from './types.mts'\nimport type {\n CliCommandConfig,\n CliSubcommand,\n} from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'report'\n\nconst description =\n 'Check whether a scan result passes the organizational policies (security, license)'\n\nconst hidden = false\n\nexport const cmdScanReport: CliSubcommand = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n fold: {\n type: 'string',\n default: constants.FOLD_SETTING_NONE,\n description: `Fold reported alerts to some degree (default '${constants.FOLD_SETTING_NONE}')`,\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n reportLevel: {\n type: 'string',\n default: constants.REPORT_LEVEL_WARN,\n description: `Which policy level alerts should be reported (default '${constants.REPORT_LEVEL_WARN}')`,\n },\n short: {\n type: 'boolean',\n default: false,\n description: 'Report only the healthy status',\n },\n license: {\n type: 'boolean',\n default: false,\n description: 'Also report the license policy status. Default: false',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <SCAN_ID> [OUTPUT_PATH]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n Options\n ${getFlagListOutput(config.flags)}\n\n When no output path is given the contents is sent to stdout.\n\n By default the result is a nested object that looks like this:\n \\`{\n [ecosystem]: {\n [pkgName]: {\n [version]: {\n [file]: {\n [line:col]: alert\n }}}}\\`\n So one alert for each occurrence in every file, version, etc, a huge response.\n\n You can --fold these up to given level: 'pkg', 'version', 'file', and 'none'.\n For example: \\`socket scan report --fold=version\\` will dedupe alerts to only\n show one alert of a particular kind, no matter how often it was found in a\n file or in how many files it was found. At most one per version that has it.\n\n By default only the warn and error policy level alerts are reported. You can\n override this and request more ('defer' < 'ignore' < 'monitor' < 'warn' < 'error')\n\n Short responses look like this:\n --json: \\`{healthy:bool}\\`\n --markdown: \\`healthy = bool\\`\n neither: \\`OK/ERR\\`\n\n Examples\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0 --json --fold=version\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0 --license --markdown --short\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const fold = cli.flags['fold'] as FOLD_SETTING\n\n const interactive = !!cli.flags['interactive']\n\n const includeLicensePolicy = !!cli.flags['license']\n\n const reportLevel = cli.flags['reportLevel'] as REPORT_LEVEL\n\n const short = !!cli.flags['short']\n\n const [scanId = '', filepath = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'dot is an invalid org, most likely you forgot the org name here?',\n },\n {\n test: !!scanId,\n message: 'Scan ID to report on',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleScanReport({\n orgSlug,\n scanId,\n includeLicensePolicy,\n outputKind,\n filepath,\n fold,\n short,\n reportLevel,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport async function outputScanConfigResult(result: CResult<unknown>) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log('')\n logger.log('Finished')\n logger.log('')\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { input, select } from '@socketsecurity/registry/lib/prompts'\n\nimport constants from '../../constants.mts'\nimport {\n detectDefaultBranch,\n getRepoName,\n gitBranch,\n} from '../../utils/git.mts'\nimport {\n readSocketJsonSync,\n writeSocketJson,\n} from '../../utils/socket-json.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { SocketJson } from '../../utils/socket-json.mts'\n\nexport async function setupScanConfig(\n cwd: string,\n defaultOnReadError = false,\n): Promise<CResult<unknown>> {\n const jsonPath = path.join(cwd, `socket.json`)\n if (fs.existsSync(jsonPath)) {\n logger.info(`Found socket.json at ${jsonPath}`)\n } else {\n logger.info(`No socket.json found at ${cwd}, will generate a new one`)\n }\n\n logger.log('')\n logger.log(\n 'Note: This tool will set up flag and argument defaults for certain',\n )\n logger.log(' CLI commands. You can still override them by explicitly')\n logger.log(' setting the flag. It is meant to be a convenience tool.')\n logger.log('')\n logger.log(\n 'This command will generate a `socket.json` file in the target cwd.',\n )\n logger.log('You can choose to add this file to your repo (handy for collab)')\n logger.log('or to add it to the ignored files, or neither. This file is only')\n logger.log('used in CLI workflows.')\n logger.log('')\n logger.log('Note: For details on a flag you can run `socket <cmd> --help`')\n logger.log('')\n\n const sockJsonCResult = readSocketJsonSync(cwd, defaultOnReadError)\n if (!sockJsonCResult.ok) {\n return sockJsonCResult\n }\n\n const sockJson = sockJsonCResult.data\n if (!sockJson.defaults) {\n sockJson.defaults = {}\n }\n if (!sockJson.defaults.scan) {\n sockJson.defaults.scan = {}\n }\n\n const targetCommand = await select({\n message: 'Which scan command do you want to configure?',\n choices: [\n {\n name: 'socket scan create',\n value: 'create',\n },\n {\n name: 'socket scan github',\n value: 'github',\n },\n {\n name: '(cancel)',\n value: '',\n description: 'Exit configurator, make no changes',\n },\n ],\n })\n switch (targetCommand) {\n case 'create': {\n if (!sockJson.defaults.scan.create) {\n sockJson.defaults.scan.create = {}\n }\n const result = await configureScan(sockJson.defaults.scan.create, cwd)\n if (!result.ok || result.data.canceled) {\n return result\n }\n break\n }\n case 'github': {\n if (!sockJson.defaults.scan.github) {\n sockJson.defaults.scan.github = {}\n }\n const result = await configureGithub(sockJson.defaults.scan.github)\n if (!result.ok || result.data.canceled) {\n return result\n }\n break\n }\n default: {\n return canceledByUser()\n }\n }\n\n logger.log('')\n logger.log('Setup complete. Writing socket.json')\n logger.log('')\n\n if (\n await select({\n message: `Do you want to write the new config to ${jsonPath} ?`,\n choices: [\n {\n name: 'yes',\n value: true,\n description: 'Update config',\n },\n {\n name: 'no',\n value: false,\n description: 'Do not update the config',\n },\n ],\n })\n ) {\n return await writeSocketJson(cwd, sockJson)\n }\n\n return canceledByUser()\n}\n\nasync function configureScan(\n config: NonNullable<\n NonNullable<NonNullable<SocketJson['defaults']>['scan']>['create']\n >,\n cwd = process.cwd(),\n): Promise<CResult<{ canceled: boolean }>> {\n const defaultRepoName = await input({\n message:\n '(--repo) What repo name (slug) should be reported to Socket for this dir?',\n default: config.repo || (await getRepoName(cwd)),\n required: false,\n // validate: async string => bool\n })\n if (defaultRepoName === undefined) {\n return canceledByUser()\n }\n if (defaultRepoName) {\n // Store it even if it's constants.SOCKET_DEFAULT_REPOSITORY because if we\n // change this default then an existing user probably would not expect the change.\n config.repo = defaultRepoName\n } else {\n delete config.repo\n }\n\n const defaultBranchName = await input({\n message:\n '(--branch) What branch name (slug) should be reported to Socket for this dir?',\n default:\n config.branch ||\n (await gitBranch(cwd)) ||\n (await detectDefaultBranch(cwd)),\n required: false,\n // validate: async string => bool\n })\n if (defaultBranchName === undefined) {\n return canceledByUser()\n }\n if (defaultBranchName) {\n // Store it even if it's constants.SOCKET_DEFAULT_BRANCH because if we change\n // this default then an existing user probably would not expect the change.\n config.branch = defaultBranchName\n } else {\n delete config.branch\n }\n\n const autoManifest = await select({\n message:\n '(--auto-manifest) Do you want to run `socket manifest auto` before creating a scan? You would need this for sbt, gradle, etc.',\n choices: [\n {\n name: 'no',\n value: 'no',\n description: 'Do not generate local manifest files',\n },\n {\n name: 'yes',\n value: 'yes',\n description:\n 'Locally generate manifest files for languages like gradle, sbt, and conda (see `socket manifest auto`), before creating a scan',\n },\n {\n name: '(leave default)',\n value: '',\n description: 'Do not store a setting for this',\n },\n ],\n default:\n config.autoManifest === true\n ? 'yes'\n : config.autoManifest === false\n ? 'no'\n : '',\n })\n if (autoManifest === undefined) {\n return canceledByUser()\n }\n if (autoManifest === 'yes') {\n config.autoManifest = true\n } else if (autoManifest === 'no') {\n config.autoManifest = false\n } else {\n delete config.autoManifest\n }\n\n const alwaysReport = await select({\n message: '(--report) Do you want to enable --report by default?',\n choices: [\n {\n name: 'no',\n value: 'no',\n description: 'Do not wait for Scan result and report by default',\n },\n {\n name: 'yes',\n value: 'yes',\n description:\n 'After submitting a Scan request, wait for scan to complete, then show a report (like --report would)',\n },\n {\n name: '(leave default)',\n value: '',\n description: 'Do not store a setting for this',\n },\n ],\n default:\n config.report === true ? 'yes' : config.report === false ? 'no' : '',\n })\n if (alwaysReport === undefined) {\n return canceledByUser()\n }\n if (alwaysReport === 'yes') {\n config.report = true\n } else if (alwaysReport === 'no') {\n config.report = false\n } else {\n delete config.report\n }\n\n return notCanceled()\n}\n\nasync function configureGithub(\n config: NonNullable<\n NonNullable<NonNullable<SocketJson['defaults']>['scan']>['github']\n >,\n): Promise<CResult<{ canceled: boolean }>> {\n // Do not store the GitHub API token. Just leads to a security rabbit hole.\n\n const all = await select({\n message:\n '(--all) Do you by default want to fetch all repos from the GitHub API and scan all known repos?',\n choices: [\n {\n name: 'no',\n value: 'no',\n description: 'Fetch repos if not given and ask which repo to run on',\n },\n {\n name: 'yes',\n value: 'yes',\n description: 'Run on all remote repos by default',\n },\n {\n name: '(leave default)',\n value: '',\n description: 'Do not store a setting for this',\n },\n ],\n default: config.all === true ? 'yes' : config.all === false ? 'no' : '',\n })\n if (all === undefined) {\n return canceledByUser()\n }\n if (all === 'yes') {\n config.all = true\n } else if (all === 'no') {\n config.all = false\n } else {\n delete config.all\n }\n\n if (!all) {\n const defaultRepos = await input({\n message:\n '(--repos) Please enter the default repos to run this on, leave empty (backspace) to fetch from GitHub and ask interactive',\n default: config.repos,\n required: false,\n // validate: async string => bool\n })\n if (defaultRepos === undefined) {\n return canceledByUser()\n }\n if (defaultRepos) {\n config.repos = defaultRepos\n } else {\n delete config.repos\n }\n }\n\n const defaultGithubApiUrl = await input({\n message:\n '(--github-api-url) Do you want to override the default github url?',\n\n default: config.githubApiUrl || constants.ENV.GITHUB_API_URL,\n required: false,\n // validate: async string => bool\n })\n if (defaultGithubApiUrl === undefined) {\n return canceledByUser()\n }\n if (\n defaultGithubApiUrl &&\n defaultGithubApiUrl !== constants.ENV.GITHUB_API_URL\n ) {\n config.githubApiUrl = defaultGithubApiUrl\n } else {\n delete config.githubApiUrl\n }\n\n const defaultOrgGithub = await input({\n message:\n '(--org-github) Do you want to change the org slug that is used when talking to the GitHub API? Defaults to your Socket org slug.',\n default: config.orgGithub || '',\n required: false,\n // validate: async string => bool\n })\n if (defaultOrgGithub === undefined) {\n return canceledByUser()\n }\n if (defaultOrgGithub) {\n config.orgGithub = defaultOrgGithub\n } else {\n delete config.orgGithub\n }\n\n return notCanceled()\n}\n\nfunction canceledByUser(): CResult<{ canceled: boolean }> {\n logger.log('')\n logger.info('User canceled')\n logger.log('')\n return { ok: true, data: { canceled: true } }\n}\n\nfunction notCanceled(): CResult<{ canceled: boolean }> {\n return { ok: true, data: { canceled: false } }\n}\n","import { outputScanConfigResult } from './output-scan-config-result.mts'\nimport { setupScanConfig } from './setup-scan-config.mts'\n\nexport async function handleScanConfig(\n cwd: string,\n defaultOnReadError = false,\n) {\n const result = await setupScanConfig(cwd, defaultOnReadError)\n\n await outputScanConfigResult(result)\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleScanConfig } from './handle-scan-config.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'setup',\n description:\n 'Start interactive configurator to customize default flag values for `socket scan` in this dir',\n hidden: false,\n flags: {\n ...commonFlags,\n defaultOnReadError: {\n type: 'boolean',\n description:\n 'If reading the socket.json fails, just use a default config? Warning: This might override the existing json file!',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [CWD=.]\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Interactive configurator to create a local json file in the target directory\n that helps to set flag defaults for \\`socket scan create\\`.\n\n This helps to configure the (Socket reported) repo and branch names, as well\n as which branch name is the \"default branch\" (main, master, etc). This way\n you don't have to specify these flags when creating a scan in this dir.\n\n This generated configuration file will only be used locally by the CLI. You\n can commit it to the repo (useful for collaboration) or choose to add it to\n your .gitignore all the same. Only this CLI will use it.\n\n Examples\n\n $ ${command}\n $ ${command} ./proj\n `,\n}\n\nexport const cmdScanSetup = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n const { defaultOnReadError = false } = cli.flags\n\n let [cwd = '.'] = cli.input\n // Note: path.resolve vs .join:\n // If given path is absolute then cwd should not affect it.\n cwd = path.resolve(process.cwd(), cwd)\n\n await handleScanConfig(cwd, Boolean(defaultOnReadError))\n}\n","import { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport { queryApiSafeText } from '../../utils/api.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { SocketArtifact } from '../../utils/alert/artifact.mts'\n\nexport async function fetchScan(\n orgSlug: string,\n scanId: string,\n): Promise<CResult<SocketArtifact[]>> {\n const result = await queryApiSafeText(\n `orgs/${orgSlug}/full-scans/${encodeURIComponent(scanId)}`,\n 'a scan',\n )\n\n if (!result.ok) {\n return result\n }\n\n const jsonsString = result.data\n\n // This is nd-json; each line is a json object\n const lines = jsonsString.split('\\n').filter(Boolean)\n let ok = true\n const data = lines.map(line => {\n try {\n return JSON.parse(line)\n } catch (e) {\n ok = false\n debugFn('error', 'caught: JSON.parse error')\n debugDir('inspect', { error: e, line })\n return null\n }\n }) as unknown as SocketArtifact[]\n\n if (ok) {\n return { ok: true, data }\n }\n\n return {\n ok: false,\n message: 'Invalid Socket API response',\n cause:\n 'The Socket API responded with at least one line that was not valid JSON. Please report if this persists.',\n }\n}\n","import fs from 'node:fs/promises'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mdTable } from '../../utils/markdown.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { SocketArtifact } from '../../utils/alert/artifact.mts'\n\nexport async function outputScanView(\n result: CResult<SocketArtifact[]>,\n orgSlug: string,\n scanId: string,\n filePath: string,\n outputKind: OutputKind,\n): Promise<void> {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (!result.ok) {\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (\n outputKind === 'json' ||\n (outputKind === 'text' && filePath && filePath.endsWith('.json'))\n ) {\n const json = serializeResultJson(result)\n\n if (filePath && filePath !== '-') {\n logger.info('Writing json results to', filePath)\n try {\n await fs.writeFile(filePath, json, 'utf8')\n logger.info(`Data successfully written to ${filePath}`)\n } catch (e) {\n process.exitCode = 1\n logger.fail('There was an error trying to write the markdown to disk')\n logger.error(e)\n logger.log(\n serializeResultJson({\n ok: false,\n message: 'File Write Failure',\n cause: 'Failed to write json to disk',\n }),\n )\n }\n return\n }\n\n logger.log(json)\n return\n }\n\n const display = result.data.map(art => {\n const author = Array.isArray(art.author)\n ? `${art.author[0]}${art.author.length > 1 ? ' et.al.' : ''}`\n : art.author\n return {\n type: art.type,\n name: art.name,\n version: art.version,\n author,\n score: JSON.stringify(art.score),\n }\n })\n\n const md = mdTable<any>(display, [\n 'type',\n 'version',\n 'name',\n 'author',\n 'score',\n ])\n\n const report =\n `\n# Scan Details\n\nThese are the artifacts and their scores found.\n\nScan ID: ${scanId}\n\n${md}\n\nView this report at: ${constants.SOCKET_WEBSITE_URL}/dashboard/org/${orgSlug}/sbom/${scanId}\n `.trim() + '\\n'\n\n if (filePath && filePath !== '-') {\n try {\n await fs.writeFile(filePath, report, 'utf8')\n logger.log(`Data successfully written to ${filePath}`)\n } catch (e) {\n process.exitCode = 1\n logger.fail('There was an error trying to write the markdown to disk')\n logger.error(e)\n }\n } else {\n logger.log(report)\n }\n}\n","import { fetchScan } from './fetch-scan.mts'\nimport { outputScanView } from './output-scan-view.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleScanView(\n orgSlug: string,\n scanId: string,\n filePath: string,\n outputKind: OutputKind,\n): Promise<void> {\n const data = await fetchScan(orgSlug, scanId)\n\n await outputScanView(data, orgSlug, scanId, filePath, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleApiCall } from '../../utils/api.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\n\nimport type { SetupSdkOptions } from '../../utils/sdk.mts'\n\nexport type StreamScanOptions = {\n file?: string | undefined\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport async function streamScan(\n orgSlug: string,\n scanId: string,\n options?: StreamScanOptions | undefined,\n) {\n const { file, sdkOpts } = {\n __proto__: null,\n ...options,\n } as StreamScanOptions\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n const sockSdk = sockSdkCResult.data\n\n logger.info('Requesting data from API...')\n\n // Note: this will write to stdout or target file. It's not a noop\n return await handleApiCall(\n sockSdk.getOrgFullScan(orgSlug, scanId, file === '-' ? undefined : file),\n { desc: 'a scan' },\n )\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleScanView } from './handle-scan-view.mts'\nimport { streamScan } from './stream-scan.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type {\n CliCommandConfig,\n CliSubcommand,\n} from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'view'\n\nconst description = 'View the raw results of a scan'\n\nconst hidden = false\n\nexport const cmdScanView: CliSubcommand = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n stream: {\n type: 'boolean',\n default: false,\n description:\n 'Only valid with --json. Streams the response as \"ndjson\" (chunks of valid json blobs).',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] <SCAN_ID> [OUTPUT_FILE]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n\n When no output path is given the contents is sent to stdout.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0\n $ ${command} 000aaaa1-0000-0a0a-00a0-00a0000000a0 ./stream.txt\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { json, markdown, org: orgFlag, stream } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n const [scanId = '', file = ''] = cli.input\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'dot is an invalid org, most likely you forgot the org name here?',\n },\n {\n test: !!scanId,\n message: 'Scan ID to view',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message:\n 'The `--json` and `--markdown` flags can not be used at the same time',\n fail: 'bad',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n {\n nook: true,\n test: !stream || !!json,\n message: 'You can only use --stream when using --json',\n fail: 'Either remove --stream or add --json',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n if (json && stream) {\n await streamScan(orgSlug, scanId, { file })\n } else {\n await handleScanView(orgSlug, scanId, file, outputKind)\n }\n}\n","import { cmdScanCreate } from './cmd-scan-create.mts'\nimport { cmdScanDel } from './cmd-scan-del.mts'\nimport { cmdScanDiff } from './cmd-scan-diff.mts'\nimport { cmdScanGithub } from './cmd-scan-github.mts'\nimport { cmdScanList } from './cmd-scan-list.mts'\nimport { cmdScanMetadata } from './cmd-scan-metadata.mts'\nimport { cmdScanReach } from './cmd-scan-reach.mts'\nimport { cmdScanReport } from './cmd-scan-report.mts'\nimport { cmdScanSetup } from './cmd-scan-setup.mts'\nimport { cmdScanView } from './cmd-scan-view.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Manage Socket scans'\n\nexport const cmdScan: CliSubcommand = {\n description,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n create: cmdScanCreate,\n del: cmdScanDel,\n diff: cmdScanDiff,\n github: cmdScanGithub,\n list: cmdScanList,\n metadata: cmdScanMetadata,\n reach: cmdScanReach,\n report: cmdScanReport,\n setup: cmdScanSetup,\n view: cmdScanView,\n },\n {\n aliases: {\n meta: {\n description: cmdScanMetadata.description,\n hidden: true,\n argv: ['metadata'],\n },\n reachability: {\n description: cmdScanReach.description,\n hidden: true,\n argv: ['reach'],\n },\n },\n argv,\n description,\n importMeta,\n name: `${parentName} scan`,\n },\n )\n },\n}\n","import { queryApiSafeJson } from '../../utils/api.mts'\n\nimport type { ThreadFeedResponse } from './types.mts'\nimport type { CResult } from '../../types.mts'\n\nexport async function fetchThreatFeed({\n direction,\n ecosystem,\n filter,\n orgSlug,\n page,\n perPage,\n pkg,\n version,\n}: {\n direction: string\n ecosystem: string\n filter: string\n orgSlug: string\n page: string\n perPage: number\n pkg: string\n version: string\n}): Promise<CResult<ThreadFeedResponse>> {\n const queryParams = new URLSearchParams([\n ['direction', direction],\n ['ecosystem', ecosystem],\n filter ? ['filter', filter] : ['', ''],\n ['page_cursor', page],\n ['per_page', String(perPage)],\n pkg ? ['name', pkg] : ['', ''],\n version ? ['version', version] : ['', ''],\n ])\n\n return await queryApiSafeJson(\n `orgs/${orgSlug}/threat-feed?${queryParams}`,\n 'the Threat Feed data',\n )\n}\n","import { createRequire } from 'node:module'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { msAtHome } from '../../utils/ms-at-home.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\n\nimport type { ThreadFeedResponse, ThreatResult } from './types.mts'\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { Widgets } from 'blessed'\n\nconst require = createRequire(import.meta.url)\n\nexport async function outputThreatFeed(\n result: CResult<ThreadFeedResponse>,\n outputKind: OutputKind,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n }\n\n if (outputKind === 'json') {\n logger.log(serializeResultJson(result))\n return\n }\n if (!result.ok) {\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n if (!result.data?.results?.length) {\n logger.warn('Did not receive any data to display...')\n return\n }\n\n const formattedOutput = formatResults(result.data.results)\n const descriptions = result.data.results.map(d => d.description)\n\n // Note: this temporarily takes over the terminal (just like `man` does).\n const ScreenWidget = /*@__PURE__*/ require('blessed/lib/widgets/screen.js')\n const screen: Widgets.Screen = new ScreenWidget({\n ...constants.blessedOptions,\n })\n // Register these keys first so you can always exit, even when it gets stuck\n // If we don't do this and the code crashes, the user must hard-kill the\n // node process just to exit it. That's very bad UX.\n // eslint-disable-next-line n/no-process-exit\n screen.key(['escape', 'q', 'C-c'], () => process.exit(0))\n\n const TableWidget = /*@__PURE__*/ require('blessed-contrib/lib/widget/table.js')\n const detailsBoxHeight = 20 // bottom N rows for details box\n const tipsBoxHeight = 1 // 1 row for tips box\n\n const table: any = new TableWidget({\n keys: 'true',\n fg: 'white',\n selectedFg: 'white',\n selectedBg: 'magenta',\n interactive: 'true',\n label: 'Threat feed',\n width: '100%',\n top: 0,\n bottom: detailsBoxHeight + tipsBoxHeight,\n border: {\n type: 'line',\n fg: 'cyan',\n },\n columnWidth: [10, 30, 20, 18, 15, 200],\n // TODO: The truncation doesn't seem to work too well yet but when we add\n // `pad` alignment fails, when we extend columnSpacing alignment fails.\n columnSpacing: 1,\n truncate: '_',\n })\n\n const BoxWidget = /*@__PURE__*/ require('blessed/lib/widgets/box.js')\n const tipsBox: Widgets.BoxElement = new BoxWidget({\n bottom: detailsBoxHeight, // sits just above the details box\n height: tipsBoxHeight,\n width: '100%',\n style: {\n fg: 'yellow',\n bg: 'black',\n },\n tags: true,\n content: '↑/↓: Move Enter: Select q/ESC: Quit',\n })\n const detailsBox: Widgets.BoxElement = new BoxWidget({\n bottom: 0,\n height: detailsBoxHeight,\n width: '100%',\n border: {\n type: 'line',\n fg: 'cyan',\n },\n label: 'Details',\n content:\n 'Use arrow keys to navigate. Press Enter to select a threat. Press q to exit.',\n style: {\n fg: 'white',\n },\n })\n\n table.setData({\n headers: [\n ' Ecosystem',\n ' Name',\n ' Version',\n ' Threat type',\n ' Detected at',\n ' Details',\n ],\n data: formattedOutput,\n })\n\n // Initialize details box with the first selection if available\n if (formattedOutput.length > 0) {\n const selectedRow = formattedOutput[0]\n if (selectedRow) {\n detailsBox.setContent(formatDetailBox(selectedRow, descriptions, 0))\n }\n }\n\n // allow control the table with the keyboard\n table.focus()\n\n // Stacking order: table (top), tipsBox (middle), detailsBox (bottom)\n screen.append(table)\n screen.append(tipsBox)\n screen.append(detailsBox)\n\n // Update details box when selection changes\n table.rows.on('select item', () => {\n const selectedIndex = table.rows.selected\n if (selectedIndex !== undefined && selectedIndex >= 0) {\n const selectedRow = formattedOutput[selectedIndex]\n if (selectedRow) {\n // Note: the spacing works around issues with the table; it refuses to pad!\n detailsBox.setContent(\n formatDetailBox(selectedRow, descriptions, selectedIndex),\n )\n screen.render()\n }\n }\n })\n\n screen.render()\n\n screen.key(['return'], () => {\n const selectedIndex = table.rows.selected\n screen.destroy()\n const selectedRow = formattedOutput[selectedIndex]\n logger.log('Last selection:\\n', selectedRow)\n })\n}\n\nfunction formatDetailBox(\n selectedRow: string[],\n descriptions: string[],\n selectedIndex: number,\n): string {\n return (\n `Ecosystem: ${selectedRow[0]?.trim()}\\n` +\n `Name: ${selectedRow[1]?.trim()}\\n` +\n `Version: ${selectedRow[2]?.trim()}\\n` +\n `Threat type: ${selectedRow[3]?.trim()}\\n` +\n `Detected at: ${selectedRow[4]?.trim()}\\n` +\n `Details: ${selectedRow[5]?.trim()}\\n` +\n `Description: ${descriptions[selectedIndex]?.trim()}`\n )\n}\n\nfunction formatResults(data: ThreatResult[]) {\n return data.map(d => {\n const ecosystem = d.purl.split('pkg:')[1]!.split('/')[0]!\n const name = d.purl.split('/')[1]!.split('@')[0]!\n const version = d.purl.split('@')[1]!\n\n const timeDiff = msAtHome(d.createdAt)\n\n // Note: the spacing works around issues with the table; it refuses to pad!\n return [\n ecosystem,\n decodeURIComponent(name),\n ` ${version}`,\n ` ${d.threatType}`,\n ` ${timeDiff}`,\n d.locationHtmlUrl,\n ]\n })\n}\n","import { fetchThreatFeed } from './fetch-threat-feed.mts'\nimport { outputThreatFeed } from './output-threat-feed.mts'\n\nimport type { OutputKind } from '../../types.mts'\n\nexport async function handleThreatFeed({\n direction,\n ecosystem,\n filter,\n orgSlug,\n outputKind,\n page,\n perPage,\n pkg,\n version,\n}: {\n direction: string\n ecosystem: string\n filter: string\n outputKind: OutputKind\n orgSlug: string\n page: string\n perPage: number\n pkg: string\n version: string\n}): Promise<void> {\n const data = await fetchThreatFeed({\n direction,\n ecosystem,\n filter,\n orgSlug,\n page,\n perPage,\n pkg,\n version,\n })\n\n await outputThreatFeed(data, outputKind)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleThreatFeed } from './handle-threat-feed.mts'\nimport constants, { NPM } from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { determineOrgSlug } from '../../utils/determine-org-slug.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport {\n getFlagApiRequirementsOutput,\n getFlagListOutput,\n} from '../../utils/output-formatting.mts'\nimport { hasDefaultApiToken } from '../../utils/sdk.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nexport const CMD_NAME = 'threat-feed'\n\nconst ECOSYSTEMS = new Set(['gem', 'golang', 'maven', NPM, 'nuget', 'pypi'])\n\nconst TYPE_FILTERS = new Set([\n 'anom',\n 'c',\n 'fp',\n 'joke',\n 'mal',\n 'secret',\n 'spy',\n 'tp',\n 'typo',\n 'u',\n 'vuln',\n])\n\nconst description = '[Beta] View the threat-feed'\n\nconst hidden = false\n\nexport const cmdThreatFeed = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const config: CliCommandConfig = {\n commandName: CMD_NAME,\n description,\n hidden,\n flags: {\n ...commonFlags,\n ...outputFlags,\n direction: {\n type: 'string',\n default: 'desc',\n description: 'Order asc or desc by the createdAt attribute',\n },\n eco: {\n type: 'string',\n default: '',\n description: 'Only show threats for a particular ecosystem',\n },\n filter: {\n type: 'string',\n default: 'mal',\n description: 'Filter what type of threats to return',\n },\n interactive: {\n type: 'boolean',\n default: true,\n description:\n 'Allow for interactive elements, asking for input. Use --no-interactive to prevent any input questions, defaulting them to cancel/no.',\n },\n org: {\n type: 'string',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n page: {\n type: 'string',\n default: '1',\n description: 'Page token',\n },\n perPage: {\n type: 'number',\n shortFlag: 'pp',\n default: 30,\n description: 'Number of items per page',\n },\n pkg: {\n type: 'string',\n default: '',\n description: 'Filter by this package name',\n },\n version: {\n type: 'string',\n default: '',\n description: 'Filter by this package version',\n },\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [ECOSYSTEM] [TYPE_FILTER]\n\n API Token Requirements\n ${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}\n - Special access\n\n This feature requires a Threat Feed license. Please contact\n sales@socket.dev if you are interested in purchasing this access.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Valid ecosystems:\n\n - gem\n - golang\n - maven\n - npm\n - nuget\n - pypi\n\n Valid type filters:\n\n - anom Anomaly\n - c Do not filter\n - fp False Positives\n - joke Joke / Fake\n - mal Malware and Possible Malware [default]\n - secret Secrets\n - spy Telemetry\n - tp False Positives and Unreviewed\n - typo Typo-squat\n - u Unreviewed\n - vuln Vulnerability\n\n Note: if you filter by package name or version, it will do so for anything\n unless you also filter by that ecosystem and/or package name. When in\n doubt, look at the threat-feed and see the names in the name/version\n column. That's what you want to search for.\n\n You can put filters as args instead, we'll try to match the strings with the\n correct filter type but since this would not allow you to search for a package\n called \"mal\", you can also specify the filters through flags.\n\n First arg that matches a typo, eco, or version enum is used as such. First arg\n that matches none of them becomes the package name filter. Rest is ignored.\n\n Note: The version filter is a prefix search, pkg name is a substring search.\n\n Examples\n $ ${command}\n $ ${command} maven --json\n $ ${command} typo\n $ ${command} npm joke 1.0.0 --per-page=5 --page=2 --direction=asc\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const {\n eco,\n json,\n markdown,\n org: orgFlag,\n pkg,\n type: typef,\n version,\n } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n const interactive = !!cli.flags['interactive']\n\n let ecoFilter = String(eco || '')\n let versionFilter = String(version || '')\n let typeFilter = String(typef || '')\n let nameFilter = String(pkg || '')\n\n const argSet = new Set(cli.input)\n cli.input.some(str => {\n if (ECOSYSTEMS.has(str)) {\n ecoFilter = str\n argSet.delete(str)\n return true\n }\n })\n\n cli.input.some(str => {\n if (/^v?\\d+\\.\\d+\\.\\d+$/.test(str)) {\n versionFilter = str\n argSet.delete(str)\n return true\n }\n })\n\n cli.input.some(str => {\n if (TYPE_FILTERS.has(str)) {\n typeFilter = str\n argSet.delete(str)\n return true\n }\n })\n\n const haves = new Set([ecoFilter, versionFilter, typeFilter])\n cli.input.some(str => {\n if (!haves.has(str)) {\n nameFilter = str\n argSet.delete(str)\n return true\n }\n })\n\n if (argSet.size) {\n logger.info(\n `Warning: ignoring these excessive args: ${Array.from(argSet).join(', ')}`,\n )\n }\n\n const hasApiToken = hasDefaultApiToken()\n\n const [orgSlug] = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n nook: true,\n test: !!orgSlug,\n message: 'Org name by default setting, --org, or auto-discovered',\n fail: 'missing',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: 'The json and markdown flags cannot be both set, pick one',\n fail: 'omit one',\n },\n {\n nook: true,\n test: hasApiToken,\n message: 'This command requires a Socket API token for access',\n fail: 'try `socket login`',\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n await handleThreatFeed({\n direction: String(cli.flags['direction'] || 'desc'),\n ecosystem: ecoFilter,\n filter: typeFilter,\n outputKind,\n orgSlug,\n page: String(cli.flags['page'] || '1'),\n perPage: Number(cli.flags['perPage']) || 30,\n pkg: nameFilter,\n version: versionFilter,\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport async function outputUninstallCompletion(\n result: CResult<{ action: string; left: string[] }>,\n targetName: string,\n) {\n if (!result.ok) {\n process.exitCode = result.code ?? 1\n\n logger.fail(failMsgWithBadge(result.message, result.cause))\n return\n }\n\n logger.log(result.message)\n logger.log('')\n logger.log(\n 'To remove the tab completion from the current shell (instance of bash) you',\n )\n logger.log(\n 'can run this command (due to a bash limitation NodeJS cannot do this):',\n )\n logger.log('')\n logger.log(` complete -r ${targetName}`)\n logger.log('')\n logger.log(\n 'Next time you open a terminal it should no longer be there, regardless.',\n )\n logger.log('')\n if (result.data.left.length) {\n logger.log(\n 'Detected more Socket Alias completions left in bashrc. Run `socket uninstall <cmd>` to remove them too.',\n )\n logger.log('')\n result.data.left.forEach(str => {\n logger.log(` - \\`${str}\\``)\n })\n logger.log('')\n }\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nimport constants from '../../constants.mts'\nimport {\n COMPLETION_CMD_PREFIX,\n getBashrcDetails,\n} from '../../utils/completion.mts'\n\nimport type { CResult } from '../../types.mts'\n\nexport async function teardownTabCompletion(\n targetName: string,\n): Promise<CResult<{ action: string; left: string[] }>> {\n const result = getBashrcDetails(targetName)\n if (!result.ok) {\n return result\n }\n\n const { completionCommand, sourcingCommand, toAddToBashrc } = result.data\n\n // Remove from ~/.bashrc if found\n const bashrc = constants.homePath\n ? path.join(constants.homePath, '.bashrc')\n : ''\n\n if (bashrc && fs.existsSync(bashrc)) {\n const content = fs.readFileSync(bashrc, 'utf8')\n\n if (content.includes(toAddToBashrc)) {\n const newContent = content\n // Try to remove the whole thing with comment first\n .replaceAll(toAddToBashrc, '')\n // Comment may have been edited away, try to remove the command at least\n .replaceAll(sourcingCommand, '')\n .replaceAll(completionCommand, '')\n\n fs.writeFileSync(bashrc, newContent, 'utf8')\n\n return {\n ok: true,\n data: {\n action: 'removed',\n left: findRemainingCompletionSetups(newContent),\n },\n message: 'Removed completion from ~/.bashrc',\n }\n } else {\n const left = findRemainingCompletionSetups(content)\n return {\n ok: true,\n data: {\n action: 'missing',\n left,\n },\n message: `Completion was not found in ~/.bashrc${left.length ? ' (you may need to manually edit your .bashrc to clean this up...)' : ''}`,\n }\n }\n } else {\n return {\n ok: true, // Eh. I think this makes most sense.\n data: { action: 'not found', left: [] },\n message: '~/.bashrc not found, skipping',\n }\n }\n}\n\nfunction findRemainingCompletionSetups(bashrc: string): string[] {\n return bashrc\n .split('\\n')\n .map(s => s.trim())\n .filter(s => s.startsWith(COMPLETION_CMD_PREFIX))\n .map(s => s.slice(COMPLETION_CMD_PREFIX.length).trim())\n}\n","import { outputUninstallCompletion } from './output-uninstall-completion.mts'\nimport { teardownTabCompletion } from './teardown-tab-completion.mts'\n\nexport async function handleUninstallCompletion(targetName: string) {\n const result = await teardownTabCompletion(targetName)\n await outputUninstallCompletion(result, targetName)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleUninstallCompletion } from './handle-uninstall-completion.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'completion',\n description: 'Uninstall bash completion for Socket CLI',\n hidden: false,\n flags: {\n ...commonFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} [options] [COMMAND_NAME=socket]\n\n Uninstalls bash tab completion for the Socket CLI. This will:\n 1. Remove tab completion from your current shell for given command\n 2. Remove the setup for given command from your ~/.bashrc\n\n The optional name is required if you installed tab completion for an alias\n other than the default \"socket\". This will NOT remove the command, only the\n tab completion that is registered for it in bash.\n\n Options\n ${getFlagListOutput(config.flags)}\n\n Examples\n\n $ ${command}\n $ ${command} sd\n `,\n}\n\nexport const cmdUninstallCompletion = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nexport async function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n const dryRun = !!cli.flags['dryRun']\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n const targetName = cli.input[0] || 'socket'\n\n await handleUninstallCompletion(String(targetName))\n}\n","import { cmdUninstallCompletion } from './cmd-uninstall-completion.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands.mts'\n\nconst description = 'Uninstall Socket CLI tab completion'\n\nexport const cmdUninstall: CliSubcommand = {\n description,\n hidden: false,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n completion: cmdUninstallCompletion,\n },\n {\n argv,\n description,\n importMeta,\n name: `${parentName} uninstall`,\n },\n )\n },\n}\n","import fs from 'node:fs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nexport function addSocketWrapper(file: string): void {\n return fs.appendFile(\n file,\n 'alias npm=\"socket npm\"\\nalias npx=\"socket npx\"\\n',\n err => {\n if (err) {\n return new Error(`There was an error setting up the alias: ${err}`)\n }\n logger.success(\n `The alias was added to ${file}. Running 'npm install' will now be wrapped in Socket's \"safe npm\" 🎉`,\n )\n logger.log(\n ` If you want to disable it at any time, run \\`socket wrapper --disable\\``,\n )\n logger.log('')\n logger.info(\n `This will only be active in new terminal sessions going forward.`,\n )\n logger.log(\n ` You will need to restart your terminal or run this command to activate the alias in the current session:`,\n )\n logger.log('')\n logger.log(` source ${file}`)\n logger.log('')\n logger.log(`(You only need to do this once)`)\n },\n )\n}\n","import fs from 'node:fs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nexport function checkSocketWrapperSetup(file: string): boolean {\n const fileContent = fs.readFileSync(file, 'utf8')\n const linesWithSocketAlias = fileContent\n .split('\\n')\n .filter(\n l => l === 'alias npm=\"socket npm\"' || l === 'alias npx=\"socket npx\"',\n )\n\n if (linesWithSocketAlias.length) {\n logger.log(\n `The Socket npm/npx wrapper is set up in your bash profile (${file}).`,\n )\n logger.log('')\n logger.log(\n `If you haven't already since enabling; Restart your terminal or run this command to activate it in the current session:`,\n )\n logger.log('')\n logger.log(` source ${file}`)\n logger.log('')\n\n return true\n }\n return false\n}\n","import fs, { existsSync } from 'node:fs'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { confirm } from '@socketsecurity/registry/lib/prompts'\n\nimport { addSocketWrapper } from './add-socket-wrapper.mts'\nimport { checkSocketWrapperSetup } from './check-socket-wrapper-setup.mts'\nimport constants from '../../constants.mts'\nimport { getBashrcDetails } from '../../utils/completion.mts'\nimport { updateInstalledTabCompletionScript } from '../install/setup-tab-completion.mts'\n\nexport async function postinstallWrapper() {\n const { bashRcPath, zshRcPath } = constants\n const socketWrapperEnabled =\n (existsSync(bashRcPath) && checkSocketWrapperSetup(bashRcPath)) ||\n (existsSync(zshRcPath) && checkSocketWrapperSetup(zshRcPath))\n\n if (!socketWrapperEnabled) {\n await setupShadowNpm(\n `\nThe Socket CLI is now successfully installed! 🎉\n\nTo better protect yourself against supply-chain attacks, our Socket npm wrapper can warn you about malicious packages whenever you run 'npm install'.\n\nDo you want to install the Socket npm wrapper (this will create an alias to the \\`socket npm\\` command)?\n `.trim(),\n )\n }\n\n // Attempt to update the existing tab completion\n let updatedTabCompletion = false\n try {\n const details = getBashrcDetails('') // Note: command is not relevant, we just want the config path\n if (details.ok) {\n if (fs.existsSync(details.data.targetPath)) {\n // Replace the file with the one from this installation\n const result = updateInstalledTabCompletionScript(\n details.data.targetPath,\n )\n if (result.ok) {\n // This will work no matter what alias(es) were registered since that\n // is controlled by bashrc and they all share the same tab script.\n logger.success('Updated the installed Socket tab completion script')\n updatedTabCompletion = true\n }\n }\n }\n } catch (e) {\n debugFn('error', 'caught: tab completion setup error')\n debugDir('inspect', { error: e })\n // Ignore. Skip tab completion setup.\n }\n if (!updatedTabCompletion) {\n // Setting up tab completion requires bashrc modification. I'm not sure if\n // it's cool to just do that from an npm install...\n logger.log('Run `socket install completion` to setup bash tab completion')\n }\n}\n\nasync function setupShadowNpm(query: string): Promise<void> {\n logger.log(`\n _____ _ _\n| __|___ ___| |_ ___| |_\n|__ | . | _| '_| -_| _|\n|_____|___|___|_,_|___|_|\n\n`)\n if (\n await confirm({\n message: query,\n default: true,\n })\n ) {\n const { bashRcPath, zshRcPath } = constants\n try {\n if (existsSync(bashRcPath)) {\n addSocketWrapper(bashRcPath)\n }\n if (existsSync(zshRcPath)) {\n addSocketWrapper(zshRcPath)\n }\n } catch (e) {\n throw new Error(\n `There was an issue setting up the alias: ${(e as any)?.['message']}`,\n )\n }\n }\n}\n","import { readFileSync, writeFileSync } from 'node:fs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nexport function removeSocketWrapper(filepath: string): void {\n let content: string | undefined\n try {\n content = readFileSync(filepath, 'utf8')\n } catch (e) {\n logger.fail(`There was an error removing the alias${e ? ':' : '.'}`)\n if (e) {\n logger.error(e)\n }\n return\n }\n\n const linesWithoutSocketAlias = content\n .split('\\n')\n .filter(\n l => l !== 'alias npm=\"socket npm\"' && l !== 'alias npx=\"socket npx\"',\n )\n const updatedContent = linesWithoutSocketAlias.join('\\n')\n try {\n writeFileSync(filepath, updatedContent, 'utf8')\n } catch (e) {\n if (e) {\n logger.error(e)\n }\n return\n }\n\n logger.success(\n `The alias was removed from ${filepath}. Running 'npm install' will now run the standard npm command in new terminals going forward.`,\n )\n logger.log('')\n logger.info(\n `Note: We cannot deactivate the alias from current terminal sessions. You have to restart existing terminal sessions to finalize this step.`,\n )\n}\n","import { existsSync } from 'node:fs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { addSocketWrapper } from './add-socket-wrapper.mts'\nimport { checkSocketWrapperSetup } from './check-socket-wrapper-setup.mts'\nimport { postinstallWrapper } from './postinstall-wrapper.mts'\nimport { removeSocketWrapper } from './remove-socket-wrapper.mts'\nimport constants from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { getOutputKind } from '../../utils/get-output-kind.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'wrapper',\n description: 'Enable or disable the Socket npm/npx wrapper',\n hidden: false,\n flags: {\n ...commonFlags,\n },\n help: (command, config) => `\n Usage\n $ ${command} <\"on\" | \"off\">\n\n Options\n ${getFlagListOutput(config.flags)}\n\n While enabled, the wrapper makes it so that when you call npm/npx on your\n machine, it will automatically actually run \\`socket npm\\` / \\`socket npx\\`\n instead.\n\n Examples\n $ ${command} on\n $ ${command} off\n `,\n}\n\nexport const cmdWrapper = {\n description: config.description,\n hidden: config.hidden,\n run,\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string },\n): Promise<void> {\n // I don't think meow would mess with this but ...\n if (argv[0] === '--postinstall') {\n await postinstallWrapper()\n return\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n // TODO: Implement json/md further.\n const { json, markdown } = cli.flags\n\n const dryRun = !!cli.flags['dryRun']\n\n let enable = false\n let disable = false\n const [arg] = cli.input\n if (arg === 'on' || arg === 'enable' || arg === 'enabled') {\n enable = true\n disable = false\n } else if (arg === 'off' || arg === 'disable' || arg === 'disabled') {\n enable = false\n disable = true\n }\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(\n outputKind,\n {\n test: enable || disable,\n message: 'Must specify \"on\" or \"off\" argument',\n fail: 'missing',\n },\n {\n nook: true,\n test: cli.input.length <= 1,\n message: 'expecting exactly one argument',\n fail: `got multiple`,\n },\n )\n if (!wasValidInput) {\n return\n }\n\n if (dryRun) {\n logger.log(constants.DRY_RUN_BAILING_NOW)\n return\n }\n\n const { bashRcPath, zshRcPath } = constants\n if (enable) {\n if (existsSync(bashRcPath) && !checkSocketWrapperSetup(bashRcPath)) {\n addSocketWrapper(bashRcPath)\n }\n if (existsSync(zshRcPath) && !checkSocketWrapperSetup(zshRcPath)) {\n addSocketWrapper(zshRcPath)\n }\n } else {\n if (existsSync(bashRcPath)) {\n removeSocketWrapper(bashRcPath)\n }\n if (existsSync(zshRcPath)) {\n removeSocketWrapper(zshRcPath)\n }\n }\n if (!existsSync(bashRcPath) && !existsSync(zshRcPath)) {\n logger.fail('There was an issue setting up the alias in your bash profile')\n }\n}\n","#!/usr/bin/env node\n\nimport { cmdAnalytics } from './commands/analytics/cmd-analytics.mts'\nimport { cmdAuditLog } from './commands/audit-log/cmd-audit-log.mts'\nimport { cmdCI } from './commands/ci/cmd-ci.mts'\nimport { cmdConfig } from './commands/config/cmd-config.mts'\nimport { cmdFix } from './commands/fix/cmd-fix.mts'\nimport { cmdInstall } from './commands/install/cmd-install.mts'\nimport { cmdJson } from './commands/json/cmd-json.mts'\nimport { cmdLogin } from './commands/login/cmd-login.mts'\nimport { cmdLogout } from './commands/logout/cmd-logout.mts'\nimport { cmdManifestCdxgen } from './commands/manifest/cmd-manifest-cdxgen.mts'\nimport { cmdManifest } from './commands/manifest/cmd-manifest.mts'\nimport { cmdNpm } from './commands/npm/cmd-npm.mts'\nimport { cmdNpx } from './commands/npx/cmd-npx.mts'\nimport { cmdOops } from './commands/oops/cmd-oops.mts'\nimport { cmdOptimize } from './commands/optimize/cmd-optimize.mts'\nimport { cmdOrganizationDependencies } from './commands/organization/cmd-organization-dependencies.mts'\nimport { cmdOrganizationPolicyLicense } from './commands/organization/cmd-organization-policy-license.mts'\nimport { cmdOrganizationPolicySecurity } from './commands/organization/cmd-organization-policy-security.mts'\nimport { cmdOrganization } from './commands/organization/cmd-organization.mts'\nimport { cmdPackage } from './commands/package/cmd-package.mts'\nimport { cmdPatch } from './commands/patch/cmd-patch.mts'\nimport { cmdRawNpm } from './commands/raw-npm/cmd-raw-npm.mts'\nimport { cmdRawNpx } from './commands/raw-npx/cmd-raw-npx.mts'\nimport { cmdRepository } from './commands/repository/cmd-repository.mts'\nimport { cmdScan } from './commands/scan/cmd-scan.mts'\nimport { cmdThreatFeed } from './commands/threat-feed/cmd-threat-feed.mts'\nimport { cmdUninstall } from './commands/uninstall/cmd-uninstall.mts'\nimport { cmdWrapper } from './commands/wrapper/cmd-wrapper.mts'\n\nexport const rootCommands = {\n analytics: cmdAnalytics,\n 'audit-log': cmdAuditLog,\n ci: cmdCI,\n cdxgen: cmdManifestCdxgen,\n config: cmdConfig,\n dependencies: cmdOrganizationDependencies,\n fix: cmdFix,\n install: cmdInstall,\n json: cmdJson,\n license: cmdOrganizationPolicyLicense,\n login: cmdLogin,\n logout: cmdLogout,\n manifest: cmdManifest,\n npm: cmdNpm,\n npx: cmdNpx,\n oops: cmdOops,\n optimize: cmdOptimize,\n organization: cmdOrganization,\n package: cmdPackage,\n patch: cmdPatch,\n 'raw-npm': cmdRawNpm,\n 'raw-npx': cmdRawNpx,\n repository: cmdRepository,\n scan: cmdScan,\n security: cmdOrganizationPolicySecurity,\n 'threat-feed': cmdThreatFeed,\n uninstall: cmdUninstall,\n wrapper: cmdWrapper,\n}\n\nexport const rootAliases = {\n audit: {\n description: `${cmdAuditLog.description} (alias)`,\n hidden: false,\n argv: ['audit-log'],\n },\n auditLog: {\n description: cmdAuditLog.description,\n hidden: true,\n argv: ['audit-log'],\n },\n auditLogs: {\n description: cmdAuditLog.description,\n hidden: true,\n argv: ['audit-log'],\n },\n ['audit-logs']: {\n description: cmdAuditLog.description,\n hidden: true,\n argv: ['audit-log'],\n },\n deps: {\n description: `${cmdOrganizationDependencies.description} (alias)`,\n hidden: false,\n argv: ['dependencies'],\n },\n feed: {\n description: `${cmdThreatFeed.description} (alias)`,\n hidden: false,\n argv: ['threat-feed'],\n },\n org: {\n description: `${cmdOrganization.description} (alias)`,\n hidden: false,\n argv: ['organization'],\n },\n orgs: {\n description: cmdOrganization.description,\n hidden: true,\n argv: ['organization'],\n },\n organizations: {\n description: cmdOrganization.description,\n hidden: true,\n argv: ['organization'],\n },\n organisation: {\n description: cmdOrganization.description,\n hidden: true,\n argv: ['organization'],\n },\n organisations: {\n description: cmdOrganization.description,\n hidden: true,\n argv: ['organization'],\n },\n pkg: {\n description: `${cmdPackage.description} (alias)`,\n hidden: false,\n argv: ['package'],\n },\n repo: {\n description: `${cmdRepository.description} (alias)`,\n hidden: false,\n argv: ['repos'],\n },\n repos: {\n description: cmdRepository.description,\n hidden: true,\n argv: ['repos'],\n },\n repositories: {\n description: cmdRepository.description,\n hidden: true,\n argv: ['repos'],\n },\n}\n","#!/usr/bin/env node\n\nimport { fileURLToPath, pathToFileURL } from 'node:url'\n\nimport meow from 'meow'\nimport { messageWithCauses, stackWithCauses } from 'pony-cause'\nimport lookupRegistryAuthToken from 'registry-auth-token'\nimport lookupRegistryUrl from 'registry-url'\nimport updateNotifier from 'tiny-updater'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { rootAliases, rootCommands } from './commands.mts'\nimport constants from './constants.mts'\nimport { AuthError, InputError, captureException } from './utils/errors.mts'\nimport { failMsgWithBadge } from './utils/fail-msg-with-badge.mts'\nimport { meowWithSubcommands } from './utils/meow-with-subcommands.mts'\nimport { serializeResultJson } from './utils/serialize-result-json.mts'\n\nconst __filename = fileURLToPath(import.meta.url)\n\nvoid (async () => {\n const registryUrl = lookupRegistryUrl()\n await updateNotifier({\n authInfo: lookupRegistryAuthToken(registryUrl, { recursive: true }),\n name: constants.SOCKET_CLI_BIN_NAME,\n registryUrl,\n ttl: 86_400_000 /* 24 hours in milliseconds */,\n version: constants.ENV.INLINED_SOCKET_CLI_VERSION,\n })\n\n try {\n await meowWithSubcommands(rootCommands, {\n aliases: rootAliases,\n argv: process.argv.slice(2),\n name: constants.SOCKET_CLI_BIN_NAME,\n importMeta: { url: `${pathToFileURL(__filename)}` } as ImportMeta,\n })\n } catch (e) {\n process.exitCode = 1\n debugFn('error', 'Uncaught error (BAD!):')\n debugDir('inspect', { error: e })\n\n let errorBody: string | undefined\n let errorTitle: string\n let errorMessage = ''\n if (e instanceof AuthError) {\n errorTitle = 'Authentication error'\n errorMessage = e.message\n } else if (e instanceof InputError) {\n errorTitle = 'Invalid input'\n errorMessage = e.message\n errorBody = e.body\n } else if (e instanceof Error) {\n errorTitle = 'Unexpected error'\n errorMessage = messageWithCauses(e)\n errorBody = stackWithCauses(e)\n } else {\n errorTitle = 'Unexpected error with no details'\n }\n\n // Try to parse the flags, find out if --json is set.\n const isJson = (() => {\n const cli = meow({\n argv: process.argv.slice(2),\n // Prevent meow from potentially exiting early.\n autoHelp: false,\n autoVersion: false,\n flags: {},\n importMeta: { url: `${pathToFileURL(__filename)}` } as ImportMeta,\n })\n return !!cli.flags['json']\n })()\n\n if (isJson) {\n logger.log(\n serializeResultJson({\n ok: false,\n message: errorTitle,\n cause: errorMessage,\n }),\n )\n } else {\n // Add 2 newlines in stderr to bump below any spinner.\n logger.error('\\n')\n logger.fail(failMsgWithBadge(errorTitle, errorMessage))\n if (errorBody) {\n debugDir('inspect', { errorBody })\n }\n }\n\n await captureException(e)\n }\n})()\n"],"names":["sdkOpts","__proto__","desc","time","process","logger","ok","message","cause","rows","cols","screen","label","barWidth","barSpacing","xOffset","maxHeight","barBgColor","data","formattedData","totalTopAlerts","sortedTopFiveAlerts","top_five_alert_types","formatted","style","line","text","baseline","xLabelPadding","xPadding","wholeNumbersOnly","legend","width","x","y","result","run","parentName","commandName","flags","file","type","description","scope","repoName","markdown","nook","test","fail","repo","filePath","perPage","outputJson","outputMarkdown","page","payload","generated","nextPage","org","user_email","debugFn","error","formattedOutput","row","keys","fg","selectedFg","selectedBg","interactive","top","border","columnWidth","columnSpacing","truncate","bottom","height","bg","tags","content","headers","table","detailsBox","default","help","typeFilter","logType","cwd","tmp","branch","commit_hash","commit_message","committers","make_default_branch","set_as_pending_head","spinner","method","body","tier1_reachability_scan_id","report_run_id","scanStatus","updateProgress","policyStatus","finishedFetching","scan","version","alerts","healthy","addAlert","options","reportLevel","policy","url","manifest","ecoMap","pkgMap","verMap","fileMap","short","includeLicensePolicy","depth","value","Package","Policy","scanId","outputKind","uploadManifests","organizations","tarHash","coanaEnv","env","stdio","tier1ReachabilityScanId","sockJson","cdxgen","count","conda","gradle","sbt","debugLog","verbose","pass","stdout","stderr","poms","strings","reject","keeping","collecting","arr","fs","sbtOpts","bin","gradleOpts","packagePaths","reachabilityOptions","scanPaths","branchName","filepath","commitMessage","commitHash","defaultBranch","pendingHead","pullRequest","reach","reachAnalysisTimeout","reachAnalysisMemoryLimit","reachDisableAnalytics","reachEcosystems","reachExcludePaths","reachSkipCache","runReachabilityAnalysis","readOnly","report","hidden","autoManifest","name","key","failed","obj","config","full","auto","get","list","set","unset","baseBranch","ghsaDetails","title","head","base","octokitPullsCreateParams","states","length","context","apiType","cacheKey","entry","index","parent","match","allPrs","state","per_page","baseRefName","GITHUB_REPOSITORY","repoInfo","author","fixEnv","fixed","ghsaLoop","overallFixed","enabled","unknownFlags","autoMerge","id","isMultiple","limit","rangeStyle","autopilot","ghsa","maxSatisfying","minSatisfying","prCheck","purl","shortFlag","testScript","allowUnknownFlags","throws","purls","toAddToBashrc","recursive","bashrcUpdated","actions","targetPath","completion","updateConfigValue","apiBaseUrl","apiProxy","apiToken","sdk","choices","enforcedOrgs","applyLogout","attemptLogout","YARN_LOCK","ipc","argvMutable","spawnPromise","cleanupPackageLock","rmSync","configuration","coerce","filter","only","profile","standard","technique","alias","array","boolean","string","argv","pathArgs","unknowns","yargv","detected","stdin","out","json","filename","required","canceled","defaultOnReadError","kotlin","scala","setup","aliases","yolo","throw","YARN_CLASSIC","peerDependencies","overrides","pkgs","pkgid","names","npmExecPath","editablePkgJson","isPlacingHigher","insertIndex","entries","updatePkgJsonField","updateResolutionsField","updatePnpmField","updateOverridesField","pkgPath","added","addedInWorkspaces","updated","updatedInWorkspaces","warnedPnpmWorkspaceRequiresNpm","overridesDataObjects","semver","package","depAliasMap","thisSpec","depObj","loggedAddingText","newSpec","concurrency","NPM_BUGGY_OVERRIDES_PATCHED_VERSION","cmdName","prod","VLT","agentVersion","pin","offset","columns","field","mw1","mw2","mw3","security","license","defaultSub","dependencies","quota","deps","self","capabilities","score","transitively","o","valid","components","Maintenance","Quality","Vulnerabilities","License","colorize","padding","artifact","namespace","supplyChain","maintenance","quality","vulnerability","missing","dupes","blocks","outputPurlsShallowScore","shallowScore","shallow","deep","exportedAt","beforeHash","afterHash","summary","severity","patchExplanation","z","patched","patchLookup","hash","onlyDirectories","absolute","patches","purlObj","code","visibility","default_branch","slug","outputCreateRepo","homepage","repoDescription","sort","results","all","direction","create","view","del","update","setAsAlertsPage","targets","updatedInput","orgSlug","showHidden","colors","maxArrayLength","id1","id2","repos","targetRepos","scanCreated","scansCreated","githubToken","repoApiUrl","lastCommitter","tmpDir","fileCount","firstFailureResult","isManifest","fileUrl","response","detailedError","repoSlug","lastCommitMessage","repoDetails","treeDetails","githubApiUrl","orgGithub","from","created_at","year","month","day","fromTime","untilTime","fold","stream","diff","github","metadata","meta","reachability","eco","pkg","cli","ecoFilter","argSet","versionFilter","nameFilter","ecosystem","action","left","zshRcPath","updatedTabCompletion","writeFileSync","enable","disable","analytics","ci","fix","install","login","logout","npm","npx","oops","optimize","organization","patch","repository","uninstall","wrapper","audit","auditLog","auditLogs","feed","orgs","organisation","organisations","repositories","authInfo","importMeta","errorTitle","errorMessage","errorBody","autoHelp","autoVersion"],"mappings":"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ACsBC;AAAS;AAC/B;AACF;AACF;AAEA;AAIE;;AASA;;AASA;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACA;AACAlD;AAEA;;;;AAIA;AACAmD;AACEC;;AAEA;AACF;AAEA;AACEC;AACAC;AACAC;AACAC;AACAC;;AAEApC;AACAqC;;AAEAC;AACE7B;AACAwB;;AAEFM;AAAwB;AACxB;AACAC;AACAC;AACF;AAEA;AACA;AACEC;AAA0B;AAC1BC;AACA3C;AACAR;AACEyC;AACAW;;AAEFC;AACAC;AACF;AACA;AACEJ;AACAC;AACA3C;AACAsC;AACE7B;AACAwB;;AAEFrD;;AAEAY;AACEyC;AACF;AACF;;AAGEc;AACA7D;AACF;;AAEA;;;AAGA;AACAP;AACAA;AACAA;;AAEA;AACAqE;AACE;AACA;AACE;AACAC;;AAEF;AACF;;AAIAtE;AACE;;AAEA;;AAIF;AACF;;ACpTO;;;;;AAKLwC;AAOF;AACE;;;;;AAKEA;AACF;;;;;;AAOEA;AACF;AACF;;AChBO;AAEP;AAEA;AAEO;;;AAGLf;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACA6B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;;AAGFY;AACEb;AACAC;;AAEFS;AACEV;AACAyC;AACAxC;AACF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;AAEwBqB;;AAAoBP;;;;;;AAU5CiC;AAEA;AAEA;AAMA;AAEA;AAGItC;AACAC;AACAxC;AACAyC;AACF;AAEEF;;AAEAvC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;;;AAGEiD;AACAH;AACAkC;AACF;AACF;;ACpJO;;;;;;;AAYHzC;AACF;AAAM3C;;;;AAGJqF;;;;AAIAC;AACF;AAAMtF;;;AAEN;AACA;AACE;AACF;AACA;AAEA;AAEI;AAAmBuF;;AACnB;AAAmBC;;AACnB;AAAsBC;;AACtB;AAAmBC;;AACnBC;AACA;;;AACA3C;AACA4C;;AAEF;AACE3F;AAAyB;AAE/B;;ACtDO;;;AAGY4F;AAAQ;AACvB7F;;;AAIF;AACA;AACE;AACF;AACA;;AAGEC;AACA4F;AACF;AACF;;ACtBA;AACA;AACA;AACA;AACA;AACO;AAIL;AACA;AACA;AACEC;AACAC;AACEC;AACAC;AACF;AACF;AACF;;ACVA;AACA;AACA;AACA;AACO;;;AAUyBlG;AAAQ;AACpCC;;;AAGF;AACA;AACE;AACF;AACA;;;;;AAMQ6F;AAAQ;;AAGdK;AACAC;AACF;;AAGEC;AACAD;AACF;;AAGE;;;AAKA;;AAIA;AACF;;AAGE;;AAMA;AACE;AACF;AAEA;;AAEA;AACA;;AAEA;;AAEI;;AAEA9F;AACAsD;;AACsBC;AAAUpC;AAAK;AACrC;AACF;AACF;AAEA;;;AAEWnB;AAAUY;;AACrB;;;AAKEZ;AACAC;AACAC;;AAGJ;;AAKE;;AAOA;AACF;AAEA4F;AAEA;;;AAOM9F;AACAC;AACAC;;;;;AAMAF;AACAC;AACAC;;AAEJ;AAEA8F;AACAF;AACF;AAEA;AACE;AACF;AACA;AACE;AACF;;;AAII9F;AACAC;AACAC;;AAEJ;;AAGEF;AACAY;;;AAGA;;AAEJ;;ACxIA;;AAEA;AACA;AACO;;;;;;AASH4E;AAQF;AAEA;AAEAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAIA;AACA;AACE;AACAS;;;;AAII9D;AACA+D;AACF;AAEAC;AAEI;;AAEA;;AACqC;AACjCC;;AAEEC;AAUF;AACA;AACF;;AACkC;;AAE9BA;AAUF;AACA;AACF;;AACqC;AACnC;AAKEA;AAUF;AACA;AACF;;AAEoC;;AAOhCA;AAUF;AACA;AACF;;AAEmC;AACjC;;AAEEA;AAUF;AACA;AACF;AAKF;AACF;AAEJ;AACF;AAEAb;AAEA;;AAEIxF;AACAY;AAAQwF;AAAQ;;AAEpB;AAEA;;;;AAIEE;;AAAiBC;;AACjBJ;;;;AAKEnG;AACAC;AAEAW;;AAEJ;;AAGEZ;AACAY;;AAEJ;AAEA;AAKE;;AAEE4F;AACAC;AACAC;;AAEF;AACF;AAEA;AAUE;;AAEA;AACA;AACA;AACE;;AAEEC;AACF;AACF;AACE;;AAEA;AACA;AACA;AACE;;AAEEC;AACF;AACF;AACE;;AAEA;AACA;AACA;AAEA;AACE;;AAEEC;AACF;AACF;AACE;;AAEA;AACA;AACA;AACA;;AAEEC;AACF;AACF;AACF;AACF;AACF;AAEA;AACE;AACA;AACE;AACF;AACA;AACE;AACF;AACA;AACE;AACF;AACA;AACE;AACF;AACA;AACE;AACF;AACA;AACE;AACF;AACA;AACE;AACF;AACA;AACE;AACF;AACA;AACE;AACF;AACA;AACE;AACF;AACA;AACA;AACF;;AC5TO;;;;;;;;AAaHC;AACsB;AAExB;AACEjH;AACF;AAEA;;AAEIC;AACA;AACF;AACAA;AACA;AACF;AAEA;;;;;;;AAUE;AAGF;AACE;AACAD;;AAEA;;AAEEC;AACA;AACF;AACAA;AACA;AACF;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AAIE;AAIA;AACEA;;AAEF;AAEAA;AACA;AACF;AAEA;AACE;AAGqC;AAC/BiH;AAGN;AACEjH;;AAEF;AAEAA;AACAA;AACA;AACF;AAEA;AACEA;AACF;AACEA;AAA8BkH;AAAY;AAC5C;AACF;AAEO;AAIL;AAEA;;AAEE;AACAd;;AAGF;AACEnG;AACAY;AACF;AACF;AAEO;AAIL;;AAOA;;AACWsG;AAAiD;;;;;AACxBT;AAAI;;AAElC;AACAU;AACA;;AAEA;AACAC;;AAEJ;;AAMF;AAEF;;AAEA;AACA;;AAEA;;AAEA;;AAKA;;AAEA;;AAEA;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;;AAKA;AAYA;AAGE;AACF;;AClNO;;;;;;;;AAQLL;AACsB;;AAEpBC;AACF;;;;AAKEK;;;;;AAKAN;AACF;AACF;;ACrBO;;AAKHjD;AACAwD;;AAEF;AAAM3H;;;AAEN;AACEG;AACF;AAEA;;;AAKEC;AACA;;AAEA;AACA;AACF;AAEA;AACEA;AACA;;AAEA;AACA;AACF;AAEA;AACEA;;AAEF;;AAGEA;AACAA;AACA;AACEA;AAGAA;AACF;AACEA;AAGF;AACAA;AACA;;AAEA;AACA;AACF;AAEAA;AACAA;AAEA;AACA;;AAEA;AACEA;AACF;AAEA;AAIME;AACA2E;AACF;AACEY;;;AAIN;AAEA;;AAEA;AACF;;AC9DO;;;AAKHR;;;;;;AAMAuC;AACF;AAAM5H;;;;AAEN;AACA;AACA;;AAEIK;AACAC;AACAC;;AAGJ;;AAEQsH;;AAER;;AAEIxH;AACAC;AACAC;;AAEJ;AAEA;AAEA;AACE;AACA;AACA;AACE;AACF;AAEA;AAEA;;AAEA;AACA;;AAMAsF;AAEA;AAGI5F;AACA4F;AACF;;AAKF;AACE;;AAEA;AACA;AACF;AAEAiC;;AAEE;;AAEA;;AAEEzH;AACAC;AACAC;;AAEJ;;AAGAsF;AACF;;AAGAA;;AAEA;AACA;AAoBE;;;AAUF;;AAEA;AACA;AACA;AACEkC;AACF;AACA;AACEA;AACF;;AAEA;;;AAGEC;;AAEAC;AACF;AAEA;AACA;;AAEA;;AAIM5H;AACAY;AACE;;AAEAiH;AAGF;AACF;AAEN;;AC3MA;AACA;;AAiBO;AACL;AACA;AACAC;AAGA;AACEC;AAAe;AACfC;AACAC;AACAC;AACAC;;;AAIAC;AAIF;AACEA;;;AAIF;;AAGEA;AAIF;AACEA;;;AAGF;;AAGEA;AAIF;;AAEE;;;;AAIEA;;;AAGF;AACF;AAEA;AACF;;ACjEO;;;;AAILC;AAMF;AACE;;AAEA;AACA;;AAEA;AACA;AAEAtI;AACAA;;AAEEA;AAGF;AACAA;;AAEEA;AAGF;;;AAIE;AACA;AACA;AACA;AACA;AACA;;;AAGA;AACEA;AACF;;;AAGA;AACEA;AACAA;;AAEF;;;;AAIE;;AAEEA;AACAA;;AAEF;AACA;AACF;AACAA;AACAA;;AAIIA;AACA;AACF;AAEFA;AACAA;;;;AASA;AACEA;AACAA;;AAEF;AACF;AACF;AAEA;;AAKUyF;AAAQ;;;AAIdzF;AAGAA;AAGAyF;;AAGE;AACA;AACA;AACA;AACAR;AACF;AAEAsD;;;;AACsBC;AAAO;;;;AACNC;;AACzB;AACE;AACEhD;AACF;AACEA;AACF;AACF;AACF;;AChIO;;;;;AAKL6C;AAOF;AACE;;;AAEQ7C;AAAQ;AAEhBzF;AACAA;AACAA;;;;;AAME;AACA;AACA;AACA;AACA;AACA;AAA2DiF;AAAI;;AAI/D;AACEjF;AACAA;;AAEF;;;AAGEA;AACA;;AAEEA;AACAA;;AAEF;AACA;AACF;;;AAGE0I;AACA;AACF;AACA;;AAEE1I;AAGA;AACF;AACA;AACA;AACA;;AAEEA;;AAEAA;AACAA;AACF;;AAEEA;AACAA;AAGAA;AACA0I;AACA;AACE1I;AACAA;AAGF;AACAA;AACAA;AACA;AACF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA0I;AACA1I;AACF;;;;;AAQA;AACEA;AACAA;;AAEF;AACF;AACF;;AChHA;AACE;AACF;AAEO;AAKL;;AAEE;AACEA;AACF;;;;AAKI;AACA2I;AACF;AACA5I;;AAEA;;AAEE;AACEC;AACF;;AAEF;AACAD;;AAEI;AACEC;AAGF;;AAEF;AACE;AACEA;AACF;AACA4I;AACF;AACF;AACF;;;AAII3I;AACAC;AACAC;;AAEJ;AACF;;AAGE;AACEH;AACF;AAEA;;AAEIC;AACAC;;;AAGJ;AAEAuE;;;AAIIxE;AACAC;;;AAGJ;AACF;;AAGED;AACAY;;;AAGA;;AAEJ;;AAEA;AACO;;;;;;AAMH;;AAEE;AACA;AACF;AACA;AACE;AACE;AACA;AACF;AACA;AACE;AACA;AACA;AACF;;AAEE;AACA;;AAEE;AACE;AACA;AACA;AACF;AACF;AACF;AACA;AACEgI;AACF;AACE;AACA;AACF;AACF;AACA;AAAA;;AAGEC;AACF;AACF;;AAGF;;ACxIO;AAKL;AACE/I;AACF;AAEA;;AAEIC;AACA;AACF;AACAA;AACA;AACF;;AAGE;;AAGE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F;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AAGA;;;;;;AAOI9C;AACF;;;AAEcQ;;;;AAMd;AAEA;;AAIItC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACE2K;AACApD;AACF;AACF;;AChGO;AAKL;AACExH;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEA;;AAGEA;AACAA;;AAEA;AACEA;AACAA;AAGF;AACF;;AAEE;AACEA;AACAA;AAGF;AACF;AACF;;AC1CO;;AAELuH;AAIF;AACE;AAEA;AACF;;ACCA;AACEtF;AACAI;AACAmI;AACAtI;AACE;;;AAGF4C;AACF;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;;AAIA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;;AAEcQ;;;;AAMd;AAEA;;AAIItC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACE2K;AACApD;AACF;AACF;;ACxFO;;AAELA;AAIF;AACE;AACA;;;;AAIE;AACE;AACA;AACA;AACEJ;AACAyD;;AAEAzD;AACF;AACA;AACE0D;AACF;AACF;AACA;;AAEA;AACA7K;AAIUC;AACAC;AACAW;;AAEEiK;AACAR;;AAEJ;AAEErK;AACAY;;AAEEiK;AACAR;AACF;AACF;AAGV;;AAMEtK;AACAA;;AAEAA;AACA;AACE;AACA;;AAEA;AACE;AACA;AACEmH;AACF;AACA;;AAIA;AACF;AACF;AACA;AACEnH;AACAA;AAGF;AACF;AACF;;ACnFA;AACEiC;AACAI;AACAmI;AACAtI;AACE;AACA;AACA6I;AACE3I;AACAyC;AACAxC;AACF;;AAEFyC;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;;;AAEoBQ;;;AAIpB;AAEA;AACEC;AACAC;AACAxC;AAEAyC;AACF;;AAEE;AACF;AAEA;AACE3C;AACA;AACF;AAEA;;AAEEuH;AACF;AACF;;AC1EO;AAIL;AACExH;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGEA;AACAA;AACAA;;AAEEA;AACAA;AACF;AACF;AACEA;AACAA;;AAEEA;AACAA;AACF;AACF;AACF;;AClCO;;;AAGLmH;AAKF;AACE;AAEA;AACF;;ACDO;AAEP;AAEA;AAEO;;;AAGLpF;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;;AAIA;AACA;AACA;;;;;;AAOI9C;AACF;;;AAEcQ;;;;AAMd;AAEA;AAEA;;AAIItC;AACAyC;AACF;;AAEiB;AACfzC;AAEAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACE2K;;AAEAxD;AACF;AACF;;AClHO;AAIL;AACEpH;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGEA;AACAA;AACAA;;AAEEA;AACAA;AACF;AACF;AACEA;AACAA;;AAEEA;AACAA;AACF;AACF;AACF;;AClCO;;AAELuH;AAIF;AACE;AAEA;AACF;;ACCO;AAEP;AAEA;AAEO;;;AAGLxF;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;;AAIA;AACA;AACA;;;;;;AAOI9C;AACF;;;AAEcQ;;;;AAMd;AAEA;;AAIItC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACE2K;AACApD;AACF;AACF;;ACjGA;AAEO;;AAELiD;AACA;AAA8BxI;AAAW;AACvC;AAEIgJ;AACAC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;AAEA;AACEiL;AACElL;AACAyC;AACAxC;AACAmI;;AAEF+C;;AAEE/C;;AAEFgD;AACEpL;AACAyC;AACAxC;AACAmI;;AAEFiD;AACErL;AACAyC;AACAxC;AAEAmI;;AAEFkD;AACEtL;AACAyC;AACAxC;AACAmI;;AAEFmD;AACEvL;AACAyC;;AAKAsI;AACAS;AACApD;;AAEF9H;AACEN;AACAyC;AACAxC;AACAmI;;AAEFqD;AACEzL;AACAyC;AACAxC;AACAmI;AACF;AACF;AAEA;AAGIxI;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACA;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;AAIIgJ;;;;AAIA9L;AACF;;AAIA;;AAEEqL;AACF;;;AAIA;AACE;AAAsCU;;AACtC;AACEC;AACF;AACEhO;AACF;AACF;AACA;;AAEEA;AACA;AACF;AAEA;AAEA;AAGI0C;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACA;AACED;AACAC;AAGA;AACF;AAEA;;AAGA;AACA;AACAiF;;;AAIA;AACEgI;AACAvK;AACF;;AAEQ+C;AAAQ;AAChB;AACA;AACA;;AAKA;;AAEA;;AAEA;AAEA;;;;;;;;;;;;;;AAcEuH;AACF;AACF;;AC9QO;AAYL;AACEjN;AAEAC;AACA;AACF;AAEAA;;AAIAA;;AAGEA;AACF;AACAA;AACAA;AACAA;AACAA;AAGAA;AACAA;AACAA;AACAA;AACAA;AACAA;AACAA;AACAA;;;AAGAA;AACAA;AACAA;AACF;;AC1CO;AAYL;AACA;AACE;AACF;;;;;AAEwDiO;;;AAGxD;AACA;;AAGA;AACE1K;AACAyF;AAA0BkF;AAAgB;AAC5C;;;;AAMA;AACA;AAIA;AAEA;;AAEE;AACElF;AACAmF;AACF;AACF;;AAGElO;AACAY;AACEuN;;;;;;;AAcAC;AACF;;AAEJ;AAEA;AACE;;AAGA;;AAEIpO;AACAC;;;AAGJ;;AAESD;AAAUY;;AACrB;AAEO;AAGL;AACA;AACE;AACF;;AAEA;AACA;;;AAUSZ;AAAUY;;AACrB;;ACjHO;AACL;;AAEF;;ACIA;AACEoB;AACAI;AACAmI;AACAtI;;;AAGA4C;AACF;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;AAIA;AACEhC;AACA;AACF;;AAIA;AACF;;ACtEA;AAEO;;AAELwK;AACA;AAA8BxI;AAAW;AACvC;AAEIsM;AACF;;;;;AAMA;AAEJ;AACF;;ACXO;AACLtO;;AAGA;AAIA;AACEA;;AAEA;AACF;;AAGEA;;AAIA;AACF;AAEAA;AACAA;AAEA;AACAA;AACF;;ACrCO;;AAEP;;ACIA;AACEiC;AACAI;AAEAmI;AACAtI;;;;AAIF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGLH;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;AAGA;AACA;AACAiD;;AAGF;;AClDO;AAMLsJ;AACAA;AACAA;AACAA;AACF;;ACYO;AAILC;AACAC;AACA;AACEvO;AAIF;;AAGEF;;AACSC;AAAWC;AAAqBC;;AAC3C;AAEA;AAEA;;;AAA8DuO;AAAS;AACvE;;AAEE1O;AACA;AACF;AAEA;AAEA;AACEH;AACA8O;AACF;AACA;;AAEE3O;AACA;AACF;;AAEQyH;;AAER;;AAIA;AAEA;AACEiD;;AAEF;;AAGA;AACE;AACExK;AAEA0O;AAGIlE;AACAvD;AACA9E;;AAGN;;AAEErC;;AACSC;AAAWC;AAAqBC;;AAC3C;AACA;;AAEA;AACF;AACE;;AAEE0E;AACF;;AAEE7E;;AACSC;AAAWC;AAAqBC;;AAC3C;AACA;AACE;AACA;AACE0O;AACF;AACF;AACF;AAEA;AACE3O;AACA0O;AAEIlE;AACAvD;AACA9E;AAEF;AAEEqI;AACAvD;AACA9E;;AAIN;;AAEErC;;AACSC;AAAWC;AAAqBC;;AAC3C;AACA;AACEH;AACAA;AACA;;AAEEA;AAGF;AACEA;AAGF;AACF;AAEAuO;AAEA;;;AAGEvO;;AAIEA;AACAA;AAGF;AACF;;AAEEA;AACF;AACF;;ACzJO;AAEP;AAEA;AAEO;;;AAGL+B;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACAsM;AACEpM;AACAC;;AAEFoM;AACErM;AACAC;AACF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;AAIA;AACEhC;AACA;AACF;AAEA;AACE;AAGF;AAEA;AAEA;AAEA;AACF;;ACxFO;AACLuO;AACAA;AACAA;AACAA;AACF;;ACFO;;AAEHO;AACA9O;;AAEEA;AACAA;AAGF;AACF;AACEA;AACF;AACF;;ACTA;AACEiC;AACAI;AACAmI;AACAtI;;;AAGA4C;AACF;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;AAIA;AACEhC;AACA;AACF;AAEA+O;AACF;;ACtCA;;;AAAiCC;AAAU;AAE3C;AAeA;AACE;;AAEA;;AAEA;AAAa;AAAQ;AAAS;AAC5B;AACE;AACF;;AAEE;AACA;AACAlN;AACF;AACEA;AACF;;;AAGEA;AACF;AACF;AACA;AACA;AACEA;AACF;AACA;AACA;AACEA;AACF;AACA;AACF;AAEO;;AAEL;AAAsBlC;;;AACtB;AACEqP;AACE;AACA;;;AAIFpH;;;AAOA;AACEqH;AACF;AACE;AACA;;;AAEUC;;AAUR;AACAD;AACAE;;AAEJ;AACF;;;AAaE;;AAEIC;;AAEJ;AAEA;AACA;AACE;AACA;;AAEA;AACF;AACF;AAEA;AACF;;ACrHA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACEC;AACE;AACA;AACA;AACA;AACA;AACA;AACA;;AAEFC;AACE;AACA;AACAC;AACAC;AACAC;AACAC;AACAC;AACAxN;;AAEFyC;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEFgL;;;;;;;;;AASAC;AACInF;AAAevI;AAAe;AAC9BuI;AAAgBvI;AAAe;AAC/BuI;AAAqBvI;AAAe;AACpCuI;AAAsBvI;;AAAkB;AAC1C;AAAEuI;AAAevI;AAAe;AAC9BuI;AAAavI;AAAe;AAC5BuI;AAAiBvI;AAAe;AAChCuI;AAAkBvI;AAAe;AACjCuI;AAAavI;AAAe;AAEhC2N;AAGY;AACV;AAmBFC;AAE2B;;AACL;;AACH;AACjB;AACkB;;AACG;AACrB;AAQ0B;;AACD;AACzB;AAGgB;AAChB;AAAsB;AAE1B;AAEA;AACE/N;AACAI;AACAmI;AACA;AACA;;;AAGF;AAEO;;;AAGLzI;AACF;AAEA;AAGIC;AAAmC;;AAGnC;AACAiO;;;AAGAjO;AACF;;;AAIA;AACA;AACE;;;;AAKF;AACE;AACEkO;AACF;AACEC;AACF;AACF;;;AAIQvE;AAAsB;AAC9B;AACE;AACA;AACA;;AAEA5L;AAGA;AACF;AAEA;AACEA;AACA;AACF;;AAEA;AACA;AACE;AACA;AACA;AACA;;AAEEoQ;AACApQ;AAMF;AACA;;AAEA;AACF;;;AAIQmP;AAAa;;AAErB;;AAEE;;AAEA;AACE;AACApP;AACF;AACF;AAEA;AACF;;AC3SA;AACEkC;AACAI;AACAmI;AACAtI;AACE;AACAoG;AACElG;AACAyC;AACAxC;AAEF;;AAEFyC;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;AACA;;;;AACwBsG;;;AAIxB;;AAGA;AACA;AACArD;AAEA;AAEA;AACEjF;;;;AAIAA;;AAEF;AAEA;;;AAGsBqQ;AAAS;AAE/B;AACErQ;AACA;AACF;AAEA;AACEA;AAGAA;AACAA;AAGAA;AAGAA;;AAEA;AACF;AAEA;;;;AAIEsI;AACF;;AAKF;;AC/GA;AACErG;AACAI;AAEAmI;AACAtI;AACE;AACA;AACAC;AACEC;AACAC;;AAGFiO;AACElO;AACAC;;AAEFkO;AACEnO;AACAC;;AAEFmG;AACEpG;AACAC;;AAGFiG;AACElG;AACAC;AACF;;AAEFyC;AACF;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;AAEQwO;AAAchO;;;;AAKtB;AACA;AACAyC;AAEA;;AAEM9C;;;;AAAoCmG;;;AAE1C;AACA;;AAKEtI;AACF;AACA;AACEyQ;AACF;;;AAGIzQ;AACF;AACEyQ;AACF;AACF;AACA;;AAKEzQ;AACF;AACA;AACEuQ;AACF;;;AAGIvQ;AACF;AACEuQ;AACF;AACF;AACA;;AAKEvQ;AACF;AACEsI;AACF;AAEA;AACEtI;;;AAGAA;AACAA;;AAEF;AAEA;AAEA;AAGIyC;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;;AAGA;AACF;AAEA3C;AAIA;AACEA;AACA;AACF;AAEA;;AAEEyQ;AACAF;;;AAGF;AACF;;AChLA;AACEtO;AACAI;AAEAmI;AACAtI;AACE;AACAgH;AACE9G;AACAC;;AAEF8G;AACE/G;AACAC;;AAGFiG;AACElG;AACAC;AACF;;AAEFyC;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;AAEQwO;AAAchO;;;;AAItB;AACA;;AAGA;AACA;AACAyC;AAEA;AAEA1B;;;;AAMuB+E;;;AAEvB;;;;AAIItI;AACF;;AAEA;AACF;;;;AAIIA;AACF;AACEmJ;AACF;AACF;;;;AAIInJ;AACF;AACEsI;AACF;AACF;AAEA;AACEtI;;;;;AAKF;;AAEA;AACA;AACA;;AAEA;AACEyC;AACAC;AACAxC;AACAyC;AACF;;AAEE;AACF;AAEA;;AAEE3C;AACAA;;AAEF;AAEA;AACEA;AACA;AACF;AAEA;AACEkJ;;;;AAOF;AACF;;ACpKA;AACA;AACA;AACA;AACA;AACA;AACEjH;AACAI;AAEAmI;AACAtI;AACE;AACAgH;AACE9G;AACAC;;AAEF8G;AACE/G;AACAC;;AAGFiG;AACElG;AACAC;AACF;;AAEFyC;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;AAEQwO;AAAchO;;;;AAItB;AACA;;AAGA;AACA;AACAyC;AAEA;AAEA1B;;;;AAMuB+E;;;AAEvB;;;;AAIItI;AACF;;AAEA;AACF;;;;AAIIA;AACF;AACEmJ;AACF;AACF;;;;AAIInJ;AACF;AACEsI;AACF;AACF;AAEA;AACEtI;;;;;AAKF;;AAEA;AACA;AACA;;AAEA;AACEyC;AACAC;AACAxC;AACAyC;AACF;;AAEE;AACF;AAEA;;AAEE3C;AACAA;;AAEF;AAEA;AACEA;AACA;AACF;AAEA;AACEkJ;;;;AAOF;AACF;;ACzKA;AACEjH;AACAI;AAEAmI;AACAtI;AACE;AACAgH;AACE9G;AACAC;;AAEFkO;AACEnO;AACAC;;AAGFmG;AACEpG;AACAC;;AAEF4G;AACE7G;AACAC;;AAEFiG;AACElG;AACAC;AACF;;AAEFyC;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;;AAEA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAAmC;;;;;AAMnCA;AACF;;AAEQwO;AAAchO;;;;AAKtB;AACA;AACAyC;;AAEA;AACA;AAEA;AAEA1B;;;;;;AAMiC+E;;;AAEjC;;;;AAIItI;AACF;AACEkJ;AACF;AACF;AACA;;AAKElJ;AACF;AACA;AACEuQ;AACF;;;AAGIvQ;AACF;AACEuQ;AACF;AACF;;;;AAIIvQ;AACF;AACEiJ;AACF;AACF;AACA;;AAKEjJ;AACF;AACEsI;AACF;AAEA;AACEtI;;;;;AAKF;;AAEA;AACA;AACA;;AAEA;AACEyC;AACAC;AACAxC;AACAyC;AACF;;AAEE;AACF;AAEA;;AAEE3C;AACAA;AACAA;;AAEF;AAEA;AACEA;AACA;AACF;AAEA;AACEkJ;AACAjE;AACAsL;;;AAMF;AACF;;AC/MO;AACL;AACExQ;AACF;AAEA;AACEC;AACA;AACF;AAEAA;AACF;;ACDO;;;AAKiBqQ;AAAS;;AAE/B;AACA;AACA;AACA;AACA;;;AAGA;AACErQ;AACF;AACEA;AACF;AAEAA;AACAA;AAGAA;AACAA;AACAA;AACAA;AACAA;AAGAA;AACAA;AACAA;;;AAKImH;AACA9E;AACF;;AAGE8E;AACA9E;AACF;;AAGE8E;AACA9E;AACF;;AAGE8E;AACA9E;AACF;;AAGE8E;AACA9E;AACF;AAGFuM;AACE;;AAEA;AACF;;AAEA;AACAA;AACE;AAIE;AACF;AACA;AAIE;AACF;;AAEF;;AAEA;;AAEElE;AACAvD;AACA9E;AACF;;AAEA;AACA;AACEnC;AACA0O;AACF;AAEA;AACA;AACE;AACF;AACA;AAEA;AACE7G;AACF;AACA;AACEA;AACF;AAEA;AACA;AACE;AAAc;;;AAGZ;;AAEA;AACF;AACA;AAAe;;;AAGb;;AAEA;AACF;AACA;AAAY;;;AAGV;;AAEA;AACF;AAC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;AAEO;;AAIGvD;AAAoB;AAC1BrF;;;;;AAKA;AACA4I;;;;;;AAYJ;AAEO;;AAIGvD;AAAoB;AAC1BrF;;;;AAIA;AACA;AACA;;;;;AAWF;AACF;AAEO;;AAIGqF;AAAoB;AAC1BrF;;;;AAIA;AACA;AACA;AACA;AACA;;;;;AAOF;AACF;AAOO;;AAKH;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACA;AACE;AACJ;AACF;;AC5NO;;ACQP;;;;;;;;AAQEuR;AACF;AAEA;AASA;AAIE;AAIF;AAEA;;AAKA;AAEA;AAIE;AACF;AAEA;AAKE;AACA;AACE;;AAEE;AACA;;AAEI;AACE;AACAE;;;AAGA;AACF;AACF;AACF;AACE;AACAK;AAGQ;AACE;AACAL;AACF;AACF;AACE;AAAmB;AAE7B;;AAEA;;;AAGA;AACF;;AAC2B;AAAe;AAC1C;AACA;AACF;AACA;AAIE;AACF;AACA;AACA;AACA;;;;;;AAME;AACEM;;AAEF;AACF;AACEA;AACAC;AACF;;AAEE;AACED;AACAC;AACF;AACF;AACA;;AAEA;AACA;AACED;AACAC;AACF;AACA;;;AAGEA;AACF;AACAC;AAEqB;;;AAKvB;AAEO;AAILC;AACF;AAEO;AAILA;AACF;AAEO;AAILA;AACF;AAEO;AAKL;AACE;AACEC;AACA;AACF;AACEC;AACA;AACF;AACEC;AACA;AACF;AACEF;AACA;AACF;AACEA;AACA;AACF;AACA;AACEE;AACA;AACJ;AACF;;AC/IA;AAEO;;;;;;AAUHC;AACF;;;;;;AAME7F;AACE8F;AACAC;AACAC;AACAC;AACAC;AACF;AACF;AAAM3S;;;;AAEN;AACA;AACA;AACA;AACA;;AAIE;AACA6R;;;AAKAzR;;AAOF;;;AAIEwS;AACF;AACEA;AAIF;AAEA;AACA;AACA;AAAuD;;AAEnD;AACA;AACAC;AAKJ;;;AAGA;AACA;AAEW;AAAQ;;AACP/H;AAAsBgI;AAAsBvM;AAAQ;AAC5D;AACA;AACA;AACA;AAAa;;AACX;AAGA;AACEwM;AACF;AACA;AAGA;;AAEE;AACA;AACA;AACA;AAC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CF;;;;;;AAMJ;AAEJ;AACF;;ACOO;AAGLjQ;;AAMF;;ACvDO;AAKL;AACED;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGE;AACAA;AACAA;AACA;AACF;AAEAA;AAGAA;AACAA;AACF;AAEO;;AAEHmU;AACE/N;AACAgO;;AAEAC;;AAEFC;;;;;;AAMED;AACF;AACF;AAEA;AACA;;AAIA;AACEE;AAGF;AACAA;AACA;AACEA;AAGF;AACEA;AAGAA;AACAA;AAGF;AACAA;AACA;AACE;AACAA;AAGAA;AACAA;AACAA;AACAA;AAGF;AACEA;AACAA;AACAA;AAGF;AACAA;AACAA;AACAA;AACAA;AACAA;;;;;;;AAOAA;AACAA;AACAA;;AAEEA;AACAA;AACA;AACEA;AACF;AACF;AACEA;AACF;AACAA;AACAA;AACAA;;AAEE;AACEA;AACF;AACEA;AACF;AACAA;AACAA;AAGF;AACEA;AACF;AACAA;AACA;AACEA;AACAA;AACAA;AAGAA;AACAA;AACAA;AACAA;AAGAA;AAGAA;;;;;;;AAOAA;AACAA;AACAA;AACAA;AAGAA;;;;;;;AAOAA;AACAA;AACAA;;AAEEA;AACAA;AACA;AACEA;AACF;AACF;AACEA;AAGF;AACAA;AACAA;AACAA;;AAEEA;AACAA;;AASF;AACEA;AAGF;AACAA;AACF;AACA;AACF;;AChNO;AAIL;AAEA;AACF;;ACZA;AACA;AACA;AACA;AACO;;;;AAOHC;;AAEA;AACE;;AAEEA;AACA;;AAEA;AACAxG;AACF;;AAEA;AACF;AACA;AACEwG;AACF;AACF;AACE;AACAlD;AAEA;AACE;AACA;AACE;AACAkD;AACA;;AAEAxG;AACF;AACEA;AACF;AACF;AAEA;AACEwG;AACF;AACF;;;AAEgBA;;AAClB;;AClCO;AAEP;AAGA;AAEO;;;AAGLzS;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;;AAEA;AAC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AAGxB;;AAQF;;AAEF;;;AAGF;AACF;AACF;AACF;AAEA;;;AAGI;AACAsW;AACA;;AAEF;AACF;AAEA;AACE;;AAAqDC;AAAsB;;AAEzE;AACF;;AAEEC;AACAvR;AACAsR;AACF;AACF;AAEA;AACE;AAGA;AACF;AAEA;;AAQE;AACEvW;AACA;AACF;AAEA;;AAEEA;AACA;AACF;AAEA;AACEA;AACAA;;AAGA;AACEA;AACF;AACE;AACA;AACEA;AACA;AACF;;AAEE;AACAA;;AAEAA;AACF;AACF;AACF;AACEA;AACAA;AACF;AACEA;;AAEAA;;AAEF;AACF;AAUO;;;;;AAKLyF;AACiB;;;;;AAKf;AACA;AACA;;AAEA;AACA;AACA;AAAa;AAAQ;;AACnB;AAAqCsI;AAAc;;AAEjD;AACF;;AAEA;AACE0I;;AAEF;;;;AAIEC;AACF;AACF;;AAIA1W;;;AAGA;AACEA;AACF;AACAA;AAEA;AACA;;AAEA;AACA;AACEC;AACAY;;AAEA;;AAGF;;;;AAKA;;AAGEX;;;AAGAA;AACAC;AACF;AAEA;AACEF;AACA0W;;AAEAxW;;AAGF;AACF;AACF;;AClQO;AAEP;AAEA;AAEO;;;AAGL4B;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACAyL;AACEvL;AACAyC;AACAxC;AAEA8K;AACAS;AACF;;AAEF9I;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;AAIIgJ;;;;AAIA9L;AACF;;AAGA;AAEA;AACES;AACAC;AACAxC;AACAyC;AACF;;AAEE;AACF;;AAGA;AACA;AACAsC;;AAGA;AACEjF;AACA;AACF;;AAGA;AACEA;AACF;;AAEQyF;AAAQ;;AAGesI;AAAc;AAG7C;;;;;AAKEtI;AACF;AACF;;ACtHO;;;;AAOHoC;AACF;;AAEA;;AAEE;;AAEA;AACE;AACA9H;AACF;AACF;AAEA;AACF;;ACjBA;AACEkC;AACAI;AACAmI;AACAtI;;;;AAIF;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGLH;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;;AAGLnV;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACAkV;AACEhV;AACAyC;AACAxC;;AAGFgV;AACEjV;AACAyC;AACAxC;;AAEF0B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;;AAGFS;AACEV;AACAwL;AACA/I;AACAxC;;AAEFY;AACEb;AACAwL;AACA/I;AACAxC;;AAEF6U;AACE9U;AACAwL;AACA/I;AACAxC;AACF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;AAEaqV;;;AAAoChU;;;;AAMjD;AAEA;AAMA;AAEA;AAGIZ;;AAEAvC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACEoX;AACAC;;;;;;AAMF;AACF;;ACnJO;;;;;;;AAUHT;AACF;AAAMhX;;;;AAEED;AAAQ;AACdC;;;AAIF;AACA;AACE;AACF;AACA;;AAIIiX;;;AAGAnM;;AAEAkM;AACF;AACE/W;AAA+B;AAErC;;AC/CO;AAKL;AACEE;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEAA;AACF;;ACtBO;;;;;;AAOH4W;AAQF;AAGA;;;;;;AAMEA;AACF;AAEA;AACF;;AChBO;AAEP;AAEA;AAEO;;;AAGL7U;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACAyH;AACEvH;AACAwL;AACA/I;AACAxC;;AAEF2U;AACE5U;AACAwL;AACA/I;AACAxC;;AAEF0B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;;AAGF4U;AACE7U;AACAwL;AACA/I;AACAxC;;AAEFuU;AACExU;AACAwL;AACA/I;AACAxC;AACF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;AAEwBqB;;;;;;AAUxB;AAEA;AAMA;AAEA;AAGIZ;AACAC;AACAxC;AACAyC;AACF;AAEEF;;AAEAvC;AACAyC;AACF;;AAGEzC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;;AAGIuC;;;;;;AAQN;;AC3JO;;AAKG5C;AAAQ;AAAMC;;;AAEtB;AACA;AACE;AACF;AACA;;AAGEC;AACF;AACF;;AC3BA;AAYO;AAIL;AACEE;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEA;AACEwT;AACIC;AAAa/I;AAA2B;AACxC+I;AAAe/I;AAA6B;AAC5C+I;AAAqB/I;AAAmC;AACxD+I;AAAyB/I;AAAuC;AAChE+I;AAAmB/I;AAAiC;AACpD+I;AAAmB/I;AAAiC;AACpD+I;AAAqB/I;;;AAI3B1K;AACF;;ACrCO;;AAOL;AACF;;ACIO;AAEP;AAEA;AAEO;;;AAGL+B;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACA6B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;AAEF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;AAEwBqB;;;;;;AAUxB;AAEA;AAMA;AAEA;AAGIZ;AACAC;AACAxC;AACAyC;AACF;AAEEF;;AAEAvC;AACAyC;AACF;;AAGEzC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;;AAGF;;AClIA;AAEO;;AAEL;AAA8BgC;AAAW;AACvC;AAEIsV;AACAC;AACArM;AACAsM;AACAC;AACF;;;;;AAMA;AAEJ;AACF;;AC5BO;AACLzN;AACE5H;AACAyC;AACAxC;;AAGF0H;AACE3H;AACAyC;AACAxC;;AAGF4H;AACE7H;AACAyC;AACAxC;;AAGF6H;AACE9H;AACA+K;AACA9K;;AAGF8H;AACE/H;AACA+K;AACA9K;;AAGF+H;AACEhI;AACAyC;AACAxC;AAEF;AACF;;ACrCO;AACL;AACA;AACA;AACEnC;AACA0O;AAEIlE;AACAvD;AACA9E;AACF;AAEEqI;AACAvD;AACA9E;;AAIN;AACA;AACF;;ACcO;AAEP;AAEA;AAEA;AACE;AACA;AACAoI;AACErI;AACAC;;AAGF8C;AACE/C;AACAwL;AACAvL;;AAEFqH;AACEtH;AACAwL;AACA/I;AACAxC;;AAEFoH;AACErH;AACAwL;AACA/I;AACAxC;;AAEFiD;AACElD;AACAwL;AACA/I;AACAxC;;AAEF4C;AACE7C;AACAC;;AAEFsH;AACEvH;AACAyC;AACAxC;;AAGF0B;AACE3B;AACAyC;AACAxC;;AAGFwH;AACEzH;AACAwL;AACAvL;;AAEFgB;AACEjB;AACAC;;AAGFyH;AACE1H;AACAyC;AACAxC;;AAEFiI;AACElI;AACAyC;AACAxC;;AAGFO;AACER;AACAwL;AACAvL;;AAEFkI;AACEnI;AACAC;;AAGFmE;AACEpE;;AAEAC;;AAEFqV;AACEtV;AACAyC;;AAEAxC;;AAGF6C;AACE9C;AACAwL;AACA/I;AACAxC;AAEF;AACF;AAEO;;;AAGLN;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;;;AAGF;;AAEJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;;;;;AAOIF;AACF;;;;;AAMEiD;;AAEAlB;;;AAGAV;;;;;;;;;AASAqU;AACAxS;;;AAwBF;;;AAGA;AACA;AACE;;AAIA;AACAgF;AACF;;;;AAME/E;AACAvC;AACA2H;;AAQF;AAMA;;AAMA;;AAEA;AACA;;;AAGIvK;AAIF;AACEyK;AACF;AACF;;;;AAIIzK;AACF;AACEuJ;AACF;AACF;;;;AAIIvJ;AACF;AACEuC;AACF;AACF;AACA;;;AAGIvC;AACF;AACEuK;AACF;AACF;;AAEA;AACA;;;AAGA;;;AAIEoN;AACAC;AACF;;AAEA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;;AAEI;;AAEE;AAEI3X;AACAC;AACAC;AACF;AAEE4D;AACAwD;AACF;AAEF;AACF;AACA;AACEsQ;AACF;AACAD;AACF;AACF;;;;AAOA;AAEA;AACE5X;AAGAA;AACAA;AAGAA;AACAA;AACAA;AAGAA;AACF;;;AAIA;AACA;AAEA;;;;AAaA;AAQA;AAGIyC;;AAEAvC;AACAyC;AACF;AAEED;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACEyK;AACAlB;;;;;AAKAI;AACA5F;;;AAGA6F;AACAC;AACAC;AACEO;AACAJ;AACAF;AACAC;;;;;AAKFM;;;;;;AAMF;AACF;;AC3fO;;AAKG3K;AAAQ;AACdC;;;AAIF;AACA;AACE;AACF;AACA;;AAGEC;AACF;AACF;;ACtBO;AAIL;AACEE;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEAA;AACF;;ACrBO;;AAOL;AACF;;ACIO;AAEP;AAEA;AAEO;;;AAGL+B;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACA6B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;AAEF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;AAEwBqB;;;;;AAQxB;AAEA;AAMA;AAEA;;;AAKInD;AACAyC;AACF;;AAGEzC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACF;;ACrHO;;;AAGL6X;AAKF;AACE7X;AACAA;AACAA;AAEA;AAMF;;ACZO;;;AAKHuH;AAKF;AAEA;AACExH;AACF;AAEA;;AAEIC;AACA;AACF;AACAA;AACA;AACF;AAEA;AACA;;AAIA;AACA;AACA;AACA;AACE;AACA;AACF;;AAGE;AACA;AACF;;AAEA;AACA;;AAEAA;;AAGI8X;AACA5Q;AACA6Q;AACAC;AACF;AAEFhY;AAGAA;AACF;AAEA;AAKE;AAEA;AACEA;;AAEE;AACEA;AACAA;AACF;AACEA;AACF;AACAA;AACF;AACF;AACE;AACAA;AACAA;AACAA;AACF;AACF;AAEA;AAGE;AAEAA;AACAA;AACAA;AACAA;AAGAA;AACAA;;AAIAA;AACAA;AACAA;;AAEAA;;AAGEa;AACEb;AACF;;AAEEA;AACF;AACF;AAEAA;;AAEEa;AACEb;AACF;;AAEEA;AACF;AACF;AAEAA;;AAEEa;AACEb;AACF;;AAEEA;AACF;AACF;AAEAA;;AAEEa;AACEb;AACF;;AAEEA;AACF;AACF;;;AAIA;;AAEE;AACEA;AACF;AACA;;AAEA;AACF;AAEAA;;AAEAA;AACAA;AAGAA;AACA;AACE;AACE;AACF;AACA;;;AAKA;AACF;AAEAA;;AAEAA;AACAA;AACAA;AACA;AACE;AACE;AACF;AACA;;;AAKA;AACF;AAEAA;AACF;;AC9MO;;;;;;AAMLuH;AAQF;AACE;;;AAGEsQ;AACF;;;;AAKEtQ;AACF;AACF;;ACdO;AAEP;AAEA;AAEO;;;AAGLxF;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACAgF;AACE9E;AACAyC;AACAxC;;AAGFF;AACEC;AACAwL;AACA/I;AACAxC;;AAGF0B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;AAEF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;AAEA;AAEA;;;;;;AAEqCqB;;;;AAMrC;AACA;AACA;AACE4U;AACF;AACA;AACEC;AACF;AAEA;AAEA;AAMA;AAEA;AAGIxV;AACAxC;AAEAyC;AAMF;;AAGEF;AACAvC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AAEAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;AACEiY;AACAC;AACAhR;;;AAGA/E;AACF;AACF;;AC/JO;;;;;;;;AAQLgW;AAUF;;AAME;AACE;AACA;AACEd;AACAH;AACF;AACA;AACE;AACF;AACAkB;AACF;AAEAA;;AAGApY;;;AAIIC;AACAC;AACAC;;AAGJ;;AAEA;AACA;AACE;AACA;AACE;AACF;;AAEF;;AAEA;AACA;AACA;;AAEE;AACE;AACF;AACF;;AAGA;AACE;AACA;;;;;;AAMEgY;AACF;;;AAEUE;;AACR;AACEC;AACF;AACF;AACF;;AAGAtY;;AAGEC;AACAY;;AAEJ;AAEA;;;;;;AAQIsX;AAQF;;;AAMA;;;;;;;AASAnY;AACA;AACF;AAEA;;;;;AAOIuH;AAQF;AAEA;;;;AAIEgR;AACF;AACA;AACE;AACF;;;AACuBC;;AAEvBxY;AAEA;;;;;AAKEwY;AACF;AACA;AACE;AACF;AACA;AAEA;AACExY;;AAGSC;AAAUY;AAAQwX;AAAmB;;AAChD;AAEA;AACA9U;AAEA;;;;;;;AAOEgV;AACF;AACA;AACE;AACF;AAEA;;;;;AAKEA;AACF;AACA;AACE;AACF;;;;AAE0CE;;;AAE1C;AACA;;AAEA;AACEhO;AACAlB;AACAG;;;AAGAzE;AACA0E;AACA5F;;;AAGA6F;AACAC;AACAC;AACEO;AACAJ;AACAF;AACAC;AACAE;AACAC;AACAC;;AAEFE;AACA/H;AACAgI;;;AAGArF;AACF;;AAESjF;AAAUY;AAAQwX;AAAkB;;AAC/C;AAEA;;;;;;;AAOEK;AASF;AACE1Y;;;AAOA;AACA;AACE;AACA;;;;;AAKEuY;AACF;;AAEE;AACEI;AACF;AACF;AACEC;AACF;AACF;;;;AAKE;AACE5Y;AAGA;AACF;;AAEEC;AACAC;AACAC;;AAEJ;;AAESF;AAAUY;;AACrB;AAEA;;;;;AAKE6X;AAOF;AACEnV;AAEA;;;AAMEA;AACA;;AACStD;A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AACF;;AAGEiD;AACAlB;;;AAGAV;;;;AAIA+G;;;;AAeF;;;;AAIA;;AAEA;AACA;AACE;;AAIA;AACAF;AACF;AAEA;;;AAMA;;;AAGA;;AAEEyN;AACF;AAEA;AAMA;AAEA;AAEA;AAGIlV;;AAEAvC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;;;;;;AAMEqJ;AACEU;AACAC;AACAC;;;;AAIF;AACF;AACF;;AC5LO;AAEP;AAGA;AAEO;;;AAGLlI;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACA4X;AACE1X;;AAEAC;;AAEF0B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;;AAGFmE;AACEpE;;AAEAC;;AAEF2E;AACE5E;AACAyC;AACAxC;;AAEFyR;AACE1R;AACAyC;AACAxC;AACF;;AAEFyC;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;AAEwBqB;;;AAIxB;;;AAMA;;AAIA;AAEA;AAEA;AAMA;AAEA;AAGIZ;;AAEAvC;AACAyC;AACF;;AAGEzC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;;;;;;;;AAQEwG;AACF;AACF;;AC/LO;AACL;AACEzG;AACF;AAEA;AACEC;AACA;AACF;AAEAA;AACAA;AACAA;AACF;;ACCO;;AAKL;AACEA;AACF;AACEA;AACF;AAEAA;AACAA;AAGAA;AACAA;AACAA;AACAA;AAGAA;AACAA;AACAA;AACAA;AACAA;AACAA;AAEA;AACA;AACE;AACF;AAEA;AACA;AACE+H;AACF;AACA;AACEA;AACF;AAEA;AACE7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C;AACA;AACF;;AAGE;AAAoCmC;AAAK;AAC3C;;AAEA;AACF;;AC3IA;AAEO;;AAEL;AAA8BH;AAAW;AACvC;AAEIsV;AACAE;AACAwC;AACAC;AACA/O;AACAgP;AACApQ;AACAS;AACAwG;AACAwG;AACF;AAEEvG;AACEmJ;;AAEE3P;;;AAGF4P;;AAEE5P;;AAEF;;;;;;AAMJ;AAEJ;AACF;;AC/CO;;;;;;;;AAQLrE;AAUF;AACE;;AAcF;;ACzBA;AAEO;AAIL;AACEpG;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGEA;AACA;AACF;;AAGA;;AAEA;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACA;AACAM;AAEA;;;;AAIA;AACEqD;AACAC;AACAC;AACAC;AACAC;AACAxD;AACAoB;AACAqC;;AAEAC;AACE7B;AACAwB;;AAEFM;AACA;AACA;AACAC;AACAC;AACF;AAEA;AACA;AACEC;AAA0B;AAC1BC;AACA3C;AACAR;AACEyC;AACAW;;AAEFC;AACAC;AACF;AACA;AACEJ;AACAC;AACA3C;AACAsC;AACE7B;AACAwB;;AAEFrD;AACAkE;AAEAtD;AACEyC;AACF;AACF;;AAGEc;AAQA7D;AACF;;AAEA;AACA;AACE;AACA;;AAEA;AACF;;AAEA;;;AAGA;AACAP;AACAA;AACAA;;AAEA;AACAqE;AACE;AACA;AACE;AACA;AACE;;;AAKF;AACF;AACF;;AAIArE;AACE;;AAEA;AACAN;AACF;AACF;AAEA;AAKE;AASF;AAEA;AACE;;;AAGE;AAEA;;AAEA;;AASF;AACF;;AC1LO;;;;;;;;;AASLmG;AAWF;AACE;;;;;;;;AAQEA;AACF;AAEA;AACF;;ACrBO;AAEP;AAEA;AAcA;AAEA;AAEO;;;AAGLpE;AACF;AAEA;AAGIC;AAAmC;AAErC;AACEC;;;AAGAC;AACE;AACA;AACAmV;AACEjV;AACAyC;AACAxC;;AAEFgY;AACEjY;AACAyC;AACAxC;;AAEFmN;AACEpN;AACAyC;AACAxC;;AAEF0B;AACE3B;AACAyC;AACAxC;;AAGFgB;AACEjB;AACAC;;AAGFY;AACEb;AACAyC;AACAxC;;AAEFS;AACEV;AACAwL;AACA/I;AACAxC;;AAEFiY;AACElY;AACAyC;AACAxC;;AAEF8D;AACE/D;AACAyC;AACAxC;AACF;;AAEFyC;AACJ;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;;AAMEqB;;AAEAjB;AACA+D;;;;AAOF;AACA;AACA;AACA;;AAGAoU;AACE;AACEC;AACAC;AACA;AACF;AACF;AAEAF;AACE;AACEG;AACAD;AACA;AACF;AACF;AAEAF;AACE;AACExV;AACA0V;AACA;AACF;AACF;AAEA;AACAF;AACE;AACEI;AACAF;AACA;AACF;AACF;;AAGEza;AAGF;AAEA;AAEA;AAMA;AAEA;AAGIyC;;AAEAvC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;AAEA;;AAEE4a;AACApL;;;;;AAKA8K;AACAnU;AACF;AACF;;ACnRO;AAIL;AACEpG;AAEAC;AACA;AACF;AAEAA;AACAA;AACAA;AAGAA;AAGAA;AACAA;AACAA;AACAA;AAGAA;AACA;AACEA;AAGAA;;AAEEA;AACF;AACAA;AACF;AACF;;AC/BO;AAGL;AACA;AACE;AACF;;;;AAE4CiO;;;AAE5C;AACA;;;AAOE;AACE;AACE;AAAA;AAEA;AAAA;;;AAOAhO;AACAY;AACEga;;;AAGF3a;;AAEJ;AACE;;AAEED;AACAY;AACEga;AACAC;;;;AAIN;AACF;;AAEI7a;AAAU;AACVY;AAAQga;AAAqBC;;AAC7B5a;;AAEJ;AACF;AAEA;;AAMA;;ACtEO;AACL;AACA;AACF;;ACIA;AACE+B;AACAI;AACAmI;AACAtI;;;AAGA4C;AACF;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEO;AAGHC;AAAmC;;;;;AAMnCA;AACF;;AAGA;AACEhC;AACA;AACF;;AAIA;AACF;;AC7DA;AAEO;;AAELwK;AACA;AAA8BxI;AAAW;AACvC;AAEIsM;AACF;;;;;AAMA;AAEJ;AACF;;ACnBO;;AAKD;AACE;AACF;AACAtO;AAGAA;AAGAA;AACAA;AAGAA;AAGAA;AACAA;AACAA;AACAA;AACF;AAEJ;;AC3BO;;;;AASHA;AAGAA;AACAA;AAGAA;AACAA;AACAA;AAEA;AACF;AACA;AACF;;ACfO;;;AACe+a;AAAU;;;AAM5B;AAEJ;;AAEA;;AAEA;AACA;AAEE;;AAEA;;;AAGE;;;AAGI;;;AAKE;AACA;AACA/a;AACAgb;AACF;AACF;AACF;;AAEAzX;;AACsBC;AAAS;AAC/B;AACF;;AAEE;AACA;AACAxD;AACF;AACF;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;;AAGME;AACA2E;AACF;;;AAEoBkW;AAAU;;AAE5B;;AAEA;AACA;;AAEA;;;AAKF;AACF;AACF;;ACpFO;AACL;;AAEEtW;;;AAGA;AACEzE;AACF;AACA;AACF;;AAOA;;AAEEib;;AAEA;AACEjb;AACF;AACA;AACF;AAEAA;AAGAA;AACAA;AAGF;;ACrBA;AACEiC;AACAI;AACAmI;AACAtI;;;AAGA4C;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAAmC;AAErC;AACA;;AAEE;AACF;;;;;AAMEA;AACF;;AAEA;;;AACcQ;;;;;AAMd;;AAEE0Y;AACAC;AACF;AACED;AACAC;AACF;AAEA;AAEA;;AAIIjb;AACAyC;AACF;AAEEF;AACAC;AACAxC;AACAyC;AACF;;AAGA;AACF;AAEA;AACE3C;AACA;AACF;;;AAEoB+a;AAAU;AAC9B;;;AAGE;;;AAGA;AACF;AACE;;AAEA;AACA;;AAEA;AACF;;AAEE/a;AACF;AACF;;AC9FO;AACLob;AACA;AACAC;AACArT;AACA8C;AACAkJ;AACAsH;AACAC;AACA/K;AACAsD;AACA0H;AACAC;AACA9U;AACA+U;AACAC;AACAC;AACAC;AACAC;AACApJ;AACAqJ;AACA;AACA;AACAC;AACA9V;AACA2N;AACA;AACAoI;AACAC;AACF;AAEO;AACLC;AACE9Z;AACAmI;;;AAGF4R;;AAEE5R;;;AAGF6R;;AAEE7R;;;AAGF;;AAEEA;;;AAGF0J;AACE7R;AACAmI;;;AAGF8R;AACEja;AACAmI;;;AAGFnH;AACEhB;AACAmI;;;AAGF+R;;AAEE/R;;;AAGF/C;;AAEE+C;;;AAGFgS;;AAEEhS;;;AAGFiS;;AAEEjS;;;AAGF8P;AACEjY;AACAmI;;;AAGF5H;AACEP;AACAmI;;;AAGF2N;;AAEE3N;;;AAGFkS;;AAEElS;;AAEF;AACF;;ACtHA;AAEA;AACE;AACA;AACEmS;AAAiDzO;AAAgB;;;;AAIjE/H;AACF;;;AAII6K;;;AAGA4L;AAAclW;AAAoC;AACpD;;;AAGAnD;;AACsBC;AAAS;AAE/B;AACA;;;AAGEqZ;;AAEF;AACEA;;;AAGF;AACEA;AACAC;AACAC;AACF;AACEF;AACF;;AAEA;;;;AAII;AACAG;AACAC;;AAEAL;AAAclW;AAAoC;AACpD;AACA;AACF;AAEA;AACE1G;AAEIC;AACAC;AACAC;AACF;AAEJ;AACE;AACAH;;AAEA;;AACwB+c;AAAU;AAClC;AACF;;AAGF;AACF","debugId":"b4ee2d73-3b07-422f-bbc3-db4f36cb62dc"}
|