socket 1.1.39 → 1.1.40
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 +8 -0
- package/dist/cli.js +15 -6
- package/dist/cli.js.map +1 -1
- package/dist/constants.js +4 -4
- package/dist/constants.js.map +1 -1
- package/dist/tsconfig.dts.tsbuildinfo +1 -1
- package/dist/types/commands/fix/coana-fix.d.mts.map +1 -1
- package/dist/utils.js +100 -100
- package/dist/utils.js.map +1 -1
- package/package.json +2 -2
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/branch-cleanup.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/pnpm/cmd-pnpm.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/validate-reachability-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/yarn/cmd-yarn.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 description: '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 description: '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 { debugFileOp } from '../../utils/debug.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mdTableStringNumber } from '../../utils/markdown.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\nimport { fileLink } from '../../utils/terminal-link.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 type OutputAnalyticsConfig = {\n filepath: string\n outputKind: OutputKind\n repo: string\n scope: string\n time: number\n}\n\nexport async function outputAnalytics(\n result: CResult<\n | SocketSdkSuccessResult<'getOrgAnalytics'>['data']\n | SocketSdkSuccessResult<'getRepoAnalytics'>['data']\n >,\n { filepath, outputKind, repo, scope, time }: OutputAnalyticsConfig,\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 debugFileOp('write', filepath)\n logger.success(`Data successfully written to ${fileLink(filepath)}`)\n } catch (e) {\n debugFileOp('write', filepath, 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 debugFileOp('write', filepath)\n logger.success(`Data successfully written to ${fileLink(filepath)}`)\n } catch (e) {\n debugFileOp('write', filepath, 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 { 0: key, 1: 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 { 0: key, 1: 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 type HandleAnalyticsConfig = {\n filepath: string\n outputKind: OutputKind\n repo: string\n scope: string\n time: number\n}\n\nexport async function handleAnalytics({\n filepath,\n outputKind,\n repo,\n scope,\n time,\n}: HandleAnalyticsConfig) {\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, {\n FLAG_JSON,\n FLAG_MARKDOWN,\n V1_MIGRATION_GUIDE_URL,\n} 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'\nimport { webLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 default: '',\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 parentName,\n importMeta,\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 {\n file: filepath,\n json,\n markdown,\n } = cli.flags as { file: string; json: boolean; markdown: boolean }\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 the ${webLink(V1_MIGRATION_GUIDE_URL, '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: !filepath || !!json || !!markdown,\n message: `The \\`--file\\` flag is only valid when using \\`${FLAG_JSON}\\` or \\`${FLAG_MARKDOWN}\\``,\n fail: 'bad',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 filepath,\n outputKind,\n repo: repoName,\n scope,\n time: time === '90' ? 90 : time === '30' ? 30 : 7,\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 { description: `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, {\n FLAG_JSON,\n OUTPUT_JSON,\n OUTPUT_MARKDOWN,\n} 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 === OUTPUT_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 === OUTPUT_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 \\`${FLAG_JSON}\\` flag`,\n )\n debugFn('error', 'Markdown conversion failed')\n debugDir('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, {\n FLAG_JSON,\n FLAG_MARKDOWN,\n V1_MIGRATION_GUIDE_URL,\n} 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 { webLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 the ${webLink(`${constants.SOCKET_WEBSITE_URL}/pricing`, 'Socket pricing page')}.\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 parentName,\n importMeta,\n })\n\n const {\n interactive,\n json,\n markdown,\n org: orgFlag,\n page,\n perPage,\n } = cli.flags as {\n interactive: boolean\n json: boolean\n markdown: boolean\n org: string\n page: number\n perPage: number\n }\n\n const dryRun = !!cli.flags['dryRun']\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 { 0: 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 the ${webLink(V1_MIGRATION_GUIDE_URL, '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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 fs from 'node:fs'\nimport path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\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 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 | undefined,\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 if (constants.ENV.SOCKET_CLI_DEBUG) {\n const fileInfo = await Promise.all(\n packagePaths.map(async p => {\n const absPath = path.resolve(process.cwd(), p)\n const stat = await fs.promises.stat(absPath)\n return { path: absPath, size: stat.size }\n }),\n )\n logger.info(\n `[DEBUG] ${new Date().toISOString()} Uploading full scan manifests: ${JSON.stringify(fileInfo)}`,\n )\n }\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 { description: '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 description: '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 { formatErrorWithDetail } from '../../utils/errors.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(status: string) {\n scanStatus = status\n updateProgress()\n }\n\n function updatePolicy(status: string) {\n policyStatus = status\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', 'Failed to parse report data line as JSON')\n debugDir('error', { 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:\n formatErrorWithDetail('Error requesting scan', e) ||\n 'Error requesting scan: (no error message found)',\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:\n formatErrorWithDetail('Error requesting policy', e) ||\n 'Error requesting policy: (no error message found)',\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, { UNKNOWN_VALUE } 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\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, {\n EXT_JSON,\n OUTPUT_JSON,\n OUTPUT_TEXT,\n} 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 === OUTPUT_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 === OUTPUT_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 unhealthy report, do we?\n // if (!scanReport.data.healthy) {\n // logger.fail(failMsgWithBadge(scanReport.message, scanReport.cause))\n // return\n // }\n\n if (\n outputKind === OUTPUT_JSON ||\n (outputKind === OUTPUT_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 // Not short so must be a regular report.\n scanReport.data as ScanReport,\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 constants from '../../constants.mts'\nimport { handleApiCall } from '../../utils/api.mts'\nimport { extractTier1ReachabilityScanId } from '../../utils/coana.mts'\nimport { spawnCoanaDlx } from '../../utils/dlx.mts'\nimport { hasEnterpriseOrgPlan } from '../../utils/organization.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\nimport { socketDevLink } from '../../utils/terminal-link.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 reachConcurrency: number\n reachDebug: boolean\n reachDisableAnalytics: boolean\n reachDisableAnalysisSplitting: 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 outputPath?: string | undefined\n packagePaths?: string[] | undefined\n reachabilityOptions: ReachabilityOptions\n repoName?: string | undefined\n spinner?: Spinner | undefined\n target: string\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 outputPath,\n packagePaths,\n reachabilityOptions,\n repoName,\n spinner,\n target,\n uploadManifests = true,\n } = { __proto__: null, ...options } as ReachabilityAnalysisOptions\n\n // Determine the analysis target - make it relative to cwd if absolute.\n let analysisTarget = target\n if (path.isAbsolute(analysisTarget)) {\n analysisTarget = path.relative(cwd, analysisTarget) || '.'\n }\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 ${socketDevLink('upgrade your plan', '/pricing')}. This feature is only available for organizations with an enterprise plan.`,\n }\n }\n\n const wasSpinning = !!spinner?.isSpinning\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 // 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 // Ensure uploaded manifest files are relative to analysis target as coana resolves SBOM manifest files relative to this path\n const uploadCResult = await handleApiCall(\n sockSdk.uploadManifestFiles(\n orgSlug,\n filepathsToUpload,\n path.resolve(cwd, analysisTarget),\n ),\n {\n description: '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 const outputFilePath = outputPath || constants.DOT_SOCKET_DOT_FACTS_JSON\n // Build Coana arguments.\n const coanaArgs = [\n 'run',\n analysisTarget,\n '--output-dir',\n path.dirname(outputFilePath),\n '--socket-mode',\n outputFilePath,\n '--disable-report-submission',\n ...(reachabilityOptions.reachAnalysisTimeout\n ? ['--analysis-timeout', `${reachabilityOptions.reachAnalysisTimeout}`]\n : []),\n ...(reachabilityOptions.reachAnalysisMemoryLimit\n ? ['--memory-limit', `${reachabilityOptions.reachAnalysisMemoryLimit}`]\n : []),\n ...(reachabilityOptions.reachConcurrency\n ? ['--concurrency', `${reachabilityOptions.reachConcurrency}`]\n : []),\n ...(reachabilityOptions.reachDebug ? ['--debug'] : []),\n ...(reachabilityOptions.reachDisableAnalytics\n ? ['--disable-analytics-sharing']\n : []),\n ...(reachabilityOptions.reachDisableAnalysisSplitting\n ? ['--disable-analysis-splitting']\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: Record<string, string> = {}\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 spawnCoanaDlx(coanaArgs, orgSlug, {\n cwd,\n env: coanaEnv,\n spinner,\n stdio: 'inherit',\n })\n\n if (wasSpinning) {\n spinner.start()\n }\n\n return coanaResult.ok\n ? {\n ok: true,\n data: {\n // Use the actual output filename for the scan.\n reachabilityReport: outputFilePath,\n tier1ReachabilityScanId:\n extractTier1ReachabilityScanId(outputFilePath),\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 {\n ENVIRONMENT_YAML,\n ENVIRONMENT_YML,\n SOCKET_JSON,\n} from '../../constants.mts'\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 { REQUIREMENTS_TXT } 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'\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 { REQUIREMENTS_TXT, SOCKET_JSON } from '../../constants.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 { debugDir, debugFn } 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 { socketDocsLink } from '../../utils/terminal-link.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 debugFn('notice', `Creating new scan for ${orgSlug}/${repoName}`)\n debugDir('inspect', {\n autoManifest,\n branchName,\n commitHash,\n defaultBranch,\n interactive,\n pendingHead,\n pullRequest,\n readOnly,\n report,\n reportLevel,\n targets,\n tmp,\n })\n\n if (autoManifest) {\n logger.info('Auto-generating manifest files ...')\n debugFn('notice', 'Auto-manifest mode enabled')\n const sockJson = readOrDefaultSocketJson(cwd)\n const detected = await detectManifestActions(sockJson, cwd)\n debugDir('inspect', { detected })\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 debugFn('warn', 'Failed to fetch supported scan file names')\n debugDir('inspect', { supportedFilesCResult })\n await outputCreateNewScan(supportedFilesCResult, {\n interactive,\n outputKind,\n })\n return\n }\n debugFn(\n 'notice',\n `Fetched ${supportedFilesCResult.data['size']} supported file types`,\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 ${socketDocsLink('/docs/manifest-file-detection-in-socket', 'docs.socket.dev')}`,\n message:\n 'TARGET (file/dir) must contain matching / supported file types for a scan',\n })\n if (!wasValidInput) {\n debugFn('warn', 'No eligible files found to scan')\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 debugFn('notice', 'Read-only mode, exiting early')\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 debugFn('notice', 'Reachability analysis enabled')\n debugDir('inspect', { reachabilityOptions: reach })\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 target: targets[0]!,\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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { 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 debugFn('notice', 'Starting CI scan')\n debugDir('inspect', { autoManifest })\n\n const orgSlugCResult = await getDefaultOrgSlug()\n if (!orgSlugCResult.ok) {\n debugFn('warn', 'Failed to get default org slug')\n debugDir('inspect', { orgSlugCResult })\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 debugFn(\n 'notice',\n `CI scan for ${orgSlug}/${repoName} on branch ${branchName}`,\n )\n debugDir('inspect', { orgSlug, cwd, branchName, repoName })\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 reachConcurrency: 1,\n reachDebug: false,\n reachDisableAnalytics: false,\n reachDisableAnalysisSplitting: 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 {\n CliCommandConfig,\n CliCommandContext,\n} from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'ci',\n description:\n 'Alias for `socket scan create --report` (creates report and exits with error if unhealthy)',\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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 { isConfigFromFlag, 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 (isConfigFromFlag()) {\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, { FLAG_JSON, FLAG_MARKDOWN } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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(({ 0: key, 1: description }) => ` - ${key} -- ${description}`)\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 as { json: boolean; markdown: boolean }\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 { isConfigFromFlag } 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 = isConfigFromFlag()\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, { FLAG_JSON, FLAG_MARKDOWN } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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(({ 0: key, 1: description }) => ` - ${key} -- ${description}`)\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 }: CliCommandContext,\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 isConfigFromFlag,\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 = isConfigFromFlag()\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, { FLAG_JSON, FLAG_MARKDOWN } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport { 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 debugFn('notice', `Setting config ${key} = ${value}`)\n debugDir('inspect', { key, value, outputKind })\n\n const result = updateConfigValue(key, value)\n\n debugFn('notice', `Config update ${result.ok ? 'succeeded' : 'failed'}`)\n debugDir('inspect', { result })\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, { FLAG_JSON, FLAG_MARKDOWN } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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(({ 0: key, 1: description }) => ` - ${key} -- ${description}`)\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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, { FLAG_JSON, FLAG_MARKDOWN } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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(({ 0: key, 1: description }) => ` - ${key} -- ${description}`)\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 argv,\n name: `${parentName} config`,\n importMeta,\n subcommands: {\n auto: cmdConfigAuto,\n get: cmdConfigGet,\n list: cmdConfigList,\n set: cmdConfigSet,\n unset: cmdConfigUnset,\n },\n },\n { description },\n )\n },\n}\n","/**\n * Branch cleanup utilities for socket fix command.\n * Manages local and remote branch lifecycle during PR creation.\n *\n * Critical distinction: Remote branches are sacred when a PR exists, disposable when they don't.\n */\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { gitDeleteBranch, gitDeleteRemoteBranch } from '../../utils/git.mts'\n\n/**\n * Clean up a stale branch (both remote and local).\n * Safe to delete both since no PR exists for this branch.\n *\n * Returns true if cleanup succeeded or should continue, false if should skip GHSA.\n */\nexport async function cleanupStaleBranch(\n branch: string,\n ghsaId: string,\n cwd: string,\n): Promise<boolean> {\n logger.warn(`Stale branch ${branch} found without open PR, cleaning up...`)\n debugFn('notice', `cleanup: deleting stale branch ${branch}`)\n\n const deleted = await gitDeleteRemoteBranch(branch, cwd)\n if (!deleted) {\n logger.error(\n `Failed to delete stale remote branch ${branch}, skipping ${ghsaId}.`,\n )\n debugFn('error', `cleanup: remote deletion failed for ${branch}`)\n return false\n }\n\n // Clean up local branch too to avoid conflicts.\n await gitDeleteBranch(branch, cwd)\n return true\n}\n\n/**\n * Clean up branches after PR creation failure.\n * Safe to delete both remote and local since no PR was created.\n */\nexport async function cleanupFailedPrBranches(\n branch: string,\n cwd: string,\n): Promise<void> {\n // Clean up pushed branch since PR creation failed.\n // Safe to delete both remote and local since no PR exists.\n await gitDeleteRemoteBranch(branch, cwd)\n await gitDeleteBranch(branch, cwd)\n}\n\n/**\n * Clean up local branch after successful PR creation.\n * Keeps remote branch - PR needs it to be mergeable.\n */\nexport async function cleanupSuccessfulPrLocalBranch(\n branch: string,\n cwd: string,\n): Promise<void> {\n // Clean up local branch only - keep remote branch for PR merge.\n await gitDeleteBranch(branch, cwd)\n}\n\n/**\n * Clean up branches in catch block after unexpected error.\n * Safe to delete both remote and local since no PR was created.\n */\nexport async function cleanupErrorBranches(\n branch: string,\n cwd: string,\n remoteBranchExists: boolean,\n): Promise<void> {\n // Clean up remote branch if it exists (push may have succeeded before error).\n // Safe to delete both remote and local since no PR was created.\n if (remoteBranchExists) {\n await gitDeleteRemoteBranch(branch, cwd)\n }\n await gitDeleteBranch(branch, cwd)\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\n/**\n * Extract unique package names with ecosystems from vulnerability details.\n */\nfunction getUniquePackages(details: GhsaDetails): string[] {\n return [\n ...new Set(\n details.vulnerabilities.nodes.map(\n v => `${v.package.name} (${v.package.ecosystem})`,\n ),\n ),\n ]\n}\n\nexport type SocketFixBranchParser = (\n branch: string,\n) => SocketFixBranchParseResult | undefined\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(\n branch: string,\n ): SocketFixBranchParseResult | undefined {\n const match = pattern.exec(branch) as [string, string] | null\n if (!match) {\n return undefined\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> | undefined,\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 = getUniquePackages(details)\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 = getUniquePackages(details)\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 GQL_PAGE_SENTINEL,\n GQL_PR_STATE_CLOSED,\n GQL_PR_STATE_MERGED,\n GQL_PR_STATE_OPEN,\n UNKNOWN_VALUE,\n} from '../../constants.mts'\nimport { formatErrorWithDetail } from '../../utils/errors.mts'\nimport { gitDeleteRemoteBranch } from '../../utils/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 type OpenPrResult =\n | { ok: true; pr: OctokitResponse<Pr> }\n | { ok: false; reason: 'already_exists'; error: RequestError }\n | {\n ok: false\n reason: 'validation_error'\n error: RequestError\n details: string\n }\n | { ok: false; reason: 'permission_denied'; error: RequestError }\n | { ok: false; reason: 'network_error'; error: RequestError }\n | { ok: false; reason: 'unknown'; error: Error }\n\nexport async function openSocketFixPr(\n owner: string,\n repo: string,\n branch: string,\n ghsaIds: string[],\n options?: OpenSocketFixPrOptions | undefined,\n): Promise<OpenPrResult> {\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 const pr = await octokit.pulls.create(octokitPullsCreateParams)\n return { ok: true, pr }\n } catch (e) {\n // Handle RequestError from Octokit.\n if (e instanceof RequestError) {\n const errors = (e.response?.data as any)?.['errors']\n const errorMessages = Array.isArray(errors)\n ? errors.map(\n d => d.message?.trim() ?? `${d.resource}.${d.field} (${d.code})`,\n )\n : []\n\n // Check for \"PR already exists\" error.\n if (\n errorMessages.some(msg =>\n msg.toLowerCase().includes('pull request already exists'),\n )\n ) {\n debugFn('error', 'Failed to open pull request: already exists')\n return { ok: false, reason: 'already_exists', error: e }\n }\n\n // Check for validation errors (e.g., no commits between branches).\n if (errors && errors.length > 0) {\n const details = errorMessages.map(d => `- ${d}`).join('\\n')\n debugFn('error', `Failed to open pull request:\\n${details}`)\n return { ok: false, reason: 'validation_error', error: e, details }\n }\n\n // Check HTTP status codes.\n if (e.status === 403 || e.status === 401) {\n debugFn('error', 'Failed to open pull request: permission denied')\n return { ok: false, reason: 'permission_denied', error: e }\n }\n\n if (e.status && e.status >= 500) {\n debugFn('error', 'Failed to open pull request: network error')\n return { ok: false, reason: 'network_error', error: e }\n }\n }\n\n // Unknown error.\n debugFn('error', `Failed to open pull request: ${e}`)\n return { ok: false, reason: 'unknown', error: e as Error }\n }\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 cleanupSocketFixPrs(\n owner: string,\n repo: string,\n ghsaId: string,\n): Promise<PrMatch[]> {\n const contextualMatches = await getSocketFixPrsWithContext(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 // Merge the base branch into the head branch to update the PR.\n await octokit.repos.merge({\n owner,\n repo,\n // The PR branch (destination).\n base: match.headRefName,\n // The target branch (source).\n head: match.baseRefName,\n })\n debugFn('notice', `pr: updating stale ${prRef}`)\n // Update cache entry - only GraphQL is used now.\n context.entry.mergeStateStatus = 'CLEAN'\n // Mark cache to be saved.\n cachesToSave.set(context.cacheKey, context.data)\n } catch (e) {\n debugFn(\n 'error',\n formatErrorWithDetail(`pr: failed to update ${prRef}`, e),\n )\n debugDir('error', e)\n }\n }\n\n // Clean up merged PR branches.\n if (match.state === GQL_PR_STATE_MERGED) {\n const { number: prNum } = match\n const prRef = `PR #${prNum}`\n try {\n const success = await gitDeleteRemoteBranch(match.headRefName)\n if (success) {\n debugFn(\n 'notice',\n `pr: deleted merged branch ${match.headRefName} for ${prRef}`,\n )\n } else {\n debugFn(\n 'warn',\n `pr: failed to delete branch ${match.headRefName} for ${prRef}`,\n )\n }\n } catch (e) {\n // Don't treat this as a hard error - branch might already be deleted.\n debugFn(\n 'warn',\n formatErrorWithDetail(\n `pr: failed to delete branch ${match.headRefName} for ${prRef}`,\n e,\n ),\n )\n debugDir('error', e)\n }\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 is PromiseFulfilledResult<PrMatch> => r.status === 'fulfilled',\n )\n\n return fulfilledMatches.map(r => r.value)\n}\n\nexport type PrAutoMergeState = {\n enabled: boolean\n details?: string[] | undefined\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 getSocketFixPrs(\n owner: string,\n repo: string,\n options?: SocketPrsOptions | undefined,\n): Promise<PrMatch[]> {\n return (await getSocketFixPrsWithContext(owner, repo, options)).map(\n d => d.match,\n )\n}\n\ntype 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\ntype GqlPullRequestsResponse = {\n repository: {\n pullRequests: {\n pageInfo: {\n hasNextPage: boolean\n endCursor: string | null\n }\n nodes: GqlPrNode[]\n }\n }\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 getSocketFixPrsWithContext(\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 octokitGraphql = getOctokitGraphql()\n const contextualMatches: ContextualPrMatch[] = []\n const states = (\n typeof statesValue === 'string'\n ? statesValue.toLowerCase() === 'all'\n ? [GQL_PR_STATE_OPEN, GQL_PR_STATE_CLOSED, GQL_PR_STATE_MERGED]\n : [statesValue]\n : statesValue\n ).map(s => s.toUpperCase())\n\n try {\n let hasNextPage = true\n let cursor: string | null = null\n let pageIndex = 0\n const gqlCacheKey = `${repo}-pr-graphql-snapshot-${states.join('-').toLowerCase()}`\n while (hasNextPage) {\n // eslint-disable-next-line no-await-in-loop\n const gqlResp = (await cacheFetch(\n `${gqlCacheKey}-page-${pageIndex}`,\n () =>\n octokitGraphql(\n `\n query($owner: String!, $repo: String!, $states: [PullRequestState!], $after: String) {\n repository(owner: $owner, name: $repo) {\n pullRequests(first: 100, states: $states, after: $after, orderBy: {field: CREATED_AT, direction: DESC}) {\n pageInfo {\n hasNextPage\n endCursor\n }\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 after: cursor,\n },\n ),\n )) as GqlPullRequestsResponse\n\n const { nodes, pageInfo } = gqlResp?.repository?.pullRequests ?? {\n nodes: [],\n pageInfo: { hasNextPage: false, endCursor: null },\n }\n\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}-page-${pageIndex}`,\n data: gqlResp,\n entry: node,\n index: i,\n parent: nodes,\n },\n match: {\n ...node,\n author: login ?? UNKNOWN_VALUE,\n },\n })\n }\n }\n\n // Continue to next page.\n hasNextPage = pageInfo.hasNextPage\n cursor = pageInfo.endCursor\n pageIndex += 1\n\n // Safety limit to prevent infinite loops.\n if (pageIndex === GQL_PAGE_SENTINEL) {\n debugFn(\n 'warn',\n `GraphQL pagination reached safety limit (${GQL_PAGE_SENTINEL} pages) for ${owner}/${repo}`,\n )\n break\n }\n\n // Early exit optimization: if we found matches and only looking for specific GHSA,\n // we can stop pagination since we likely found what we need.\n if (contextualMatches.length > 0 && ghsaId) {\n break\n }\n }\n } catch (e) {\n debugFn('error', `GraphQL pagination failed for ${owner}/${repo}`)\n debugDir('error', e)\n }\n\n return contextualMatches\n}\n","import { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { debugFn, isDebug } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getSocketFixPrs } 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 | undefined {\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 undefined\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 | undefined\n}\n\nexport interface MissingEnvVars {\n missing: string[]\n present: string[]\n}\n\n/**\n * Get formatted instructions for setting CI environment variables.\n */\nexport function getCiEnvInstructions(): string {\n return (\n 'To enable automatic pull request creation, run in CI with these environment variables:\\n' +\n ' - CI=1\\n' +\n ' - SOCKET_CLI_GITHUB_TOKEN=<your-github-token>\\n' +\n ' - SOCKET_CLI_GIT_USER_NAME=<git-username>\\n' +\n ' - SOCKET_CLI_GIT_USER_EMAIL=<git-email>'\n )\n}\n\n/**\n * Check which required CI environment variables are missing.\n * Returns lists of missing and present variables.\n */\nexport function checkCiEnvVars(): MissingEnvVars {\n const {\n CI,\n SOCKET_CLI_GIT_USER_EMAIL,\n SOCKET_CLI_GIT_USER_NAME,\n SOCKET_CLI_GITHUB_TOKEN,\n } = constants.ENV\n\n const missing: string[] = []\n const present: string[] = []\n\n if (CI) {\n present.push('CI')\n } else {\n missing.push('CI')\n }\n\n if (SOCKET_CLI_GIT_USER_EMAIL) {\n present.push('SOCKET_CLI_GIT_USER_EMAIL')\n } else {\n missing.push('SOCKET_CLI_GIT_USER_EMAIL')\n }\n\n if (SOCKET_CLI_GIT_USER_NAME) {\n present.push('SOCKET_CLI_GIT_USER_NAME')\n } else {\n missing.push('SOCKET_CLI_GIT_USER_NAME')\n }\n\n if (SOCKET_CLI_GITHUB_TOKEN) {\n present.push('SOCKET_CLI_GITHUB_TOKEN')\n } else {\n missing.push('SOCKET_CLI_GITHUB_TOKEN (or GITHUB_TOKEN)')\n }\n\n return { missing, present }\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 const envCheck = checkCiEnvVars()\n\n // Provide clear feedback about missing environment variables.\n if (constants.ENV.CI && envCheck.missing.length > 1) {\n // CI is set but other required vars are missing.\n const missingExceptCi = envCheck.missing.filter(v => v !== 'CI')\n if (missingExceptCi.length) {\n logger.warn(\n `CI mode detected, but pull request creation is disabled due to missing environment variables:\\n` +\n ` Missing: ${joinAnd(missingExceptCi)}\\n` +\n ` Set these variables to enable automatic pull request creation.`,\n )\n }\n } else if (\n // If not in CI but some CI-related env vars are set.\n !constants.ENV.CI &&\n envCheck.present.length &&\n // then log about it when in debug mode.\n isDebug('notice')\n ) {\n debugFn(\n 'notice',\n `miss: fixEnv.isCi is false, expected ${joinAnd(envCheck.missing)} to be set`,\n )\n }\n\n let repoInfo: RepoInfo | undefined\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 getSocketFixPrs(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 { promises as fs } from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { readJsonSync } from '@socketsecurity/registry/lib/fs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport {\n cleanupErrorBranches,\n cleanupFailedPrBranches,\n cleanupStaleBranch,\n cleanupSuccessfulPrLocalBranch,\n} from './branch-cleanup.mts'\nimport {\n checkCiEnvVars,\n getCiEnvInstructions,\n getFixEnv,\n} from './env-helpers.mts'\nimport { getSocketFixBranchName, getSocketFixCommitMessage } from './git.mts'\nimport { getSocketFixPrs, openSocketFixPr } from './pull-request.mts'\nimport {\n DOT_SOCKET_DOT_FACTS_JSON,\n FLAG_DRY_RUN,\n GQL_PR_STATE_OPEN,\n} from '../../constants.mts'\nimport { handleApiCall } from '../../utils/api.mts'\nimport { cmdFlagValueToArray } from '../../utils/cmd.mts'\nimport { spawnCoanaDlx } from '../../utils/dlx.mts'\nimport { getErrorCause } from '../../utils/errors.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'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\ntype DiscoverGhsaIdsOptions = {\n cwd?: string | undefined\n limit?: number | undefined\n spinner?: Spinner | undefined\n}\n\n/**\n * Discovers GHSA IDs by running coana without applying fixes.\n * Returns a list of GHSA IDs, optionally limited.\n */\nasync function discoverGhsaIds(\n orgSlug: string,\n tarHash: string,\n fixConfig: FixConfig,\n options?: DiscoverGhsaIdsOptions | undefined,\n): Promise<string[]> {\n const {\n cwd = process.cwd(),\n limit,\n spinner,\n } = {\n __proto__: null,\n ...options,\n } as DiscoverGhsaIdsOptions\n\n const foundCResult = await spawnCoanaDlx(\n [\n 'compute-fixes-and-upgrade-purls',\n cwd,\n '--manifests-tar-hash',\n tarHash,\n ...(fixConfig.rangeStyle ? ['--range-style', fixConfig.rangeStyle] : []),\n ...(fixConfig.minimumReleaseAge\n ? ['--minimum-release-age', fixConfig.minimumReleaseAge]\n : []),\n ...(fixConfig.include.length ? ['--include', ...fixConfig.include] : []),\n ...(fixConfig.exclude.length ? ['--exclude', ...fixConfig.exclude] : []),\n ...(fixConfig.disableMajorUpdates ? ['--disable-major-updates'] : []),\n ...(fixConfig.showAffectedDirectDependencies\n ? ['--show-affected-direct-dependencies']\n : []),\n ...fixConfig.unknownFlags,\n ],\n orgSlug,\n { cwd, spinner },\n )\n\n if (foundCResult.ok) {\n const foundIds = cmdFlagValueToArray(\n /(?<=Vulnerabilities found:).*/.exec(foundCResult.data),\n )\n return limit !== undefined ? foundIds.slice(0, limit) : foundIds\n }\n return []\n}\n\nexport async function coanaFix(\n fixConfig: FixConfig,\n): Promise<CResult<{ data?: unknown; fixed: boolean }>> {\n const {\n applyFixes,\n autopilot,\n cwd,\n disableMajorUpdates,\n exclude,\n ghsas,\n include,\n limit,\n minimumReleaseAge,\n orgSlug,\n outputFile,\n showAffectedDirectDependencies,\n spinner,\n } = 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 // Exclude any .socket.facts.json files that happen to be in the scan\n // folder before the analysis was run.\n const filepathsToUpload = scanFilepaths.filter(\n p => path.basename(p).toLowerCase() !== DOT_SOCKET_DOT_FACTS_JSON,\n )\n const uploadCResult = await handleApiCall(\n sockSdk.uploadManifestFiles(orgSlug, filepathsToUpload, cwd),\n {\n description: '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 // Inform user about local mode when fixes will be applied.\n if (applyFixes && ghsas.length) {\n const envCheck = checkCiEnvVars()\n if (envCheck.present.length) {\n // Some CI vars are set but not all - show what's missing.\n if (envCheck.missing.length) {\n logger.info(\n 'Running in local mode - fixes will be applied directly to your working directory.\\n' +\n `Missing environment variables for PR creation: ${joinAnd(envCheck.missing)}`,\n )\n }\n } else {\n // No CI vars are present - show general local mode message.\n logger.info(\n 'Running in local mode - fixes will be applied directly to your working directory.\\n' +\n getCiEnvInstructions(),\n )\n }\n }\n\n let ids: string[]\n if (isAll && limit > 0) {\n ids = await discoverGhsaIds(orgSlug, tarHash, fixConfig, {\n cwd,\n limit,\n spinner,\n })\n } else if (limit > 0) {\n ids = ghsas.slice(0, limit)\n } else {\n ids = []\n }\n\n if (limit < 1 || ids.length === 0) {\n spinner?.stop()\n return { ok: true, data: { fixed: false } }\n }\n\n // Create a temporary file for the output.\n const tmpDir = os.tmpdir()\n const tmpFile = path.join(tmpDir, `socket-fix-${Date.now()}.json`)\n\n try {\n const fixCResult = await spawnCoanaDlx(\n [\n 'compute-fixes-and-upgrade-purls',\n cwd,\n '--manifests-tar-hash',\n tarHash,\n '--apply-fixes-to',\n ...ids,\n ...(fixConfig.rangeStyle\n ? ['--range-style', fixConfig.rangeStyle]\n : []),\n ...(minimumReleaseAge\n ? ['--minimum-release-age', minimumReleaseAge]\n : []),\n ...(include.length ? ['--include', ...include] : []),\n ...(exclude.length ? ['--exclude', ...exclude] : []),\n ...(!applyFixes ? [FLAG_DRY_RUN] : []),\n '--output-file',\n tmpFile,\n ...(disableMajorUpdates ? ['--disable-major-updates'] : []),\n ...(showAffectedDirectDependencies\n ? ['--show-affected-direct-dependencies']\n : []),\n ...fixConfig.unknownFlags,\n ],\n fixConfig.orgSlug,\n { cwd, spinner, stdio: 'inherit' },\n )\n\n spinner?.stop()\n\n if (!fixCResult.ok) {\n return fixCResult\n }\n\n // Read the temporary file to get the actual fixes result.\n const fixesResultJson = readJsonSync(tmpFile, { throws: false })\n\n // Copy to outputFile if provided.\n if (outputFile) {\n logger.info(`Copying fixes result to ${outputFile}`)\n const tmpContent = await fs.readFile(tmpFile, 'utf8')\n await fs.writeFile(outputFile, tmpContent, 'utf8')\n }\n\n return { ok: true, data: { data: fixesResultJson, fixed: true } }\n } finally {\n // Clean up the temporary file.\n try {\n await fs.unlink(tmpFile)\n } catch {\n // Ignore cleanup errors.\n }\n }\n }\n\n // Adjust limit based on open Socket Fix PRs.\n let adjustedLimit = limit\n if (shouldOpenPrs && fixEnv.repoInfo) {\n try {\n const openPrs = await getSocketFixPrs(\n fixEnv.repoInfo.owner,\n fixEnv.repoInfo.repo,\n { states: GQL_PR_STATE_OPEN },\n )\n const openPrCount = openPrs.length\n // Reduce limit by number of open PRs to avoid creating too many.\n adjustedLimit = Math.max(0, limit - openPrCount)\n if (openPrCount > 0) {\n debugFn(\n 'notice',\n `limit: adjusted from ${limit} to ${adjustedLimit} (${openPrCount} open Socket Fix ${pluralize('PR', openPrCount)}`,\n )\n }\n } catch (e) {\n debugFn('warn', 'Failed to count open PRs, using original limit')\n debugDir('error', e)\n }\n }\n\n const shouldSpawnCoana = adjustedLimit > 0\n\n let ids: string[] | undefined\n\n if (shouldSpawnCoana && isAll) {\n ids = await discoverGhsaIds(orgSlug, tarHash, fixConfig, {\n cwd,\n limit: adjustedLimit,\n spinner,\n })\n } else if (shouldSpawnCoana) {\n ids = ghsas.slice(0, adjustedLimit)\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.\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 spawnCoanaDlx(\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 ...(minimumReleaseAge\n ? ['--minimum-release-age', minimumReleaseAge]\n : []),\n ...(include.length ? ['--include', ...include] : []),\n ...(exclude.length ? ['--exclude', ...exclude] : []),\n ...(disableMajorUpdates ? ['--disable-major-updates'] : []),\n ...(showAffectedDirectDependencies\n ? ['--show-affected-direct-dependencies']\n : []),\n ...fixConfig.unknownFlags,\n ],\n fixConfig.orgSlug,\n { cwd, spinner, stdio: 'inherit' },\n )\n\n if (!fixCResult.ok) {\n logger.error(`Update failed for ${ghsaId}: ${getErrorCause(fixCResult)}`)\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 an open PR already exists for this GHSA.\n // eslint-disable-next-line no-await-in-loop\n const existingOpenPrs = await getSocketFixPrs(\n fixEnv.repoInfo.owner,\n fixEnv.repoInfo.repo,\n { ghsaId, states: GQL_PR_STATE_OPEN },\n )\n\n if (existingOpenPrs.length > 0) {\n const prNum = existingOpenPrs[0]!.number\n logger.info(`PR #${prNum} already exists for ${ghsaId}, skipping.`)\n debugFn('notice', `skip: open PR #${prNum} exists for ${ghsaId}`)\n continue ghsaLoop\n }\n\n // If branch exists but no open PR, delete the stale branch.\n // This handles cases where PR creation failed but branch was pushed.\n // eslint-disable-next-line no-await-in-loop\n if (await gitRemoteBranchExists(branch, cwd)) {\n // eslint-disable-next-line no-await-in-loop\n const shouldContinue = await cleanupStaleBranch(branch, ghsaId, cwd)\n if (!shouldContinue) {\n continue ghsaLoop\n }\n }\n\n // Check for GitHub token before doing any git operations.\n if (!fixEnv.githubToken) {\n logger.error(\n 'Cannot create pull request: SOCKET_CLI_GITHUB_TOKEN environment variable is not set.\\n' +\n 'Set SOCKET_CLI_GITHUB_TOKEN or GITHUB_TOKEN to enable PR creation.',\n )\n debugFn('error', `skip: missing GitHub token for ${ghsaId}`)\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 prResult = 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 (prResult.ok) {\n const { data } = prResult.pr\n const prRef = `PR #${data.number}`\n\n logger.success(`Opened ${prRef} for ${ghsaId}.`)\n\n if (autopilot) {\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 // Clean up local branch only - keep remote branch for PR merge.\n // eslint-disable-next-line no-await-in-loop\n await cleanupSuccessfulPrLocalBranch(branch, cwd)\n } else {\n // Handle PR creation failures.\n if (prResult.reason === 'already_exists') {\n logger.info(\n `PR already exists for ${ghsaId} (this should not happen due to earlier check).`,\n )\n // Don't delete branch - PR exists and needs it.\n } else if (prResult.reason === 'validation_error') {\n logger.error(\n `Failed to create PR for ${ghsaId}:\\n${prResult.details}`,\n )\n // eslint-disable-next-line no-await-in-loop\n await cleanupFailedPrBranches(branch, cwd)\n } else if (prResult.reason === 'permission_denied') {\n logger.error(\n `Failed to create PR for ${ghsaId}: Permission denied. Check SOCKET_CLI_GITHUB_TOKEN permissions.`,\n )\n // eslint-disable-next-line no-await-in-loop\n await cleanupFailedPrBranches(branch, cwd)\n } else if (prResult.reason === 'network_error') {\n logger.error(\n `Failed to create PR for ${ghsaId}: Network error. Please try again.`,\n )\n // eslint-disable-next-line no-await-in-loop\n await cleanupFailedPrBranches(branch, cwd)\n } else {\n logger.error(\n `Failed to create PR for ${ghsaId}: ${prResult.error.message}`,\n )\n // eslint-disable-next-line no-await-in-loop\n await cleanupFailedPrBranches(branch, cwd)\n }\n }\n\n // Reset back to base branch for next iteration.\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 } catch (e) {\n logger.warn(\n `Unexpected condition: Push failed for ${ghsaId}, skipping PR creation.`,\n )\n debugDir('error', e)\n // Clean up branches (push may have succeeded before error).\n // eslint-disable-next-line no-await-in-loop\n const remoteBranchExists = await gitRemoteBranchExists(branch, cwd)\n // eslint-disable-next-line no-await-in-loop\n await cleanupErrorBranches(branch, cwd, remoteBranchExists)\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(adjustedLimit, ids.length)}`,\n )\n if (count >= adjustedLimit) {\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 { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { coanaFix } from './coana-fix.mts'\nimport { outputFixResult } from './output-fix-result.mts'\nimport { convertCveToGhsa } from '../../utils/cve-to-ghsa.mts'\nimport { convertPurlToGhsas } from '../../utils/purl-to-ghsa.mts'\n\nimport type { FixConfig } from './types.mts'\nimport type { OutputKind } from '../../types.mts'\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\n\nconst GHSA_FORMAT_REGEXP = /^GHSA-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}$/\nconst CVE_FORMAT_REGEXP = /^CVE-\\d{4}-\\d{4,}$/\n\nexport type HandleFixConfig = Remap<\n FixConfig & {\n applyFixes: boolean\n ghsas: string[]\n orgSlug: string\n outputKind: OutputKind\n unknownFlags: string[]\n }\n>\n\n/**\n * Converts mixed CVE/GHSA/PURL IDs to GHSA IDs only.\n * Filters out invalid IDs and logs conversion results.\n */\nexport async function convertIdsToGhsas(ids: string[]): Promise<string[]> {\n debugFn('notice', `Converting ${ids.length} IDs to GHSA format`)\n debugDir('inspect', { ids })\n\n const validGhsas: string[] = []\n const errors: string[] = []\n\n for (const id of ids) {\n const trimmedId = id.trim()\n\n if (trimmedId.startsWith('GHSA-')) {\n // Already a GHSA ID, validate format\n if (GHSA_FORMAT_REGEXP.test(trimmedId)) {\n validGhsas.push(trimmedId)\n } else {\n errors.push(`Invalid GHSA format: ${trimmedId}`)\n }\n } else if (trimmedId.startsWith('CVE-')) {\n // Convert CVE to GHSA\n if (!CVE_FORMAT_REGEXP.test(trimmedId)) {\n errors.push(`Invalid CVE format: ${trimmedId}`)\n continue\n }\n\n // eslint-disable-next-line no-await-in-loop\n const conversionResult = await convertCveToGhsa(trimmedId)\n if (conversionResult.ok) {\n validGhsas.push(conversionResult.data)\n logger.info(`Converted ${trimmedId} to ${conversionResult.data}`)\n } else {\n errors.push(`${trimmedId}: ${conversionResult.message}`)\n }\n } else if (trimmedId.startsWith('pkg:')) {\n // Convert PURL to GHSAs\n // eslint-disable-next-line no-await-in-loop\n const conversionResult = await convertPurlToGhsas(trimmedId)\n if (conversionResult.ok && conversionResult.data.length) {\n validGhsas.push(...conversionResult.data)\n logger.info(\n `Converted ${trimmedId} to ${conversionResult.data.length} GHSA(s): ${joinAnd(conversionResult.data)}`,\n )\n } else {\n errors.push(\n `${trimmedId}: ${conversionResult.message || 'No GHSAs found'}`,\n )\n }\n } else {\n // Neither CVE, GHSA, nor PURL, skip\n errors.push(\n `Unsupported ID format (expected CVE, GHSA, or PURL): ${trimmedId}`,\n )\n }\n }\n\n if (errors.length) {\n logger.warn(\n `Skipped ${errors.length} invalid IDs:\\n${errors.map(e => ` - ${e}`).join('\\n')}`,\n )\n debugDir('inspect', { errors })\n }\n\n debugFn('notice', `Converted to ${validGhsas.length} valid GHSA IDs`)\n debugDir('inspect', { validGhsas })\n\n return validGhsas\n}\n\nexport async function handleFix({\n applyFixes,\n autopilot,\n cwd,\n disableMajorUpdates,\n exclude,\n ghsas,\n include,\n limit,\n minSatisfying,\n minimumReleaseAge,\n orgSlug,\n outputFile,\n outputKind,\n prCheck,\n rangeStyle,\n showAffectedDirectDependencies,\n spinner,\n unknownFlags,\n}: HandleFixConfig) {\n debugFn('notice', `Starting fix command for ${orgSlug}`)\n debugDir('inspect', {\n applyFixes,\n autopilot,\n cwd,\n disableMajorUpdates,\n exclude,\n ghsas,\n include,\n limit,\n minSatisfying,\n minimumReleaseAge,\n outputFile,\n outputKind,\n prCheck,\n rangeStyle,\n showAffectedDirectDependencies,\n unknownFlags,\n })\n\n await outputFixResult(\n await coanaFix({\n applyFixes,\n autopilot,\n cwd,\n disableMajorUpdates,\n exclude,\n // Convert mixed CVE/GHSA/PURL inputs to GHSA IDs only.\n ghsas: await convertIdsToGhsas(ghsas),\n include,\n limit,\n minimumReleaseAge,\n minSatisfying,\n orgSlug,\n outputFile,\n prCheck,\n rangeStyle,\n showAffectedDirectDependencies,\n spinner,\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, {\n ERROR_UNABLE_RESOLVE_ORG,\n FLAG_ID,\n} 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 = 'Fix CVEs in dependencies'\n\nconst hidden = false\n\nexport const cmdFix = {\n description,\n hidden,\n run,\n}\n\nconst generalFlags: MeowFlags = {\n autopilot: {\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 applyFixes: {\n aliases: ['onlyCompute'],\n type: 'boolean',\n default: true,\n description:\n 'Compute fixes only, do not apply them. Logs what upgrades would be applied. If combined with --output-file, the output file will contain the upgrades that would be applied.',\n // Hidden to allow custom documenting of the negated `--no-apply-fixes` variant.\n hidden: true,\n },\n exclude: {\n type: 'string',\n default: [],\n description:\n 'Exclude workspaces matching these glob patterns. Can be provided as comma separated values or as multiple flags',\n isMultiple: true,\n hidden: false,\n },\n include: {\n type: 'string',\n default: [],\n description:\n 'Include workspaces matching these glob patterns. Can be provided as comma separated values or as multiple flags',\n isMultiple: true,\n hidden: false,\n },\n majorUpdates: {\n type: 'boolean',\n default: true,\n description:\n 'Allow major version updates. Use --no-major-updates to disable.',\n // Hidden to allow custom documenting of the negated `--no-major-updates` variant.\n hidden: true,\n },\n id: {\n type: 'string',\n default: [],\n description: `Provide a list of vulnerability identifiers to compute fixes for:\n - ${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 )} (e.g., GHSA-xxxx-xxxx-xxxx)\n - ${terminalLink(\n 'CVE IDs',\n 'https://cve.mitre.org/cve/identifiers/',\n )} (e.g., CVE-${new Date().getFullYear()}-1234) - automatically converted to GHSA\n - ${terminalLink(\n 'PURLs',\n 'https://github.com/package-url/purl-spec',\n )} (e.g., pkg:npm/package@1.0.0) - automatically converted to GHSA\n Can be provided as comma separated values 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 * pin - Use the exact version (e.g. 1.2.3)\n * preserve - Retain the existing version range style as-is\n `.trim(),\n },\n outputFile: {\n type: 'string',\n default: '',\n description: 'Path to store upgrades as a JSON file at this path.',\n },\n minimumReleaseAge: {\n type: 'string',\n default: '',\n description:\n 'Set a minimum age requirement for suggested upgrade versions (e.g., 1h, 2d, 3w). A higher age requirement reduces the risk of upgrading to malicious versions. For example, setting the value to 1 week (1w) gives ecosystem maintainers one week to remove potentially malicious versions.',\n },\n showAffectedDirectDependencies: {\n type: 'boolean',\n default: false,\n description:\n 'List the direct dependencies responsible for introducing transitive vulnerabilities and list the updates required to resolve the vulnerabilities',\n },\n}\n\nconst hiddenFlags: MeowFlags = {\n autoMerge: {\n ...generalFlags['autopilot'],\n hidden: true,\n } as MeowFlag,\n ghsa: {\n ...generalFlags['id'],\n hidden: true,\n } as MeowFlag,\n\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 }: CliCommandContext,\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({\n ...config.flags,\n // Explicitly document the negated --no-apply-fixes variant.\n noApplyFixes: {\n ...config.flags['applyFixes'],\n hidden: false,\n } as MeowFlag,\n // Explicitly document the negated --no-major-updates variant.\n noMajorUpdates: {\n ...config.flags['majorUpdates'],\n description:\n 'Do not suggest or apply fixes that require major version updates of direct or transitive dependencies',\n hidden: false,\n } as MeowFlag,\n })}\n\n Environment Variables (for CI/PR mode)\n CI Set to enable CI mode\n SOCKET_CLI_GITHUB_TOKEN GitHub token for PR creation (or GITHUB_TOKEN)\n SOCKET_CLI_GIT_USER_NAME Git username for commits\n SOCKET_CLI_GIT_USER_EMAIL Git email for commits\n\n Examples\n $ ${command}\n $ ${command} ${FLAG_ID} CVE-2021-23337\n $ ${command} ./path/to/project --range-style pin\n `,\n }\n\n const cli = meowOrExit(\n {\n argv,\n config,\n parentName,\n importMeta,\n },\n { allowUnknownFlags: false },\n )\n\n const {\n applyFixes,\n autopilot,\n exclude,\n include,\n json,\n limit,\n majorUpdates,\n markdown,\n maxSatisfying,\n minimumReleaseAge,\n outputFile,\n prCheck,\n rangeStyle,\n showAffectedDirectDependencies,\n // We patched in this feature with `npx custompatch meow` at\n // socket-cli/patches/meow#13.2.0.patch.\n unknownFlags = [],\n } = cli.flags as {\n applyFixes: boolean\n autopilot: boolean\n exclude: string[]\n include: string[]\n json: boolean\n limit: number\n majorUpdates: boolean\n markdown: boolean\n maxSatisfying: boolean\n minSatisfying: boolean\n minimumReleaseAge: string\n outputFile: string\n prCheck: boolean\n rangeStyle: RangeStyle\n showAffectedDirectDependencies: boolean\n unknownFlags?: string[]\n }\n\n const dryRun = !!cli.flags['dryRun']\n\n const minSatisfying =\n (cli.flags['minSatisfying'] as boolean) || !maxSatisfying\n\n const disableMajorUpdates = !majorUpdates\n\n const outputKind = getOutputKind(json, 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: !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_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 `${ERROR_UNABLE_RESOLVE_ORG}.\\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 const { spinner } = constants\n\n const ghsas = arrayUnique([\n ...cmdFlagValueToArray(cli.flags['id']),\n ...cmdFlagValueToArray(cli.flags['ghsa']),\n ...cmdFlagValueToArray(cli.flags['purl']),\n ])\n\n const includePatterns = cmdFlagValueToArray(include)\n const excludePatterns = cmdFlagValueToArray(exclude)\n\n await handleFix({\n applyFixes,\n autopilot,\n cwd,\n disableMajorUpdates,\n exclude: excludePatterns,\n ghsas,\n include: includePatterns,\n limit,\n minimumReleaseAge,\n minSatisfying,\n orgSlug,\n outputFile,\n outputKind,\n prCheck,\n rangeStyle,\n showAffectedDirectDependencies,\n spinner,\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 argv,\n name: `${parentName} install`,\n importMeta,\n subcommands: {\n completion: cmdInstallCompletion,\n },\n },\n { description },\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, { REDACTED, SOCKET_JSON } 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 { SOCKET_JSON } from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'json',\n description: `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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 {\n CONFIG_KEY_API_BASE_URL,\n CONFIG_KEY_API_PROXY,\n CONFIG_KEY_API_TOKEN,\n CONFIG_KEY_ENFORCED_ORGS,\n} from '../../constants.mts'\nimport { 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(CONFIG_KEY_ENFORCED_ORGS, enforcedOrgs)\n updateConfigValue(CONFIG_KEY_API_TOKEN, apiToken)\n updateConfigValue(CONFIG_KEY_API_BASE_URL, apiBaseUrl)\n updateConfigValue(CONFIG_KEY_API_PROXY, apiProxy)\n}\n","import { 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, {\n CONFIG_KEY_API_BASE_URL,\n CONFIG_KEY_API_PROXY,\n CONFIG_KEY_API_TOKEN,\n CONFIG_KEY_DEFAULT_ORG,\n} from '../../constants.mts'\nimport {\n getConfigValueOrUndef,\n isConfigFromFlag,\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 { socketDocsLink } from '../../utils/terminal-link.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(CONFIG_KEY_API_BASE_URL) ?? undefined\n apiProxy ??= getConfigValueOrUndef(CONFIG_KEY_API_PROXY) ?? undefined\n const apiTokenInput = await password({\n message: `Enter your ${socketDocsLink('/docs/api-keys', 'Socket.dev API token')} (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 description: '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(CONFIG_KEY_DEFAULT_ORG, orgSlugs[0])\n\n const previousPersistedToken = getConfigValueOrUndef(CONFIG_KEY_API_TOKEN)\n try {\n applyLogin(apiToken, enforcedOrgs, apiBaseUrl, apiProxy)\n logger.success(\n `API credentials ${previousPersistedToken === apiToken ? 'refreshed' : previousPersistedToken ? 'updated' : 'set'}`,\n )\n if (isConfigFromFlag()) {\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 default: '',\n description: 'API server to connect to for login',\n },\n apiProxy: {\n type: 'string',\n default: '',\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 parentName,\n importMeta,\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, apiProxy } = cli.flags as {\n apiBaseUrl?: string | undefined\n apiProxy?: string | undefined\n }\n\n await attemptLogin(apiBaseUrl, apiProxy)\n}\n","import {\n CONFIG_KEY_API_BASE_URL,\n CONFIG_KEY_API_PROXY,\n CONFIG_KEY_API_TOKEN,\n CONFIG_KEY_ENFORCED_ORGS,\n} from '../../constants.mts'\nimport { updateConfigValue } from '../../utils/config.mts'\n\nexport function applyLogout() {\n updateConfigValue(CONFIG_KEY_API_TOKEN, null)\n updateConfigValue(CONFIG_KEY_API_BASE_URL, null)\n updateConfigValue(CONFIG_KEY_API_PROXY, null)\n updateConfigValue(CONFIG_KEY_ENFORCED_ORGS, null)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { applyLogout } from './apply-logout.mts'\nimport { isConfigFromFlag } from '../../utils/config.mts'\n\nexport function attemptLogout() {\n try {\n applyLogout()\n logger.success('Successfully logged out')\n if (isConfigFromFlag()) {\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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, { FLAG_HELP, NPM, PNPM, YARN } from '../../constants.mts'\nimport { spawnCdxgenDlx, spawnSynpDlx } from '../../utils/dlx.mts'\nimport { findUp } from '../../utils/fs.mts'\nimport { isYarnBerry } from '../../utils/yarn-version.mts'\n\nimport type { ShadowBinResult } from '../../shadow/npm/bin.mts'\nimport type { DlxOptions } from '../../utils/dlx.mts'\n\nconst { PACKAGE_LOCK_JSON, PNPM_LOCK_YAML, 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 argvObjectToArray(argvObj: ArgvObject): string[] {\n if (argvObj['help']) {\n return [FLAG_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 const argvMutable = { __proto__: null, ...argvObj } as ArgvObject\n\n const shadowOpts: DlxOptions = {\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\n // Detect package manager based on lockfiles.\n const pnpmLockPath = await findUp(PNPM_LOCK_YAML, { onlyFiles: true })\n\n const npmLockPath = pnpmLockPath\n ? undefined\n : await findUp(PACKAGE_LOCK_JSON, { onlyFiles: true })\n\n const yarnLockPath =\n pnpmLockPath || npmLockPath\n ? undefined\n : await findUp(YARN_LOCK, { onlyFiles: true })\n\n const agent = pnpmLockPath ? PNPM : yarnLockPath && isYarnBerry() ? YARN : NPM\n\n let cleanupPackageLock = false\n if (\n yarnLockPath &&\n argvMutable['type'] !== YARN &&\n nodejsPlatformTypes.has(argvMutable['type'] as string)\n ) {\n if (npmLockPath) {\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 synpResult = await spawnSynpDlx(\n ['--source-file', `./${YARN_LOCK}`],\n {\n ...shadowOpts,\n agent,\n },\n )\n await synpResult.spawnPromise\n argvMutable['type'] = NPM\n cleanupPackageLock = true\n } catch {}\n }\n }\n\n // Use appropriate package manager for cdxgen.\n const shadowResult = await spawnCdxgenDlx(argvObjectToArray(argvMutable), {\n ...shadowOpts,\n agent,\n })\n\n shadowResult.spawnPromise.process.on('exit', () => {\n if (cleanupPackageLock) {\n try {\n // TODO: Consider using trash instead of rmSync for safer deletion.\n // This removes the temporary package-lock.json we created for cdxgen.\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 { joinAnd } from '@socketsecurity/registry/lib/arrays'\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, { FLAG_HELP } from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { filterFlags, isHelpFlag } from '../../utils/cmd.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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: 'Run cdxgen for SBOM generation',\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 context: CliCommandContext,\n): Promise<void> {\n const { parentName } = {\n __proto__: null,\n ...context,\n } as CliCommandContext\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 as { dryRun: boolean }\n\n // Filter Socket flags from argv but keep --no-banner and --help for cdxgen.\n const argsToProcess = filterFlags(argv, { ...commonFlags, ...outputFlags }, [\n '--no-banner',\n FLAG_HELP,\n '-h',\n ])\n const yargv = {\n ...yargsParse(argsToProcess 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)}: ${joinAnd(unknowns)}`,\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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, {\n ENVIRONMENT_YAML,\n ENVIRONMENT_YML,\n FLAG_JSON,\n FLAG_MARKDOWN,\n REQUIREMENTS_TXT,\n SOCKET_JSON,\n} 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 {\n CliCommandConfig,\n CliCommandContext,\n} from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'conda',\n description: `[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 default: '',\n description: `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 default: '',\n description: 'Output path (relative to cwd)',\n },\n stdout: {\n type: 'boolean',\n description: `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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { dryRun, json, markdown } = cli.flags as {\n dryRun: boolean\n json: boolean\n markdown: boolean\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 sockJson = readOrDefaultSocketJson(cwd)\n\n let {\n file: filename,\n out,\n stdin,\n stdout,\n verbose,\n } = cli.flags as {\n file: string\n out: string\n stdin: boolean | undefined\n stdout: boolean | undefined\n verbose: boolean | undefined\n }\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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,\n out,\n outputKind,\n 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, { REQUIREMENTS_TXT, SOCKET_JSON } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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(\n `Using default --gradle-opts from ${SOCKET_JSON}:`,\n gradleOpts,\n )\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, { REQUIREMENTS_TXT, SOCKET_JSON } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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(\n `Using default --gradle-opts from ${SOCKET_JSON}:`,\n gradleOpts,\n )\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, { REQUIREMENTS_TXT, SOCKET_JSON } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 { REQUIREMENTS_TXT, SOCKET_JSON } from '../../constants.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(\n `This command will generate a ${SOCKET_JSON} file in the target cwd.`,\n )\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, { SOCKET_JSON } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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: `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 }: CliCommandContext,\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 { REQUIREMENTS_TXT } from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\n): Promise<void> {\n await meowWithSubcommands(\n {\n argv,\n name: `${parentName} ${config.commandName}`,\n importMeta,\n subcommands: {\n auto: cmdManifestAuto,\n cdxgen: cmdManifestCdxgen,\n conda: cmdManifestConda,\n gradle: cmdManifestGradle,\n kotlin: cmdManifestKotlin,\n scala: cmdManifestScala,\n setup: cmdManifestSetup,\n },\n },\n {\n aliases: {\n yolo: {\n description: config.description,\n hidden: true,\n argv: ['auto'],\n },\n },\n description: config.description,\n flags: config.flags,\n },\n )\n}\n","import { createRequire } from 'node:module'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants, {\n FLAG_DRY_RUN,\n FLAG_HELP,\n FLAG_JSON,\n NPM,\n} from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { filterFlags } from '../../utils/cmd.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagApiRequirementsOutput } from '../../utils/output-formatting.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} from '../../utils/meow-with-subcommands.mts'\n\nconst require = createRequire(import.meta.url)\n\nexport const CMD_NAME = NPM\n\nconst description = 'Wraps npm with Socket security scanning'\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 context: CliCommandContext,\n): Promise<void> {\n const { parentName } = { __proto__: null, ...context } as CliCommandContext\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 \\`${FLAG_DRY_RUN}\\` and \\`${FLAG_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 $ ${command} exec 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 shadowNpmBin = /*@__PURE__*/ require(constants.shadowNpmBinPath)\n\n process.exitCode = 1\n\n // Filter Socket flags from argv but keep --json for npm.\n const argsToForward = filterFlags(argv, { ...commonFlags, ...outputFlags }, [\n FLAG_JSON,\n ])\n const { spawnPromise } = await shadowNpmBin(argsToForward, {\n stdio: 'inherit',\n })\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, { FLAG_DRY_RUN, FLAG_HELP, 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 {\n CliCommandConfig,\n CliCommandContext,\n} from '../../utils/meow-with-subcommands.mts'\n\nconst require = createRequire(import.meta.url)\n\nconst CMD_NAME = NPX\n\nconst description = 'Wraps npx with Socket security scanning'\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 }: CliCommandContext,\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 \\`${FLAG_DRY_RUN}\\` and \\`${FLAG_HELP}\\` flags are caught here.\n\n Use \\`socket wrapper on\\` to alias this command as \\`${NPX}\\`.\n\n Examples\n $ ${command} cowsay\n $ ${command} cowsay@1.6.0 hello\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 shadowNpxBin = /*@__PURE__*/ require(constants.shadowNpxBinPath)\n\n process.exitCode = 1\n\n const { spawnPromise } = await shadowNpxBin(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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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, { FLAG_PROD } 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 // On Windows, npm is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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 // On Windows, bun is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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', FLAG_PROD, '--depth', 'Infinity'],\n {\n cwd,\n // On Windows, pnpm is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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 // On Windows, pnpm is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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 // On Windows, yarn is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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', FLAG_PROD], {\n cwd,\n // On Windows, yarn is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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 { safeNpa } 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 parsing it with npm-package-arg\n // and seeing if it will coerce to a version.\n semver.coerce((safeNpa(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 parsing it with npm-package-arg\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 (safeNpa(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\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', 'Lockfile update failed')\n debugDir('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\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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { 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 debugFn('notice', `Starting optimization for ${cwd}`)\n debugDir('inspect', { cwd, outputKind, pin, prod })\n\n const pkgEnvCResult = await detectAndValidatePackageEnvironment(cwd, {\n cmdName: CMD_NAME,\n logger,\n prod,\n })\n if (!pkgEnvCResult.ok) {\n process.exitCode = pkgEnvCResult.code ?? 1\n debugFn('warn', 'Package environment validation failed')\n debugDir('inspect', { pkgEnvCResult })\n await outputOptimizeResult(pkgEnvCResult, outputKind)\n return\n }\n\n const pkgEnvDetails = pkgEnvCResult.data\n if (!pkgEnvDetails) {\n process.exitCode = 1\n debugFn('warn', 'No package environment details found')\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 debugFn(\n 'notice',\n `Detected package manager: ${pkgEnvDetails.agent} v${pkgEnvDetails.agentVersion}`,\n )\n debugDir('inspect', { pkgEnvDetails })\n\n const { agent, agentVersion } = pkgEnvDetails\n if (agent === VLT) {\n process.exitCode = 1\n debugFn('warn', `${agent} does not support overrides`)\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 debugFn('notice', 'Applying optimization')\n const optimizationResult = await applyOptimization(pkgEnvDetails, {\n pin,\n prod,\n })\n\n if (!optimizationResult.ok) {\n process.exitCode = optimizationResult.code ?? 1\n }\n debugFn(\n 'notice',\n `Optimization ${optimizationResult.ok ? 'succeeded' : 'failed'}`,\n )\n debugDir('inspect', { optimizationResult })\n await outputOptimizeResult(optimizationResult, outputKind)\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 latest version',\n },\n prod: {\n type: 'boolean',\n default: false,\n description: 'Add overrides for production dependencies only',\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} ./path/to/project --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 description: '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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport { 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 debugFn(\n 'notice',\n `Fetching dependencies with limit=${limit}, offset=${offset}`,\n )\n debugDir('inspect', { limit, offset, outputKind })\n\n const result = await fetchDependencies({ limit, offset })\n\n debugFn(\n 'notice',\n `Dependencies ${result.ok ? 'fetched successfully' : 'fetch failed'}`,\n )\n debugDir('inspect', { result })\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, { FLAG_JSON, FLAG_MARKDOWN } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 description: '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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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 { 0: 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 description: '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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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 { 0: 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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport { 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 debugFn('notice', 'Fetching organization list')\n debugDir('inspect', { outputKind })\n\n const data = await fetchOrganization()\n\n debugFn(\n 'notice',\n `Organization list ${data.ok ? 'fetched successfully' : 'fetch failed'}`,\n )\n debugDir('inspect', { data })\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, { FLAG_JSON, FLAG_MARKDOWN } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 argv,\n name: `${parentName} policy`,\n importMeta,\n subcommands: {\n security: cmdOrganizationPolicySecurity,\n license: cmdOrganizationPolicyLicense,\n },\n },\n {\n description,\n defaultSub: 'list', // Backwards compat\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(), { description: '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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 argv,\n name: `${parentName} organization`,\n importMeta,\n subcommands: {\n dependencies: cmdOrganizationDependencies,\n list: cmdOrganizationList,\n quota: cmdOrganizationQuota,\n policy: cmdOrganizationPolicy,\n },\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 description,\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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport { 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 debugFn('notice', `Fetching deep score for ${purl}`)\n debugDir('inspect', { purl, outputKind })\n\n const result = await fetchPurlDeepScore(purl)\n\n debugFn(\n 'notice',\n `Deep score ${result.ok ? 'fetched successfully' : 'fetch failed'}`,\n )\n debugDir('inspect', { result })\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { 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): ${joinAnd(purls)}`,\n )\n\n const batchPackageCResult = await handleApiCall(\n sockSdk.batchPackageFetch(\n { components: purls.map(purl => ({ purl })) },\n {\n alerts: 'true',\n },\n ),\n { description: '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 { joinAnd } from '@socketsecurity/registry/lib/arrays'\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}/${low.length}):`\n const padding = ` ${' '.repeat(Math.max(0, 20 - colorless.length))}`\n\n if (colorize) {\n return `- Alerts (${colors.red(bad.length as any)}/${colors.yellow(mid.length as any)}/${low.length}):${\n padding\n }${joinAnd([\n ...bad.map(a => colors.red(`${colors.dim(`[${a.severity}] `)}${a.type}`)),\n ...mid.map(a =>\n colors.yellow(`${colors.dim(`[${a.severity}] `)}${a.type}`),\n ),\n ...low.map(a => `${colors.dim(`[${a.severity}] `)}${a.type}`),\n ])}`\n }\n return `${colorless}${padding}${joinAnd([\n ...bad.map(a => `[${a.severity}] ${a.type}`),\n ...mid.map(a => `[${a.severity}] ${a.type}`),\n ...low.map(a => `[${a.severity}] ${a.type}`),\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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport { 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 debugFn('notice', `Fetching shallow scores for ${purls.length} packages`)\n debugDir('inspect', { purls, outputKind })\n\n const packageData = await fetchPurlsShallowScore(purls)\n\n debugFn(\n 'notice',\n `Shallow scores ${packageData.ok ? 'fetched successfully' : 'fetch failed'}`,\n )\n debugDir('inspect', { packageData })\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 argv,\n name: `${parentName} package`,\n importMeta,\n subcommands: {\n score: cmdPackageScore,\n shallow: cmdPackageShallow,\n },\n },\n {\n aliases: {\n deep: {\n description,\n hidden: true,\n argv: ['score'],\n },\n },\n description,\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'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { OUTPUT_JSON } 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'\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 === OUTPUT_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 logger.log('')\n\n if (patched.length) {\n logger.group(\n `Successfully processed patches for ${patched.length} ${pluralize('package', patched.length)}:`,\n )\n for (const pkg of patched) {\n logger.success(pkg)\n }\n logger.groupEnd()\n } else {\n logger.warn('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 fastGlob from 'fast-glob'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { debugDir } from '@socketsecurity/registry/lib/debug'\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 {\n DOT_SOCKET_DIR,\n MANIFEST_JSON,\n NODE_MODULES,\n NPM,\n UTF8,\n} from '../../constants.mts'\nimport { getErrorCause } from '../../utils/errors.mts'\nimport { findUp } from '../../utils/fs.mts'\nimport { getPurlObject, normalizePurl } from '../../utils/purl.mts'\n\nimport type { PatchRecord } from './manifest-schema.mts'\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { PackageURL } from '@socketregistry/packageurl-js'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\ntype PatchEntry = {\n key: string\n patch: PatchRecord\n purl: string\n purlObj: PackageURL\n}\n\ntype PatchFileInfo = {\n beforeHash: string\n afterHash: string\n}\n\ntype ApplyNpmPatchesOptions = {\n cwd?: string | undefined\n dryRun?: boolean | undefined\n purlObjs?: PackageURL[] | undefined\n spinner?: Spinner | undefined\n}\n\ntype ApplyNpmPatchesResult = {\n passed: string[]\n failed: string[]\n}\n\nasync function applyNpmPatches(\n socketDir: string,\n patches: PatchEntry[],\n options?: ApplyNpmPatchesOptions | undefined,\n): Promise<ApplyNpmPatchesResult> {\n const {\n cwd = process.cwd(),\n dryRun = false,\n purlObjs,\n spinner,\n } = { __proto__: null, ...options } as ApplyNpmPatchesOptions\n\n const wasSpinning = !!spinner?.isSpinning\n\n spinner?.start()\n\n const patchLookup = new Map<string, PatchEntry>()\n for (const patchInfo of patches) {\n patchLookup.set(patchInfo.purl, patchInfo)\n }\n\n const nmPaths = await findNodeModulesPaths(cwd)\n\n spinner?.stop()\n\n logger.log(\n `Found ${nmPaths.length} ${NODE_MODULES} ${pluralize('folder', nmPaths.length)}`,\n )\n\n logger.group('')\n\n spinner?.start()\n\n const result: ApplyNpmPatchesResult = {\n passed: [],\n failed: [],\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 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\n const purl = `pkg:npm/${pkgJson.name}@${pkgJson.version}`\n const purlObj = getPurlObject(purl, { throws: false })\n if (!purlObj) {\n continue\n }\n\n // Skip if specific packages requested and this isn't one of them\n if (\n purlObjs?.length &&\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(purl)\n if (!patchInfo) {\n continue\n }\n\n spinner?.stop()\n\n logger.log(\n `Found match: ${pkgJson.name}@${pkgJson.version} at ${pkgPath}`,\n )\n logger.log(`Patch key: ${patchInfo.key}`)\n logger.group(`Processing files:`)\n\n spinner?.start()\n\n let passed = true\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 const filePatchPassed = await processFilePatch(\n pkgPath,\n fileName,\n fileInfo,\n socketDir,\n {\n dryRun,\n spinner,\n },\n )\n if (!filePatchPassed) {\n passed = false\n }\n }\n\n logger.groupEnd()\n\n if (passed) {\n result.passed.push(purl)\n } else {\n result.failed.push(purl)\n }\n }\n }\n }\n\n spinner?.stop()\n\n logger.groupEnd()\n\n if (wasSpinning) {\n spinner.start()\n }\n return result\n}\n\n/**\n * Compute SHA256 hash of file contents.\n */\nasync function computeSHA256(filepath: string): Promise<CResult<string>> {\n try {\n const content = await fs.readFile(filepath)\n const hash = crypto.createHash('sha256')\n hash.update(content)\n return {\n ok: true,\n data: hash.digest('hex'),\n }\n } catch (e) {\n return {\n ok: false,\n message: 'Failed to compute file hash',\n cause: `Unable to read file ${filepath}: ${getErrorCause(e)}`,\n }\n }\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 fastGlob.glob([`**/${NODE_MODULES}`], {\n absolute: true,\n cwd: path.dirname(rootNmPath),\n dot: true,\n followSymbolicLinks: false,\n onlyDirectories: true,\n })\n}\n\ntype ProcessFilePatchOptions = {\n dryRun?: boolean | undefined\n spinner?: Spinner | undefined\n}\n\nasync function processFilePatch(\n pkgPath: string,\n fileName: string,\n fileInfo: PatchFileInfo,\n socketDir: string,\n options?: ProcessFilePatchOptions | undefined,\n): Promise<boolean> {\n const { dryRun, spinner } = {\n __proto__: null,\n ...options,\n } as ProcessFilePatchOptions\n\n const wasSpinning = !!spinner?.isSpinning\n\n spinner?.stop()\n\n const filepath = path.join(pkgPath, fileName)\n if (!existsSync(filepath)) {\n logger.log(`File not found: ${fileName}`)\n if (wasSpinning) {\n spinner?.start()\n }\n return false\n }\n\n const currentHashResult = await computeSHA256(filepath)\n if (!currentHashResult.ok) {\n logger.log(\n `Failed to compute hash for: ${fileName}: ${currentHashResult.cause || currentHashResult.message}`,\n )\n if (wasSpinning) {\n spinner?.start()\n }\n return false\n }\n\n if (currentHashResult.data === fileInfo.afterHash) {\n logger.success(`File already patched: ${fileName}`)\n logger.group()\n logger.log(`Current hash: ${currentHashResult.data}`)\n logger.groupEnd()\n if (wasSpinning) {\n spinner?.start()\n }\n return true\n }\n\n if (currentHashResult.data !== fileInfo.beforeHash) {\n logger.fail(`File hash mismatch: ${fileName}`)\n logger.group()\n logger.log(`Expected: ${fileInfo.beforeHash}`)\n logger.log(`Current: ${currentHashResult.data}`)\n logger.log(`Target: ${fileInfo.afterHash}`)\n logger.groupEnd()\n if (wasSpinning) {\n spinner?.start()\n }\n return false\n }\n\n logger.success(`File matches expected hash: ${fileName}`)\n logger.group()\n logger.log(`Current hash: ${currentHashResult.data}`)\n logger.log(`Ready to patch to: ${fileInfo.afterHash}`)\n logger.group()\n\n if (dryRun) {\n logger.log(`(dry run - no changes made)`)\n logger.groupEnd()\n logger.groupEnd()\n if (wasSpinning) {\n spinner?.start()\n }\n return false\n }\n\n const blobPath = path.join(socketDir, 'blobs', fileInfo.afterHash)\n if (!existsSync(blobPath)) {\n logger.fail(`Error: Patch file not found at ${blobPath}`)\n logger.groupEnd()\n logger.groupEnd()\n if (wasSpinning) {\n spinner?.start()\n }\n return false\n }\n\n spinner?.start()\n\n let result = true\n try {\n await fs.copyFile(blobPath, filepath)\n\n // Verify the hash after copying to ensure file integrity.\n const verifyHashResult = await computeSHA256(filepath)\n if (!verifyHashResult.ok) {\n logger.error(\n `Failed to verify hash after patch: ${verifyHashResult.cause || verifyHashResult.message}`,\n )\n result = false\n } else if (verifyHashResult.data !== fileInfo.afterHash) {\n logger.error(`Hash verification failed after patch`)\n logger.group()\n logger.log(`Expected: ${fileInfo.afterHash}`)\n logger.log(`Got: ${verifyHashResult.data}`)\n logger.groupEnd()\n result = false\n } else {\n logger.success(`Patch applied successfully`)\n }\n } catch (e) {\n logger.error('Error applying patch')\n debugDir('error', e)\n result = false\n }\n logger.groupEnd()\n logger.groupEnd()\n\n spinner?.stop()\n\n if (wasSpinning) {\n spinner?.start()\n }\n return result\n}\n\nexport interface HandlePatchConfig {\n cwd: string\n dryRun: boolean\n outputKind: OutputKind\n purlObjs: PackageURL[]\n spinner: 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, DOT_SOCKET_DIR)\n const manifestPath = path.join(dotSocketDirPath, MANIFEST_JSON)\n const manifestContent = await fs.readFile(manifestPath, UTF8)\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 purl = normalizePurl(key)\n if (purls.length && !purls.includes(purl)) {\n continue\n }\n const purlObj = getPurlObject(purl, { 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 purl,\n purlObj,\n })\n }\n\n if (purls.length) {\n spinner.start(`Checking patches for: ${joinAnd(purls)}`)\n } else {\n spinner.start('Scanning all dependencies for available patches')\n }\n\n const patched = []\n\n const npmPatches = patchesByEcosystem.get(NPM)\n if (npmPatches) {\n const patchingResults = await applyNpmPatches(\n dotSocketDirPath,\n npmPatches,\n {\n cwd,\n dryRun,\n purlObjs,\n spinner,\n },\n )\n patched.push(...patchingResults.passed)\n }\n\n spinner.stop()\n\n await outputPatchResult(\n {\n ok: true,\n data: {\n patched,\n },\n },\n outputKind,\n )\n } catch (e) {\n spinner.stop()\n\n let message = 'Failed to apply patches'\n let cause = getErrorCause(e)\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 await outputPatchResult(\n {\n ok: false,\n code: 1,\n message,\n cause,\n },\n outputKind,\n )\n }\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport { arrayUnique } from '@socketsecurity/registry/lib/arrays'\n\nimport { handlePatch } from './handle-patch.mts'\nimport constants, { DOT_SOCKET_DIR, MANIFEST_JSON } from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { cmdFlagValueToArray } from '../../utils/cmd.mts'\nimport { InputError } from '../../utils/errors.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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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} ./path/to/project --package lodash,react\n `,\n }\n\n const cli = meowOrExit(\n {\n argv,\n config,\n parentName,\n importMeta,\n },\n { allowUnknownFlags: false },\n )\n\n const { dryRun, json, markdown } = cli.flags as {\n dryRun: boolean\n json: boolean\n markdown: boolean\n }\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(outputKind, {\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 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, DOT_SOCKET_DIR)\n if (!existsSync(dotSocketDirPath)) {\n throw new InputError(\n `No ${DOT_SOCKET_DIR} directory found in current directory`,\n )\n }\n\n const manifestPath = path.join(dotSocketDirPath, MANIFEST_JSON)\n if (!existsSync(manifestPath)) {\n throw new InputError(\n `No ${MANIFEST_JSON} found in ${DOT_SOCKET_DIR} directory`,\n )\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 { createRequire } from 'node:module'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants, { FLAG_DRY_RUN, FLAG_HELP, PNPM } from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { filterFlags } from '../../utils/cmd.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagApiRequirementsOutput } from '../../utils/output-formatting.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} from '../../utils/meow-with-subcommands.mts'\n\nconst require = createRequire(import.meta.url)\n\nexport const CMD_NAME = PNPM\n\nconst description = 'Wraps pnpm with Socket security scanning'\n\nconst hidden = true\n\nexport const cmdPnpm = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n context: CliCommandContext,\n): Promise<void> {\n const { parentName } = { __proto__: null, ...context } as CliCommandContext\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 \"${PNPM}\" is passed to the ${PNPM} command.\n Only the \\`${FLAG_DRY_RUN}\\` and \\`${FLAG_HELP}\\` flags are caught here.\n\n Use \\`socket wrapper on\\` to alias this command as \\`${PNPM}\\`.\n\n Examples\n $ ${command}\n $ ${command} install\n $ ${command} add package-name\n $ ${command} dlx package-name\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 shadowPnpmBin = /*@__PURE__*/ require(constants.shadowPnpmBinPath)\n\n process.exitCode = 1\n\n // Filter Socket flags from argv.\n const filteredArgv = filterFlags(argv, config.flags)\n\n const { spawnPromise } = await shadowPnpmBin(filteredArgv, {\n stdio: 'inherit',\n })\n\n await spawnPromise\n process.exitCode = 0\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 // On Windows, npm is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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, { FLAG_DRY_RUN, FLAG_HELP } from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 \\`${FLAG_DRY_RUN}\\` and \\`${FLAG_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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 // On Windows, npx is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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, { FLAG_DRY_RUN, FLAG_HELP } from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 \\`${FLAG_DRY_RUN}\\` and \\`${FLAG_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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 { description: '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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport { 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 debugFn('notice', `Creating repository ${orgSlug}/${repoName}`)\n debugDir('inspect', {\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n outputKind,\n })\n\n const data = await fetchCreateRepo({\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n })\n\n debugFn('notice', `Repository creation ${data.ok ? 'succeeded' : 'failed'}`)\n debugDir('inspect', { data })\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, { V1_MIGRATION_GUIDE_URL } 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 { webLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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 { 0: 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 the ${webLink(V1_MIGRATION_GUIDE_URL, '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 description: '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, { V1_MIGRATION_GUIDE_URL } 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 { webLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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 { 0: 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 the ${webLink(V1_MIGRATION_GUIDE_URL, '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 { description: '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 { description: '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 { Direction } from './types.mts'\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: Direction,\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 { Direction } from './types.mts'\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: Direction\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, { FLAG_JSON, FLAG_MARKDOWN } 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 { Direction } from './types.mts'\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 default: '',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n perPage: {\n type: 'number',\n default: 30,\n description: 'Number of results per page',\n shortFlag: 'pp',\n },\n page: {\n type: 'number',\n default: 1,\n description: 'Page number',\n shortFlag: 'p',\n },\n sort: {\n type: 'string',\n default: 'created_at',\n description: 'Sorting option',\n shortFlag: 's',\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 parentName,\n importMeta,\n })\n\n const {\n all,\n direction = 'desc',\n dryRun,\n interactive,\n json,\n markdown,\n org: orgFlag,\n page,\n perPage,\n sort,\n } = cli.flags as {\n all: boolean\n direction: Direction\n dryRun: boolean\n interactive: boolean\n json: boolean\n markdown: boolean\n org: string\n page: number\n perPage: number\n sort: string\n }\n\n const hasApiToken = hasDefaultApiToken()\n\n const { 0: orgSlug } = await determineOrgSlug(orgFlag, interactive, dryRun)\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 \\`${FLAG_JSON}\\` and \\`${FLAG_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,\n direction,\n orgSlug,\n outputKind,\n page,\n perPage,\n 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 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 { description: '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, { V1_MIGRATION_GUIDE_URL } 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 { webLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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 { 0: 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 the ${webLink(V1_MIGRATION_GUIDE_URL, '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 description: '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, {\n FLAG_JSON,\n FLAG_MARKDOWN,\n V1_MIGRATION_GUIDE_URL,\n} 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 { webLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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 { 0: 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 the ${webLink(V1_MIGRATION_GUIDE_URL, '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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 argv,\n name: `${parentName} repository`,\n importMeta,\n subcommands: {\n create: cmdRepositoryCreate,\n view: cmdRepositoryView,\n list: cmdRepositoryList,\n del: cmdRepositoryDel,\n update: cmdRepositoryUpdate,\n },\n },\n { description },\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 reachConcurrency: {\n type: 'number',\n default: 1,\n description:\n 'Set the maximum number of concurrent reachability analysis runs. It is recommended to choose a concurrency level that ensures each analysis run has at least the --reach-analysis-memory-limit amount of memory available. NPM reachability analysis does not support concurrent execution, so the concurrency level is ignored for NPM.',\n },\n reachDebug: {\n type: 'boolean',\n default: false,\n description:\n 'Enable debug mode for reachability analysis. Provides verbose logging from the reachability CLI.',\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 reachDisableAnalysisSplitting: {\n type: 'boolean',\n default: false,\n description:\n 'Limits Coana to at most 1 reachability analysis run per workspace.',\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 { existsSync, promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nexport type ReachabilityTargetValidation = {\n isDirectory: boolean\n isInsideCwd: boolean\n isValid: boolean\n targetExists: boolean\n}\n\n/**\n * Validates that a target directory meets the requirements for reachability analysis.\n *\n * @param targets - Array of target paths to validate.\n * @param cwd - Current working directory.\n * @returns Validation result object with boolean flags.\n */\nexport async function validateReachabilityTarget(\n targets: string[],\n cwd: string,\n): Promise<ReachabilityTargetValidation> {\n const result: ReachabilityTargetValidation = {\n isDirectory: false,\n isInsideCwd: false,\n isValid: targets.length === 1,\n targetExists: false,\n }\n\n if (!result.isValid || !targets[0]) {\n return result\n }\n\n // Resolve cwd to absolute path to handle relative cwd values.\n const absoluteCwd = path.resolve(cwd)\n\n // Resolve target path to absolute for validation.\n const targetPath = path.isAbsolute(targets[0])\n ? targets[0]\n : path.resolve(absoluteCwd, targets[0])\n\n // Check if target is inside cwd.\n const relativePath = path.relative(absoluteCwd, targetPath)\n result.isInsideCwd =\n !relativePath.startsWith('..') && !path.isAbsolute(relativePath)\n\n result.targetExists = existsSync(targetPath)\n if (result.targetExists) {\n const targetStat = await fs.stat(targetPath)\n result.isDirectory = targetStat.isDirectory()\n }\n\n return result\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 { validateReachabilityTarget } from './validate-reachability-target.mts'\nimport constants, { REQUIREMENTS_TXT, SOCKET_JSON } 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 { readOrDefaultSocketJsonUp } from '../../utils/socket-json.mts'\nimport { socketDashboardLink } from '../../utils/terminal-link.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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 default: '',\n description: 'Branch name',\n shortFlag: 'b',\n },\n commitHash: {\n type: 'string',\n default: '',\n description: 'Commit hash',\n shortFlag: 'ch',\n },\n commitMessage: {\n type: 'string',\n default: '',\n description: 'Commit message',\n shortFlag: 'm',\n },\n committers: {\n type: 'string',\n default: '',\n description: 'Committers',\n shortFlag: 'c',\n },\n cwd: {\n type: 'string',\n default: '',\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 default: 0,\n description: 'Pull request number',\n shortFlag: 'pr',\n },\n org: {\n type: 'string',\n default: '',\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 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 aliases: ['pendingHead'],\n },\n tmp: {\n type: 'boolean',\n default: false,\n description:\n 'Set the visibility (true/false) of the scan in your dashboard.',\n shortFlag: 't',\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 }: CliCommandContext,\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 ${socketDashboardLink('/org/YOURORG/alerts', '\"alerts page\"')} 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 parentName,\n importMeta,\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 reachConcurrency,\n reachDebug,\n reachDisableAnalysisSplitting,\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 reachConcurrency: number\n reachDebug: boolean\n reachDisableAnalytics: boolean\n reachDisableAnalysisSplitting: 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 { 0: orgSlug } = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const processCwd = process.cwd()\n const cwd =\n cwdOverride && cwdOverride !== '.' && cwdOverride !== processCwd\n ? path.resolve(processCwd, cwdOverride)\n : processCwd\n\n const sockJson = await readOrDefaultSocketJsonUp(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 isUsingNonDefaultConcurrency =\n reachConcurrency !== reachabilityFlags['reachConcurrency']?.default\n\n const isUsingNonDefaultAnalytics =\n reachDisableAnalytics !==\n reachabilityFlags['reachDisableAnalytics']?.default\n\n const isUsingAnyReachabilityFlags =\n isUsingNonDefaultMemoryLimit ||\n isUsingNonDefaultTimeout ||\n isUsingNonDefaultConcurrency ||\n isUsingNonDefaultAnalytics ||\n hasReachEcosystems ||\n hasReachExcludePaths ||\n reachSkipCache ||\n reachDisableAnalysisSplitting\n\n // Validate target constraints when --reach is enabled.\n const reachTargetValidation = reach\n ? await validateReachabilityTarget(targets, cwd)\n : {\n isDirectory: false,\n isInsideCwd: false,\n isValid: true,\n targetExists: false,\n }\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 nook: true,\n test: !reach || reachTargetValidation.isValid,\n message:\n 'Reachability analysis requires exactly one target directory when --reach is enabled',\n fail: 'provide exactly one directory path',\n },\n {\n nook: true,\n test: !reach || reachTargetValidation.isDirectory,\n message:\n 'Reachability analysis target must be a directory when --reach is enabled',\n fail: 'provide a directory path, not a file',\n },\n {\n nook: true,\n test: !reach || reachTargetValidation.targetExists,\n message: 'Target directory must exist when --reach is enabled',\n fail: 'provide an existing directory path',\n },\n {\n nook: true,\n test: !reach || reachTargetValidation.isInsideCwd,\n message:\n 'Target directory must be inside the current working directory when --reach is enabled',\n fail: 'provide a path inside the working directory',\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 reachConcurrency: Number(reachConcurrency),\n reachDebug: Boolean(reachDebug),\n reachDisableAnalysisSplitting: Boolean(reachDisableAnalysisSplitting),\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 description: '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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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'\nimport { fileLink } from '../../utils/terminal-link.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 \\`${fileLink(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 { 0: key, 1: 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 { 0: key, 1: 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, { FLAG_JSON, FLAG_MARKDOWN } 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 CliCommandContext,\n} 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 }: CliCommandContext,\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 const SOCKET_SBOM_URL_PREFIX_LENGTH = SOCKET_SBOM_URL_PREFIX.length\n\n const {\n depth,\n dryRun,\n file,\n json,\n markdown,\n org: orgFlag,\n } = cli.flags as {\n depth: number\n dryRun: boolean\n file: string\n json: boolean\n markdown: boolean\n org: string\n }\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 { 0: 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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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,\n id2,\n depth,\n orgSlug,\n outputKind,\n file,\n })\n}\n","import {\n createWriteStream,\n existsSync,\n promises as fs,\n mkdirSync,\n mkdtempSync,\n} 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 { debugApiRequest, debugApiResponse } from '../../utils/debug.mts'\nimport { formatErrorWithDetail } from '../../utils/errors.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) {\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 = 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 reachConcurrency: 1,\n reachDebug: false,\n reachDisableAnalysisSplitting: false,\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 debugApiRequest('GET', fileUrl)\n let downloadUrlResponse: Response\n try {\n downloadUrlResponse = await fetch(fileUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n debugApiResponse('GET', fileUrl, downloadUrlResponse.status)\n } catch (e) {\n debugApiResponse('GET', fileUrl, undefined, e)\n throw e\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 debugApiRequest('GET', downloadUrl)\n response = await fetch(downloadUrl)\n debugApiResponse('GET', downloadUrl, response.status)\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 (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n\n const fileStream = 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 (e) {\n if (!response) {\n debugApiResponse('GET', downloadUrl, undefined, e)\n }\n logger.fail(\n 'An error was thrown while trying to download a manifest file... url:',\n downloadUrl,\n )\n debugDir('error', e)\n\n // If an error occurs and fileStream was created, attempt to clean up.\n if (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 try {\n await fs.unlink(localPath)\n } catch (e) {\n logger.fail(\n formatErrorWithDetail(`Error deleting partial file ${localPath}`, e),\n )\n }\n }\n // Construct a more informative error message\n let detailedError = `Error during download of ${downloadUrl}: ${(e as { message: string }).message}`\n if ((e as { cause: string }).cause) {\n // Include cause if available (e.g., from network errors)\n detailedError += `\\nCause: ${(e 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 debugApiRequest('GET', commitApiUrl)\n let commitResponse: Response\n try {\n commitResponse = await fetch(commitApiUrl, {\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n debugApiResponse('GET', commitApiUrl, commitResponse.status)\n } catch (e) {\n debugApiResponse('GET', commitApiUrl, undefined, e)\n throw e\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 let repoDetailsResponse: Response\n try {\n debugApiRequest('GET', repoApiUrl)\n repoDetailsResponse = await fetch(repoApiUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n debugApiResponse('GET', repoApiUrl, repoDetailsResponse.status)\n } catch (e) {\n debugApiResponse('GET', repoApiUrl, undefined, e)\n throw e\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 let treeResponse: Response\n try {\n debugApiRequest('GET', treeApiUrl)\n treeResponse = await fetch(treeApiUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n debugApiResponse('GET', treeApiUrl, treeResponse.status)\n } catch (e) {\n debugApiResponse('GET', treeApiUrl, undefined, e)\n throw e\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 default: constants.ENV.SOCKET_CLI_GITHUB_TOKEN,\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 default: DEFAULT_GITHUB_URL,\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 default: '',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n orgGithub: {\n type: 'string',\n default: '',\n description:\n 'Alternate GitHub Org if the name is different than the Socket Org',\n },\n repos: {\n type: 'string',\n default: '',\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 | undefined\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 { 0: 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 { description: '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, { V1_MIGRATION_GUIDE_URL } 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 { webLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\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 }: CliCommandContext,\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 { 0: 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 the ${webLink(V1_MIGRATION_GUIDE_URL, '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 description: '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 { 0: key, 1: 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 CliCommandContext,\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 }: CliCommandContext,\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 { 0: 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 { 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 {\n outputKind,\n outputPath,\n }: { outputKind: OutputKind; outputPath: string },\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 actualOutputPath = outputPath || constants.DOT_SOCKET_DOT_FACTS_JSON\n\n logger.log('')\n logger.success('Reachability analysis completed successfully!')\n logger.info(`Reachability report has been written to: ${actualOutputPath}`)\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 outputPath: string\n reachabilityOptions: ReachabilityOptions\n targets: string[]\n}\n\nexport async function handleScanReach({\n cwd,\n interactive: _interactive,\n orgSlug,\n outputKind,\n outputPath,\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, {\n outputKind,\n outputPath,\n })\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 outputPath,\n packagePaths,\n reachabilityOptions,\n spinner,\n target: targets[0]!,\n uploadManifests: true,\n })\n\n spinner.stop()\n\n await outputScanReach(result, { outputKind, outputPath })\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 { validateReachabilityTarget } from './validate-reachability-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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 default: '',\n description: 'working directory, defaults to process.cwd()',\n },\n org: {\n type: 'string',\n default: '',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n output: {\n type: 'string',\n default: '',\n description:\n 'Path to write the reachability report to (must end with .json). Defaults to .socket.facts.json in the current working directory.',\n shortFlag: 'o',\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 }: CliCommandContext,\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 unless the --output flag is specified.\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 $ ${command} --output custom-report.json\n $ ${command} ./proj --output ./reports/analysis.json\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 output: outputPath,\n reachAnalysisMemoryLimit,\n reachAnalysisTimeout,\n reachConcurrency,\n reachDebug,\n reachDisableAnalysisSplitting,\n reachDisableAnalytics,\n reachSkipCache,\n } = cli.flags as {\n cwd: string\n interactive: boolean\n json: boolean\n markdown: boolean\n org: string\n output: string\n reachAnalysisTimeout: number\n reachAnalysisMemoryLimit: number\n reachConcurrency: number\n reachDebug: boolean\n reachDisableAnalytics: boolean\n reachDisableAnalysisSplitting: 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 !== '.' && cwdOverride !== processCwd\n ? path.resolve(processCwd, cwdOverride)\n : processCwd\n\n // Accept zero or more paths. Default to cwd() if none given.\n let targets = cli.input.length ? 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 { 0: orgSlug } = await determineOrgSlug(orgFlag, interactive, dryRun)\n\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n // Validate target constraints for reachability analysis.\n const targetValidation = await validateReachabilityTarget(targets, cwd)\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 nook: true,\n test: !outputPath || outputPath.endsWith('.json'),\n message: 'The --output path must end with .json',\n fail: 'use a path ending with .json',\n },\n {\n nook: true,\n test: targetValidation.isValid,\n message: 'Reachability analysis requires exactly one target directory',\n fail: 'provide exactly one directory path',\n },\n {\n nook: true,\n test: targetValidation.isDirectory,\n message: 'Reachability analysis target must be a directory',\n fail: 'provide a directory path, not a file',\n },\n {\n nook: true,\n test: targetValidation.targetExists,\n message: 'Target directory must exist',\n fail: 'provide an existing directory path',\n },\n {\n nook: true,\n test: targetValidation.isInsideCwd,\n message: 'Target directory must be inside the current working directory',\n fail: 'provide a path inside the working directory',\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 interactive,\n orgSlug,\n outputKind,\n outputPath: outputPath || '',\n reachabilityOptions: {\n reachAnalysisTimeout: Number(reachAnalysisTimeout),\n reachAnalysisMemoryLimit: Number(reachAnalysisMemoryLimit),\n reachConcurrency: Number(reachConcurrency),\n reachDebug: Boolean(reachDebug),\n reachDisableAnalytics: Boolean(reachDisableAnalytics),\n reachDisableAnalysisSplitting: Boolean(reachDisableAnalysisSplitting),\n reachEcosystems,\n reachExcludePaths,\n reachSkipCache: Boolean(reachSkipCache),\n },\n targets,\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 CliCommandContext,\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 }: CliCommandContext,\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 { 0: 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, { SOCKET_JSON } 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, { SOCKET_JSON } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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: `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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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', 'Failed to parse scan result line as JSON')\n debugDir('error', { error: e, line })\n return undefined\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'\nimport { fileLink } from '../../utils/terminal-link.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 ${fileLink(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 ${fileLink(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 { description: '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, { FLAG_JSON, FLAG_MARKDOWN } 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 CliCommandContext,\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 }: CliCommandContext,\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 { 0: 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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 argv,\n name: `${parentName} scan`,\n importMeta,\n subcommands: {\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 {\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 description,\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 { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { 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'\nimport { mailtoLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 ${mailtoLink('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: ${joinAnd(Array.from(argSet))}`,\n )\n }\n\n const hasApiToken = hasDefaultApiToken()\n\n const { 0: 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 argv,\n name: `${parentName} uninstall`,\n importMeta,\n subcommands: {\n completion: cmdUninstallCompletion,\n },\n },\n { description },\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 { getErrorCause } from '../../utils/errors.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('warn', 'Tab completion setup failed (non-fatal)')\n debugDir('warn', 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: ${getErrorCause(e)}`,\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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","import { createRequire } from 'node:module'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants, { FLAG_DRY_RUN, FLAG_HELP, YARN } from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { filterFlags } from '../../utils/cmd.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagApiRequirementsOutput } from '../../utils/output-formatting.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} from '../../utils/meow-with-subcommands.mts'\n\nconst require = createRequire(import.meta.url)\n\nexport const CMD_NAME = YARN\n\nconst description = 'Wraps yarn with Socket security scanning'\n\nconst hidden = true\n\nexport const cmdYarn = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n context: CliCommandContext,\n): Promise<void> {\n const { parentName } = { __proto__: null, ...context } as CliCommandContext\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 \"${YARN}\" is passed to the ${YARN} command.\n Only the \\`${FLAG_DRY_RUN}\\` and \\`${FLAG_HELP}\\` flags are caught here.\n\n Use \\`socket wrapper on\\` to alias this command as \\`${YARN}\\`.\n\n Examples\n $ ${command}\n $ ${command} install\n $ ${command} add package-name\n $ ${command} dlx package-name\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 shadowYarnBin = /*@__PURE__*/ require(constants.shadowYarnBinPath)\n\n process.exitCode = 1\n\n // Filter Socket flags from argv.\n const filteredArgv = filterFlags(argv, config.flags)\n\n const { spawnPromise } = await shadowYarnBin(filteredArgv, {\n stdio: 'inherit',\n })\n\n await spawnPromise\n process.exitCode = 0\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 { cmdPnpm } from './commands/pnpm/cmd-pnpm.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'\nimport { cmdYarn } from './commands/yarn/cmd-yarn.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 pnpm: cmdPnpm,\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 yarn: cmdYarn,\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'\nimport colors from 'yoctocolors-cjs'\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'\nimport { socketPackageLink } from './utils/terminal-link.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 logCallback: (name: string, version: string, latest: string) => {\n logger.log(\n `\\n\\n📦 Update available for ${colors.cyan(name)}: ${colors.gray(version)} → ${colors.green(latest)}`,\n )\n logger.log(\n `📝 ${socketPackageLink('npm', name, `files/${latest}/CHANGELOG.md`, 'View changelog')}`,\n )\n },\n })\n\n try {\n await meowWithSubcommands(\n {\n name: constants.SOCKET_CLI_BIN_NAME,\n argv: process.argv.slice(2),\n importMeta: { url: `${pathToFileURL(__filename)}` } as ImportMeta,\n subcommands: rootCommands,\n },\n { aliases: rootAliases },\n )\n } catch (e) {\n process.exitCode = 1\n debugFn('error', 'CLI uncaught error')\n debugDir('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__","description","time","process","logger","debugFileOp","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","default","importMeta","scope","repoName","markdown","nook","test","fail","repo","perPage","outputJson","outputMarkdown","page","payload","desc","generated","nextPage","org","user_email","debugFn","debugDir","formattedOutput","row","keys","fg","selectedFg","selectedBg","interactive","top","border","columnWidth","columnSpacing","truncate","bottom","height","bg","tags","content","headers","table","detailsBox","help","typeFilter","logType","cwd","tmp","path","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","error","finishedFetching","scan","version","alerts","healthy","addAlert","options","reportLevel","policy","url","manifest","ecoMap","pkgMap","verMap","fileMap","short","scanReport","depth","value","Package","Policy","includeLicensePolicy","scanId","outputKind","uploadManifests","organizations","tarHash","coanaEnv","env","stdio","reachabilityReport","sockJson","cdxgen","count","conda","gradle","sbt","debugLog","verbose","pass","stdout","stderr","poms","strings","reject","keeping","collecting","arr","fs","sbtOpts","bin","gradleOpts","detected","supportedFilesCResult","packagePaths","reachabilityOptions","scanPaths","tier1ReachabilityScanId","branchName","filepath","autoManifest","orgSlugCResult","commitMessage","commitHash","defaultBranch","pendingHead","pullRequest","reach","reachAnalysisTimeout","reachAnalysisMemoryLimit","reachConcurrency","reachDebug","reachDisableAnalytics","reachDisableAnalysisSplitting","reachEcosystems","reachExcludePaths","reachSkipCache","runReachabilityAnalysis","readOnly","report","hidden","name","key","failed","obj","config","full","subcommands","auto","get","list","set","unset","baseBranch","ghsaDetails","title","head","base","octokitPullsCreateParams","pr","reason","details","after","pageInfo","nodes","hasNextPage","endCursor","length","context","apiType","cacheKey","entry","index","parent","match","pageIndex","GITHUB_REPOSITORY","SOCKET_CLI_GITHUB_TOKEN","present","missing","repoInfo","author","states","fixEnv","ids","fixed","throws","limit","ghsaLoop","overallFixed","enabled","validGhsas","errors","unknownFlags","ghsas","autopilot","applyFixes","exclude","isMultiple","include","majorUpdates","id","rangeStyle","outputFile","minimumReleaseAge","showAffectedDirectDependencies","autoMerge","ghsa","maxSatisfying","minSatisfying","prCheck","purl","shortFlag","testScript","noApplyFixes","noMajorUpdates","allowUnknownFlags","toAddToBashrc","recursive","bashrcUpdated","actions","targetPath","completion","updateConfigValue","apiBaseUrl","apiProxy","apiToken","sdk","choices","enforcedOrgs","applyLogout","attemptLogout","YARN_LOCK","ipc","onlyFiles","argvMutable","agent","cleanupPackageLock","rmSync","configuration","coerce","filter","only","profile","standard","technique","alias","array","boolean","string","argv","dryRun","pathArgs","unknowns","yargv","spawnPromise","stdin","out","filename","json","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","state","added","addedInWorkspaces","updated","updatedInWorkspaces","warnedPnpmWorkspaceRequiresNpm","overridesDataObjects","semver","package","depAliasMap","thisSpec","depObj","loggedAddingText","newSpec","concurrency","NPM_BUGGY_OVERRIDES_PATCHED_VERSION","cmdName","prod","VLT","pkgEnvCResult","pkgEnvDetails","agentVersion","optimizationResult","pin","offset","columns","field","mw1","mw2","mw3","security","license","dependencies","quota","deps","self","capabilities","score","transitively","o","valid","purls","components","Maintenance","Quality","Vulnerabilities","License","colorize","padding","artifact","namespace","supplyChain","maintenance","quality","vulnerability","dupes","blocks","packageData","outputPurlsShallowScore","shallowScore","shallow","deep","exportedAt","beforeHash","afterHash","summary","severity","patchExplanation","z","patched","passed","hash","onlyDirectories","absolute","dot","followSymbolicLinks","patches","purlObj","code","visibility","default_branch","slug","outputCreateRepo","homepage","repoDescription","sort","per_page","results","all","direction","create","view","del","update","isDirectory","isInsideCwd","isValid","targetExists","setAsAlertsPage","targets","updatedInput","orgSlug","showHidden","colors","maxArrayLength","id1","id2","repos","targetRepos","scanCreated","scansCreated","githubToken","repoApiUrl","lastCommitter","tmpDir","fileCount","firstFailureResult","isManifest","fileUrl","debugApiRequest","downloadUrlResponse","response","detailedError","repoSlug","commitResponse","lastCommitMessage","repoDetailsResponse","repoDetails","treeResponse","treeDetails","githubApiUrl","orgGithub","from","created_at","year","month","day","fromTime","untilTime","outputPath","target","output","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","pnpm","oops","optimize","organization","patch","repository","uninstall","wrapper","yarn","audit","auditLog","auditLogs","feed","orgs","organisation","organisations","repositories","authInfo","logCallback","errorTitle","errorMessage","errorBody","autoHelp","autoVersion"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWO;;AAIGA;AAAQ;AACdC;;;AAIF;AACA;AACE;AACF;AACA;AAEA;AACEC;AACF;AACF;;AClBO;;AAKGF;AAAQ;AACdC;;;AAIF;AACA;AACE;AACF;AACA;AAEA;AACEC;AACF;AACF;;AChBuC;;AAGvC;AAEA;;AAeA;AACA;AAuBO;;;;;AAKgCC;AAA4B;AAEjE;AACEC;AACF;AAEA;;AAEIC;AACA;AACF;AACAA;AACA;AACF;;AAGE;AAEA;;;AAGIC;;;AAGAA;;AAEAD;AAEIE;AACAC;AACAC;AACF;AAEJ;AACF;AACEJ;AACF;AAEA;AACF;AAEA;;;;AAME;AACA;;;AAGIC;;;AAGAA;AACAD;AACF;AACF;AACEA;AACF;AACF;;AAEA;AACF;AAkBO;;AAOP;;AAEA;;AAEA;AAoCA;;AAEA;AACA;;AAIA;;AAEA;AACA;AAEA;AAEA;AACE;AACA;AACE;AACF;AACA;AACA;AAA8BK;AAASC;AAASC;AAAO;;;;;;;;;AA2DvD;AACA;AACEC;AACAC;AACAC;AACAC;AACAC;AACAC;AACF;;AAEA;AACAN;;;AAIEO;AACF;;AAGA;AACAP;AACF;AAEO;;;;AAOL;AACEQ;AACF;AAEA;AACE;;AAEE;AACA;AACEC;;AAEAA;AACF;AACF;AACF;AACA;AACE;AACED;AACF;AACF;AAEA;AAGA;AAAa;AAAQ;;AACnBE;AACF;;AAGE;AACAC;;AAEJ;AAEO;;;;AAOL;AACEH;AACF;AAEA;AACE;;AAEE;AACA;AACEC;AACF;AACEA;AACF;AACF;AACF;AAEA;AACE;AACA;;AAEE;AACEG;AACF;AACEA;AACF;AACF;AACF;AAEA;AAGA;AAAa;AAAQ;;AACnBF;AACF;;AAGE;AACAC;;AAEJ;AAEA;;AAEA;AAEA;AAOE;;AAEEE;AAASC;AAAcC;AAAcC;;AACrCC;AACAC;AACAd;AACAe;AACAC;AACEC;;AAEFpB;AACF;AAEAD;AAEA;AACEsB;AACAC;;AAGFT;AACF;;AC/YO;;;;;AAKLvB;AACqB;AACrB;;AAKEiC;;AAEAA;AACF;AACEA;AACE7B;AACAC;;AAEJ;;AAEE4B;AACE7B;;AAEAY;;AAEJ;;;;;;AAOEhB;AACF;AACF;;AC3BO;AAEP;AAEA;AAEO;;;AAGLkC;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;AACA;AACAC;AACEC;AACAC;AACAzC;AACF;;;AAEgBsC;AAAM;AAE5B;AACA;;AAEA;AACA;;AAEA;;AAEA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;;;;AAOII;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;AAME;AACEzC;AACF;;AAEA0C;AACA;AACEC;AACF;AACA;AACE3C;AACF;;AAEAA;AACF;;AAGEsC;;AAEAM;;;;AAQF;AAEA;AAEA;AAGIC;AACAC;;AAEAC;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AAGAzC;AACA0C;AACF;;AAGE1C;AACA0C;AACF;AAEEF;;AAEAxC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;;;;AAKE8C;;AAEAhD;AACF;AACF;;AC3KO;;AAIGH;AAAQ;AAAMC;;;AAEtB;AACA;AACE;AACF;AACA;;;;;;AAE4CmD;AAAQ;AAClDnD;;;;AAME;AACAoD;AACA;AACAC;;AAEAZ;AACAa;;AAEF;;AAC0C;AAE9C;;AC9BA;AAEO;;;;;AAOHH;AAOF;AAEA;AACEhD;AACF;;AAGEC;;;;AAKI+C;AACF;AAEJ;AAEA;AACE/C;AACA;AACF;;;;;;AAQM+C;AACF;AAEF;AACF;AAEA;AACF;AAEA;;AAKI;AACF;AACA;AACA;AACA;AAAc;AAAgBI;;AAC9B;AAIA;AACE;AACF;AACA;AACF;AAEO;;;;AAMHJ;AAMF;AAEA;;AAEA;AAEA;AACE7C;AACAY;AACEsC;AACAC;;AAIAC;AACAC;;;;AAIE;;;;;;;AAOEC;AACF;;;;;;;AAOEA;;;AAGN;AACF;AACF;AAEO;;;;AAMHT;AAMF;;;;AAaF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;;AAGI/C;AAGAyD;AACAC;AACA;AACF;AACF;AAEA;AAIE;;AASA;;AASA;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACA;AACAnD;AAEA;;;;AAIA;AACAoD;AACEC;;AAEA;AACF;AAEA;AACEC;AACAC;AACAC;AACAC;AACAC;;AAEArC;AACAsC;;AAEAC;AACE9B;AACAyB;;AAEFM;AAAwB;AACxB;AACAC;AACAC;AACF;AAEA;AACA;AACEC;AAA0B;AAC1BC;AACA5C;AACAR;AACE0C;AACAW;;AAEFC;AACAC;AACF;AACA;AACEJ;AACAC;AACA5C;AACAuC;AACE9B;AACAyB;;AAEFtD;;AAEAY;AACE0C;AACF;AACF;;AAGEc;AACA9D;AACF;;AAEA;;;AAGA;AACAP;AACAA;AACAA;;AAEA;AACAsE;AACE;AACA;AACE;AACAC;;AAEF;AACF;;AAIAvE;AACE;;AAEA;;AAIF;AACF;;ACxTO;;;;;AAKLwC;AAOF;AACE;;;;;AAKEA;AACF;;;;;;AAOEA;AACF;AACF;;ACRO;AAEP;AAEA;AAEO;;;AAGLf;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;AACA;AACA8B;AACE5B;AACAC;AACAzC;;AAGF0D;AACElB;AACAxC;;AAGFqD;AACEb;AACAxC;;AAEFkD;AACEV;AACAC;AACAzC;AACF;;AAEFkF;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOIxC;AACF;;;;;AAMEgB;;AAEAR;;;;;AAgBFiC;AAEA;;AAEQ;AAAW;AAMnB;AAEA;AAGIrC;AACAC;;AAEAC;AACF;AAEEF;;AAEAxC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;;;AAGEkD;AACAH;AACAkC;AACF;AACF;;ACjKO;;;;;;;AAYHxC;AACF;AAAM7C;;;;AAGJsF;;;;AAIAC;AACF;AAAMvF;;;AAEN;AACA;AACE;AACF;AACA;AAEA;AACE;AAEI;;;AAESwF;;;AACX;;AAKJ;AAEA;AAEI;AAAmBC;;AACnB;AAAmBC;;AACnB;AAAsBC;;AACtB;AAAmBC;;AACnBC;AACA;;;AACA3C;AACA4C;;AAEF;AACE7F;AAAgC;AAEtC;;ACzEO;;;AAGY8F;AAAQ;AACvB/F;;;AAIF;AACA;AACE;AACF;AACA;;AAGEC;AACA8F;AACF;AACF;;ACtBA;AACA;AACA;AACA;AACA;AACO;AAIL;AACA;AACA;AACEC;AACAC;AACEC;AACAC;AACF;AACF;AACF;;ACTA;AACA;AACA;AACA;AACO;;;AAUyBpG;AAAQ;AACpCC;;;AAGF;AACA;AACE;AACF;AACA;;;;;AAMQ+F;AAAQ;;AAGdK;AACAC;AACF;;AAGEC;AACAD;AACF;;AAGE;;;AAKA;;AAIA;AACF;;AAGE;;AAMA;AACE;AACF;AAEA;;AAEA;AACA;;AAEA;;AAEI;;AAEA/F;AACAuD;;AACoB0C;AAAU9E;AAAK;AACnC;AACF;AACF;AAEA;;;AAEWnB;AAAUY;;AACrB;;;AAKEZ;AACAC;AACAC;;AAGJ;;AAKE;;AAOA;AACF;AAEA6F;AAEA;;;AAOM/F;AACAC;AACAC;;;;;AAQAF;AACAC;AACAC;;AAIJ;AAEAgG;AACAH;AACF;AAEA;AACE;AACF;AACA;AACE;AACF;;;AAII/F;AACAC;AACAC;;AAEJ;;AAGEF;AACAY;;;AAGA;;AAEJ;;AC7IA;AACA;AACO;;;;;;AASH6E;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;AACAU;;;;AAIIhE;AACAiE;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;AAEAd;AAEA;;AAEIzF;AACAY;AAAQ0F;AAAQ;;AAEpB;AAEA;;;;AAIEE;;AAAiBC;;AACjBJ;;;;AAKErG;AACAC;AAEAW;;AAEJ;;AAGEZ;AACAY;;AAEJ;AAEA;AAKE;;AAEE8F;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;;ACtTO;;;;;;;;AAaHC;AACsB;AAExB;AACEpH;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;AAGM;AACAoH;AAIN;AACEpH;;AAEF;AAEAA;AACAA;AACA;AACF;AAEA;AACEA;AACF;AACEA;AAA8BqH;AAAY;AAC5C;AACF;AAEO;AAIL;AAEA;;AAEE;AACAd;;AAGF;AACErG;AACAY;AACF;AACF;AAEO;AAIL;;AAOA;;AACWwG;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;;ACvNO;;;;;;;;AAQLL;AACsB;;AAEpBM;AACF;;;;AAKEC;;;;;AAKAP;AACF;AACF;;ACrBO;;AAKHlD;AACA0D;;AAEF;AAAM/H;;;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;AAIMG;AACAmC;AACF;AACEqD;;;AAIN;AAEA;;AAEA;AACF;;AC5DO;;;AAKHT;;;;;;;;AAQA0C;AACF;AAAMhI;;;;AAEN;;AAEA;;AAEA;;AAEA;AACA;AACA;;AAEIM;AACAC;AACAC;;AAGJ;;AAEQyH;;AAER;;AAEI3H;AACAC;AACAC;;AAEJ;AAEA;AAEA;AAEA;AACE;AACA;AACA;AACE;AACF;AAEA;;AAEA;AACA;;AAMAuF;;AAEA;;AAQI9F;AACA8F;AACF;;AAKF;AACE;;AAEA;AACA;AACF;AAEAmC;;AAEE;;AAEA;;AAEE5H;AACAC;AACAC;;AAEJ;;AAGAuF;AACF;;AAGAA;AAEA;AACA;AACA;AA2BE;;;AAUF;;AAEA;AACA;AACA;AACEoC;AACF;AACA;AACEA;AACF;;AAEA;;;AAGEC;;AAEAC;AACF;AAEA;;AAEA;;AAIM/H;AACAY;AACE;AACAoH;;AAGF;AACF;AAEN;;AChOA;AACA;;AAuBO;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;;ACvEO;;;;AAILC;AAMF;AACE;;AAEA;AACA;;AAEA;AACA;AAEA1I;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;;AAKU2F;AAAQ;;;AAId3F;AAGAA;AAGA2F;;AAGE;AACA;AACA;AACA;AACAT;AACF;AAEAyD;;;;AACsBC;AAAO;;;;AACNC;;AACzB;AACE;AACElD;AACF;AACEA;AACF;AACF;AACF;;AChIO;;;;;AAKL+C;AAOF;AACE;;;AAEQ/C;AAAQ;AAEhB3F;AACAA;AACAA;;;;;AAME;AACA;AACA;AACA;AACA;AACA;AAA2DkF;AAAI;;AAI/D;AACElF;AACAA;;AAEF;;;AAGEA;AACA;;AAEEA;AACAA;;AAEF;AACA;AACF;;;AAGE8I;AACA;AACF;AACA;;AAEE9I;AAGA;AACF;AACA;AACA;AACA;;AAEEA;;AAEAA;AACAA;AACF;;AAEEA;AACAA;AAGAA;AACA8I;AACA;AACE9I;AACAA;AAGF;AACAA;AACAA;AACA;AACF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA8I;AACA9I;AACF;;;;;AAQA;AACEA;AACAA;;AAEF;AACF;AACF;;AChHA;AACE;AACF;AAEO;AAKL;;AAEE;AACEA;AACF;;;;AAKI;AACA+I;AACF;AACAhJ;;AAEA;;AAEE;AACEC;AACF;;AAEF;AACAD;;AAEI;AACEC;AAGF;;AAEF;AACE;AACEA;AACF;AACAgJ;AACF;AACF;AACF;;;AAII9I;AACAC;AACAC;;AAEJ;AACF;;AAGE;AACEJ;AACF;AAEA;;AAEIE;AACAC;;;AAGJ;AAEAwE;;;AAIIzE;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;AACEmI;AACF;AACE;AACA;AACF;AACF;AACA;AAAA;;AAG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zH;;AAEN;AACA;AACEG;AACA;;AAEEA;AAGF;AACEA;AACF;AACF;AACEA;AACF;AACF;AACE;AACEG;;AAIIkL;AACA/D;;AAEF;AAEE+D;AACA/D;AACAzH;;AAEN;AACA;AACEG;AACA;;AAEEA;AACF;AACEA;AACF;AACF;AACEA;AACF;AACF;AACF;AACF;;AC3GO;;AAEL2H;AAIF;AACE;AAEA;AACF;;ACIO;AAEP;AAGA;AAEO;;;AAGL3F;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACU;AAAQ;;AAElB;;;;;;AAOI9C;AACF;;;AAEcS;;;;AAMd;AAEA;;AAIIvC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;AACEsL;AACA3D;AACF;AACF;;AClGO;AAKL;AACE5H;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEA;;AAGEA;AACAA;;AAEA;AACEA;AACAA;AAGF;AACF;;AAEE;AACEA;AACAA;AAGF;AACF;AACF;;AC1CO;;AAEL2H;AAIF;AACE;AAEA;AACF;;ACIA;AACEzF;AACArC;AACAuL;AACAjJ;AACE;;;AAGF4C;AACF;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACU;AAAQ;;;AAGlB;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAA8B;;;;;AAM9BA;AACF;;;AAEcS;;;;AAMd;AAEA;;AAIIvC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;AACEsL;AACA3D;AACF;AACF;;AC1FO;;AAELA;AAIF;AACE;AACA;;;;AAIE;AACE;AACA;AACA;AACEL;AACAiE;;AAEAjE;AACF;AACA;AACEkE;AACF;AACF;AACA;;AAEA;AACAxL;AAIUE;AACAC;AACAW;;AAEE2K;AACAP;;AAEJ;AAEEhL;AACAY;;AAEE2K;AACAP;AACF;AACF;AAGV;;AAMElL;AACAA;;AAEAA;AACA;AACE;AACA;;AAEA;AACE;AACA;AACEsH;AACF;AACA;;AAIA;AACF;AACF;AACA;AACEtH;AACAA;AAGF;AACF;AACF;;AChFA;AACEkC;AACArC;AACAuL;AACAjJ;AACE;AACA;AACAuJ;AACErJ;AACAC;AACAzC;AACF;;AAEFkF;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAA8B;;;;;AAM9BA;AACF;;;;AAEoBS;;;AAIpB;AAEA;AACEC;AACAC;AACAzC;AACA0C;AACF;;AAEE;AACF;AAEA;AACE7C;AACA;AACF;AAEA;;AAEE2H;AACF;AACF;;AC5EO;AAIL;AACE5H;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGEA;AACAA;AACAA;;AAEEA;AACAA;AACF;AACF;AACEA;AACAA;;AAEEA;AACAA;AACF;AACF;AACF;;AChCO;;;AAGLsH;AAKF;;;;;AAEoCK;AAAW;AAE7C;AAEAlE;;AACsB1B;AAAO;AAE7B;AACF;;ACNO;AAEP;AAEA;AAEO;;;AAGLC;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACU;AAAQ;;;AAGlB;AACA;AACA;;;;;;AAOI9C;AACF;;;AAEcS;;;;AAMd;AAEA;AAEA;;AAIIvC;AACA0C;AACF;;AAEiB;AACf1C;AAEA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;AACEsL;;AAEAhE;AACF;AACF;;ACpHO;AAIL;AACEvH;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGEA;AACAA;AACAA;;AAEEA;AACAA;AACF;AACF;AACEA;AACAA;;AAEEA;AACAA;AACF;AACF;AACF;;AClCO;;AAEL2H;AAIF;AACE;AAEA;AACF;;ACIO;AAEP;AAEA;AAEO;;;AAGL3F;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACU;AAAQ;;;AAGlB;AACA;AACA;;;;;;AAOI9C;AACF;;;AAEcS;;;;AAMd;AAEA;;AAIIvC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;AACEsL;AACA3D;AACF;AACF;;ACnGA;AAEO;;AAELyD;AACA;AAA8BnJ;AAAW;AACvC;;;;AAKI0J;AACEC;AACAC;AACAC;AACAC;AACAC;AACF;AACF;AACEnM;AAAY;AAElB;AACF;;AC/BA;AACA;AACA;AACA;AACA;AACA;;;AAOA;AACA;AACA;AACA;AACA;AACA;AACO;AAKLG;AACAyD;;;;AAOEA;AACA;AACF;;AAEA;AACA;AACA;AACF;;AAEA;AACA;AACA;AACA;AACO;AAIL;AACA;AACA;AACA;AACF;;AAEA;AACA;AACA;AACA;AACO;AAIL;AACA;AACF;;AAEA;AACA;AACA;AACA;AACO;AAKL;AACA;AACA;AACE;AACF;AACA;AACF;;AC3EA;;AAEA;AACA;AACA;AACA;AACE;AAOF;AA4BO;;AAEP;AAEO;;AAEP;AAEO;AAIL;;AAEF;AAEO;AAIL;;AAEE;AACA;AACA;;AAEE;AACF;AACA;AACA;AAUF;AACA;AAKI;;AAEA;AACE;;AAEF;AACA;AACF;AAEJ;AAEO;AACL;AACA;AAGF;;AC1DO;;AAOGwI;AAAqBC;AAAY;AACvCtM;;;AAIF;;AAGE;;;AAGEuM;AACAC;AACAC;AACAxG;;;AAEoByG;AAAyB;;;AAEtCpM;AAAUqM;;;AAEnB;;;AAGE;;AAMA;AACA;AAKE9I;;AACSvD;AAAWsM;AAA0BrG;;AAChD;;AAEA;AACA;AACE;AACA1C;;AACSvD;AAAWsM;AAA4BrG;AAAUsG;;AAC5D;;AAEA;;AAEEhJ;;AACSvD;AAAWsM;AAA6BrG;;AACnD;;AAGE1C;;AACSvD;AAAWsM;AAAyBrG;;AAC/C;AACF;;AAEA;AACA1C;;AACSvD;AAAWsM;AAAmBrG;;AACzC;AACF;AAmIO;AAKL;AAGF;AAsCA;;;;;AASE;AACEvG;;;AAGF;AACA;AACA;;AAEA;;;;;AAYE;AACA;AACE;AACA;AAKN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AAKc8M;AACF;;;AAISC;AAAS;AACtBC;AACAD;AAAYE;AAAoBC;AAAgB;;AAGlD;AAAkBC;;AAChB;AACA;;;;;AAKIC;AACEC;AACAC;AACApM;AACAqM;AACAC;AACAC;;AAEFC;AACE;;AAEF;AACF;AACF;AACF;;AAEA;;;AAGAC;;AAEA;;;AAME;AACF;;AAEA;AACA;AACA;AACE;AACF;AACF;;;AAGA7J;AACF;AAEA;AACF;;ACvZA;;AACU8J;;;AAEN/J;AACF;;AAEA;AACA;AACE;AACF;;;AAGEX;;AAEJ;AAiBA;AACA;AACA;AACO;;AAQP;;AAEA;AACA;AACA;AACA;AACO;;;;;AAKH2K;;;;AAMF;AACEC;AACF;AACEC;AACF;AAEA;AACED;AACF;AACEC;AACF;AAEA;AACED;AACF;AACEC;AACF;AAEA;AACED;AACF;AACEC;AACF;;;AAEkBD;;AACpB;AAEO;AACL;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACE;AACA;;AAEE1N;AAKF;AACF;AACE;;AAGA;;;AAOF;AAEA;AACA;;AAEA;;AAEE;AACEyD;AACF;AACAmK;AACF;AAEA;AAGQC;AACAC;;;;;;;;;AAWNF;;AAEJ;;ACjGA;AACA;AACA;AACA;AACA;;AAOI1I;;AAEAS;AACF;AACE/F;;;;;AAuBO+F;AAAQ;;AAIf;AAGA;AACF;AACA;AACF;AAEO;;;;;;;;;;;;;;AAgBHA;AACF;AAEA;;AACsBoI;AAAO;;AAI7B;AACA;AACE;AACF;AAEA;AAEA;AAAkEpI;AAAQ;AAC1E;AACE;AACF;AAEA;;AAEET;AACF;AACA;AACA;;AAIA;AAGIrF;AACA8F;AACF;AAGF;AACE;AACF;AAEA;;;;AAIIzF;AACAC;;;AAIJ;;;;AASE;AACA;AACE;AACA;AACE;AACA;AACEH;AAIF;AACF;AACE;;AAKF;AACF;AAEA;AACA;;;;AAII2F;AACF;AACF;;AAEA;AACEqI;AACF;;;;AAIW9N;AAAUY;AAAQmN;AAAa;;AAC1C;;AAEA;AACA;AACA;;;;;AA6BoBhG;AAAiB;;AAKnC;AACE;AACF;;AAEA;AACA;AAAgDiG;AAAc;;AAE9D;AACA;AACElO;;;AAGF;;AAESE;AAAUY;AAAQA;AAAuBmN;AAAY;;AAChE;AACE;;AAEE;AACF;AACE;AAAA;AAEJ;AACF;;AAEA;;AAEA;;AAEI;AAGIH;AAA0B;AAE9B;AACA;;;AAGErK;AAIF;;AAEAA;AACAC;AACF;AACF;AAEA;AAEA;;;;AAKIyK;AACAxI;AACF;;;AAGF;AAEA;AACElC;AACF;AAEA;AACEA;AACF;;;;AAIWvD;AAAUY;AAAQmN;AAAa;;AAC1C;AAEAxK;AAEA;AACA;;;;;AAOA;AACA2K;AAA4BrB;;AAC1B;AACAtJ;;AAEA;AACA;;;;AAwBkBwE;AAAiB;AAGnC;;AAEE;AACF;;AAEA;AACA;AACA;;AAOA;AACExE;AACA;AACF;AAEA4K;AAEA;;AAGE;AACA;AACA;;AAGYP;AAA0B;AAGtC;AACE;;;AAGA;AACF;;AAEA;AACA;AACA;AACA;AACE;;;AAGE;AACF;AACF;;AAEA;AACA;AACE9N;AAIAyD;AACA;AACF;AAEAA;AAEA;AACAA;AAKA;AACE;AACA;AACA;AACC;AACD;;;;;AAQE;AAEF;AACC;;AAGDzD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;;AAEA;AACA;AACA;;AAOA;AACA;AAIE;;;;AAKEkM;AACF;;;AAIQpL;;AACR;;AAIA;;;AAGE;;;AACiBwN;AAAQ;AACzB;AACEtO;AACF;;AAIEA;AACF;;;AAGF;;AAEA;AACA;AACA;AACF;AACE;AACA;AACEA;AAGA;AACF;;AAIE;AACA;AACF;AACEA;AAGA;AACA;AACF;AACEA;AAGA;AACA;AACF;AACEA;AAGA;AACA;AACF;AACF;;AAEA;AACA;AACA;AACA;AACA;;AAEAA;AAGA0D;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACF;AAEA2E;AACA5E;;AAKE;AACF;AACF;;;AAKEvD;AACAY;AAAQmN;AAAoB;;AAEhC;;AC7kBO;AAIL;AACElO;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEAA;AACAA;AACF;;ACbA;AACA;AAYA;AACA;AACA;AACA;AACO;;;AAEiBgO;AAAI;;;AAK1B;AACE;AAEA;AACE;AACA;AACEO;AACF;AACEC;AACF;;AAEA;AACA;AACEA;AACA;AACF;;AAEA;AACA;;AAEED;;AAEF;;AAEA;;AAEA;AACA;AACA;;AAEEA;AACAvO;AAGF;AACEwO;AAGF;AACF;AACE;AACAA;AAGF;AACF;;;;AAMwBA;AAAO;AAC/B;;;AAGsBD;AAAW;AAEjC;AACF;AAEO;;;;;;;;;;;;;;;;;;AAkBLE;AACe;AACfhL;;;;;;;;;;;;;;;;;AAiBEgL;AACF;AAEA;;;;;;AAOI;AACAC;;;;;;;;;;;AAWAD;;AAIN;;ACjIO;AAEP;AAEA;AAEA;AAEO;;;AAGLzM;AACF;AAEA;AACE2M;AACEtM;AACAC;AACAzC;;AAKF+O;;AAEEvM;AACAC;AACAzC;AAEA;AACAuL;;AAEFyD;AACExM;AACAC;AACAzC;AAEAiP;AACA1D;;AAEF2D;AACE1M;AACAC;AACAzC;AAEAiP;AACA1D;;AAEF4D;AACE3M;AACAC;AACAzC;AAEA;AACAuL;;AAEF6D;AACE5M;AACAC;AACAzC;AACJ;AAIA;AAIA;AAIA;AACIiP;;AAEFX;AACE9L;AACAC;;;AAGF4M;AACE7M;AACAC;AACAzC;AACJ;AACA;AACA;AACA;AACA;;AAEEsP;AACE9M;AACAC;AACAzC;;AAEFuP;AACE/M;AACAC;AACAzC;;AAGFwP;AACEhN;AACAC;AACAzC;AAEF;AACF;AAEA;AACEyP;;AAEElE;;AAEFmE;;AAEEnE;;AAGFoE;AACEnN;AACAC;AACAzC;AACAuL;;AAEFqE;AACEpN;AACAC;AACAzC;AAEAuL;;AAEFsE;AACErN;AACAC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AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;AACAqO;AACEnO;AACAC;AACAzC;;AAEF4Q;AACEpO;AACAC;AACAzC;AACF;;AAEFkF;AACJ;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOIxC;AACF;;AAIA;AACEvC;AACA;AACF;AAEA;AACE;AAGF;;;AAEoByQ;;AAKpB;AACF;;ACxFO;AACLF;AACAA;AACAA;AACAA;AACF;;ACRO;;AAEHO;AACA9Q;;AAEEA;AACAA;AAGF;AACF;AACEA;AACF;AACF;;ACNA;AACEkC;AACArC;AACAuL;AACAjJ;;;AAGA4C;AACF;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAA8B;;;;;AAM9BA;AACF;;AAIA;AACEjC;AACA;AACF;AAEA+Q;AACF;;ACzCA;;;AAA2CC;AAAU;AAErD;AAeA;AACE;;AAEA;;AAEA;AAAa;AAAQ;AAAS;AAC5B;AACE;AACF;;AAEE;AACA;AACAjP;AACF;AACEA;AACF;;;AAGEA;AACF;AACF;AACA;AACA;AACEA;AACF;AACA;AACA;AACEA;AACF;AACA;AACF;AAEO;AACL;AAAsBnC;;;AAEtB;AACEqR;AACE;AACA;;;AAIFhJ;;;AAGF;AACA;AAAoDiJ;AAAgB;;AAI9BA;AAAgB;AAEtD;AAGgCA;AAAgB;AAEhD;;AAGA;AAKE;AACEC;AACF;AACE;AACA;;AAEE;AAGI;AACAC;AACF;;AAGFD;AACAE;;AAEJ;AACF;;AAEA;;AAEE;AACAD;AACF;;AAGE;;AAEI;AACA;AACAE;;AAEJ;AAEA;AACA;AACE;AACA;;AAEA;AACF;AACF;AAEA;AACF;;AC3HA;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;AACAxP;;AAEFC;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;;AAEFwP;;;;;;;;;AASAC;AACIzG;AAAejJ;AAAe;AAC9BiJ;AAAgBjJ;AAAe;AAC/BiJ;AAAqBjJ;AAAe;AACpCiJ;AAAsBjJ;;AAAkB;AAC1C;AAAEiJ;AAAejJ;AAAe;AAC9BiJ;AAAajJ;AAAe;AAC5BiJ;AAAiBjJ;AAAe;AAChCiJ;AAAkBjJ;AAAe;AACjCiJ;AAAajJ;AAAe;AAEhC2P;AAGY;AACV;AAmBFC;AAE2B;;AACL;;AACH;AACjB;AACkB;;AACG;AACrB;AAQ0B;;AACD;AACzB;AAGgB;AAChB;AAAsB;AAE1B;AAEA;AACE/P;AACArC;AACAuL;AACA;AACA;;;AAGF;AAEO;;;AAGLpJ;AACF;AAEA;;AAKUC;AAAW;AACjBrC;;;;AAIA;AACAsS;;;AAGAjQ;AACF;;AAEQkQ;;;AAER;AACA;AAA0C;;;AAK1C;AACE;;;;AAKF;AACE;AACEC;AACF;AACEC;AACF;AACF;;;AAIQtF;AAAsB;AAC9B;AACE;AACA;AACA;;AAEA/M;AAGA;AACF;AAEA;AACEA;AACA;AACF;;AAEA;AACA;AACE;AACA;AACA;AACA;;AAEEsS;AACAtS;AAMF;AACA;;AAEA;AACF;;;AAIQuS;AAAa;;AAErB;;AAEE;;AAEA;AACE;AACAxS;AACF;AACF;AAEA;AACF;;ACtTA;AACEmC;AACArC;AACAuL;AACAjJ;AACE;AACAuG;AACErG;AACAC;AACAzC;AAEF;;AAEFkF;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAA8B;;;;;AAM9BA;AACF;AACA;;;;AACwByG;;;AAIxB;;AAGA;AACA;AACAxD;AAEA;AAEA;AACElF;;;;AAIAA;;AAEF;AAEA;;;AAGsBwJ;AAAS;AAE/B;AACExJ;AACA;AACF;AAEA;AACEA;AAGAA;AACAA;AAGAA;AAGAA;;AAEA;AACF;AAEA;;;;AAIE0I;AACF;;AAKF;;ACxGA;AACExG;AACArC;AACAuL;AACAjJ;AACE;AACA;AACAC;AACEC;AACAC;;;AAGFkQ;AACEnQ;AACAxC;;AAEF4S;AACEpQ;AACAC;AACAzC;;AAEF+I;AACEvG;;;AAGFqG;AACErG;AACAxC;AACF;;AAEFkF;AACF;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAA8B;;;;;AAM9BA;AACF;;;;AAEsBS;;;AAOtB;AACA;AACAwC;AAEA;;AAGE9C;;;;AAIAsG;;;AASF;AACA;;;AAMA;AACA;AACEgK;AACF;;;;AAIE;AACEA;AACF;AACF;AACA;;;AAMA;AACA;AACED;AACF;;;;AAIE;AACEA;AACF;AACF;AACA;;;AAMA;AACE/J;AACF;AAEA;AACE1I;;;AAGAA;AACAA;;AAEF;AAEA;AAEA;AAGI2C;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA7C;AAIA;AACEA;AACA;AACF;AAEA;;;;;AAKE0I;AACF;AACF;;ACnMA;AACExG;AACArC;AAEAuL;AACAjJ;AACE;AACAmH;AACEjH;AACAxC;;AAEF0J;AACElH;AACAxC;;AAGF6I;AACErG;AACAxC;AACF;;AAEFkF;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;;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L;AACF;AAEO;;AAMH;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACE;AACJ;AACF;;AC5BO;;;;;AAKHC;AACF;AACA;AAGqB1T;;;AAIGA;;;AAICA;;;AAKfA;;AAAyC;AAGvC;;AACd;;ACvBA;;;;;;;;AAQEyT;AACF;AAEO;;;AAKIhR;AAAkBkR;;AAC7B;;AAEA;AACA;AACO;;;AAKIlR;AAAWkR;;AACtB;;AAEA;AACA;AACO;;;AAMIlR;AAAYkR;;AACvB;AAEO;;;AAKIlR;AAAWkR;;AACtB;;AAEA;AACA;AACO;;;AAKIlR;AAAkBkR;;AAC7B;;AAEA;AACA;AACO;;;AAKIlR;AAAoBkR;;AAC/B;AASO;;AAKH;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACA;AACE;AACJ;AACF;;AC9FA;;;;;;;AAAmDF;AAAa;AAEzD;AACL;AACA;AACA;AACF;AAEO;AAKL;AACA;AACA;AACA;;AAIA;AACF;AAEO;AACL;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;;AAIJ;AAEO;AACL;AACA;AACA;AACF;AAEO;AACL;AACA;AACE;AACA;AACA;AACA;AACA;;AAIJ;AAEO;;AAOH;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACA;AACE;AACJ;AACF;;AC9EA;;;;;;AAAyCA;AAAa;AAEtD;;AAEI;AACF;AACA;;AAEEG;;AAEF;AACE;AACF;AACA;AACA;;;AAAwBC;;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;;AAEEC;AACF;AACF;AACA;AACF;AAEA;;AAEI;AACF;AACA;AACA;AACA;;AAEA;AACF;AAEA;;;AAGI9K;;AAGI;AACA;AACA;;;;;AAMR;AAEO;;AAIG1D;AAAoB;AAC1BtF;;;;AAIA;AACA;AACA;;AAGI;AACA;AACA;;;;AAKN;AACF;AAEO;;AAIGsF;AAAoB;AAC1BtF;;;;AAIJ;AAEO;;AAIGsF;AAAqByO;AAAY;AACvC/T;;;AAGF;;AAEE;AACE;AACF;AACF;;;AAGEgJ;AAGI;AACA;;;AAIE;AACA;AACA;;;;;AAOV;AAEO;;AAIG1D;AAAoB;AAC1BtF;;;;;AAKA;AACAgJ;;AAMM;AACA;AACA;;;;;AAOV;AAEO;;AAIG1D;AAAoB;AAC1BtF;;;;AAIA;AACA;AACA;;AAMM;AACA;AACA;;;;AAMR;AACF;AAEO;;AAIGsF;AAAoB;AAC1BtF;;;;AAIA;AACA;AACA;AACA;AACA;;AAGI;AACA;AACA;;;;AAKN;AACF;AAOO;;AAKH;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACA;AACE;AACJ;AACF;;AC9OO;;ACQP;;;;;;;;AAQEyT;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;;;;;;AAMEC;AACEC;AACAC;AACAC;AACAC;AACAC;AACF;AACF;AAAM9U;;;;AAEN;AACA;AACA;AACA;AACA;;AAIE;AACA+T;;;AAKA3T;;AAOF;;;AAIE2U;AACF;AACEA;AAIF;AAEA;AACA;AACA;AAAuD;;AAEnD;AACA;AACAC;AAKJ;;;AAGA;AACA;AAEW;AAAQ;;AACPvJ;AAAsBwJ;AAAsBvO;AAAQ;AAC5D;AACA;AACA;AACA;AAAa;;AACX;AAGA;AACEwO;AACF;AACA;AAGA;;AAEE;AACA;AACA;AACA;AACE;AACA;AAEE;AACA;AACAF;AAIFG;AACAC;AACAX;;AAEEA;AACF;;AAEE1O;AACAsP;AACF;AACF;AACAH;AACF;AACF;AACA;AACE;AACA;AACA;AACA;;AAMwC5P;AAAcyO;AAAY;AAClE;AACA;;AAEsBtR;AAAK;AACvB;AACA;;AAOE;AACA;AACA;;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA6S;AACF;AACE;AAGA;;AAIM;AACA;AACA;AACA;AACA;AACAN;;AAOF;AACEM;AACF;AACF;AACF;AACEA;AACF;AACF;;AAEE3B;AACA;AACAc;;AAEE1O;AACAsP;AACF;AACF;AACF;AACF;AACEE;AAAe;AAErB;AACF;AACEA;AAAe;AAGnB;AACE;AACA;AAGI;;;;AAOIxP;AACF;AAEF;AAcE;AACE0O;AACF;AACF;AACF;AACEc;AAAe;AAErB;AAEA;;AAIE;AACE;;AAAwB9S;;;AAMxB;AACF;AACA;AACF;AAEA;AACF;;ACxSA;AAAQ+S;AAAoC;AAQrC;;AAKHC;;AAEA1P;AACF;AACE/F;;;AAIF;;;;AAKyC+F;AAAQ;AAE/C;;;AAKA;;;AAIAlC;AACAC;AAEA;;AAEA;;AAGExD;AACAC;AACAC;;AAKJ;;AAIA;;AAEA;;AAESF;AAAUY;;AACrB;;ACzDO;;AAEEwU;AAAqB;;AAUpB3P;AAAQ;;;;;;AAQdA;AACF;AAEA;AACA;;AAGA;AACE;AACE0P;;AAEA1P;AACF;AAEA;;AAEE;AACF;AACF;;;AAIEzF;AACAY;;AAEEyT;;;AAGAE;AACF;;AAEJ;;AC1DO;AAUL;AACE1U;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEA;AAEA;;AAIA;AACA;AACEA;AAGF;AACA;AACEA;AACF;AAEAA;AACAA;AACAA;AACF;AAEA;;AAMA;;AC9CA;AAAQuV;AAAI;AAEL;;;;AAILD;AAMF;AACE7R;;;;;AAC4C6R;AAAK;AAEjD;AACED;;AAEAC;AACF;AACA;AACEvV;AACA0D;;AACsB+R;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;;;;;AAM9BM;AACF;;;;AAQA;AAEA;AAEA;AAGII;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;;AAGF;;AChFA;AAEO;;AAELoL;AACA;AAA8BnJ;AAAW;AACvC;;;;AAKI0J;AACE0K;AACAvK;AACAwK;AACA1P;AACF;AACF;AAEEsM;AACEqD;;AAEEnL;;;AAGFgL;;AAEEhL;AACA8G;;AAEFiE;;AAEE/K;AACA8G;AACF;;AAEFrS;AACF;AAEJ;AACF;;ACKO;AAGLG;;AAMF;;ACvDO;AAKL;AACED;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGE;AACAA;AACAA;AACA;AACF;AAEAA;AAGAA;AACAA;AACF;AAEO;;AAEHwW;AACEjQ;AACAkQ;;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;;AC9MO;AAILnT;;;AAC4BkE;AAAW;AAEvC;AAEAlE;;AAIsB1B;AAAO;AAE7B;AACF;;ACvBA;AACA;AACA;AACA;AACO;;;;AAOH8U;;AAEA;AACE;;AAEEA;AAC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EzI;AACAyM;AACAjP;;AAGFkL;AACE1I;AACAyM;AACAjP;;AAGFmL;AACE3I;AACAC;AACAzC;AAEF;AACF;;ACvDO;AACL;AACA;AACA;AACEM;AACAyQ;AAEIvF;AACA/D;AACAzH;AACF;AAEEwL;AACA/D;AACAzH;;AAIN;AACA;AACF;;ACZA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AAIL;AACEsa;AACAC;AACAC;AACAC;;;AAIA;AACF;;AAEA;AACA;;AAEA;;;AAKA;;AAEAvY;AAGAA;;;AAGEA;AACF;AAEA;AACF;;ACXO;AAEP;AAEA;AAEA;AACE;AACA;AACAiI;AACE3H;AACAxC;;AAGFwF;AACEhD;AACAC;AACAzC;AACA+P;;AAEFzF;AACE9H;AACAC;AACAzC;AACA+P;;AAEF1F;AACE7H;AACAC;AACAzC;AACA+P;;AAEFpK;AACEnD;AACAC;AACAzC;AACA+P;;AAEF1K;AACE7C;AACAC;AACAzC;;AAEFuK;AACE/H;AACAC;AACAzC;;AAGFoE;AACE5B;AACAC;AACAzC;;AAGFyK;AACEjI;AACAC;AACAzC;AACA+P;;AAEFrM;AACElB;AACAC;AACAzC;;AAGF0K;AACElI;AACAC;AACAzC;;AAEFqL;AACE7I;AACAC;AACAzC;;AAGFiD;AACET;AACAuN;AACA/P;;AAEFsL;AACE9I;AACAxC;;AAGF8G;AACEtE;;AAEAxC;;AAEF0a;AACElY;AACAC;AACAzC;;;AAIFsF;AACE9C;AACAC;AACAzC;AAEA+P;AACF;AACF;AAEO;;;AAGL5N;AACF;AAEA;AAGIC;AAA8B;AAEhC;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;;;;;;AAOII;AACF;;;;;AAME2C;;AAEAjB;;;AAGAV;;;;;;;;;;;;AAYAgX;AACApV;;;AA2BF;;;AAGA;AACA;AACE;;AAIA;AACA2F;AACF;;;;AAMEzF;AACAvC;AACAqI;;;AAQI;AAAW;AAMjB;;AAMA;;AAEA;AACA;;;;AAOE;AACEnB;AACF;AACF;;;;;AAKE;AACEF;AACF;AACF;;;;;AAKE;AACErH;AACF;AACF;AACA;;;;AAIE;AACE0I;AACF;AACF;;AAEA;AACA;;;AAGA;;;AAIEqP;AACAC;AACF;;AAEA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;;AAEI;;AAEE;AAEIva;AACAC;AACAC;AACF;AAEE6D;AACA0D;AACF;AAEF;AACF;AACA;AACE+S;AACF;AACAD;AACF;AACF;;;;AAOA;AAEA;AACEza;AAGAA;AACAA;AAGAA;AACAA;AACAA;AAGAA;AACF;;;AAIA;AACA;AAEA;;;;;AAgBA;;AAUA;;AAIMma;AACAC;AACAC;AACAC;;AAGN;AAGI3X;;AAEAxC;AACA0C;AACF;AAEED;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AAEA0C;AACF;AAEEF;AACAC;AACAzC;AAEA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AAEA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;AACEgK;AACAF;;;;;AAKAM;AACAnG;;;AAGAoG;AACAC;AACAC;AACEU;AACAL;AACAJ;AACAC;AACAC;AACAC;AACAE;;;;;AAKFK;;;;;;AAMF;AACF;;ACvjBO;;AAKGvL;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;;ACOO;AAEP;AAEA;AAEO;;;AAGLgC;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;AACA;AACA8B;AACE5B;AACAC;AACAzC;;AAGF0D;AACElB;AACAxC;AAEF;;AAEFkF;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;AAEwBsB;;;;;AAQxB;AAEA;AAMA;AAEA;;;AAKIpD;AACA0C;AACF;;AAGE1C;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;AACF;;ACxHO;;;AAGL0a;AAKF;AACE1a;AACAA;AACAA;AAEA;AAMF;;ACXO;;;AAKH2H;AAKF;AAEA;AACE5H;AACF;AAEA;;AAEIC;AACA;AACF;AACAA;AACA;AACF;AAEA;AACA;;AAIA;AACA;AACA;AACA;AACE;AACA;AACF;;AAGE;AACA;AACF;;AAEA;AACA;;AAEAA;;AAGI2a;AACAtT;AACAuT;AACAC;AACF;AAEF7a;AAGAA;AACF;AAEA;AAKE;AAEA;AACEA;;AAEE;AACEA;AACAA;AACF;;AAEA;AACAA;AACF;AACF;AACE;AACAA;AACAA;AACAA;AACF;AACF;AAEA;AAGE;AAEAA;AACAA;AACAA;AACAA;AAGAA;AACAA;;AAIAA;AACAA;AACAA;;AAEAA;;AAGEc;AACEd;AACF;;AAEEA;AACF;AACF;AAEAA;;AAEEc;AACEd;AACF;;AAEEA;AACF;AACF;AAEAA;;AAEEc;AACEd;AACF;;AAEEA;AACF;AACF;AAEAA;;AAEEc;AACEd;AACF;;AAEEA;AACF;AACF;;;AAIA;;AAEE;AACEA;AACF;AACA;;AAEA;AACF;AAEAA;;AAEAA;AACAA;AAGAA;AACA;AAAa;AAAQ;;AACnB;AACE;AACF;AACA;;;AAKA;AACF;AAEAA;;AAEAA;AACAA;AACAA;AACA;AAAa;AAAQ;;AACnB;AACE;AACF;AACA;;;AAKA;AACF;AAEAA;AACF;;AC/MO;;;;;;AAML2H;AAQF;AACE;;;AAGE+S;AACF;;;;AAKE/S;AACF;AACF;;ACXO;AAEP;AAEA;AAEO;;;AAGL3F;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;AACA;AACAkF;AACEhF;AACAC;AACAzC;;AAGFuC;AACEC;AACAuN;AACAtN;AACAzC;;AAGFoE;AACE5B;AACAC;AACAzC;;AAGF0D;AACElB;AACAxC;AAEF;;AAEFkF;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;AAEA;AACA;;;;;;;AAQEsB;;;AAYF;AACA;AACA;AACEuX;AACF;AACA;AACEC;AACF;AAEA;;AAEQ;AAAW;AAMnB;AAEA;AAGInY;AACAzC;AAEA0C;AAMF;;AAGEF;AACAxC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;;;;;;AAMEoC;AACF;AACF;;ACpKO;;;;;;;;AAQL4Y;AAUF;;AAME;AACE;AACA;AACElB;AACAJ;AACF;AACA;AACE;AACF;AACAuB;AACF;AAEAA;;AAGAjb;;;AAIIE;AACAC;AACAC;;AAGJ;;AAEA;AACA;AACE;AACA;AACE;AACF;;AAEF;;AAEA;AACA;AACA;;AAEE;AACE;AACF;AACF;;AAGA;AACE;AACA;;;;;;AAME4a;AACF;;;AAEUE;;AACR;AACEC;AACF;AACF;AACF;;AAGAnb;;AAGEE;AACAY;;AAEJ;AAEA;;;;;;AAQIka;AAQF;;;AAMA;;;;;;;AASAhb;AACA;AACF;AAEA;;;;;AAOI2H;AAQF;AAEA;;;;AAIEyT;AACF;AACA;AACE;AACF;;;AACuBC;;AAEvBrb;AAEA;;;;;AAKEqb;AACF;AACA;AACE;AACF;AACA;AAEA;AACErb;;AAGSE;AAAUY;AAAQoa;AAAmB;;AAChD;AAEA;AACAzX;AAEA;;;;;;;AAOE2X;AACF;AACA;AACE;AACF;AAEA;;;;;AAKEA;AACF;AACA;AACE;AACF;;;;AAE0CE;;;AAE1C;AACA;;AAEA;AACEtR;AACAF;AACAK;;;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AACE;AACA;AACAgb;AACE9a;;AAEAxC;;AAEFoE;AACE5B;AACAC;AACAzC;;AAGF0D;AACElB;AACAxC;;AAGF8G;AACEtE;;AAEAxC;;AAEFsH;AACE9E;AACAC;AACAzC;;AAEFuW;AACE/T;AACAC;AACAzC;AACF;;AAEFkF;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;;;;AAEwBsB;;;AAIxB;;;AAMA;;AAIA;AAEA;;AAEQ;AAAW;AAMnB;AAEA;AAGIZ;;AAEAxC;AACA0C;AACF;;AAGE1C;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;;;;;;;;AAQE2G;AACF;AACF;;AChMO;AACL;AACE5G;AACF;AAEA;AACEC;AACA;AACF;AAEAA;AACAA;AACAA;AACF;;ACCO;;AAKL;;AAEA;;AAEA;AAEAA;AACAA;AAGAA;AACAA;AACAA;AACAA;AAGAA;AACAA;AACAA;AACAA;AACAA;AACAA;AAEA;AACA;AACE;AACF;AAEA;AACA;AACEmI;AACF;AACA;AACEA;AACF;AAEA;AACEhI;AACAyQ;AAEIvF;AACA/D;AACF;AAEE+D;AACA/D;AACF;AAEE+D;AACA/D;AACAzH;;AAGN;AACA;AACE;AAAe;;;AAGb;AACA;;AAEE;AACF;AACA;AACF;AACA;AAAe;;;AAGb;AACA;;AAEE;AACF;AACA;AACF;AACA;AAAS;;AAET;AACF;AAEAG;AACAA;AACAA;;;AAKI4Q;AAEIvF;AACA/D;AACAzH;AACF;AAEEwL;AACA/D;AACAzH;;AAGN;AAEA;AACF;;AAGF;AAEA;AAME;AACEM;;AAGAyS;AACA;AACF;;;AAGA;AACA;AACE;AACA;;AAEF;;AAEA;AAEA;AACEzS;AAEAmC;AAIAsQ;AACA;AACF;;;AA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ACA;AACA;AACA;AACAO;AAEA;;;;AAIA;AACEsD;AACAC;AACAC;AACAC;AACAC;AACAzD;AACAoB;AACAsC;;AAEAC;AACE9B;AACAyB;;AAEFM;AACA;AACA;AACAC;AACAC;AACF;AAEA;AACA;AACEC;AAA0B;AAC1BC;AACA5C;AACAR;AACE0C;AACAW;;AAEFC;AACAC;AACF;AACA;AACEJ;AACAC;AACA5C;AACAuC;AACE9B;AACAyB;;AAEFtD;AACAmE;AAEAvD;AACE0C;AACF;AACF;;AAGEc;AAQA9D;AACF;;AAEA;AACA;AACE;AACA;;AAEA;AACF;;AAEA;;;AAGA;AACAP;AACAA;AACAA;;AAEA;AACAsE;AACE;AACA;AACE;AACA;AACE;;;AAKF;AACF;AACF;;AAIAtE;AACE;;AAEA;AACAP;AACF;AACF;AAEA;AAKE;AASF;AAEA;AACE;;;AAGE;AAEA;;AAEA;;AASF;AACF;;AC1LO;;;;;;;;;AASLsG;AAWF;AACE;;;;;;;;AAQEA;AACF;AAEA;AACF;;AChBO;AAEP;AAEA;AAcA;AAEA;AAEO;;;AAGLtE;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;AACA;AACA2X;AACEzX;AACAC;AACAzC;;AAEF6d;AACErb;AACAC;AACAzC;;AAEF4R;AACEpP;AACAC;AACAzC;;AAEFoE;AACE5B;AACAC;AACAzC;;AAGF0D;AACElB;AACAxC;;AAGFqD;AACEb;AACAC;AACAzC;;AAEFkD;AACEV;AACAuN;AACAtN;AACAzC;;AAEF8d;AACEtb;AACAC;AACAzC;;AAEFyG;AACEjE;AACAC;AACAzC;AACF;;AAEFkF;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;;;;;AAMEsB;;AAEAlB;AACAiE;;;;AAOF;AACA;AACA;AACA;;AAGAsX;AACE;AACEC;AACAC;AACA;AACF;AACF;AAEAF;AACE;AACEG;AACAD;AACA;AACF;AACF;AAEAF;AACE;AACE5Y;AACA8Y;AACA;AACF;AACF;AAEA;AACAF;AACE;AACEI;AACAF;AACA;AACF;AACF;;AAGE9d;AAGF;AAEA;;AAEQ;AAAW;AAMnB;AAEA;AAGI2C;;AAEAxC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;;AAEEie;AACAxM;;;;;AAKAkM;AACArX;AACF;AACF;;ACxRO;AAIL;AACEvG;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;;;;AAE4CiQ;;;AAE5C;AACA;;;AAOE;AACE;AACE;AAAA;AAEA;AAAA;;;AAOA/P;AACAY;AACEod;;;AAGF/d;;AAEJ;AACE;;AAEED;AACAY;AACEod;AACAC;;;;AAIN;AACF;;AAEIje;AAAU;AACVY;AAAQod;AAAqBC;;AAC7Bhe;;AAEJ;AACF;AAEA;;AAMA;;ACtEO;AACL;AACA;AACF;;ACOA;AACE+B;AACArC;AACAuL;AACAjJ;;;AAGA4C;AACF;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEO;AAGHC;AAA8B;;;;;AAM9BM;AACF;;AAGA;AACEvC;AACA;AACF;;AAIA;AACF;;AChEA;AAEO;;AAELoL;AACA;AAA8BnJ;AAAW;AACvC;;;;AAKI0J;AACE2E;AACF;AACF;AACEzQ;AAAY;AAElB;AACF;;ACnBO;;AAKD;AACE;AACF;AACAG;AAGAA;AAGAA;AACAA;AAGAA;AAGAA;AACAA;AACAA;AACAA;AACF;AAEJ;;AC3BO;;;;AASHA;AAGAA;AACAA;AAGAA;AACAA;AACAA;AAEA;AACF;AACA;AACF;;ACdO;;;AACeoe;AAAU;;;AAM5B;AAEJ;;AAEA;;AAEA;AACA;AAEE;;AAEA;;;AAGE;;;AAGI;;;AAKE;AACA;AACApe;AACAqe;AACF;AACF;AACF;;AAEA5a;AACAC;AACA;AACF;;AAEE;AACA;AACA1D;AACF;AACF;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;;AAGMG;AACAmC;AACF;;;AAEoB8b;AAAU;;AAE5B;;AAEA;AACA;;AAEA;;;AAKF;AACF;AACF;;ACrFO;AACL;;AAEEzZ;;;AAGA;AACE3E;AACF;AACA;AACF;;AAOA;;AAEEse;;AAEA;AACEte;AACF;AACA;AACF;AAEAA;AAGAA;AACAA;AAGF;;AClBA;AACEkC;AACArC;AACAuL;AACAjJ;;;AAGA4C;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACA;;AAEE;AACF;;;;;AAMEA;AACF;;AAEA;;;AACcS;;;;;AAMd;;AAEE6b;AACAC;AACF;AACED;AACAC;AACF;AAEA;AAEA;;AAIIre;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;;;AAEoBoe;AAAU;AAC9B;;;AAGE;;;AAGA;AACF;AACE;;AAEA;AACA;;AAEA;AACF;;AAEEpe;AACF;AACF;;ACjHA;AAEO;AAEP;AAEA;AAEO;;;AAGLgC;AACF;AAEA;;AAKUC;AAAW;AAAMrC;;;AACzB;AACEsC;;;AAGAC;;;;AAIJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;;;;;AAOIF;AACF;;AAIA;AACEjC;AACA;AACF;;;;AAMA;;;AAGQuS;AAAa;AACnBtK;AACF;AAEA;;AAEF;;ACxDO;AACLwW;AACA;AACAC;AACAtW;AACAqD;AACA4K;AACAsI;AACAC;AACAjM;AACAyD;AACAyI;AACAC;AACAhY;AACAiY;AACAC;AACAC;AACAC;AACAC;AACAC;AACAvK;AACAwK;AACA;AACA;AACAC;AACAjZ;AACA8P;AACA;AACAoJ;AACAC;AACAC;AACF;AAEO;AACLC;AACE7f;AACAuL;;;AAGFuU;;AAEEvU;;;AAGFwU;;AAEExU;;;AAGF;;AAEEA;;;AAGFmL;AACE1W;AACAuL;;;AAGFyU;AACEhgB;AACAuL;;;AAGF7H;AACE1D;AACAuL;;;AAGF0U;;AAEE1U;;;AAGFvD;;AAEEuD;;;AAGF2U;;AAEE3U;;;AAGF4U;;AAEE5U;;;AAGFuS;AACE9d;AACAuL;;;AAGFtI;AACEjD;AACAuL;;;AAGF4P;;AAEE5P;;;AAGF6U;;AAEE7U;;AAEF;AACF;;ACxHA;AAEA;AACE;AACA;AACE8U;AAAiDhQ;AAAgB;;;;AAIjE5J;AACA6Z;;AAIEngB;AAGF;AACF;;AAGE;;;AAIIuC;AAAcsE;;AACd8E;AACF;AACEuH;AAAqB;;;AAIzBzP;AACAC;AAEA;AACA;;;AAGE0c;;AAEF;AACEA;;;AAGF;AACEA;AACAC;AACAC;AACF;AACEF;AACF;;AAEA;;;;AAII;AACAG;AACAC;;AAEAje;AAAcsE;AAAoC;AACpD;AACA;AACF;AAEA;AACE7G;AAEIE;AACAC;AACAC;AACF;AAEJ;AACE;AACAJ;;AAEA;;AACwBsgB;AAAU;AAClC;AACF;;AAGF;AACF","debugId":"8693f005-3cc6-4712-ba1e-c0aa7f093c42"}
|
|
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/branch-cleanup.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/pnpm/cmd-pnpm.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/validate-reachability-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/yarn/cmd-yarn.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 description: '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 description: '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 { debugFileOp } from '../../utils/debug.mts'\nimport { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'\nimport { mdTableStringNumber } from '../../utils/markdown.mts'\nimport { serializeResultJson } from '../../utils/serialize-result-json.mts'\nimport { fileLink } from '../../utils/terminal-link.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 type OutputAnalyticsConfig = {\n filepath: string\n outputKind: OutputKind\n repo: string\n scope: string\n time: number\n}\n\nexport async function outputAnalytics(\n result: CResult<\n | SocketSdkSuccessResult<'getOrgAnalytics'>['data']\n | SocketSdkSuccessResult<'getRepoAnalytics'>['data']\n >,\n { filepath, outputKind, repo, scope, time }: OutputAnalyticsConfig,\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 debugFileOp('write', filepath)\n logger.success(`Data successfully written to ${fileLink(filepath)}`)\n } catch (e) {\n debugFileOp('write', filepath, 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 debugFileOp('write', filepath)\n logger.success(`Data successfully written to ${fileLink(filepath)}`)\n } catch (e) {\n debugFileOp('write', filepath, 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 { 0: key, 1: 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 { 0: key, 1: 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 type HandleAnalyticsConfig = {\n filepath: string\n outputKind: OutputKind\n repo: string\n scope: string\n time: number\n}\n\nexport async function handleAnalytics({\n filepath,\n outputKind,\n repo,\n scope,\n time,\n}: HandleAnalyticsConfig) {\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, {\n FLAG_JSON,\n FLAG_MARKDOWN,\n V1_MIGRATION_GUIDE_URL,\n} 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'\nimport { webLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 default: '',\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 parentName,\n importMeta,\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 {\n file: filepath,\n json,\n markdown,\n } = cli.flags as { file: string; json: boolean; markdown: boolean }\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 the ${webLink(V1_MIGRATION_GUIDE_URL, '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: !filepath || !!json || !!markdown,\n message: `The \\`--file\\` flag is only valid when using \\`${FLAG_JSON}\\` or \\`${FLAG_MARKDOWN}\\``,\n fail: 'bad',\n },\n {\n nook: true,\n test: !json || !markdown,\n message: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 filepath,\n outputKind,\n repo: repoName,\n scope,\n time: time === '90' ? 90 : time === '30' ? 30 : 7,\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 { description: `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, {\n FLAG_JSON,\n OUTPUT_JSON,\n OUTPUT_MARKDOWN,\n} 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 === OUTPUT_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 === OUTPUT_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 \\`${FLAG_JSON}\\` flag`,\n )\n debugFn('error', 'Markdown conversion failed')\n debugDir('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, {\n FLAG_JSON,\n FLAG_MARKDOWN,\n V1_MIGRATION_GUIDE_URL,\n} 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 { webLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 the ${webLink(`${constants.SOCKET_WEBSITE_URL}/pricing`, 'Socket pricing page')}.\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 parentName,\n importMeta,\n })\n\n const {\n interactive,\n json,\n markdown,\n org: orgFlag,\n page,\n perPage,\n } = cli.flags as {\n interactive: boolean\n json: boolean\n markdown: boolean\n org: string\n page: number\n perPage: number\n }\n\n const dryRun = !!cli.flags['dryRun']\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 { 0: 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 the ${webLink(V1_MIGRATION_GUIDE_URL, '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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 fs from 'node:fs'\nimport path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants.mts'\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 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 | undefined,\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 if (constants.ENV.SOCKET_CLI_DEBUG) {\n const fileInfo = await Promise.all(\n packagePaths.map(async p => {\n const absPath = path.resolve(process.cwd(), p)\n const stat = await fs.promises.stat(absPath)\n return { path: absPath, size: stat.size }\n }),\n )\n logger.info(\n `[DEBUG] ${new Date().toISOString()} Uploading full scan manifests: ${JSON.stringify(fileInfo)}`,\n )\n }\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 { description: '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 description: '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 { formatErrorWithDetail } from '../../utils/errors.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(status: string) {\n scanStatus = status\n updateProgress()\n }\n\n function updatePolicy(status: string) {\n policyStatus = status\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', 'Failed to parse report data line as JSON')\n debugDir('error', { 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:\n formatErrorWithDetail('Error requesting scan', e) ||\n 'Error requesting scan: (no error message found)',\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:\n formatErrorWithDetail('Error requesting policy', e) ||\n 'Error requesting policy: (no error message found)',\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, { UNKNOWN_VALUE } 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\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, {\n EXT_JSON,\n OUTPUT_JSON,\n OUTPUT_TEXT,\n} 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 === OUTPUT_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 === OUTPUT_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 unhealthy report, do we?\n // if (!scanReport.data.healthy) {\n // logger.fail(failMsgWithBadge(scanReport.message, scanReport.cause))\n // return\n // }\n\n if (\n outputKind === OUTPUT_JSON ||\n (outputKind === OUTPUT_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 // Not short so must be a regular report.\n scanReport.data as ScanReport,\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 constants from '../../constants.mts'\nimport { handleApiCall } from '../../utils/api.mts'\nimport { extractTier1ReachabilityScanId } from '../../utils/coana.mts'\nimport { spawnCoanaDlx } from '../../utils/dlx.mts'\nimport { hasEnterpriseOrgPlan } from '../../utils/organization.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\nimport { socketDevLink } from '../../utils/terminal-link.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 reachConcurrency: number\n reachDebug: boolean\n reachDisableAnalytics: boolean\n reachDisableAnalysisSplitting: 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 outputPath?: string | undefined\n packagePaths?: string[] | undefined\n reachabilityOptions: ReachabilityOptions\n repoName?: string | undefined\n spinner?: Spinner | undefined\n target: string\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 outputPath,\n packagePaths,\n reachabilityOptions,\n repoName,\n spinner,\n target,\n uploadManifests = true,\n } = { __proto__: null, ...options } as ReachabilityAnalysisOptions\n\n // Determine the analysis target - make it relative to cwd if absolute.\n let analysisTarget = target\n if (path.isAbsolute(analysisTarget)) {\n analysisTarget = path.relative(cwd, analysisTarget) || '.'\n }\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 ${socketDevLink('upgrade your plan', '/pricing')}. This feature is only available for organizations with an enterprise plan.`,\n }\n }\n\n const wasSpinning = !!spinner?.isSpinning\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 // 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 // Ensure uploaded manifest files are relative to analysis target as coana resolves SBOM manifest files relative to this path\n const uploadCResult = await handleApiCall(\n sockSdk.uploadManifestFiles(\n orgSlug,\n filepathsToUpload,\n path.resolve(cwd, analysisTarget),\n ),\n {\n description: '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 const outputFilePath = outputPath || constants.DOT_SOCKET_DOT_FACTS_JSON\n // Build Coana arguments.\n const coanaArgs = [\n 'run',\n analysisTarget,\n '--output-dir',\n path.dirname(outputFilePath),\n '--socket-mode',\n outputFilePath,\n '--disable-report-submission',\n ...(reachabilityOptions.reachAnalysisTimeout\n ? ['--analysis-timeout', `${reachabilityOptions.reachAnalysisTimeout}`]\n : []),\n ...(reachabilityOptions.reachAnalysisMemoryLimit\n ? ['--memory-limit', `${reachabilityOptions.reachAnalysisMemoryLimit}`]\n : []),\n ...(reachabilityOptions.reachConcurrency\n ? ['--concurrency', `${reachabilityOptions.reachConcurrency}`]\n : []),\n ...(reachabilityOptions.reachDebug ? ['--debug'] : []),\n ...(reachabilityOptions.reachDisableAnalytics\n ? ['--disable-analytics-sharing']\n : []),\n ...(reachabilityOptions.reachDisableAnalysisSplitting\n ? ['--disable-analysis-splitting']\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: Record<string, string> = {}\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 spawnCoanaDlx(coanaArgs, orgSlug, {\n cwd,\n env: coanaEnv,\n spinner,\n stdio: 'inherit',\n })\n\n if (wasSpinning) {\n spinner.start()\n }\n\n return coanaResult.ok\n ? {\n ok: true,\n data: {\n // Use the actual output filename for the scan.\n reachabilityReport: outputFilePath,\n tier1ReachabilityScanId:\n extractTier1ReachabilityScanId(outputFilePath),\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 {\n ENVIRONMENT_YAML,\n ENVIRONMENT_YML,\n SOCKET_JSON,\n} from '../../constants.mts'\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 { REQUIREMENTS_TXT } 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'\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 { REQUIREMENTS_TXT, SOCKET_JSON } from '../../constants.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 { debugDir, debugFn } 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 { socketDocsLink } from '../../utils/terminal-link.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 debugFn('notice', `Creating new scan for ${orgSlug}/${repoName}`)\n debugDir('inspect', {\n autoManifest,\n branchName,\n commitHash,\n defaultBranch,\n interactive,\n pendingHead,\n pullRequest,\n readOnly,\n report,\n reportLevel,\n targets,\n tmp,\n })\n\n if (autoManifest) {\n logger.info('Auto-generating manifest files ...')\n debugFn('notice', 'Auto-manifest mode enabled')\n const sockJson = readOrDefaultSocketJson(cwd)\n const detected = await detectManifestActions(sockJson, cwd)\n debugDir('inspect', { detected })\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 debugFn('warn', 'Failed to fetch supported scan file names')\n debugDir('inspect', { supportedFilesCResult })\n await outputCreateNewScan(supportedFilesCResult, {\n interactive,\n outputKind,\n })\n return\n }\n debugFn(\n 'notice',\n `Fetched ${supportedFilesCResult.data['size']} supported file types`,\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 ${socketDocsLink('/docs/manifest-file-detection-in-socket', 'docs.socket.dev')}`,\n message:\n 'TARGET (file/dir) must contain matching / supported file types for a scan',\n })\n if (!wasValidInput) {\n debugFn('warn', 'No eligible files found to scan')\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 debugFn('notice', 'Read-only mode, exiting early')\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 debugFn('notice', 'Reachability analysis enabled')\n debugDir('inspect', { reachabilityOptions: reach })\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 target: targets[0]!,\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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { 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 debugFn('notice', 'Starting CI scan')\n debugDir('inspect', { autoManifest })\n\n const orgSlugCResult = await getDefaultOrgSlug()\n if (!orgSlugCResult.ok) {\n debugFn('warn', 'Failed to get default org slug')\n debugDir('inspect', { orgSlugCResult })\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 debugFn(\n 'notice',\n `CI scan for ${orgSlug}/${repoName} on branch ${branchName}`,\n )\n debugDir('inspect', { orgSlug, cwd, branchName, repoName })\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 reachConcurrency: 1,\n reachDebug: false,\n reachDisableAnalytics: false,\n reachDisableAnalysisSplitting: 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 {\n CliCommandConfig,\n CliCommandContext,\n} from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'ci',\n description:\n 'Alias for `socket scan create --report` (creates report and exits with error if unhealthy)',\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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 { isConfigFromFlag, 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 (isConfigFromFlag()) {\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, { FLAG_JSON, FLAG_MARKDOWN } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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(({ 0: key, 1: description }) => ` - ${key} -- ${description}`)\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 as { json: boolean; markdown: boolean }\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 { isConfigFromFlag } 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 = isConfigFromFlag()\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, { FLAG_JSON, FLAG_MARKDOWN } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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(({ 0: key, 1: description }) => ` - ${key} -- ${description}`)\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 }: CliCommandContext,\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 isConfigFromFlag,\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 = isConfigFromFlag()\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, { FLAG_JSON, FLAG_MARKDOWN } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport { 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 debugFn('notice', `Setting config ${key} = ${value}`)\n debugDir('inspect', { key, value, outputKind })\n\n const result = updateConfigValue(key, value)\n\n debugFn('notice', `Config update ${result.ok ? 'succeeded' : 'failed'}`)\n debugDir('inspect', { result })\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, { FLAG_JSON, FLAG_MARKDOWN } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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(({ 0: key, 1: description }) => ` - ${key} -- ${description}`)\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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, { FLAG_JSON, FLAG_MARKDOWN } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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(({ 0: key, 1: description }) => ` - ${key} -- ${description}`)\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 argv,\n name: `${parentName} config`,\n importMeta,\n subcommands: {\n auto: cmdConfigAuto,\n get: cmdConfigGet,\n list: cmdConfigList,\n set: cmdConfigSet,\n unset: cmdConfigUnset,\n },\n },\n { description },\n )\n },\n}\n","/**\n * Branch cleanup utilities for socket fix command.\n * Manages local and remote branch lifecycle during PR creation.\n *\n * Critical distinction: Remote branches are sacred when a PR exists, disposable when they don't.\n */\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { gitDeleteBranch, gitDeleteRemoteBranch } from '../../utils/git.mts'\n\n/**\n * Clean up a stale branch (both remote and local).\n * Safe to delete both since no PR exists for this branch.\n *\n * Returns true if cleanup succeeded or should continue, false if should skip GHSA.\n */\nexport async function cleanupStaleBranch(\n branch: string,\n ghsaId: string,\n cwd: string,\n): Promise<boolean> {\n logger.warn(`Stale branch ${branch} found without open PR, cleaning up...`)\n debugFn('notice', `cleanup: deleting stale branch ${branch}`)\n\n const deleted = await gitDeleteRemoteBranch(branch, cwd)\n if (!deleted) {\n logger.error(\n `Failed to delete stale remote branch ${branch}, skipping ${ghsaId}.`,\n )\n debugFn('error', `cleanup: remote deletion failed for ${branch}`)\n return false\n }\n\n // Clean up local branch too to avoid conflicts.\n await gitDeleteBranch(branch, cwd)\n return true\n}\n\n/**\n * Clean up branches after PR creation failure.\n * Safe to delete both remote and local since no PR was created.\n */\nexport async function cleanupFailedPrBranches(\n branch: string,\n cwd: string,\n): Promise<void> {\n // Clean up pushed branch since PR creation failed.\n // Safe to delete both remote and local since no PR exists.\n await gitDeleteRemoteBranch(branch, cwd)\n await gitDeleteBranch(branch, cwd)\n}\n\n/**\n * Clean up local branch after successful PR creation.\n * Keeps remote branch - PR needs it to be mergeable.\n */\nexport async function cleanupSuccessfulPrLocalBranch(\n branch: string,\n cwd: string,\n): Promise<void> {\n // Clean up local branch only - keep remote branch for PR merge.\n await gitDeleteBranch(branch, cwd)\n}\n\n/**\n * Clean up branches in catch block after unexpected error.\n * Safe to delete both remote and local since no PR was created.\n */\nexport async function cleanupErrorBranches(\n branch: string,\n cwd: string,\n remoteBranchExists: boolean,\n): Promise<void> {\n // Clean up remote branch if it exists (push may have succeeded before error).\n // Safe to delete both remote and local since no PR was created.\n if (remoteBranchExists) {\n await gitDeleteRemoteBranch(branch, cwd)\n }\n await gitDeleteBranch(branch, cwd)\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\n/**\n * Extract unique package names with ecosystems from vulnerability details.\n */\nfunction getUniquePackages(details: GhsaDetails): string[] {\n return [\n ...new Set(\n details.vulnerabilities.nodes.map(\n v => `${v.package.name} (${v.package.ecosystem})`,\n ),\n ),\n ]\n}\n\nexport type SocketFixBranchParser = (\n branch: string,\n) => SocketFixBranchParseResult | undefined\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(\n branch: string,\n ): SocketFixBranchParseResult | undefined {\n const match = pattern.exec(branch) as [string, string] | null\n if (!match) {\n return undefined\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> | undefined,\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 = getUniquePackages(details)\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 = getUniquePackages(details)\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 GQL_PAGE_SENTINEL,\n GQL_PR_STATE_CLOSED,\n GQL_PR_STATE_MERGED,\n GQL_PR_STATE_OPEN,\n UNKNOWN_VALUE,\n} from '../../constants.mts'\nimport { formatErrorWithDetail } from '../../utils/errors.mts'\nimport { gitDeleteRemoteBranch } from '../../utils/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 type OpenPrResult =\n | { ok: true; pr: OctokitResponse<Pr> }\n | { ok: false; reason: 'already_exists'; error: RequestError }\n | {\n ok: false\n reason: 'validation_error'\n error: RequestError\n details: string\n }\n | { ok: false; reason: 'permission_denied'; error: RequestError }\n | { ok: false; reason: 'network_error'; error: RequestError }\n | { ok: false; reason: 'unknown'; error: Error }\n\nexport async function openSocketFixPr(\n owner: string,\n repo: string,\n branch: string,\n ghsaIds: string[],\n options?: OpenSocketFixPrOptions | undefined,\n): Promise<OpenPrResult> {\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 const pr = await octokit.pulls.create(octokitPullsCreateParams)\n return { ok: true, pr }\n } catch (e) {\n // Handle RequestError from Octokit.\n if (e instanceof RequestError) {\n const errors = (e.response?.data as any)?.['errors']\n const errorMessages = Array.isArray(errors)\n ? errors.map(\n d => d.message?.trim() ?? `${d.resource}.${d.field} (${d.code})`,\n )\n : []\n\n // Check for \"PR already exists\" error.\n if (\n errorMessages.some(msg =>\n msg.toLowerCase().includes('pull request already exists'),\n )\n ) {\n debugFn('error', 'Failed to open pull request: already exists')\n return { ok: false, reason: 'already_exists', error: e }\n }\n\n // Check for validation errors (e.g., no commits between branches).\n if (errors && errors.length > 0) {\n const details = errorMessages.map(d => `- ${d}`).join('\\n')\n debugFn('error', `Failed to open pull request:\\n${details}`)\n return { ok: false, reason: 'validation_error', error: e, details }\n }\n\n // Check HTTP status codes.\n if (e.status === 403 || e.status === 401) {\n debugFn('error', 'Failed to open pull request: permission denied')\n return { ok: false, reason: 'permission_denied', error: e }\n }\n\n if (e.status && e.status >= 500) {\n debugFn('error', 'Failed to open pull request: network error')\n return { ok: false, reason: 'network_error', error: e }\n }\n }\n\n // Unknown error.\n debugFn('error', `Failed to open pull request: ${e}`)\n return { ok: false, reason: 'unknown', error: e as Error }\n }\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 cleanupSocketFixPrs(\n owner: string,\n repo: string,\n ghsaId: string,\n): Promise<PrMatch[]> {\n const contextualMatches = await getSocketFixPrsWithContext(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 // Merge the base branch into the head branch to update the PR.\n await octokit.repos.merge({\n owner,\n repo,\n // The PR branch (destination).\n base: match.headRefName,\n // The target branch (source).\n head: match.baseRefName,\n })\n debugFn('notice', `pr: updating stale ${prRef}`)\n // Update cache entry - only GraphQL is used now.\n context.entry.mergeStateStatus = 'CLEAN'\n // Mark cache to be saved.\n cachesToSave.set(context.cacheKey, context.data)\n } catch (e) {\n debugFn(\n 'error',\n formatErrorWithDetail(`pr: failed to update ${prRef}`, e),\n )\n debugDir('error', e)\n }\n }\n\n // Clean up merged PR branches.\n if (match.state === GQL_PR_STATE_MERGED) {\n const { number: prNum } = match\n const prRef = `PR #${prNum}`\n try {\n const success = await gitDeleteRemoteBranch(match.headRefName)\n if (success) {\n debugFn(\n 'notice',\n `pr: deleted merged branch ${match.headRefName} for ${prRef}`,\n )\n } else {\n debugFn(\n 'warn',\n `pr: failed to delete branch ${match.headRefName} for ${prRef}`,\n )\n }\n } catch (e) {\n // Don't treat this as a hard error - branch might already be deleted.\n debugFn(\n 'warn',\n formatErrorWithDetail(\n `pr: failed to delete branch ${match.headRefName} for ${prRef}`,\n e,\n ),\n )\n debugDir('error', e)\n }\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 is PromiseFulfilledResult<PrMatch> => r.status === 'fulfilled',\n )\n\n return fulfilledMatches.map(r => r.value)\n}\n\nexport type PrAutoMergeState = {\n enabled: boolean\n details?: string[] | undefined\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 getSocketFixPrs(\n owner: string,\n repo: string,\n options?: SocketPrsOptions | undefined,\n): Promise<PrMatch[]> {\n return (await getSocketFixPrsWithContext(owner, repo, options)).map(\n d => d.match,\n )\n}\n\ntype 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\ntype GqlPullRequestsResponse = {\n repository: {\n pullRequests: {\n pageInfo: {\n hasNextPage: boolean\n endCursor: string | null\n }\n nodes: GqlPrNode[]\n }\n }\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 getSocketFixPrsWithContext(\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 octokitGraphql = getOctokitGraphql()\n const contextualMatches: ContextualPrMatch[] = []\n const states = (\n typeof statesValue === 'string'\n ? statesValue.toLowerCase() === 'all'\n ? [GQL_PR_STATE_OPEN, GQL_PR_STATE_CLOSED, GQL_PR_STATE_MERGED]\n : [statesValue]\n : statesValue\n ).map(s => s.toUpperCase())\n\n try {\n let hasNextPage = true\n let cursor: string | null = null\n let pageIndex = 0\n const gqlCacheKey = `${repo}-pr-graphql-snapshot-${states.join('-').toLowerCase()}`\n while (hasNextPage) {\n // eslint-disable-next-line no-await-in-loop\n const gqlResp = (await cacheFetch(\n `${gqlCacheKey}-page-${pageIndex}`,\n () =>\n octokitGraphql(\n `\n query($owner: String!, $repo: String!, $states: [PullRequestState!], $after: String) {\n repository(owner: $owner, name: $repo) {\n pullRequests(first: 100, states: $states, after: $after, orderBy: {field: CREATED_AT, direction: DESC}) {\n pageInfo {\n hasNextPage\n endCursor\n }\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 after: cursor,\n },\n ),\n )) as GqlPullRequestsResponse\n\n const { nodes, pageInfo } = gqlResp?.repository?.pullRequests ?? {\n nodes: [],\n pageInfo: { hasNextPage: false, endCursor: null },\n }\n\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}-page-${pageIndex}`,\n data: gqlResp,\n entry: node,\n index: i,\n parent: nodes,\n },\n match: {\n ...node,\n author: login ?? UNKNOWN_VALUE,\n },\n })\n }\n }\n\n // Continue to next page.\n hasNextPage = pageInfo.hasNextPage\n cursor = pageInfo.endCursor\n pageIndex += 1\n\n // Safety limit to prevent infinite loops.\n if (pageIndex === GQL_PAGE_SENTINEL) {\n debugFn(\n 'warn',\n `GraphQL pagination reached safety limit (${GQL_PAGE_SENTINEL} pages) for ${owner}/${repo}`,\n )\n break\n }\n\n // Early exit optimization: if we found matches and only looking for specific GHSA,\n // we can stop pagination since we likely found what we need.\n if (contextualMatches.length > 0 && ghsaId) {\n break\n }\n }\n } catch (e) {\n debugFn('error', `GraphQL pagination failed for ${owner}/${repo}`)\n debugDir('error', e)\n }\n\n return contextualMatches\n}\n","import { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { debugFn, isDebug } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getSocketFixPrs } 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 | undefined {\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 undefined\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 | undefined\n}\n\nexport interface MissingEnvVars {\n missing: string[]\n present: string[]\n}\n\n/**\n * Get formatted instructions for setting CI environment variables.\n */\nexport function getCiEnvInstructions(): string {\n return (\n 'To enable automatic pull request creation, run in CI with these environment variables:\\n' +\n ' - CI=1\\n' +\n ' - SOCKET_CLI_GITHUB_TOKEN=<your-github-token>\\n' +\n ' - SOCKET_CLI_GIT_USER_NAME=<git-username>\\n' +\n ' - SOCKET_CLI_GIT_USER_EMAIL=<git-email>'\n )\n}\n\n/**\n * Check which required CI environment variables are missing.\n * Returns lists of missing and present variables.\n */\nexport function checkCiEnvVars(): MissingEnvVars {\n const {\n CI,\n SOCKET_CLI_GIT_USER_EMAIL,\n SOCKET_CLI_GIT_USER_NAME,\n SOCKET_CLI_GITHUB_TOKEN,\n } = constants.ENV\n\n const missing: string[] = []\n const present: string[] = []\n\n if (CI) {\n present.push('CI')\n } else {\n missing.push('CI')\n }\n\n if (SOCKET_CLI_GIT_USER_EMAIL) {\n present.push('SOCKET_CLI_GIT_USER_EMAIL')\n } else {\n missing.push('SOCKET_CLI_GIT_USER_EMAIL')\n }\n\n if (SOCKET_CLI_GIT_USER_NAME) {\n present.push('SOCKET_CLI_GIT_USER_NAME')\n } else {\n missing.push('SOCKET_CLI_GIT_USER_NAME')\n }\n\n if (SOCKET_CLI_GITHUB_TOKEN) {\n present.push('SOCKET_CLI_GITHUB_TOKEN')\n } else {\n missing.push('SOCKET_CLI_GITHUB_TOKEN (or GITHUB_TOKEN)')\n }\n\n return { missing, present }\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 const envCheck = checkCiEnvVars()\n\n // Provide clear feedback about missing environment variables.\n if (constants.ENV.CI && envCheck.missing.length > 1) {\n // CI is set but other required vars are missing.\n const missingExceptCi = envCheck.missing.filter(v => v !== 'CI')\n if (missingExceptCi.length) {\n logger.warn(\n `CI mode detected, but pull request creation is disabled due to missing environment variables:\\n` +\n ` Missing: ${joinAnd(missingExceptCi)}\\n` +\n ` Set these variables to enable automatic pull request creation.`,\n )\n }\n } else if (\n // If not in CI but some CI-related env vars are set.\n !constants.ENV.CI &&\n envCheck.present.length &&\n // then log about it when in debug mode.\n isDebug('notice')\n ) {\n debugFn(\n 'notice',\n `miss: fixEnv.isCi is false, expected ${joinAnd(envCheck.missing)} to be set`,\n )\n }\n\n let repoInfo: RepoInfo | undefined\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 getSocketFixPrs(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 { promises as fs } from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { readJsonSync } from '@socketsecurity/registry/lib/fs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport {\n cleanupErrorBranches,\n cleanupFailedPrBranches,\n cleanupStaleBranch,\n cleanupSuccessfulPrLocalBranch,\n} from './branch-cleanup.mts'\nimport {\n checkCiEnvVars,\n getCiEnvInstructions,\n getFixEnv,\n} from './env-helpers.mts'\nimport { getSocketFixBranchName, getSocketFixCommitMessage } from './git.mts'\nimport { getSocketFixPrs, openSocketFixPr } from './pull-request.mts'\nimport {\n DOT_SOCKET_DOT_FACTS_JSON,\n FLAG_DRY_RUN,\n GQL_PR_STATE_OPEN,\n} from '../../constants.mts'\nimport { handleApiCall } from '../../utils/api.mts'\nimport { spawnCoanaDlx } from '../../utils/dlx.mts'\nimport { getErrorCause } from '../../utils/errors.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'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\ntype DiscoverGhsaIdsOptions = {\n cwd?: string | undefined\n limit?: number | undefined\n spinner?: Spinner | undefined\n}\n\n/**\n * Discovers GHSA IDs by running coana without applying fixes.\n * Returns a list of GHSA IDs, optionally limited.\n */\nasync function discoverGhsaIds(\n orgSlug: string,\n tarHash: string,\n options?: DiscoverGhsaIdsOptions | undefined,\n): Promise<string[]> {\n const {\n cwd = process.cwd(),\n limit,\n spinner,\n } = {\n __proto__: null,\n ...options,\n } as DiscoverGhsaIdsOptions\n\n const foundCResult = await spawnCoanaDlx(\n ['find-vulnerabilities', cwd, '--manifests-tar-hash', tarHash],\n orgSlug,\n { cwd, spinner },\n { stdio: 'pipe' },\n )\n\n if (foundCResult.ok) {\n // Coana prints ghsaIds as json-formatted string on the final line of the output\n const foundIds: string[] = []\n try {\n const ghsaIdsRaw = foundCResult.data.trim().split('\\n').pop()\n if (ghsaIdsRaw) {\n foundIds.push(...JSON.parse(ghsaIdsRaw))\n }\n } catch {}\n return limit !== undefined ? foundIds.slice(0, limit) : foundIds\n }\n return []\n}\n\nexport async function coanaFix(\n fixConfig: FixConfig,\n): Promise<CResult<{ data?: unknown; fixed: boolean }>> {\n const {\n applyFixes,\n autopilot,\n cwd,\n disableMajorUpdates,\n exclude,\n ghsas,\n include,\n limit,\n minimumReleaseAge,\n orgSlug,\n outputFile,\n showAffectedDirectDependencies,\n spinner,\n } = 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 // Exclude any .socket.facts.json files that happen to be in the scan\n // folder before the analysis was run.\n const filepathsToUpload = scanFilepaths.filter(\n p => path.basename(p).toLowerCase() !== DOT_SOCKET_DOT_FACTS_JSON,\n )\n const uploadCResult = await handleApiCall(\n sockSdk.uploadManifestFiles(orgSlug, filepathsToUpload, cwd),\n {\n description: '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 // Inform user about local mode when fixes will be applied.\n if (applyFixes && ghsas.length) {\n const envCheck = checkCiEnvVars()\n if (envCheck.present.length) {\n // Some CI vars are set but not all - show what's missing.\n if (envCheck.missing.length) {\n logger.info(\n 'Running in local mode - fixes will be applied directly to your working directory.\\n' +\n `Missing environment variables for PR creation: ${joinAnd(envCheck.missing)}`,\n )\n }\n } else {\n // No CI vars are present - show general local mode message.\n logger.info(\n 'Running in local mode - fixes will be applied directly to your working directory.\\n' +\n getCiEnvInstructions(),\n )\n }\n }\n\n let ids: string[]\n if (isAll && limit > 0) {\n ids = await discoverGhsaIds(orgSlug, tarHash, {\n cwd,\n limit,\n spinner,\n })\n } else if (limit > 0) {\n ids = ghsas.slice(0, limit)\n } else {\n ids = []\n }\n\n if (limit < 1 || ids.length === 0) {\n spinner?.stop()\n return { ok: true, data: { fixed: false } }\n }\n\n // Create a temporary file for the output.\n const tmpDir = os.tmpdir()\n const tmpFile = path.join(tmpDir, `socket-fix-${Date.now()}.json`)\n\n try {\n const fixCResult = await spawnCoanaDlx(\n [\n 'compute-fixes-and-upgrade-purls',\n cwd,\n '--manifests-tar-hash',\n tarHash,\n '--apply-fixes-to',\n ...ids,\n ...(fixConfig.rangeStyle\n ? ['--range-style', fixConfig.rangeStyle]\n : []),\n ...(minimumReleaseAge\n ? ['--minimum-release-age', minimumReleaseAge]\n : []),\n ...(include.length ? ['--include', ...include] : []),\n ...(exclude.length ? ['--exclude', ...exclude] : []),\n ...(!applyFixes ? [FLAG_DRY_RUN] : []),\n '--output-file',\n tmpFile,\n ...(disableMajorUpdates ? ['--disable-major-updates'] : []),\n ...(showAffectedDirectDependencies\n ? ['--show-affected-direct-dependencies']\n : []),\n ...fixConfig.unknownFlags,\n ],\n fixConfig.orgSlug,\n { cwd, spinner, stdio: 'inherit' },\n )\n\n spinner?.stop()\n\n if (!fixCResult.ok) {\n return fixCResult\n }\n\n // Read the temporary file to get the actual fixes result.\n const fixesResultJson = readJsonSync(tmpFile, { throws: false })\n\n // Copy to outputFile if provided.\n if (outputFile) {\n logger.info(`Copying fixes result to ${outputFile}`)\n const tmpContent = await fs.readFile(tmpFile, 'utf8')\n await fs.writeFile(outputFile, tmpContent, 'utf8')\n }\n\n return { ok: true, data: { data: fixesResultJson, fixed: true } }\n } finally {\n // Clean up the temporary file.\n try {\n await fs.unlink(tmpFile)\n } catch {\n // Ignore cleanup errors.\n }\n }\n }\n\n // Adjust limit based on open Socket Fix PRs.\n let adjustedLimit = limit\n if (shouldOpenPrs && fixEnv.repoInfo) {\n try {\n const openPrs = await getSocketFixPrs(\n fixEnv.repoInfo.owner,\n fixEnv.repoInfo.repo,\n { states: GQL_PR_STATE_OPEN },\n )\n const openPrCount = openPrs.length\n // Reduce limit by number of open PRs to avoid creating too many.\n adjustedLimit = Math.max(0, limit - openPrCount)\n if (openPrCount > 0) {\n debugFn(\n 'notice',\n `limit: adjusted from ${limit} to ${adjustedLimit} (${openPrCount} open Socket Fix ${pluralize('PR', openPrCount)}`,\n )\n }\n } catch (e) {\n debugFn('warn', 'Failed to count open PRs, using original limit')\n debugDir('error', e)\n }\n }\n\n const shouldSpawnCoana = adjustedLimit > 0\n\n let ids: string[] | undefined\n\n if (shouldSpawnCoana && isAll) {\n ids = await discoverGhsaIds(orgSlug, tarHash, {\n cwd,\n limit: adjustedLimit,\n spinner,\n })\n } else if (shouldSpawnCoana) {\n ids = ghsas.slice(0, adjustedLimit)\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.\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 spawnCoanaDlx(\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 ...(minimumReleaseAge\n ? ['--minimum-release-age', minimumReleaseAge]\n : []),\n ...(include.length ? ['--include', ...include] : []),\n ...(exclude.length ? ['--exclude', ...exclude] : []),\n ...(disableMajorUpdates ? ['--disable-major-updates'] : []),\n ...(showAffectedDirectDependencies\n ? ['--show-affected-direct-dependencies']\n : []),\n ...fixConfig.unknownFlags,\n ],\n fixConfig.orgSlug,\n { cwd, spinner, stdio: 'inherit' },\n )\n\n if (!fixCResult.ok) {\n logger.error(`Update failed for ${ghsaId}: ${getErrorCause(fixCResult)}`)\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 an open PR already exists for this GHSA.\n // eslint-disable-next-line no-await-in-loop\n const existingOpenPrs = await getSocketFixPrs(\n fixEnv.repoInfo.owner,\n fixEnv.repoInfo.repo,\n { ghsaId, states: GQL_PR_STATE_OPEN },\n )\n\n if (existingOpenPrs.length > 0) {\n const prNum = existingOpenPrs[0]!.number\n logger.info(`PR #${prNum} already exists for ${ghsaId}, skipping.`)\n debugFn('notice', `skip: open PR #${prNum} exists for ${ghsaId}`)\n continue ghsaLoop\n }\n\n // If branch exists but no open PR, delete the stale branch.\n // This handles cases where PR creation failed but branch was pushed.\n // eslint-disable-next-line no-await-in-loop\n if (await gitRemoteBranchExists(branch, cwd)) {\n // eslint-disable-next-line no-await-in-loop\n const shouldContinue = await cleanupStaleBranch(branch, ghsaId, cwd)\n if (!shouldContinue) {\n continue ghsaLoop\n }\n }\n\n // Check for GitHub token before doing any git operations.\n if (!fixEnv.githubToken) {\n logger.error(\n 'Cannot create pull request: SOCKET_CLI_GITHUB_TOKEN environment variable is not set.\\n' +\n 'Set SOCKET_CLI_GITHUB_TOKEN or GITHUB_TOKEN to enable PR creation.',\n )\n debugFn('error', `skip: missing GitHub token for ${ghsaId}`)\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 prResult = 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 (prResult.ok) {\n const { data } = prResult.pr\n const prRef = `PR #${data.number}`\n\n logger.success(`Opened ${prRef} for ${ghsaId}.`)\n\n if (autopilot) {\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 // Clean up local branch only - keep remote branch for PR merge.\n // eslint-disable-next-line no-await-in-loop\n await cleanupSuccessfulPrLocalBranch(branch, cwd)\n } else {\n // Handle PR creation failures.\n if (prResult.reason === 'already_exists') {\n logger.info(\n `PR already exists for ${ghsaId} (this should not happen due to earlier check).`,\n )\n // Don't delete branch - PR exists and needs it.\n } else if (prResult.reason === 'validation_error') {\n logger.error(\n `Failed to create PR for ${ghsaId}:\\n${prResult.details}`,\n )\n // eslint-disable-next-line no-await-in-loop\n await cleanupFailedPrBranches(branch, cwd)\n } else if (prResult.reason === 'permission_denied') {\n logger.error(\n `Failed to create PR for ${ghsaId}: Permission denied. Check SOCKET_CLI_GITHUB_TOKEN permissions.`,\n )\n // eslint-disable-next-line no-await-in-loop\n await cleanupFailedPrBranches(branch, cwd)\n } else if (prResult.reason === 'network_error') {\n logger.error(\n `Failed to create PR for ${ghsaId}: Network error. Please try again.`,\n )\n // eslint-disable-next-line no-await-in-loop\n await cleanupFailedPrBranches(branch, cwd)\n } else {\n logger.error(\n `Failed to create PR for ${ghsaId}: ${prResult.error.message}`,\n )\n // eslint-disable-next-line no-await-in-loop\n await cleanupFailedPrBranches(branch, cwd)\n }\n }\n\n // Reset back to base branch for next iteration.\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 } catch (e) {\n logger.warn(\n `Unexpected condition: Push failed for ${ghsaId}, skipping PR creation.`,\n )\n debugDir('error', e)\n // Clean up branches (push may have succeeded before error).\n // eslint-disable-next-line no-await-in-loop\n const remoteBranchExists = await gitRemoteBranchExists(branch, cwd)\n // eslint-disable-next-line no-await-in-loop\n await cleanupErrorBranches(branch, cwd, remoteBranchExists)\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(adjustedLimit, ids.length)}`,\n )\n if (count >= adjustedLimit) {\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 { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { coanaFix } from './coana-fix.mts'\nimport { outputFixResult } from './output-fix-result.mts'\nimport { convertCveToGhsa } from '../../utils/cve-to-ghsa.mts'\nimport { convertPurlToGhsas } from '../../utils/purl-to-ghsa.mts'\n\nimport type { FixConfig } from './types.mts'\nimport type { OutputKind } from '../../types.mts'\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\n\nconst GHSA_FORMAT_REGEXP = /^GHSA-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}$/\nconst CVE_FORMAT_REGEXP = /^CVE-\\d{4}-\\d{4,}$/\n\nexport type HandleFixConfig = Remap<\n FixConfig & {\n applyFixes: boolean\n ghsas: string[]\n orgSlug: string\n outputKind: OutputKind\n unknownFlags: string[]\n }\n>\n\n/**\n * Converts mixed CVE/GHSA/PURL IDs to GHSA IDs only.\n * Filters out invalid IDs and logs conversion results.\n */\nexport async function convertIdsToGhsas(ids: string[]): Promise<string[]> {\n debugFn('notice', `Converting ${ids.length} IDs to GHSA format`)\n debugDir('inspect', { ids })\n\n const validGhsas: string[] = []\n const errors: string[] = []\n\n for (const id of ids) {\n const trimmedId = id.trim()\n\n if (trimmedId.startsWith('GHSA-')) {\n // Already a GHSA ID, validate format\n if (GHSA_FORMAT_REGEXP.test(trimmedId)) {\n validGhsas.push(trimmedId)\n } else {\n errors.push(`Invalid GHSA format: ${trimmedId}`)\n }\n } else if (trimmedId.startsWith('CVE-')) {\n // Convert CVE to GHSA\n if (!CVE_FORMAT_REGEXP.test(trimmedId)) {\n errors.push(`Invalid CVE format: ${trimmedId}`)\n continue\n }\n\n // eslint-disable-next-line no-await-in-loop\n const conversionResult = await convertCveToGhsa(trimmedId)\n if (conversionResult.ok) {\n validGhsas.push(conversionResult.data)\n logger.info(`Converted ${trimmedId} to ${conversionResult.data}`)\n } else {\n errors.push(`${trimmedId}: ${conversionResult.message}`)\n }\n } else if (trimmedId.startsWith('pkg:')) {\n // Convert PURL to GHSAs\n // eslint-disable-next-line no-await-in-loop\n const conversionResult = await convertPurlToGhsas(trimmedId)\n if (conversionResult.ok && conversionResult.data.length) {\n validGhsas.push(...conversionResult.data)\n logger.info(\n `Converted ${trimmedId} to ${conversionResult.data.length} GHSA(s): ${joinAnd(conversionResult.data)}`,\n )\n } else {\n errors.push(\n `${trimmedId}: ${conversionResult.message || 'No GHSAs found'}`,\n )\n }\n } else {\n // Neither CVE, GHSA, nor PURL, skip\n errors.push(\n `Unsupported ID format (expected CVE, GHSA, or PURL): ${trimmedId}`,\n )\n }\n }\n\n if (errors.length) {\n logger.warn(\n `Skipped ${errors.length} invalid IDs:\\n${errors.map(e => ` - ${e}`).join('\\n')}`,\n )\n debugDir('inspect', { errors })\n }\n\n debugFn('notice', `Converted to ${validGhsas.length} valid GHSA IDs`)\n debugDir('inspect', { validGhsas })\n\n return validGhsas\n}\n\nexport async function handleFix({\n applyFixes,\n autopilot,\n cwd,\n disableMajorUpdates,\n exclude,\n ghsas,\n include,\n limit,\n minSatisfying,\n minimumReleaseAge,\n orgSlug,\n outputFile,\n outputKind,\n prCheck,\n rangeStyle,\n showAffectedDirectDependencies,\n spinner,\n unknownFlags,\n}: HandleFixConfig) {\n debugFn('notice', `Starting fix command for ${orgSlug}`)\n debugDir('inspect', {\n applyFixes,\n autopilot,\n cwd,\n disableMajorUpdates,\n exclude,\n ghsas,\n include,\n limit,\n minSatisfying,\n minimumReleaseAge,\n outputFile,\n outputKind,\n prCheck,\n rangeStyle,\n showAffectedDirectDependencies,\n unknownFlags,\n })\n\n await outputFixResult(\n await coanaFix({\n applyFixes,\n autopilot,\n cwd,\n disableMajorUpdates,\n exclude,\n // Convert mixed CVE/GHSA/PURL inputs to GHSA IDs only.\n ghsas: await convertIdsToGhsas(ghsas),\n include,\n limit,\n minimumReleaseAge,\n minSatisfying,\n orgSlug,\n outputFile,\n prCheck,\n rangeStyle,\n showAffectedDirectDependencies,\n spinner,\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, {\n ERROR_UNABLE_RESOLVE_ORG,\n FLAG_ID,\n} 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 = 'Fix CVEs in dependencies'\n\nconst hidden = false\n\nexport const cmdFix = {\n description,\n hidden,\n run,\n}\n\nconst generalFlags: MeowFlags = {\n autopilot: {\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 applyFixes: {\n aliases: ['onlyCompute'],\n type: 'boolean',\n default: true,\n description:\n 'Compute fixes only, do not apply them. Logs what upgrades would be applied. If combined with --output-file, the output file will contain the upgrades that would be applied.',\n // Hidden to allow custom documenting of the negated `--no-apply-fixes` variant.\n hidden: true,\n },\n exclude: {\n type: 'string',\n default: [],\n description:\n 'Exclude workspaces matching these glob patterns. Can be provided as comma separated values or as multiple flags',\n isMultiple: true,\n hidden: false,\n },\n include: {\n type: 'string',\n default: [],\n description:\n 'Include workspaces matching these glob patterns. Can be provided as comma separated values or as multiple flags',\n isMultiple: true,\n hidden: false,\n },\n majorUpdates: {\n type: 'boolean',\n default: true,\n description:\n 'Allow major version updates. Use --no-major-updates to disable.',\n // Hidden to allow custom documenting of the negated `--no-major-updates` variant.\n hidden: true,\n },\n id: {\n type: 'string',\n default: [],\n description: `Provide a list of vulnerability identifiers to compute fixes for:\n - ${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 )} (e.g., GHSA-xxxx-xxxx-xxxx)\n - ${terminalLink(\n 'CVE IDs',\n 'https://cve.mitre.org/cve/identifiers/',\n )} (e.g., CVE-${new Date().getFullYear()}-1234) - automatically converted to GHSA\n - ${terminalLink(\n 'PURLs',\n 'https://github.com/package-url/purl-spec',\n )} (e.g., pkg:npm/package@1.0.0) - automatically converted to GHSA\n Can be provided as comma separated values 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 * pin - Use the exact version (e.g. 1.2.3)\n * preserve - Retain the existing version range style as-is\n `.trim(),\n },\n outputFile: {\n type: 'string',\n default: '',\n description: 'Path to store upgrades as a JSON file at this path.',\n },\n minimumReleaseAge: {\n type: 'string',\n default: '',\n description:\n 'Set a minimum age requirement for suggested upgrade versions (e.g., 1h, 2d, 3w). A higher age requirement reduces the risk of upgrading to malicious versions. For example, setting the value to 1 week (1w) gives ecosystem maintainers one week to remove potentially malicious versions.',\n },\n showAffectedDirectDependencies: {\n type: 'boolean',\n default: false,\n description:\n 'List the direct dependencies responsible for introducing transitive vulnerabilities and list the updates required to resolve the vulnerabilities',\n },\n}\n\nconst hiddenFlags: MeowFlags = {\n autoMerge: {\n ...generalFlags['autopilot'],\n hidden: true,\n } as MeowFlag,\n ghsa: {\n ...generalFlags['id'],\n hidden: true,\n } as MeowFlag,\n\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 }: CliCommandContext,\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({\n ...config.flags,\n // Explicitly document the negated --no-apply-fixes variant.\n noApplyFixes: {\n ...config.flags['applyFixes'],\n hidden: false,\n } as MeowFlag,\n // Explicitly document the negated --no-major-updates variant.\n noMajorUpdates: {\n ...config.flags['majorUpdates'],\n description:\n 'Do not suggest or apply fixes that require major version updates of direct or transitive dependencies',\n hidden: false,\n } as MeowFlag,\n })}\n\n Environment Variables (for CI/PR mode)\n CI Set to enable CI mode\n SOCKET_CLI_GITHUB_TOKEN GitHub token for PR creation (or GITHUB_TOKEN)\n SOCKET_CLI_GIT_USER_NAME Git username for commits\n SOCKET_CLI_GIT_USER_EMAIL Git email for commits\n\n Examples\n $ ${command}\n $ ${command} ${FLAG_ID} CVE-2021-23337\n $ ${command} ./path/to/project --range-style pin\n `,\n }\n\n const cli = meowOrExit(\n {\n argv,\n config,\n parentName,\n importMeta,\n },\n { allowUnknownFlags: false },\n )\n\n const {\n applyFixes,\n autopilot,\n exclude,\n include,\n json,\n limit,\n majorUpdates,\n markdown,\n maxSatisfying,\n minimumReleaseAge,\n outputFile,\n prCheck,\n rangeStyle,\n showAffectedDirectDependencies,\n // We patched in this feature with `npx custompatch meow` at\n // socket-cli/patches/meow#13.2.0.patch.\n unknownFlags = [],\n } = cli.flags as {\n applyFixes: boolean\n autopilot: boolean\n exclude: string[]\n include: string[]\n json: boolean\n limit: number\n majorUpdates: boolean\n markdown: boolean\n maxSatisfying: boolean\n minSatisfying: boolean\n minimumReleaseAge: string\n outputFile: string\n prCheck: boolean\n rangeStyle: RangeStyle\n showAffectedDirectDependencies: boolean\n unknownFlags?: string[]\n }\n\n const dryRun = !!cli.flags['dryRun']\n\n const minSatisfying =\n (cli.flags['minSatisfying'] as boolean) || !maxSatisfying\n\n const disableMajorUpdates = !majorUpdates\n\n const outputKind = getOutputKind(json, 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: !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_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 `${ERROR_UNABLE_RESOLVE_ORG}.\\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 const { spinner } = constants\n\n const ghsas = arrayUnique([\n ...cmdFlagValueToArray(cli.flags['id']),\n ...cmdFlagValueToArray(cli.flags['ghsa']),\n ...cmdFlagValueToArray(cli.flags['purl']),\n ])\n\n const includePatterns = cmdFlagValueToArray(include)\n const excludePatterns = cmdFlagValueToArray(exclude)\n\n await handleFix({\n applyFixes,\n autopilot,\n cwd,\n disableMajorUpdates,\n exclude: excludePatterns,\n ghsas,\n include: includePatterns,\n limit,\n minimumReleaseAge,\n minSatisfying,\n orgSlug,\n outputFile,\n outputKind,\n prCheck,\n rangeStyle,\n showAffectedDirectDependencies,\n spinner,\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 argv,\n name: `${parentName} install`,\n importMeta,\n subcommands: {\n completion: cmdInstallCompletion,\n },\n },\n { description },\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, { REDACTED, SOCKET_JSON } 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 { SOCKET_JSON } from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'json',\n description: `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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 {\n CONFIG_KEY_API_BASE_URL,\n CONFIG_KEY_API_PROXY,\n CONFIG_KEY_API_TOKEN,\n CONFIG_KEY_ENFORCED_ORGS,\n} from '../../constants.mts'\nimport { 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(CONFIG_KEY_ENFORCED_ORGS, enforcedOrgs)\n updateConfigValue(CONFIG_KEY_API_TOKEN, apiToken)\n updateConfigValue(CONFIG_KEY_API_BASE_URL, apiBaseUrl)\n updateConfigValue(CONFIG_KEY_API_PROXY, apiProxy)\n}\n","import { 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, {\n CONFIG_KEY_API_BASE_URL,\n CONFIG_KEY_API_PROXY,\n CONFIG_KEY_API_TOKEN,\n CONFIG_KEY_DEFAULT_ORG,\n} from '../../constants.mts'\nimport {\n getConfigValueOrUndef,\n isConfigFromFlag,\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 { socketDocsLink } from '../../utils/terminal-link.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(CONFIG_KEY_API_BASE_URL) ?? undefined\n apiProxy ??= getConfigValueOrUndef(CONFIG_KEY_API_PROXY) ?? undefined\n const apiTokenInput = await password({\n message: `Enter your ${socketDocsLink('/docs/api-keys', 'Socket.dev API token')} (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 description: '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(CONFIG_KEY_DEFAULT_ORG, orgSlugs[0])\n\n const previousPersistedToken = getConfigValueOrUndef(CONFIG_KEY_API_TOKEN)\n try {\n applyLogin(apiToken, enforcedOrgs, apiBaseUrl, apiProxy)\n logger.success(\n `API credentials ${previousPersistedToken === apiToken ? 'refreshed' : previousPersistedToken ? 'updated' : 'set'}`,\n )\n if (isConfigFromFlag()) {\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 default: '',\n description: 'API server to connect to for login',\n },\n apiProxy: {\n type: 'string',\n default: '',\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 parentName,\n importMeta,\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, apiProxy } = cli.flags as {\n apiBaseUrl?: string | undefined\n apiProxy?: string | undefined\n }\n\n await attemptLogin(apiBaseUrl, apiProxy)\n}\n","import {\n CONFIG_KEY_API_BASE_URL,\n CONFIG_KEY_API_PROXY,\n CONFIG_KEY_API_TOKEN,\n CONFIG_KEY_ENFORCED_ORGS,\n} from '../../constants.mts'\nimport { updateConfigValue } from '../../utils/config.mts'\n\nexport function applyLogout() {\n updateConfigValue(CONFIG_KEY_API_TOKEN, null)\n updateConfigValue(CONFIG_KEY_API_BASE_URL, null)\n updateConfigValue(CONFIG_KEY_API_PROXY, null)\n updateConfigValue(CONFIG_KEY_ENFORCED_ORGS, null)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { applyLogout } from './apply-logout.mts'\nimport { isConfigFromFlag } from '../../utils/config.mts'\n\nexport function attemptLogout() {\n try {\n applyLogout()\n logger.success('Successfully logged out')\n if (isConfigFromFlag()) {\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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, { FLAG_HELP, NPM, PNPM, YARN } from '../../constants.mts'\nimport { spawnCdxgenDlx, spawnSynpDlx } from '../../utils/dlx.mts'\nimport { findUp } from '../../utils/fs.mts'\nimport { isYarnBerry } from '../../utils/yarn-version.mts'\n\nimport type { ShadowBinResult } from '../../shadow/npm/bin.mts'\nimport type { DlxOptions } from '../../utils/dlx.mts'\n\nconst { PACKAGE_LOCK_JSON, PNPM_LOCK_YAML, 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 argvObjectToArray(argvObj: ArgvObject): string[] {\n if (argvObj['help']) {\n return [FLAG_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 const argvMutable = { __proto__: null, ...argvObj } as ArgvObject\n\n const shadowOpts: DlxOptions = {\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\n // Detect package manager based on lockfiles.\n const pnpmLockPath = await findUp(PNPM_LOCK_YAML, { onlyFiles: true })\n\n const npmLockPath = pnpmLockPath\n ? undefined\n : await findUp(PACKAGE_LOCK_JSON, { onlyFiles: true })\n\n const yarnLockPath =\n pnpmLockPath || npmLockPath\n ? undefined\n : await findUp(YARN_LOCK, { onlyFiles: true })\n\n const agent = pnpmLockPath ? PNPM : yarnLockPath && isYarnBerry() ? YARN : NPM\n\n let cleanupPackageLock = false\n if (\n yarnLockPath &&\n argvMutable['type'] !== YARN &&\n nodejsPlatformTypes.has(argvMutable['type'] as string)\n ) {\n if (npmLockPath) {\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 synpResult = await spawnSynpDlx(\n ['--source-file', `./${YARN_LOCK}`],\n {\n ...shadowOpts,\n agent,\n },\n )\n await synpResult.spawnPromise\n argvMutable['type'] = NPM\n cleanupPackageLock = true\n } catch {}\n }\n }\n\n // Use appropriate package manager for cdxgen.\n const shadowResult = await spawnCdxgenDlx(argvObjectToArray(argvMutable), {\n ...shadowOpts,\n agent,\n })\n\n shadowResult.spawnPromise.process.on('exit', () => {\n if (cleanupPackageLock) {\n try {\n // TODO: Consider using trash instead of rmSync for safer deletion.\n // This removes the temporary package-lock.json we created for cdxgen.\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 { joinAnd } from '@socketsecurity/registry/lib/arrays'\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, { FLAG_HELP } from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { filterFlags, isHelpFlag } from '../../utils/cmd.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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: 'Run cdxgen for SBOM generation',\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 context: CliCommandContext,\n): Promise<void> {\n const { parentName } = {\n __proto__: null,\n ...context,\n } as CliCommandContext\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 as { dryRun: boolean }\n\n // Filter Socket flags from argv but keep --no-banner and --help for cdxgen.\n const argsToProcess = filterFlags(argv, { ...commonFlags, ...outputFlags }, [\n '--no-banner',\n FLAG_HELP,\n '-h',\n ])\n const yargv = {\n ...yargsParse(argsToProcess 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)}: ${joinAnd(unknowns)}`,\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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, {\n ENVIRONMENT_YAML,\n ENVIRONMENT_YML,\n FLAG_JSON,\n FLAG_MARKDOWN,\n REQUIREMENTS_TXT,\n SOCKET_JSON,\n} 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 {\n CliCommandConfig,\n CliCommandContext,\n} from '../../utils/meow-with-subcommands.mts'\n\nconst config: CliCommandConfig = {\n commandName: 'conda',\n description: `[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 default: '',\n description: `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 default: '',\n description: 'Output path (relative to cwd)',\n },\n stdout: {\n type: 'boolean',\n description: `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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName,\n })\n\n const { dryRun, json, markdown } = cli.flags as {\n dryRun: boolean\n json: boolean\n markdown: boolean\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 sockJson = readOrDefaultSocketJson(cwd)\n\n let {\n file: filename,\n out,\n stdin,\n stdout,\n verbose,\n } = cli.flags as {\n file: string\n out: string\n stdin: boolean | undefined\n stdout: boolean | undefined\n verbose: boolean | undefined\n }\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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,\n out,\n outputKind,\n 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, { REQUIREMENTS_TXT, SOCKET_JSON } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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(\n `Using default --gradle-opts from ${SOCKET_JSON}:`,\n gradleOpts,\n )\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, { REQUIREMENTS_TXT, SOCKET_JSON } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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(\n `Using default --gradle-opts from ${SOCKET_JSON}:`,\n gradleOpts,\n )\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, { REQUIREMENTS_TXT, SOCKET_JSON } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 { REQUIREMENTS_TXT, SOCKET_JSON } from '../../constants.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(\n `This command will generate a ${SOCKET_JSON} file in the target cwd.`,\n )\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, { SOCKET_JSON } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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: `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 }: CliCommandContext,\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 { REQUIREMENTS_TXT } from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagListOutput } from '../../utils/output-formatting.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\n): Promise<void> {\n await meowWithSubcommands(\n {\n argv,\n name: `${parentName} ${config.commandName}`,\n importMeta,\n subcommands: {\n auto: cmdManifestAuto,\n cdxgen: cmdManifestCdxgen,\n conda: cmdManifestConda,\n gradle: cmdManifestGradle,\n kotlin: cmdManifestKotlin,\n scala: cmdManifestScala,\n setup: cmdManifestSetup,\n },\n },\n {\n aliases: {\n yolo: {\n description: config.description,\n hidden: true,\n argv: ['auto'],\n },\n },\n description: config.description,\n flags: config.flags,\n },\n )\n}\n","import { createRequire } from 'node:module'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants, {\n FLAG_DRY_RUN,\n FLAG_HELP,\n FLAG_JSON,\n NPM,\n} from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { filterFlags } from '../../utils/cmd.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagApiRequirementsOutput } from '../../utils/output-formatting.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} from '../../utils/meow-with-subcommands.mts'\n\nconst require = createRequire(import.meta.url)\n\nexport const CMD_NAME = NPM\n\nconst description = 'Wraps npm with Socket security scanning'\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 context: CliCommandContext,\n): Promise<void> {\n const { parentName } = { __proto__: null, ...context } as CliCommandContext\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 \\`${FLAG_DRY_RUN}\\` and \\`${FLAG_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 $ ${command} exec 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 shadowNpmBin = /*@__PURE__*/ require(constants.shadowNpmBinPath)\n\n process.exitCode = 1\n\n // Filter Socket flags from argv but keep --json for npm.\n const argsToForward = filterFlags(argv, { ...commonFlags, ...outputFlags }, [\n FLAG_JSON,\n ])\n const { spawnPromise } = await shadowNpmBin(argsToForward, {\n stdio: 'inherit',\n })\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, { FLAG_DRY_RUN, FLAG_HELP, 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 {\n CliCommandConfig,\n CliCommandContext,\n} from '../../utils/meow-with-subcommands.mts'\n\nconst require = createRequire(import.meta.url)\n\nconst CMD_NAME = NPX\n\nconst description = 'Wraps npx with Socket security scanning'\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 }: CliCommandContext,\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 \\`${FLAG_DRY_RUN}\\` and \\`${FLAG_HELP}\\` flags are caught here.\n\n Use \\`socket wrapper on\\` to alias this command as \\`${NPX}\\`.\n\n Examples\n $ ${command} cowsay\n $ ${command} cowsay@1.6.0 hello\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 shadowNpxBin = /*@__PURE__*/ require(constants.shadowNpxBinPath)\n\n process.exitCode = 1\n\n const { spawnPromise } = await shadowNpxBin(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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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, { FLAG_PROD } 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 // On Windows, npm is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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 // On Windows, bun is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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', FLAG_PROD, '--depth', 'Infinity'],\n {\n cwd,\n // On Windows, pnpm is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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 // On Windows, pnpm is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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 // On Windows, yarn is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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', FLAG_PROD], {\n cwd,\n // On Windows, yarn is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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 { safeNpa } 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 parsing it with npm-package-arg\n // and seeing if it will coerce to a version.\n semver.coerce((safeNpa(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 parsing it with npm-package-arg\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 (safeNpa(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\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', 'Lockfile update failed')\n debugDir('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\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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { 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 debugFn('notice', `Starting optimization for ${cwd}`)\n debugDir('inspect', { cwd, outputKind, pin, prod })\n\n const pkgEnvCResult = await detectAndValidatePackageEnvironment(cwd, {\n cmdName: CMD_NAME,\n logger,\n prod,\n })\n if (!pkgEnvCResult.ok) {\n process.exitCode = pkgEnvCResult.code ?? 1\n debugFn('warn', 'Package environment validation failed')\n debugDir('inspect', { pkgEnvCResult })\n await outputOptimizeResult(pkgEnvCResult, outputKind)\n return\n }\n\n const pkgEnvDetails = pkgEnvCResult.data\n if (!pkgEnvDetails) {\n process.exitCode = 1\n debugFn('warn', 'No package environment details found')\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 debugFn(\n 'notice',\n `Detected package manager: ${pkgEnvDetails.agent} v${pkgEnvDetails.agentVersion}`,\n )\n debugDir('inspect', { pkgEnvDetails })\n\n const { agent, agentVersion } = pkgEnvDetails\n if (agent === VLT) {\n process.exitCode = 1\n debugFn('warn', `${agent} does not support overrides`)\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 debugFn('notice', 'Applying optimization')\n const optimizationResult = await applyOptimization(pkgEnvDetails, {\n pin,\n prod,\n })\n\n if (!optimizationResult.ok) {\n process.exitCode = optimizationResult.code ?? 1\n }\n debugFn(\n 'notice',\n `Optimization ${optimizationResult.ok ? 'succeeded' : 'failed'}`,\n )\n debugDir('inspect', { optimizationResult })\n await outputOptimizeResult(optimizationResult, outputKind)\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 latest version',\n },\n prod: {\n type: 'boolean',\n default: false,\n description: 'Add overrides for production dependencies only',\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} ./path/to/project --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 description: '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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport { 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 debugFn(\n 'notice',\n `Fetching dependencies with limit=${limit}, offset=${offset}`,\n )\n debugDir('inspect', { limit, offset, outputKind })\n\n const result = await fetchDependencies({ limit, offset })\n\n debugFn(\n 'notice',\n `Dependencies ${result.ok ? 'fetched successfully' : 'fetch failed'}`,\n )\n debugDir('inspect', { result })\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, { FLAG_JSON, FLAG_MARKDOWN } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 description: '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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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 { 0: 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 description: '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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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 { 0: 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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport { 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 debugFn('notice', 'Fetching organization list')\n debugDir('inspect', { outputKind })\n\n const data = await fetchOrganization()\n\n debugFn(\n 'notice',\n `Organization list ${data.ok ? 'fetched successfully' : 'fetch failed'}`,\n )\n debugDir('inspect', { data })\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, { FLAG_JSON, FLAG_MARKDOWN } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 argv,\n name: `${parentName} policy`,\n importMeta,\n subcommands: {\n security: cmdOrganizationPolicySecurity,\n license: cmdOrganizationPolicyLicense,\n },\n },\n {\n description,\n defaultSub: 'list', // Backwards compat\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(), { description: '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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 argv,\n name: `${parentName} organization`,\n importMeta,\n subcommands: {\n dependencies: cmdOrganizationDependencies,\n list: cmdOrganizationList,\n quota: cmdOrganizationQuota,\n policy: cmdOrganizationPolicy,\n },\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 description,\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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport { 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 debugFn('notice', `Fetching deep score for ${purl}`)\n debugDir('inspect', { purl, outputKind })\n\n const result = await fetchPurlDeepScore(purl)\n\n debugFn(\n 'notice',\n `Deep score ${result.ok ? 'fetched successfully' : 'fetch failed'}`,\n )\n debugDir('inspect', { result })\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { 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): ${joinAnd(purls)}`,\n )\n\n const batchPackageCResult = await handleApiCall(\n sockSdk.batchPackageFetch(\n { components: purls.map(purl => ({ purl })) },\n {\n alerts: 'true',\n },\n ),\n { description: '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 { joinAnd } from '@socketsecurity/registry/lib/arrays'\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}/${low.length}):`\n const padding = ` ${' '.repeat(Math.max(0, 20 - colorless.length))}`\n\n if (colorize) {\n return `- Alerts (${colors.red(bad.length as any)}/${colors.yellow(mid.length as any)}/${low.length}):${\n padding\n }${joinAnd([\n ...bad.map(a => colors.red(`${colors.dim(`[${a.severity}] `)}${a.type}`)),\n ...mid.map(a =>\n colors.yellow(`${colors.dim(`[${a.severity}] `)}${a.type}`),\n ),\n ...low.map(a => `${colors.dim(`[${a.severity}] `)}${a.type}`),\n ])}`\n }\n return `${colorless}${padding}${joinAnd([\n ...bad.map(a => `[${a.severity}] ${a.type}`),\n ...mid.map(a => `[${a.severity}] ${a.type}`),\n ...low.map(a => `[${a.severity}] ${a.type}`),\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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport { 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 debugFn('notice', `Fetching shallow scores for ${purls.length} packages`)\n debugDir('inspect', { purls, outputKind })\n\n const packageData = await fetchPurlsShallowScore(purls)\n\n debugFn(\n 'notice',\n `Shallow scores ${packageData.ok ? 'fetched successfully' : 'fetch failed'}`,\n )\n debugDir('inspect', { packageData })\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 argv,\n name: `${parentName} package`,\n importMeta,\n subcommands: {\n score: cmdPackageScore,\n shallow: cmdPackageShallow,\n },\n },\n {\n aliases: {\n deep: {\n description,\n hidden: true,\n argv: ['score'],\n },\n },\n description,\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'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { OUTPUT_JSON } 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'\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 === OUTPUT_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 logger.log('')\n\n if (patched.length) {\n logger.group(\n `Successfully processed patches for ${patched.length} ${pluralize('package', patched.length)}:`,\n )\n for (const pkg of patched) {\n logger.success(pkg)\n }\n logger.groupEnd()\n } else {\n logger.warn('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 fastGlob from 'fast-glob'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { debugDir } from '@socketsecurity/registry/lib/debug'\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 {\n DOT_SOCKET_DIR,\n MANIFEST_JSON,\n NODE_MODULES,\n NPM,\n UTF8,\n} from '../../constants.mts'\nimport { getErrorCause } from '../../utils/errors.mts'\nimport { findUp } from '../../utils/fs.mts'\nimport { getPurlObject, normalizePurl } from '../../utils/purl.mts'\n\nimport type { PatchRecord } from './manifest-schema.mts'\nimport type { CResult, OutputKind } from '../../types.mts'\nimport type { PackageURL } from '@socketregistry/packageurl-js'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\ntype PatchEntry = {\n key: string\n patch: PatchRecord\n purl: string\n purlObj: PackageURL\n}\n\ntype PatchFileInfo = {\n beforeHash: string\n afterHash: string\n}\n\ntype ApplyNpmPatchesOptions = {\n cwd?: string | undefined\n dryRun?: boolean | undefined\n purlObjs?: PackageURL[] | undefined\n spinner?: Spinner | undefined\n}\n\ntype ApplyNpmPatchesResult = {\n passed: string[]\n failed: string[]\n}\n\nasync function applyNpmPatches(\n socketDir: string,\n patches: PatchEntry[],\n options?: ApplyNpmPatchesOptions | undefined,\n): Promise<ApplyNpmPatchesResult> {\n const {\n cwd = process.cwd(),\n dryRun = false,\n purlObjs,\n spinner,\n } = { __proto__: null, ...options } as ApplyNpmPatchesOptions\n\n const wasSpinning = !!spinner?.isSpinning\n\n spinner?.start()\n\n const patchLookup = new Map<string, PatchEntry>()\n for (const patchInfo of patches) {\n patchLookup.set(patchInfo.purl, patchInfo)\n }\n\n const nmPaths = await findNodeModulesPaths(cwd)\n\n spinner?.stop()\n\n logger.log(\n `Found ${nmPaths.length} ${NODE_MODULES} ${pluralize('folder', nmPaths.length)}`,\n )\n\n logger.group('')\n\n spinner?.start()\n\n const result: ApplyNpmPatchesResult = {\n passed: [],\n failed: [],\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 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\n const purl = `pkg:npm/${pkgJson.name}@${pkgJson.version}`\n const purlObj = getPurlObject(purl, { throws: false })\n if (!purlObj) {\n continue\n }\n\n // Skip if specific packages requested and this isn't one of them\n if (\n purlObjs?.length &&\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(purl)\n if (!patchInfo) {\n continue\n }\n\n spinner?.stop()\n\n logger.log(\n `Found match: ${pkgJson.name}@${pkgJson.version} at ${pkgPath}`,\n )\n logger.log(`Patch key: ${patchInfo.key}`)\n logger.group(`Processing files:`)\n\n spinner?.start()\n\n let passed = true\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 const filePatchPassed = await processFilePatch(\n pkgPath,\n fileName,\n fileInfo,\n socketDir,\n {\n dryRun,\n spinner,\n },\n )\n if (!filePatchPassed) {\n passed = false\n }\n }\n\n logger.groupEnd()\n\n if (passed) {\n result.passed.push(purl)\n } else {\n result.failed.push(purl)\n }\n }\n }\n }\n\n spinner?.stop()\n\n logger.groupEnd()\n\n if (wasSpinning) {\n spinner.start()\n }\n return result\n}\n\n/**\n * Compute SHA256 hash of file contents.\n */\nasync function computeSHA256(filepath: string): Promise<CResult<string>> {\n try {\n const content = await fs.readFile(filepath)\n const hash = crypto.createHash('sha256')\n hash.update(content)\n return {\n ok: true,\n data: hash.digest('hex'),\n }\n } catch (e) {\n return {\n ok: false,\n message: 'Failed to compute file hash',\n cause: `Unable to read file ${filepath}: ${getErrorCause(e)}`,\n }\n }\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 fastGlob.glob([`**/${NODE_MODULES}`], {\n absolute: true,\n cwd: path.dirname(rootNmPath),\n dot: true,\n followSymbolicLinks: false,\n onlyDirectories: true,\n })\n}\n\ntype ProcessFilePatchOptions = {\n dryRun?: boolean | undefined\n spinner?: Spinner | undefined\n}\n\nasync function processFilePatch(\n pkgPath: string,\n fileName: string,\n fileInfo: PatchFileInfo,\n socketDir: string,\n options?: ProcessFilePatchOptions | undefined,\n): Promise<boolean> {\n const { dryRun, spinner } = {\n __proto__: null,\n ...options,\n } as ProcessFilePatchOptions\n\n const wasSpinning = !!spinner?.isSpinning\n\n spinner?.stop()\n\n const filepath = path.join(pkgPath, fileName)\n if (!existsSync(filepath)) {\n logger.log(`File not found: ${fileName}`)\n if (wasSpinning) {\n spinner?.start()\n }\n return false\n }\n\n const currentHashResult = await computeSHA256(filepath)\n if (!currentHashResult.ok) {\n logger.log(\n `Failed to compute hash for: ${fileName}: ${currentHashResult.cause || currentHashResult.message}`,\n )\n if (wasSpinning) {\n spinner?.start()\n }\n return false\n }\n\n if (currentHashResult.data === fileInfo.afterHash) {\n logger.success(`File already patched: ${fileName}`)\n logger.group()\n logger.log(`Current hash: ${currentHashResult.data}`)\n logger.groupEnd()\n if (wasSpinning) {\n spinner?.start()\n }\n return true\n }\n\n if (currentHashResult.data !== fileInfo.beforeHash) {\n logger.fail(`File hash mismatch: ${fileName}`)\n logger.group()\n logger.log(`Expected: ${fileInfo.beforeHash}`)\n logger.log(`Current: ${currentHashResult.data}`)\n logger.log(`Target: ${fileInfo.afterHash}`)\n logger.groupEnd()\n if (wasSpinning) {\n spinner?.start()\n }\n return false\n }\n\n logger.success(`File matches expected hash: ${fileName}`)\n logger.group()\n logger.log(`Current hash: ${currentHashResult.data}`)\n logger.log(`Ready to patch to: ${fileInfo.afterHash}`)\n logger.group()\n\n if (dryRun) {\n logger.log(`(dry run - no changes made)`)\n logger.groupEnd()\n logger.groupEnd()\n if (wasSpinning) {\n spinner?.start()\n }\n return false\n }\n\n const blobPath = path.join(socketDir, 'blobs', fileInfo.afterHash)\n if (!existsSync(blobPath)) {\n logger.fail(`Error: Patch file not found at ${blobPath}`)\n logger.groupEnd()\n logger.groupEnd()\n if (wasSpinning) {\n spinner?.start()\n }\n return false\n }\n\n spinner?.start()\n\n let result = true\n try {\n await fs.copyFile(blobPath, filepath)\n\n // Verify the hash after copying to ensure file integrity.\n const verifyHashResult = await computeSHA256(filepath)\n if (!verifyHashResult.ok) {\n logger.error(\n `Failed to verify hash after patch: ${verifyHashResult.cause || verifyHashResult.message}`,\n )\n result = false\n } else if (verifyHashResult.data !== fileInfo.afterHash) {\n logger.error(`Hash verification failed after patch`)\n logger.group()\n logger.log(`Expected: ${fileInfo.afterHash}`)\n logger.log(`Got: ${verifyHashResult.data}`)\n logger.groupEnd()\n result = false\n } else {\n logger.success(`Patch applied successfully`)\n }\n } catch (e) {\n logger.error('Error applying patch')\n debugDir('error', e)\n result = false\n }\n logger.groupEnd()\n logger.groupEnd()\n\n spinner?.stop()\n\n if (wasSpinning) {\n spinner?.start()\n }\n return result\n}\n\nexport interface HandlePatchConfig {\n cwd: string\n dryRun: boolean\n outputKind: OutputKind\n purlObjs: PackageURL[]\n spinner: 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, DOT_SOCKET_DIR)\n const manifestPath = path.join(dotSocketDirPath, MANIFEST_JSON)\n const manifestContent = await fs.readFile(manifestPath, UTF8)\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 purl = normalizePurl(key)\n if (purls.length && !purls.includes(purl)) {\n continue\n }\n const purlObj = getPurlObject(purl, { 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 purl,\n purlObj,\n })\n }\n\n if (purls.length) {\n spinner.start(`Checking patches for: ${joinAnd(purls)}`)\n } else {\n spinner.start('Scanning all dependencies for available patches')\n }\n\n const patched = []\n\n const npmPatches = patchesByEcosystem.get(NPM)\n if (npmPatches) {\n const patchingResults = await applyNpmPatches(\n dotSocketDirPath,\n npmPatches,\n {\n cwd,\n dryRun,\n purlObjs,\n spinner,\n },\n )\n patched.push(...patchingResults.passed)\n }\n\n spinner.stop()\n\n await outputPatchResult(\n {\n ok: true,\n data: {\n patched,\n },\n },\n outputKind,\n )\n } catch (e) {\n spinner.stop()\n\n let message = 'Failed to apply patches'\n let cause = getErrorCause(e)\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 await outputPatchResult(\n {\n ok: false,\n code: 1,\n message,\n cause,\n },\n outputKind,\n )\n }\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport { arrayUnique } from '@socketsecurity/registry/lib/arrays'\n\nimport { handlePatch } from './handle-patch.mts'\nimport constants, { DOT_SOCKET_DIR, MANIFEST_JSON } from '../../constants.mts'\nimport { commonFlags, outputFlags } from '../../flags.mts'\nimport { checkCommandInput } from '../../utils/check-input.mts'\nimport { cmdFlagValueToArray } from '../../utils/cmd.mts'\nimport { InputError } from '../../utils/errors.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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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} ./path/to/project --package lodash,react\n `,\n }\n\n const cli = meowOrExit(\n {\n argv,\n config,\n parentName,\n importMeta,\n },\n { allowUnknownFlags: false },\n )\n\n const { dryRun, json, markdown } = cli.flags as {\n dryRun: boolean\n json: boolean\n markdown: boolean\n }\n\n const outputKind = getOutputKind(json, markdown)\n\n const wasValidInput = checkCommandInput(outputKind, {\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 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, DOT_SOCKET_DIR)\n if (!existsSync(dotSocketDirPath)) {\n throw new InputError(\n `No ${DOT_SOCKET_DIR} directory found in current directory`,\n )\n }\n\n const manifestPath = path.join(dotSocketDirPath, MANIFEST_JSON)\n if (!existsSync(manifestPath)) {\n throw new InputError(\n `No ${MANIFEST_JSON} found in ${DOT_SOCKET_DIR} directory`,\n )\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 { createRequire } from 'node:module'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants, { FLAG_DRY_RUN, FLAG_HELP, PNPM } from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { filterFlags } from '../../utils/cmd.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagApiRequirementsOutput } from '../../utils/output-formatting.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} from '../../utils/meow-with-subcommands.mts'\n\nconst require = createRequire(import.meta.url)\n\nexport const CMD_NAME = PNPM\n\nconst description = 'Wraps pnpm with Socket security scanning'\n\nconst hidden = true\n\nexport const cmdPnpm = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n context: CliCommandContext,\n): Promise<void> {\n const { parentName } = { __proto__: null, ...context } as CliCommandContext\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 \"${PNPM}\" is passed to the ${PNPM} command.\n Only the \\`${FLAG_DRY_RUN}\\` and \\`${FLAG_HELP}\\` flags are caught here.\n\n Use \\`socket wrapper on\\` to alias this command as \\`${PNPM}\\`.\n\n Examples\n $ ${command}\n $ ${command} install\n $ ${command} add package-name\n $ ${command} dlx package-name\n `,\n }\n\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 shadowPnpmBin = /*@__PURE__*/ require(constants.shadowPnpmBinPath)\n\n process.exitCode = 1\n\n // Filter Socket flags from argv.\n const filteredArgv = filterFlags(argv, config.flags)\n\n const { spawnPromise } = await shadowPnpmBin(filteredArgv, {\n stdio: 'inherit',\n })\n\n await spawnPromise\n process.exitCode = 0\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 // On Windows, npm is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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, { FLAG_DRY_RUN, FLAG_HELP } from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 \\`${FLAG_DRY_RUN}\\` and \\`${FLAG_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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 // On Windows, npx is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\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, { FLAG_DRY_RUN, FLAG_HELP } from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 \\`${FLAG_DRY_RUN}\\` and \\`${FLAG_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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 { description: '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 { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport { 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 debugFn('notice', `Creating repository ${orgSlug}/${repoName}`)\n debugDir('inspect', {\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n outputKind,\n })\n\n const data = await fetchCreateRepo({\n defaultBranch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility,\n })\n\n debugFn('notice', `Repository creation ${data.ok ? 'succeeded' : 'failed'}`)\n debugDir('inspect', { data })\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, { V1_MIGRATION_GUIDE_URL } 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 { webLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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 { 0: 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 the ${webLink(V1_MIGRATION_GUIDE_URL, '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 description: '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, { V1_MIGRATION_GUIDE_URL } 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 { webLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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 { 0: 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 the ${webLink(V1_MIGRATION_GUIDE_URL, '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 { description: '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 { description: '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 { Direction } from './types.mts'\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: Direction,\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 { Direction } from './types.mts'\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: Direction\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, { FLAG_JSON, FLAG_MARKDOWN } 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 { Direction } from './types.mts'\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 default: '',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n perPage: {\n type: 'number',\n default: 30,\n description: 'Number of results per page',\n shortFlag: 'pp',\n },\n page: {\n type: 'number',\n default: 1,\n description: 'Page number',\n shortFlag: 'p',\n },\n sort: {\n type: 'string',\n default: 'created_at',\n description: 'Sorting option',\n shortFlag: 's',\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 parentName,\n importMeta,\n })\n\n const {\n all,\n direction = 'desc',\n dryRun,\n interactive,\n json,\n markdown,\n org: orgFlag,\n page,\n perPage,\n sort,\n } = cli.flags as {\n all: boolean\n direction: Direction\n dryRun: boolean\n interactive: boolean\n json: boolean\n markdown: boolean\n org: string\n page: number\n perPage: number\n sort: string\n }\n\n const hasApiToken = hasDefaultApiToken()\n\n const { 0: orgSlug } = await determineOrgSlug(orgFlag, interactive, dryRun)\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 \\`${FLAG_JSON}\\` and \\`${FLAG_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,\n direction,\n orgSlug,\n outputKind,\n page,\n perPage,\n 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 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 { description: '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, { V1_MIGRATION_GUIDE_URL } 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 { webLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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 { 0: 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 the ${webLink(V1_MIGRATION_GUIDE_URL, '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 description: '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, {\n FLAG_JSON,\n FLAG_MARKDOWN,\n V1_MIGRATION_GUIDE_URL,\n} 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 { webLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 parentName,\n importMeta,\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 { 0: 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 the ${webLink(V1_MIGRATION_GUIDE_URL, '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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 argv,\n name: `${parentName} repository`,\n importMeta,\n subcommands: {\n create: cmdRepositoryCreate,\n view: cmdRepositoryView,\n list: cmdRepositoryList,\n del: cmdRepositoryDel,\n update: cmdRepositoryUpdate,\n },\n },\n { description },\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 reachConcurrency: {\n type: 'number',\n default: 1,\n description:\n 'Set the maximum number of concurrent reachability analysis runs. It is recommended to choose a concurrency level that ensures each analysis run has at least the --reach-analysis-memory-limit amount of memory available. NPM reachability analysis does not support concurrent execution, so the concurrency level is ignored for NPM.',\n },\n reachDebug: {\n type: 'boolean',\n default: false,\n description:\n 'Enable debug mode for reachability analysis. Provides verbose logging from the reachability CLI.',\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 reachDisableAnalysisSplitting: {\n type: 'boolean',\n default: false,\n description:\n 'Limits Coana to at most 1 reachability analysis run per workspace.',\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 { existsSync, promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nexport type ReachabilityTargetValidation = {\n isDirectory: boolean\n isInsideCwd: boolean\n isValid: boolean\n targetExists: boolean\n}\n\n/**\n * Validates that a target directory meets the requirements for reachability analysis.\n *\n * @param targets - Array of target paths to validate.\n * @param cwd - Current working directory.\n * @returns Validation result object with boolean flags.\n */\nexport async function validateReachabilityTarget(\n targets: string[],\n cwd: string,\n): Promise<ReachabilityTargetValidation> {\n const result: ReachabilityTargetValidation = {\n isDirectory: false,\n isInsideCwd: false,\n isValid: targets.length === 1,\n targetExists: false,\n }\n\n if (!result.isValid || !targets[0]) {\n return result\n }\n\n // Resolve cwd to absolute path to handle relative cwd values.\n const absoluteCwd = path.resolve(cwd)\n\n // Resolve target path to absolute for validation.\n const targetPath = path.isAbsolute(targets[0])\n ? targets[0]\n : path.resolve(absoluteCwd, targets[0])\n\n // Check if target is inside cwd.\n const relativePath = path.relative(absoluteCwd, targetPath)\n result.isInsideCwd =\n !relativePath.startsWith('..') && !path.isAbsolute(relativePath)\n\n result.targetExists = existsSync(targetPath)\n if (result.targetExists) {\n const targetStat = await fs.stat(targetPath)\n result.isDirectory = targetStat.isDirectory()\n }\n\n return result\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 { validateReachabilityTarget } from './validate-reachability-target.mts'\nimport constants, { REQUIREMENTS_TXT, SOCKET_JSON } 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 { readOrDefaultSocketJsonUp } from '../../utils/socket-json.mts'\nimport { socketDashboardLink } from '../../utils/terminal-link.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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 default: '',\n description: 'Branch name',\n shortFlag: 'b',\n },\n commitHash: {\n type: 'string',\n default: '',\n description: 'Commit hash',\n shortFlag: 'ch',\n },\n commitMessage: {\n type: 'string',\n default: '',\n description: 'Commit message',\n shortFlag: 'm',\n },\n committers: {\n type: 'string',\n default: '',\n description: 'Committers',\n shortFlag: 'c',\n },\n cwd: {\n type: 'string',\n default: '',\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 default: 0,\n description: 'Pull request number',\n shortFlag: 'pr',\n },\n org: {\n type: 'string',\n default: '',\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 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 aliases: ['pendingHead'],\n },\n tmp: {\n type: 'boolean',\n default: false,\n description:\n 'Set the visibility (true/false) of the scan in your dashboard.',\n shortFlag: 't',\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 }: CliCommandContext,\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 ${socketDashboardLink('/org/YOURORG/alerts', '\"alerts page\"')} 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 parentName,\n importMeta,\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 reachConcurrency,\n reachDebug,\n reachDisableAnalysisSplitting,\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 reachConcurrency: number\n reachDebug: boolean\n reachDisableAnalytics: boolean\n reachDisableAnalysisSplitting: 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 { 0: orgSlug } = await determineOrgSlug(\n String(orgFlag || ''),\n interactive,\n dryRun,\n )\n\n const processCwd = process.cwd()\n const cwd =\n cwdOverride && cwdOverride !== '.' && cwdOverride !== processCwd\n ? path.resolve(processCwd, cwdOverride)\n : processCwd\n\n const sockJson = await readOrDefaultSocketJsonUp(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 isUsingNonDefaultConcurrency =\n reachConcurrency !== reachabilityFlags['reachConcurrency']?.default\n\n const isUsingNonDefaultAnalytics =\n reachDisableAnalytics !==\n reachabilityFlags['reachDisableAnalytics']?.default\n\n const isUsingAnyReachabilityFlags =\n isUsingNonDefaultMemoryLimit ||\n isUsingNonDefaultTimeout ||\n isUsingNonDefaultConcurrency ||\n isUsingNonDefaultAnalytics ||\n hasReachEcosystems ||\n hasReachExcludePaths ||\n reachSkipCache ||\n reachDisableAnalysisSplitting\n\n // Validate target constraints when --reach is enabled.\n const reachTargetValidation = reach\n ? await validateReachabilityTarget(targets, cwd)\n : {\n isDirectory: false,\n isInsideCwd: false,\n isValid: true,\n targetExists: false,\n }\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 nook: true,\n test: !reach || reachTargetValidation.isValid,\n message:\n 'Reachability analysis requires exactly one target directory when --reach is enabled',\n fail: 'provide exactly one directory path',\n },\n {\n nook: true,\n test: !reach || reachTargetValidation.isDirectory,\n message:\n 'Reachability analysis target must be a directory when --reach is enabled',\n fail: 'provide a directory path, not a file',\n },\n {\n nook: true,\n test: !reach || reachTargetValidation.targetExists,\n message: 'Target directory must exist when --reach is enabled',\n fail: 'provide an existing directory path',\n },\n {\n nook: true,\n test: !reach || reachTargetValidation.isInsideCwd,\n message:\n 'Target directory must be inside the current working directory when --reach is enabled',\n fail: 'provide a path inside the working directory',\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 reachConcurrency: Number(reachConcurrency),\n reachDebug: Boolean(reachDebug),\n reachDisableAnalysisSplitting: Boolean(reachDisableAnalysisSplitting),\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 description: '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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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'\nimport { fileLink } from '../../utils/terminal-link.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 \\`${fileLink(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 { 0: key, 1: 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 { 0: key, 1: 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, { FLAG_JSON, FLAG_MARKDOWN } 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 CliCommandContext,\n} 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 }: CliCommandContext,\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 const SOCKET_SBOM_URL_PREFIX_LENGTH = SOCKET_SBOM_URL_PREFIX.length\n\n const {\n depth,\n dryRun,\n file,\n json,\n markdown,\n org: orgFlag,\n } = cli.flags as {\n depth: number\n dryRun: boolean\n file: string\n json: boolean\n markdown: boolean\n org: string\n }\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 { 0: 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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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,\n id2,\n depth,\n orgSlug,\n outputKind,\n file,\n })\n}\n","import {\n createWriteStream,\n existsSync,\n promises as fs,\n mkdirSync,\n mkdtempSync,\n} 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 { debugApiRequest, debugApiResponse } from '../../utils/debug.mts'\nimport { formatErrorWithDetail } from '../../utils/errors.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) {\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 = 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 reachConcurrency: 1,\n reachDebug: false,\n reachDisableAnalysisSplitting: false,\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 debugApiRequest('GET', fileUrl)\n let downloadUrlResponse: Response\n try {\n downloadUrlResponse = await fetch(fileUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n debugApiResponse('GET', fileUrl, downloadUrlResponse.status)\n } catch (e) {\n debugApiResponse('GET', fileUrl, undefined, e)\n throw e\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 debugApiRequest('GET', downloadUrl)\n response = await fetch(downloadUrl)\n debugApiResponse('GET', downloadUrl, response.status)\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 (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n\n const fileStream = 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 (e) {\n if (!response) {\n debugApiResponse('GET', downloadUrl, undefined, e)\n }\n logger.fail(\n 'An error was thrown while trying to download a manifest file... url:',\n downloadUrl,\n )\n debugDir('error', e)\n\n // If an error occurs and fileStream was created, attempt to clean up.\n if (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 try {\n await fs.unlink(localPath)\n } catch (e) {\n logger.fail(\n formatErrorWithDetail(`Error deleting partial file ${localPath}`, e),\n )\n }\n }\n // Construct a more informative error message\n let detailedError = `Error during download of ${downloadUrl}: ${(e as { message: string }).message}`\n if ((e as { cause: string }).cause) {\n // Include cause if available (e.g., from network errors)\n detailedError += `\\nCause: ${(e 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 debugApiRequest('GET', commitApiUrl)\n let commitResponse: Response\n try {\n commitResponse = await fetch(commitApiUrl, {\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n debugApiResponse('GET', commitApiUrl, commitResponse.status)\n } catch (e) {\n debugApiResponse('GET', commitApiUrl, undefined, e)\n throw e\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 let repoDetailsResponse: Response\n try {\n debugApiRequest('GET', repoApiUrl)\n repoDetailsResponse = await fetch(repoApiUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n debugApiResponse('GET', repoApiUrl, repoDetailsResponse.status)\n } catch (e) {\n debugApiResponse('GET', repoApiUrl, undefined, e)\n throw e\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 let treeResponse: Response\n try {\n debugApiRequest('GET', treeApiUrl)\n treeResponse = await fetch(treeApiUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${githubToken}`,\n },\n })\n debugApiResponse('GET', treeApiUrl, treeResponse.status)\n } catch (e) {\n debugApiResponse('GET', treeApiUrl, undefined, e)\n throw e\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 default: constants.ENV.SOCKET_CLI_GITHUB_TOKEN,\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 default: DEFAULT_GITHUB_URL,\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 default: '',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n orgGithub: {\n type: 'string',\n default: '',\n description:\n 'Alternate GitHub Org if the name is different than the Socket Org',\n },\n repos: {\n type: 'string',\n default: '',\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 | undefined\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 { 0: 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 { description: '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, { V1_MIGRATION_GUIDE_URL } 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 { webLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\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 }: CliCommandContext,\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 { 0: 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 the ${webLink(V1_MIGRATION_GUIDE_URL, '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 description: '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 { 0: key, 1: 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 CliCommandContext,\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 }: CliCommandContext,\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 { 0: 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 { 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 {\n outputKind,\n outputPath,\n }: { outputKind: OutputKind; outputPath: string },\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 actualOutputPath = outputPath || constants.DOT_SOCKET_DOT_FACTS_JSON\n\n logger.log('')\n logger.success('Reachability analysis completed successfully!')\n logger.info(`Reachability report has been written to: ${actualOutputPath}`)\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 outputPath: string\n reachabilityOptions: ReachabilityOptions\n targets: string[]\n}\n\nexport async function handleScanReach({\n cwd,\n interactive: _interactive,\n orgSlug,\n outputKind,\n outputPath,\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, {\n outputKind,\n outputPath,\n })\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 outputPath,\n packagePaths,\n reachabilityOptions,\n spinner,\n target: targets[0]!,\n uploadManifests: true,\n })\n\n spinner.stop()\n\n await outputScanReach(result, { outputKind, outputPath })\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 { validateReachabilityTarget } from './validate-reachability-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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 default: '',\n description: 'working directory, defaults to process.cwd()',\n },\n org: {\n type: 'string',\n default: '',\n description:\n 'Force override the organization slug, overrides the default org from config',\n },\n output: {\n type: 'string',\n default: '',\n description:\n 'Path to write the reachability report to (must end with .json). Defaults to .socket.facts.json in the current working directory.',\n shortFlag: 'o',\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 }: CliCommandContext,\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 unless the --output flag is specified.\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 $ ${command} --output custom-report.json\n $ ${command} ./proj --output ./reports/analysis.json\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 output: outputPath,\n reachAnalysisMemoryLimit,\n reachAnalysisTimeout,\n reachConcurrency,\n reachDebug,\n reachDisableAnalysisSplitting,\n reachDisableAnalytics,\n reachSkipCache,\n } = cli.flags as {\n cwd: string\n interactive: boolean\n json: boolean\n markdown: boolean\n org: string\n output: string\n reachAnalysisTimeout: number\n reachAnalysisMemoryLimit: number\n reachConcurrency: number\n reachDebug: boolean\n reachDisableAnalytics: boolean\n reachDisableAnalysisSplitting: 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 !== '.' && cwdOverride !== processCwd\n ? path.resolve(processCwd, cwdOverride)\n : processCwd\n\n // Accept zero or more paths. Default to cwd() if none given.\n let targets = cli.input.length ? 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 { 0: orgSlug } = await determineOrgSlug(orgFlag, interactive, dryRun)\n\n const hasApiToken = hasDefaultApiToken()\n\n const outputKind = getOutputKind(json, markdown)\n\n // Validate target constraints for reachability analysis.\n const targetValidation = await validateReachabilityTarget(targets, cwd)\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 nook: true,\n test: !outputPath || outputPath.endsWith('.json'),\n message: 'The --output path must end with .json',\n fail: 'use a path ending with .json',\n },\n {\n nook: true,\n test: targetValidation.isValid,\n message: 'Reachability analysis requires exactly one target directory',\n fail: 'provide exactly one directory path',\n },\n {\n nook: true,\n test: targetValidation.isDirectory,\n message: 'Reachability analysis target must be a directory',\n fail: 'provide a directory path, not a file',\n },\n {\n nook: true,\n test: targetValidation.targetExists,\n message: 'Target directory must exist',\n fail: 'provide an existing directory path',\n },\n {\n nook: true,\n test: targetValidation.isInsideCwd,\n message: 'Target directory must be inside the current working directory',\n fail: 'provide a path inside the working directory',\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 interactive,\n orgSlug,\n outputKind,\n outputPath: outputPath || '',\n reachabilityOptions: {\n reachAnalysisTimeout: Number(reachAnalysisTimeout),\n reachAnalysisMemoryLimit: Number(reachAnalysisMemoryLimit),\n reachConcurrency: Number(reachConcurrency),\n reachDebug: Boolean(reachDebug),\n reachDisableAnalytics: Boolean(reachDisableAnalytics),\n reachDisableAnalysisSplitting: Boolean(reachDisableAnalysisSplitting),\n reachEcosystems,\n reachExcludePaths,\n reachSkipCache: Boolean(reachSkipCache),\n },\n targets,\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 CliCommandContext,\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 }: CliCommandContext,\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 { 0: 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, { SOCKET_JSON } 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, { SOCKET_JSON } 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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: `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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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', 'Failed to parse scan result line as JSON')\n debugDir('error', { error: e, line })\n return undefined\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'\nimport { fileLink } from '../../utils/terminal-link.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 ${fileLink(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 ${fileLink(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 { description: '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, { FLAG_JSON, FLAG_MARKDOWN } 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 CliCommandContext,\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 }: CliCommandContext,\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 { 0: 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: `The \\`${FLAG_JSON}\\` and \\`${FLAG_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 argv,\n name: `${parentName} scan`,\n importMeta,\n subcommands: {\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 {\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 description,\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 { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { 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'\nimport { mailtoLink } from '../../utils/terminal-link.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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 ${mailtoLink('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: ${joinAnd(Array.from(argSet))}`,\n )\n }\n\n const hasApiToken = hasDefaultApiToken()\n\n const { 0: 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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n parentName,\n importMeta,\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 argv,\n name: `${parentName} uninstall`,\n importMeta,\n subcommands: {\n completion: cmdUninstallCompletion,\n },\n },\n { description },\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 { getErrorCause } from '../../utils/errors.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('warn', 'Tab completion setup failed (non-fatal)')\n debugDir('warn', 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: ${getErrorCause(e)}`,\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 {\n CliCommandConfig,\n CliCommandContext,\n} 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 }: CliCommandContext,\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","import { createRequire } from 'node:module'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants, { FLAG_DRY_RUN, FLAG_HELP, YARN } from '../../constants.mts'\nimport { commonFlags } from '../../flags.mts'\nimport { filterFlags } from '../../utils/cmd.mts'\nimport { meowOrExit } from '../../utils/meow-with-subcommands.mts'\nimport { getFlagApiRequirementsOutput } from '../../utils/output-formatting.mts'\n\nimport type {\n CliCommandConfig,\n CliCommandContext,\n} from '../../utils/meow-with-subcommands.mts'\n\nconst require = createRequire(import.meta.url)\n\nexport const CMD_NAME = YARN\n\nconst description = 'Wraps yarn with Socket security scanning'\n\nconst hidden = true\n\nexport const cmdYarn = {\n description,\n hidden,\n run,\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n context: CliCommandContext,\n): Promise<void> {\n const { parentName } = { __proto__: null, ...context } as CliCommandContext\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 \"${YARN}\" is passed to the ${YARN} command.\n Only the \\`${FLAG_DRY_RUN}\\` and \\`${FLAG_HELP}\\` flags are caught here.\n\n Use \\`socket wrapper on\\` to alias this command as \\`${YARN}\\`.\n\n Examples\n $ ${command}\n $ ${command} install\n $ ${command} add package-name\n $ ${command} dlx package-name\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 shadowYarnBin = /*@__PURE__*/ require(constants.shadowYarnBinPath)\n\n process.exitCode = 1\n\n // Filter Socket flags from argv.\n const filteredArgv = filterFlags(argv, config.flags)\n\n const { spawnPromise } = await shadowYarnBin(filteredArgv, {\n stdio: 'inherit',\n })\n\n await spawnPromise\n process.exitCode = 0\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 { cmdPnpm } from './commands/pnpm/cmd-pnpm.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'\nimport { cmdYarn } from './commands/yarn/cmd-yarn.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 pnpm: cmdPnpm,\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 yarn: cmdYarn,\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'\nimport colors from 'yoctocolors-cjs'\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'\nimport { socketPackageLink } from './utils/terminal-link.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 logCallback: (name: string, version: string, latest: string) => {\n logger.log(\n `\\n\\n📦 Update available for ${colors.cyan(name)}: ${colors.gray(version)} → ${colors.green(latest)}`,\n )\n logger.log(\n `📝 ${socketPackageLink('npm', name, `files/${latest}/CHANGELOG.md`, 'View changelog')}`,\n )\n },\n })\n\n try {\n await meowWithSubcommands(\n {\n name: constants.SOCKET_CLI_BIN_NAME,\n argv: process.argv.slice(2),\n importMeta: { url: `${pathToFileURL(__filename)}` } as ImportMeta,\n subcommands: rootCommands,\n },\n { aliases: rootAliases },\n )\n } catch (e) {\n process.exitCode = 1\n debugFn('error', 'CLI uncaught error')\n debugDir('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__","description","time","process","logger","debugFileOp","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","default","importMeta","scope","repoName","markdown","nook","test","fail","repo","perPage","outputJson","outputMarkdown","page","payload","desc","generated","nextPage","org","user_email","debugFn","debugDir","formattedOutput","row","keys","fg","selectedFg","selectedBg","interactive","top","border","columnWidth","columnSpacing","truncate","bottom","height","bg","tags","content","headers","table","detailsBox","help","typeFilter","logType","cwd","tmp","path","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","error","finishedFetching","scan","version","alerts","healthy","addAlert","options","reportLevel","policy","url","manifest","ecoMap","pkgMap","verMap","fileMap","short","scanReport","depth","value","Package","Policy","includeLicensePolicy","scanId","outputKind","uploadManifests","organizations","tarHash","coanaEnv","env","stdio","reachabilityReport","sockJson","cdxgen","count","conda","gradle","sbt","debugLog","verbose","pass","stdout","stderr","poms","strings","reject","keeping","collecting","arr","fs","sbtOpts","bin","gradleOpts","detected","supportedFilesCResult","packagePaths","reachabilityOptions","scanPaths","tier1ReachabilityScanId","branchName","filepath","autoManifest","orgSlugCResult","commitMessage","commitHash","defaultBranch","pendingHead","pullRequest","reach","reachAnalysisTimeout","reachAnalysisMemoryLimit","reachConcurrency","reachDebug","reachDisableAnalytics","reachDisableAnalysisSplitting","reachEcosystems","reachExcludePaths","reachSkipCache","runReachabilityAnalysis","readOnly","report","hidden","name","key","failed","obj","config","full","subcommands","auto","get","list","set","unset","baseBranch","ghsaDetails","title","head","base","octokitPullsCreateParams","pr","reason","details","after","pageInfo","nodes","hasNextPage","endCursor","length","context","apiType","cacheKey","entry","index","parent","match","pageIndex","GITHUB_REPOSITORY","SOCKET_CLI_GITHUB_TOKEN","present","missing","repoInfo","author","states","fixEnv","ids","fixed","throws","limit","ghsaLoop","overallFixed","enabled","validGhsas","errors","unknownFlags","ghsas","autopilot","applyFixes","exclude","isMultiple","include","majorUpdates","id","rangeStyle","outputFile","minimumReleaseAge","showAffectedDirectDependencies","autoMerge","ghsa","maxSatisfying","minSatisfying","prCheck","purl","shortFlag","testScript","noApplyFixes","noMajorUpdates","allowUnknownFlags","toAddToBashrc","recursive","bashrcUpdated","actions","targetPath","completion","updateConfigValue","apiBaseUrl","apiProxy","apiToken","sdk","choices","enforcedOrgs","applyLogout","attemptLogout","YARN_LOCK","ipc","onlyFiles","argvMutable","agent","cleanupPackageLock","rmSync","configuration","coerce","filter","only","profile","standard","technique","alias","array","boolean","string","argv","dryRun","pathArgs","unknowns","yargv","spawnPromise","stdin","out","filename","json","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","state","added","addedInWorkspaces","updated","updatedInWorkspaces","warnedPnpmWorkspaceRequiresNpm","overridesDataObjects","semver","package","depAliasMap","thisSpec","depObj","loggedAddingText","newSpec","concurrency","NPM_BUGGY_OVERRIDES_PATCHED_VERSION","cmdName","prod","VLT","pkgEnvCResult","pkgEnvDetails","agentVersion","optimizationResult","pin","offset","columns","field","mw1","mw2","mw3","security","license","dependencies","quota","deps","self","capabilities","score","transitively","o","valid","purls","components","Maintenance","Quality","Vulnerabilities","License","colorize","padding","artifact","namespace","supplyChain","maintenance","quality","vulnerability","dupes","blocks","packageData","outputPurlsShallowScore","shallowScore","shallow","deep","exportedAt","beforeHash","afterHash","summary","severity","patchExplanation","z","patched","passed","hash","onlyDirectories","absolute","dot","followSymbolicLinks","patches","purlObj","code","visibility","default_branch","slug","outputCreateRepo","homepage","repoDescription","sort","per_page","results","all","direction","create","view","del","update","isDirectory","isInsideCwd","isValid","targetExists","setAsAlertsPage","targets","updatedInput","orgSlug","showHidden","colors","maxArrayLength","id1","id2","repos","targetRepos","scanCreated","scansCreated","githubToken","repoApiUrl","lastCommitter","tmpDir","fileCount","firstFailureResult","isManifest","fileUrl","debugApiRequest","downloadUrlResponse","response","detailedError","repoSlug","commitResponse","lastCommitMessage","repoDetailsResponse","repoDetails","treeResponse","treeDetails","githubApiUrl","orgGithub","from","created_at","year","month","day","fromTime","untilTime","outputPath","target","output","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","pnpm","oops","optimize","organization","patch","repository","uninstall","wrapper","yarn","audit","auditLog","auditLogs","feed","orgs","organisation","organisations","repositories","authInfo","logCallback","errorTitle","errorMessage","errorBody","autoHelp","autoVersion"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWO;;AAIGA;AAAQ;AACdC;;;AAIF;AACA;AACE;AACF;AACA;AAEA;AACEC;AACF;AACF;;AClBO;;AAKGF;AAAQ;AACdC;;;AAIF;AACA;AACE;AACF;AACA;AAEA;AACEC;AACF;AACF;;AChBuC;;AAGvC;AAEA;;AAeA;AACA;AAuBO;;;;;AAKgCC;AAA4B;AAEjE;AACEC;AACF;AAEA;;AAEIC;AACA;AACF;AACAA;AACA;AACF;;AAGE;AAEA;;;AAGIC;;;AAGAA;;AAEAD;AAEIE;AACAC;AACAC;AACF;AAEJ;AACF;AACEJ;AACF;AAEA;AACF;AAEA;;;;AAME;AACA;;;AAGIC;;;AAGAA;AACAD;AACF;AACF;AACEA;AACF;AACF;;AAEA;AACF;AAkBO;;AAOP;;AAEA;;AAEA;AAoCA;;AAEA;AACA;;AAIA;;AAEA;AACA;AAEA;AAEA;AACE;AACA;AACE;AACF;AACA;AACA;AAA8BK;AAASC;AAASC;AAAO;;;;;;;;;AA2DvD;AACA;AACEC;AACAC;AACAC;AACAC;AACAC;AACAC;AACF;;AAEA;AACAN;;;AAIEO;AACF;;AAGA;AACAP;AACF;AAEO;;;;AAOL;AACEQ;AACF;AAEA;AACE;;AAEE;AACA;AACEC;;AAEAA;AACF;AACF;AACF;AACA;AACE;AACED;AACF;AACF;AAEA;AAGA;AAAa;AAAQ;;AACnBE;AACF;;AAGE;AACAC;;AAEJ;AAEO;;;;AAOL;AACEH;AACF;AAEA;AACE;;AAEE;AACA;AACEC;AACF;AACEA;AACF;AACF;AACF;AAEA;AACE;AACA;;AAEE;AACEG;AACF;AACEA;AACF;AACF;AACF;AAEA;AAGA;AAAa;AAAQ;;AACnBF;AACF;;AAGE;AACAC;;AAEJ;AAEA;;AAEA;AAEA;AAOE;;AAEEE;AAASC;AAAcC;AAAcC;;AACrCC;AACAC;AACAd;AACAe;AACAC;AACEC;;AAEFpB;AACF;AAEAD;AAEA;AACEsB;AACAC;;AAGFT;AACF;;AC/YO;;;;;AAKLvB;AACqB;AACrB;;AAKEiC;;AAEAA;AACF;AACEA;AACE7B;AACAC;;AAEJ;;AAEE4B;AACE7B;;AAEAY;;AAEJ;;;;;;AAOEhB;AACF;AACF;;AC3BO;AAEP;AAEA;AAEO;;;AAGLkC;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;AACA;AACAC;AACEC;AACAC;AACAzC;AACF;;;AAEgBsC;AAAM;AAE5B;AACA;;AAEA;AACA;;AAEA;;AAEA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;;;;AAOII;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;AAME;AACEzC;AACF;;AAEA0C;AACA;AACEC;AACF;AACA;AACE3C;AACF;;AAEAA;AACF;;AAGEsC;;AAEAM;;;;AAQF;AAEA;AAEA;AAGIC;AACAC;;AAEAC;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AAGAzC;AACA0C;AACF;;AAGE1C;AACA0C;AACF;AAEEF;;AAEAxC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;;;;AAKE8C;;AAEAhD;AACF;AACF;;AC3KO;;AAIGH;AAAQ;AAAMC;;;AAEtB;AACA;AACE;AACF;AACA;;;;;;AAE4CmD;AAAQ;AAClDnD;;;;AAME;AACAoD;AACA;AACAC;;AAEAZ;AACAa;;AAEF;;AAC0C;AAE9C;;AC9BA;AAEO;;;;;AAOHH;AAOF;AAEA;AACEhD;AACF;;AAGEC;;;;AAKI+C;AACF;AAEJ;AAEA;AACE/C;AACA;AACF;;;;;;AAQM+C;AACF;AAEF;AACF;AAEA;AACF;AAEA;;AAKI;AACF;AACA;AACA;AACA;AAAc;AAAgBI;;AAC9B;AAIA;AACE;AACF;AACA;AACF;AAEO;;;;AAMHJ;AAMF;AAEA;;AAEA;AAEA;AACE7C;AACAY;AACEsC;AACAC;;AAIAC;AACAC;;;;AAIE;;;;;;;AAOEC;AACF;;;;;;;AAOEA;;;AAGN;AACF;AACF;AAEO;;;;AAMHT;AAMF;;;;AAaF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;;AAGI/C;AAGAyD;AACAC;AACA;AACF;AACF;AAEA;AAIE;;AASA;;AASA;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACA;AACAnD;AAEA;;;;AAIA;AACAoD;AACEC;;AAEA;AACF;AAEA;AACEC;AACAC;AACAC;AACAC;AACAC;;AAEArC;AACAsC;;AAEAC;AACE9B;AACAyB;;AAEFM;AAAwB;AACxB;AACAC;AACAC;AACF;AAEA;AACA;AACEC;AAA0B;AAC1BC;AACA5C;AACAR;AACE0C;AACAW;;AAEFC;AACAC;AACF;AACA;AACEJ;AACAC;AACA5C;AACAuC;AACE9B;AACAyB;;AAEFtD;;AAEAY;AACE0C;AACF;AACF;;AAGEc;AACA9D;AACF;;AAEA;;;AAGA;AACAP;AACAA;AACAA;;AAEA;AACAsE;AACE;AACA;AACE;AACAC;;AAEF;AACF;;AAIAvE;AACE;;AAEA;;AAIF;AACF;;ACxTO;;;;;AAKLwC;AAOF;AACE;;;;;AAKEA;AACF;;;;;;AAOEA;AACF;AACF;;ACRO;AAEP;AAEA;AAEO;;;AAGLf;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;AACA;AACA8B;AACE5B;AACAC;AACAzC;;AAGF0D;AACElB;AACAxC;;AAGFqD;AACEb;AACAxC;;AAEFkD;AACEV;AACAC;AACAzC;AACF;;AAEFkF;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOIxC;AACF;;;;;AAMEgB;;AAEAR;;;;;AAgBFiC;AAEA;;AAEQ;AAAW;AAMnB;AAEA;AAGIrC;AACAC;;AAEAC;AACF;AAEEF;;AAEAxC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;;;AAGEkD;AACAH;AACAkC;AACF;AACF;;ACjKO;;;;;;;AAYHxC;AACF;AAAM7C;;;;AAGJsF;;;;AAIAC;AACF;AAAMvF;;;AAEN;AACA;AACE;AACF;AACA;AAEA;AACE;AAEI;;;AAESwF;;;AACX;;AAKJ;AAEA;AAEI;AAAmBC;;AACnB;AAAmBC;;AACnB;AAAsBC;;AACtB;AAAmBC;;AACnBC;AACA;;;AACA3C;AACA4C;;AAEF;AACE7F;AAAgC;AAEtC;;ACzEO;;;AAGY8F;AAAQ;AACvB/F;;;AAIF;AACA;AACE;AACF;AACA;;AAGEC;AACA8F;AACF;AACF;;ACtBA;AACA;AACA;AACA;AACA;AACO;AAIL;AACA;AACA;AACEC;AACAC;AACEC;AACAC;AACF;AACF;AACF;;ACTA;AACA;AACA;AACA;AACO;;;AAUyBpG;AAAQ;AACpCC;;;AAGF;AACA;AACE;AACF;AACA;;;;;AAMQ+F;AAAQ;;AAGdK;AACAC;AACF;;AAGEC;AACAD;AACF;;AAGE;;;AAKA;;AAIA;AACF;;AAGE;;AAMA;AACE;AACF;AAEA;;AAEA;AACA;;AAEA;;AAEI;;AAEA/F;AACAuD;;AACoB0C;AAAU9E;AAAK;AACnC;AACF;AACF;AAEA;;;AAEWnB;AAAUY;;AACrB;;;AAKEZ;AACAC;AACAC;;AAGJ;;AAKE;;AAOA;AACF;AAEA6F;AAEA;;;AAOM/F;AACAC;AACAC;;;;;AAQAF;AACAC;AACAC;;AAIJ;AAEAgG;AACAH;AACF;AAEA;AACE;AACF;AACA;AACE;AACF;;;AAII/F;AACAC;AACAC;;AAEJ;;AAGEF;AACAY;;;AAGA;;AAEJ;;AC7IA;AACA;AACO;;;;;;AASH6E;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;AACAU;;;;AAIIhE;AACAiE;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;AAEAd;AAEA;;AAEIzF;AACAY;AAAQ0F;AAAQ;;AAEpB;AAEA;;;;AAIEE;;AAAiBC;;AACjBJ;;;;AAKErG;AACAC;AAEAW;;AAEJ;;AAGEZ;AACAY;;AAEJ;AAEA;AAKE;;AAEE8F;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;;ACtTO;;;;;;;;AAaHC;AACsB;AAExB;AACEpH;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;AAGM;AACAoH;AAIN;AACEpH;;AAEF;AAEAA;AACAA;AACA;AACF;AAEA;AACEA;AACF;AACEA;AAA8BqH;AAAY;AAC5C;AACF;AAEO;AAIL;AAEA;;AAEE;AACAd;;AAGF;AACErG;AACAY;AACF;AACF;AAEO;AAIL;;AAOA;;AACWwG;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;;ACvNO;;;;;;;;AAQLL;AACsB;;AAEpBM;AACF;;;;AAKEC;;;;;AAKAP;AACF;AACF;;ACrBO;;AAKHlD;AACA0D;;AAEF;AAAM/H;;;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;AAIMG;AACAmC;AACF;AACEqD;;;AAIN;AAEA;;AAEA;AACF;;AC5DO;;;AAKHT;;;;;;;;AAQA0C;AACF;AAAMhI;;;;AAEN;;AAEA;;AAEA;;AAEA;AACA;AACA;;AAEIM;AACAC;AACAC;;AAGJ;;AAEQyH;;AAER;;AAEI3H;AACAC;AACAC;;AAEJ;AAEA;AAEA;AAEA;AACE;AACA;AACA;AACE;AACF;AAEA;;AAEA;AACA;;AAMAuF;;AAEA;;AAQI9F;AACA8F;AACF;;AAKF;AACE;;AAEA;AACA;AACF;AAEAmC;;AAEE;;AAEA;;AAEE5H;AACAC;AACAC;;AAEJ;;AAGAuF;AACF;;AAGAA;AAEA;AACA;AACA;AA2BE;;;AAUF;;AAEA;AACA;AACA;AACEoC;AACF;AACA;AACEA;AACF;;AAEA;;;AAGEC;;AAEAC;AACF;AAEA;;AAEA;;AAIM/H;AACAY;AACE;AACAoH;;AAGF;AACF;AAEN;;AChOA;AACA;;AAuBO;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;;ACvEO;;;;AAILC;AAMF;AACE;;AAEA;AACA;;AAEA;AACA;AAEA1I;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;;AAKU2F;AAAQ;;;AAId3F;AAGAA;AAGA2F;;AAGE;AACA;AACA;AACA;AACAT;AACF;AAEAyD;;;;AACsBC;AAAO;;;;AACNC;;AACzB;AACE;AACElD;AACF;AACEA;AACF;AACF;AACF;;AChIO;;;;;AAKL+C;AAOF;AACE;;;AAEQ/C;AAAQ;AAEhB3F;AACAA;AACAA;;;;;AAME;AACA;AACA;AACA;AACA;AACA;AAA2DkF;AAAI;;AAI/D;AACElF;AACAA;;AAEF;;;AAGEA;AACA;;AAEEA;AACAA;;AAEF;AACA;AACF;;;AAGE8I;AACA;AACF;AACA;;AAEE9I;AAGA;AACF;AACA;AACA;AACA;;AAEEA;;AAEAA;AACAA;AACF;;AAEEA;AACAA;AAGAA;AACA8I;AACA;AACE9I;AACAA;AAGF;AACAA;AACAA;AACA;AACF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA8I;AACA9I;AACF;;;;;AAQA;AACEA;AACAA;;AAEF;AACF;AACF;;AChHA;AACE;AACF;AAEO;AAKL;;AAEE;AACEA;AACF;;;;AAKI;AACA+I;AACF;AACAhJ;;AAEA;;AAEE;AACEC;AACF;;AAEF;AACAD;;AAEI;AACEC;AAGF;;AAEF;AACE;AACEA;AACF;AACAgJ;AACF;AACF;AACF;;;AAII9I;AACAC;AACAC;;AAEJ;AACF;;AAGE;AACEJ;AACF;AAEA;;AAEIE;AACAC;;;AAGJ;AAEAwE;;;AAIIzE;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;AACEmI;AACF;AACE;AACA;AACF;AACF;AACA;AAAA;;AAGEC;AACF;AACF;;AAGF;;ACvIO;AAKL;AACEnJ;AACF;AAEA;;AAEIC;AACA;AACF;AACAA;AACA;AACF;;AAGE;;AAGEA;AACF;;AAEA;AAEA;AACF;;;AAIEmJ;AACAA;AACAA;AAGAA;AACAA;;AAEAA;AACAA;AACA;;AAGEnJ;AACF;;AAEA;AACA;AACF;;;AAIEA;AACF;AACEoJ;AACF;AACF;;AC/DO;;;;;AAKLV;AAOF;;AAGE;AACF;;ACRO;;;;AAILA;AAMF;AACE;AAEA;;AAEA;AAEA;AACE1I;AACA;AACE;;;;AAIAqJ;;AAMF;AACF;AAEA;AACErJ;AAGA;AACE;AACA;AACA;AACAsJ;;AAIAZ;AACAa;AAKF;AACF;AAEA;AACEvJ;AAGA;;;;;;AAMA;AACF;AACF;;AC5BO;;;;;;;;;;;;;;;;;;;AAmBLmF;AACyB;;;;;;;;;;;;;;AAcvBA;AACF;AAEA;AACEnF;AACAyD;AACA;;;AAEsB+F;AAAS;AAC/B;;;;AAIEd;AACF;AACA1I;AACF;;AAEQ2F;AAAQ;AAEhB;AAAkEA;AAAQ;AAC1E;AACElC;;AACsBgG;AAAsB;;;AAG1C9B;AACF;AACA;AACF;;AAMAhC;AAEA;;AAEET;AACF;AAEAS;AAIA;AACEhD;AACAC;;AAEAzC;AAEF;;AAEEsD;AACA;AACF;AAEAzD;;AAIsB0J;AAAa;AAEnC;AACE1J;AACAyD;AACA;AACF;;AAGA;;AAEA;;AAEEzD;AACAA;AACAyD;;AACsBkG;AAA2B;;AAIjD;;;;;AAKEA;;;;AAIF;;AAIA;;;AACwDhC;AAAW;AACjE;AACF;AAEA3H;AAEA;AAEA4J;AAEI;AACA;;AAQJC;AACF;;;;;;;AAWIC;AACF;;;;AAKE3E;AACF;AAGF;AAEA;AACE;AACF;AAEA;AACE;AACE;AACE4E;;AAEAtC;;;;;AAKAN;AACF;AACF;AACE;AAEIjH;AACAC;AACAC;;AAEF;;AAGEuH;AACF;AAEJ;AACF;;;;AAG4DA;AAAW;AACvE;AACF;;ACtPO;AACLlE;;AACsBuG;AAAa;AAEnC;AACA;AACEvG;;AACsBwG;AAAe;AACrClK;AACA;AACAC;AACA;AACF;AAEA;AACA;AACA;AACA;;;;;;AAMgDyC;AAAS;AAEzD;;;AAGEyH;AACAC;AACA3E;;AAEA4E;AACAnG;;AAEA0D;AACA;AACA0C;AACAC;AACAC;AACEC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;;;AAGFC;AACAC;;;AAGA;AACAhG;AACF;AACF;;AC3DA;AACEjD;AACArC;AAEAuL;AACAjJ;AACE;AACA6H;AACE3H;AACA;AACAC;AACAzC;AAEF;;AAEFkF;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAA8B;;;;;AAM9BM;AACF;;AAIA;AACEvC;AACA;AACF;;AAGF;;ACtEO;AAGL;AACA;AACA;;;;AAIIE;AACAC;AACAC;;AAEJ;;AAGE;;AAEEF;AACAC;AACAC;;AAGJ;;AAGE;;AAEEF;AACAC;AACAC;;AAGJ;;;AAIIF;AACAC;AACAC;;AAGJ;;AAGE;;;AAGIF;AACAC;AACAC;;AAGJ;AAEA;AACA;;AAEIF;AACAC;AACAC;;AAEJ;AAEA;;AAEIF;AACAY;AACAX;;AAEJ;;AAGED;AACAY;AACAX;;AAEJ;;AAGE;;;AAGID;AACAC;AACAC;;AAGJ;AAEA;AACA;;AAEIF;AACAC;AACAC;;AAGJ;;AAGEF;AACAY;AACAX;;AAEJ;;;AAIID;AACAC;AACAC;;AAEJ;;AAEA;;AAEEF;AACAC;AACAC;;AAEJ;AAEA;AAGE;AACA;AACE;AACF;;AAEQyH;;AACR;AACE;AACF;AACA;AACA;;AAEA;AACA;AACF;AAEA;AACE;AACA;AACE;AACF;;AAEQA;;;AAEV;;ACnJO;AAKL;AACE9H;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGEA;AACAA;AACAA;AAGAA;;;;AAIIA;AACAA;AACF;AACF;AACAA;AACF;;AAEIA;AACAA;AACF;;AAEAA;;AAGEA;AAGF;AACE;AACEG;;AAIIkL;AACA/D;;AAEF;AAEE+D;AACA/D;AACAzH;;AAEN;AACA;AACEG;AACA;;AAEEA;AAGF;AACEA;AACF;AACF;AACEA;AACF;AACF;AACE;AACEG;;AAIIkL;AACA/D;;AAEF;AAEE+D;AACA/D;AACAzH;;AAEN;AACA;AACEG;AACA;;AAEEA;AACF;AACEA;AACF;AACF;AACEA;AACF;AACF;AACF;AACF;;AC3GO;;AAEL2H;AAIF;AACE;AAEA;AACF;;ACIO;AAEP;AAGA;AAEO;;;AAGL3F;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACU;AAAQ;;AAElB;;;;;;AAOI9C;AACF;;;AAEcS;;;;AAMd;AAEA;;AAIIvC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;AACEsL;AACA3D;AACF;AACF;;AClGO;AAKL;AACE5H;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEA;;AAGEA;AACAA;;AAEA;AACEA;AACAA;AAGF;AACF;;AAEE;AACEA;AACAA;AAGF;AACF;AACF;;AC1CO;;AAEL2H;AAIF;AACE;AAEA;AACF;;ACIA;AACEzF;AACArC;AACAuL;AACAjJ;AACE;;;AAGF4C;AACF;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACU;AAAQ;;;AAGlB;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAA8B;;;;;AAM9BA;AACF;;;AAEcS;;;;AAMd;AAEA;;AAIIvC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;AACEsL;AACA3D;AACF;AACF;;AC1FO;;AAELA;AAIF;AACE;AACA;;;;AAIE;AACE;AACA;AACA;AACEL;AACAiE;;AAEAjE;AACF;AACA;AACEkE;AACF;AACF;AACA;;AAEA;AACAxL;AAIUE;AACAC;AACAW;;AAEE2K;AACAP;;AAEJ;AAEEhL;AACAY;;AAEE2K;AACAP;AACF;AACF;AAGV;;AAMElL;AACAA;;AAEAA;AACA;AACE;AACA;;AAEA;AACE;AACA;AACEsH;AACF;AACA;;AAIA;AACF;AACF;AACA;AACEtH;AACAA;AAGF;AACF;AACF;;AChFA;AACEkC;AACArC;AACAuL;AACAjJ;AACE;AACA;AACAuJ;AACErJ;AACAC;AACAzC;AACF;;AAEFkF;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAA8B;;;;;AAM9BA;AACF;;;;AAEoBS;;;AAIpB;AAEA;AACEC;AACAC;AACAzC;AACA0C;AACF;;AAEE;AACF;AAEA;AACE7C;AACA;AACF;AAEA;;AAEE2H;AACF;AACF;;AC5EO;AAIL;AACE5H;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGEA;AACAA;AACAA;;AAEEA;AACAA;AACF;AACF;AACEA;AACAA;;AAEEA;AACAA;AACF;AACF;AACF;;AChCO;;;AAGLsH;AAKF;;;;;AAEoCK;AAAW;AAE7C;AAEAlE;;AACsB1B;AAAO;AAE7B;AACF;;ACNO;AAEP;AAEA;AAEO;;;AAGLC;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACU;AAAQ;;;AAGlB;AACA;AACA;;;;;;AAOI9C;AACF;;;AAEcS;;;;AAMd;AAEA;AAEA;;AAIIvC;AACA0C;AACF;;AAEiB;AACf1C;AAEA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;AACEsL;;AAEAhE;AACF;AACF;;ACpHO;AAIL;AACEvH;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGEA;AACAA;AACAA;;AAEEA;AACAA;AACF;AACF;AACEA;AACAA;;AAEEA;AACAA;AACF;AACF;AACF;;AClCO;;AAEL2H;AAIF;AACE;AAEA;AACF;;ACIO;AAEP;AAEA;AAEO;;;AAGL3F;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;;;AAGF4C;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACU;AAAQ;;;AAGlB;AACA;AACA;;;;;;AAOI9C;AACF;;;AAEcS;;;;AAMd;AAEA;;AAIIvC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;AACEsL;AACA3D;AACF;AACF;;ACnGA;AAEO;;AAELyD;AACA;AAA8BnJ;AAAW;AACvC;;;;AAKI0J;AACEC;AACAC;AACAC;AACAC;AACAC;AACF;AACF;AACEnM;AAAY;AAElB;AACF;;AC/BA;AACA;AACA;AACA;AACA;AACA;;;AAOA;AACA;AACA;AACA;AACA;AACA;AACO;AAKLG;AACAyD;;;;AAOEA;AACA;AACF;;AAEA;AACA;AACA;AACF;;AAEA;AACA;AACA;AACA;AACO;AAIL;AACA;AACA;AACA;AACF;;AAEA;AACA;AACA;AACA;AACO;AAIL;AACA;AACF;;AAEA;AACA;AACA;AACA;AACO;AAKL;AACA;AACA;AACE;AACF;AACA;AACF;;AC3EA;;AAEA;AACA;AACA;AACA;AACE;AAOF;AA4BO;;AAEP;AAEO;;AAEP;AAEO;AAIL;;AAEF;AAEO;AAIL;;AAEE;AACA;AACA;;AAEE;AACF;AACA;AACA;AAUF;AACA;AAKI;;AAEA;AACE;;AAEF;AACA;AACF;AAEJ;AAEO;AACL;AACA;AAGF;;AC1DO;;AAOGwI;AAAqBC;AAAY;AACvCtM;;;AAIF;;AAGE;;;AAGEuM;AACAC;AACAC;AACAxG;;;AAEoByG;AAAyB;;;AAEtCpM;AAAUqM;;;AAEnB;;;AAGE;;AAMA;AACA;AAKE9I;;AACSvD;AAAWsM;AAA0BrG;;AAChD;;AAEA;AACA;AACE;AACA1C;;AACSvD;AAAWsM;AAA4BrG;AAAUsG;;AAC5D;;AAEA;;AAEEhJ;;AACSvD;AAAWsM;AAA6BrG;;AACnD;;AAGE1C;;AACSvD;AAAWsM;AAAyBrG;;AAC/C;AACF;;AAEA;AACA1C;;AACSvD;AAAWsM;AAAmBrG;;AACzC;AACF;AAmIO;AAKL;AAGF;AAsCA;;;;;AASE;AACEvG;;;AAGF;AACA;AACA;;AAEA;;;;;AAYE;AACA;AACE;AACA;AAKN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AAKc8M;AACF;;;AAISC;AAAS;AACtBC;AACAD;AAAYE;AAAoBC;AAAgB;;AAGlD;AAAkBC;;AAChB;AACA;;;;;AAKIC;AACEC;AACAC;AACApM;AACAqM;AACAC;AACAC;;AAEFC;AACE;;AAEF;AACF;AACF;AACF;;AAEA;;;AAGAC;;AAEA;;;AAME;AACF;;AAEA;AACA;AACA;AACE;AACF;AACF;;;AAGA7J;AACF;AAEA;AACF;;ACvZA;;AACU8J;;;AAEN/J;AACF;;AAEA;AACA;AACE;AACF;;;AAGEX;;AAEJ;AAiBA;AACA;AACA;AACO;;AAQP;;AAEA;AACA;AACA;AACA;AACO;;;;;AAKH2K;;;;AAMF;AACEC;AACF;AACEC;AACF;AAEA;AACED;AACF;AACEC;AACF;AAEA;AACED;AACF;AACEC;AACF;AAEA;AACED;AACF;AACEC;AACF;;;AAEkBD;;AACpB;AAEO;AACL;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACE;AACA;;AAEE1N;AAKF;AACF;AACE;;AAGA;;;AAOF;AAEA;AACA;;AAEA;;AAEE;AACEyD;AACF;AACAmK;AACF;AAEA;AAGQC;AACAC;;;;;;;;;AAWNF;;AAEJ;;AClGA;AACA;AACA;AACA;AACA;;AAMI1I;;AAEAS;AACF;AACE/F;;;AAIF;;AAGS+F;AAAQ;AACbsC;AAAc;;AAIhB;;;AAGE;AACA;;AAEA;;AAEF;AACF;AACA;AACF;AAEO;;;;;;;;;;;;;;AAgBHtC;AACF;AAEA;;AACsBoI;AAAO;;AAI7B;AACA;AACE;AACF;AAEA;AAEA;AAAkEpI;AAAQ;AAC1E;AACE;AACF;AAEA;;AAEET;AACF;AACA;AACA;;AAIA;AAGIrF;AACA8F;AACF;AAGF;AACE;AACF;AAEA;;;;AAIIzF;AACAC;;;AAIJ;;;;AASE;AACA;AACE;AACA;AACE;AACA;AACEH;AAIF;AACF;AACE;;AAKF;AACF;AAEA;AACA;AACEgO;;;AAGErI;AACF;AACF;;AAEA;AACEqI;AACF;;;;AAIW9N;AAAUY;AAAQmN;AAAa;;AAC1C;;AAEA;AACA;AACA;;;;;AA6BoBhG;AAAiB;;AAKnC;AACE;AACF;;AAEA;AACA;AAAgDiG;AAAc;;AAE9D;AACA;AACElO;;;AAGF;;AAESE;AAAUY;AAAQA;AAAuBmN;AAAY;;AAChE;AACE;;AAEE;AACF;AACE;AAAA;AAEJ;AACF;;AAEA;;AAEA;;AAEI;AAGIH;AAA0B;AAE9B;AACA;;;AAGErK;AAIF;;AAEAA;AACAC;AACF;AACF;AAEA;AAEA;;AAGEsK;;AAEEG;AACAxI;AACF;;;AAGF;AAEA;AACElC;AACF;AAEA;AACEA;AACF;;;;AAIWvD;AAAUY;AAAQmN;AAAa;;AAC1C;AAEAxK;AAEA;AACA;;;;;AAOA;AACA2K;AAA4BrB;;AAC1B;AACAtJ;;AAEA;AACA;;;;AAwBkBwE;AAAiB;AAGnC;;AAEE;AACF;;AAEA;AACA;AACA;;AAOA;AACExE;AACA;AACF;AAEA4K;AAEA;;AAGE;AACA;AACA;;AAGYP;AAA0B;AAGtC;AACE;;;AAGA;AACF;;AAEA;AACA;AACA;AACA;AACE;;;AAGE;AACF;AACF;;AAEA;AACA;AACE9N;AAIAyD;AACA;AACF;AAEAA;AAEA;AACAA;AAKA;AACE;AACA;AACA;AACC;AACD;;;;;AAQE;AAEF;AACC;;AAGDzD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;;AAEA;AACA;AACA;;AAOA;AACA;AAIE;;;;AAKEkM;AACF;;;AAIQpL;;AACR;;AAIA;;;AAGE;;;AACiBwN;AAAQ;AACzB;AACEtO;AACF;;AAIEA;AACF;;;AAGF;;AAEA;AACA;AACA;AACF;AACE;AACA;AACEA;AAGA;AACF;;AAIE;AACA;AACF;AACEA;AAGA;AACA;AACF;AACEA;AAGA;AACA;AACF;AACEA;AAGA;AACA;AACF;AACF;;AAEA;AACA;AACA;AACA;AACA;;AAEAA;AAGA0D;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACF;AAEA2E;AACA5E;;AAKE;AACF;AACF;;;AAKEvD;AACAY;AAAQmN;AAAoB;;AAEhC;;ACjkBO;AAIL;AACElO;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;AAEAA;AACAA;AACF;;ACbA;AACA;AAYA;AACA;AACA;AACA;AACO;;;AAEiBgO;AAAI;;;AAK1B;AACE;AAEA;AACE;AACA;AACEO;AACF;AACEC;AACF;;AAEA;AACA;AACEA;AACA;AACF;;AAEA;AACA;;AAEED;;AAEF;;AAEA;;AAEA;AACA;AACA;;AAEEA;AACAvO;AAGF;AACEwO;AAGF;AACF;AACE;AACAA;AAGF;AACF;;;;AAMwBA;AAAO;AAC/B;;;AAGsBD;AAAW;AAEjC;AACF;AAEO;;;;;;;;;;;;;;;;;;AAkBLE;AACe;AACfhL;;;;;;;;;;;;;;;;;AAiBEgL;AACF;AAEA;;;;;;AAOI;AACAC;;;;;;;;;;;AAWAD;;AAIN;;ACjIO;AAEP;AAEA;AAEA;AAEO;;;AAGLzM;AACF;AAEA;AACE2M;AACEtM;AACAC;AACAzC;;AAKF+O;;AAEEvM;AACAC;AACAzC;AAEA;AACAuL;;AAEFyD;AACExM;AACAC;AACAzC;AAEAiP;AACA1D;;AAEF2D;AACE1M;AACAC;AACAzC;AAEAiP;AACA1D;;AAEF4D;AACE3M;AACAC;AACAzC;AAEA;AACAuL;;AAEF6D;AACE5M;AACAC;AACAzC;AACJ;AAIA;AAIA;AAIA;AACIiP;;AAEFX;AACE9L;AACAC;;;AAGF4M;AACE7M;AACAC;AACAzC;AACJ;AACA;AACA;AACA;AACA;;AAEEsP;AACE9M;AACAC;AACAzC;;AAEFuP;AACE/M;AACAC;AACAzC;;AAGFwP;AACEhN;AACAC;AACAzC;AAEF;AACF;AAEA;AACEyP;;AAEElE;;AAEFmE;;AAEEnE;;AAGFoE;AACEnN;AACAC;AACAzC;AACAuL;;AAEFqE;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;AACF;;ACvJO;AAEP;AAEA;AAEO;;;AAGLgC;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;AACAqO;AACEnO;AACAC;AACAzC;;AAEF4Q;AACEpO;AACAC;AACAzC;AACF;;AAEFkF;AACJ;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOIxC;AACF;;AAIA;AACEvC;AACA;AACF;AAEA;AACE;AAGF;;;AAEoByQ;;AAKpB;AACF;;ACxFO;AACLF;AACAA;AACAA;AACAA;AACF;;ACRO;;AAEHO;AACA9Q;;AAEEA;AACAA;AAGF;AACF;AACEA;AACF;AACF;;ACNA;AACEkC;AACArC;AACAuL;AACAjJ;;;AAGA4C;AACF;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAA8B;;;;;AAM9BA;AACF;;AAIA;AACEjC;AACA;AACF;AAEA+Q;AACF;;ACzCA;;;AAA2CC;AAAU;AAErD;AAeA;AACE;;AAEA;;AAEA;AAAa;AAAQ;AAAS;AAC5B;AACE;AACF;;AAEE;AACA;AACAjP;AACF;AACEA;AACF;;;AAGEA;AACF;AACF;AACA;AACA;AACEA;AACF;AACA;AACA;AACEA;AACF;AACA;AACF;AAEO;AACL;AAAsBnC;;;AAEtB;AACEqR;AACE;AACA;;;AAIFhJ;;;AAGF;AACA;AAAoDiJ;AAAgB;;AAI9BA;AAAgB;AAEtD;AAGgCA;AAAgB;AAEhD;;AAGA;AAKE;AACEC;AACF;AACE;AACA;;AAEE;AAGI;AACAC;AACF;;AAGFD;AACAE;;AAEJ;AACF;;AAEA;;AAEE;AACAD;AACF;;AAGE;;AAEI;AACA;AACAE;;AAEJ;AAEA;AACA;AACE;AACA;;AAEA;AACF;AACF;AAEA;AACF;;AC3HA;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;AACAxP;;AAEFC;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;;AAEFwP;;;;;;;;;AASAC;AACIzG;AAAejJ;AAAe;AAC9BiJ;AAAgBjJ;AAAe;AAC/BiJ;AAAqBjJ;AAAe;AACpCiJ;AAAsBjJ;;AAAkB;AAC1C;AAAEiJ;AAAejJ;AAAe;AAC9BiJ;AAAajJ;AAAe;AAC5BiJ;AAAiBjJ;AAAe;AAChCiJ;AAAkBjJ;AAAe;AACjCiJ;AAAajJ;AAAe;AAEhC2P;AAGY;AACV;AAmBFC;AAE2B;;AACL;;AACH;AACjB;AACkB;;AACG;AACrB;AAQ0B;;AACD;AACzB;AAGgB;AAChB;AAAsB;AAE1B;AAEA;AACE/P;AACArC;AACAuL;AACA;AACA;;;AAGF;AAEO;;;AAGLpJ;AACF;AAEA;;AAKUC;AAAW;AACjBrC;;;;AAIA;AACAsS;;;AAGAjQ;AACF;;AAEQkQ;;;AAER;AACA;AAA0C;;;AAK1C;AACE;;;;AAKF;AACE;AACEC;AACF;AACEC;AACF;AACF;;;AAIQtF;AAAsB;AAC9B;AACE;AACA;AACA;;AAEA/M;AAGA;AACF;AAEA;AACEA;AACA;AACF;;AAEA;AACA;AACE;AACA;AACA;AACA;;AAEEsS;AACAtS;AAMF;AACA;;AAEA;AACF;;;AAIQuS;AAAa;;AAErB;;AAEE;;AAEA;AACE;AACAxS;AACF;AACF;AAEA;AACF;;ACtTA;AACEmC;AACArC;AACAuL;AACAjJ;AACE;AACAuG;AACErG;AACAC;AACAzC;AAEF;;AAEFkF;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAA8B;;;;;AAM9BA;AACF;AACA;;;;AACwByG;;;AAIxB;;AAGA;AACA;AACAxD;AAEA;AAEA;AACElF;;;;AAIAA;;AAEF;AAEA;;;AAGsBwJ;AAAS;AAE/B;AACExJ;AACA;AACF;AAEA;AACEA;AAGAA;AACAA;AAGAA;AAGAA;;AAEA;AACF;AAEA;;;;AAIE0I;AACF;;AAKF;;ACxGA;AACExG;AACArC;AACAuL;AACAjJ;AACE;AACA;AACAC;AACEC;AACAC;;;AAGFkQ;AACEnQ;AACAxC;;AAEF4S;AACEpQ;AACAC;AACAzC;;AAEF+I;AACEvG;;;AAGFqG;AACErG;AACAxC;AACF;;AAEFkF;AACF;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAA8B;;;;;AAM9BA;AACF;;;;AAEsBS;;;AAOtB;AACA;AACAwC;AAEA;;AAGE9C;;;;AAIAsG;;;AASF;AACA;;;AAMA;AACA;AACEgK;AACF;;;;AAIE;AACEA;AACF;AACF;AACA;;;AAMA;AACA;AACED;AACF;;;;AAIE;AACEA;AACF;AACF;AACA;;;AAMA;AACE/J;AACF;AAEA;AACE1I;;;AAGAA;AACAA;;AAEF;AAEA;AAEA;AAGI2C;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA7C;AAIA;AACEA;AACA;AACF;AAEA;;;;;AAKE0I;AACF;AACF;;ACnMA;AACExG;AACArC;AAEAuL;AACAjJ;AACE;AACAmH;AACEjH;AACAxC;;AAEF0J;AACElH;AACAxC;;AAGF6I;AACErG;AACAxC;AACF;;AAEFkF;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AAC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C9EA;;;;;;AAAyCiT;AAAa;AAE/C;AACL;AACF;AAEO;AACL;AACF;AAEO;;AAMH;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACE;AACJ;AACF;;AC5BO;;;;;AAKHC;AACF;AACA;AAGqB1T;;;AAIGA;;;AAICA;;;AAKfA;;AAAyC;AAGvC;;AACd;;ACvBA;;;;;;;;AAQEyT;AACF;AAEO;;;AAKIhR;AAAkBkR;;AAC7B;;AAEA;AACA;AACO;;;AAKIlR;AAAWkR;;AACtB;;AAEA;AACA;AACO;;;AAMIlR;AAAYkR;;AACvB;AAEO;;;AAKIlR;AAAWkR;;AACtB;;AAEA;AACA;AACO;;;AAKIlR;AAAkBkR;;AAC7B;;AAEA;AACA;AACO;;;AAKIlR;AAAoBkR;;AAC/B;AASO;;AAKH;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACA;AACE;AACJ;AACF;;AC9FA;;;;;;;AAAmDF;AAAa;AAEzD;AACL;AACA;AACA;AACF;AAEO;AAKL;AACA;AACA;AACA;;AAIA;AACF;AAEO;AACL;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;;AAIJ;AAEO;AACL;AACA;AACA;AACF;AAEO;AACL;AACA;AACE;AACA;AACA;AACA;AACA;;AAIJ;AAEO;;AAOH;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACA;AACE;AACJ;AACF;;AC9EA;;;;;;AAAyCA;AAAa;AAEtD;;AAEI;AACF;AACA;;AAEEG;;AAEF;AACE;AACF;AACA;AACA;;;AAAwBC;;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;;AAEEC;AACF;AACF;AACA;AACF;AAEA;;AAEI;AACF;AACA;AACA;AACA;;AAEA;AACF;AAEA;;;AAGI9K;;AAGI;AACA;AACA;;;;;AAMR;AAEO;;AAIG1D;AAAoB;AAC1BtF;;;;AAIA;AACA;AACA;;AAGI;AACA;AACA;;;;AAKN;AACF;AAEO;;AAIGsF;AAAoB;AAC1BtF;;;;AAIJ;AAEO;;AAIGsF;AAAqByO;AAAY;AACvC/T;;;AAGF;;AAEE;AACE;AACF;AACF;;;AAGEgJ;AAGI;AACA;;;AAIE;AACA;AACA;;;;;AAOV;AAEO;;AAIG1D;AAAoB;AAC1BtF;;;;;AAKA;AACAgJ;;AAMM;AACA;AACA;;;;;AAOV;AAEO;;AAIG1D;AAAoB;AAC1BtF;;;;AAIA;AACA;AACA;;AAMM;AACA;AACA;;;;AAMR;AACF;AAEO;;AAIGsF;AAAoB;AAC1BtF;;;;AAIA;AACA;AACA;AACA;AACA;;AAGI;AACA;AACA;;;;AAKN;AACF;AAOO;;AAKH;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACA;AACE;AACJ;AACF;;AC9OO;;ACQP;;;;;;;;AAQEyT;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;;;;;;AAMEC;AACEC;AACAC;AACAC;AACAC;AACAC;AACF;AACF;AAAM9U;;;;AAEN;AACA;AACA;AACA;AACA;;AAIE;AACA+T;;;AAKA3T;;AAOF;;;AAIE2U;AACF;AACEA;AAIF;AAEA;AACA;AACA;AAAuD;;AAEnD;AACA;AACAC;AAKJ;;;AAGA;AACA;AAEW;AAAQ;;AACPvJ;AAA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A;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAA8B;;;;;AAM9BM;AACF;;;;AAQA;AAEA;AAEA;AAGII;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;;AAGF;;AChFA;AAEO;;AAELoL;AACA;AAA8BnJ;AAAW;AACvC;;;;AAKI0J;AACE0K;AACAvK;AACAwK;AACA1P;AACF;AACF;AAEEsM;AACEqD;;AAEEnL;;;AAGFgL;;AAEEhL;AACA8G;;AAEFiE;;AAEE/K;AACA8G;AACF;;AAEFrS;AACF;AAEJ;AACF;;ACKO;AAGLG;;AAMF;;ACvDO;AAKL;AACED;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGE;AACAA;AACAA;AACA;AACF;AAEAA;AAGAA;AACAA;AACF;AAEO;;AAEHwW;AACEjQ;AACAkQ;;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;;AC9MO;AAILnT;;;AAC4BkE;AAAW;AAEvC;AAEAlE;;AAIsB1B;AAAO;AAE7B;AACF;;ACvBA;AACA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;AACAzC;;AAGFgL;AACExI;AACAC;AACAzC;;AAGFiL;AACEzI;AACAyM;AACAjP;;AAGFkL;AACE1I;AACAyM;AACAjP;;AAGFmL;AACE3I;AACAC;AACAzC;AAEF;AACF;;ACvDO;AACL;AACA;AACA;AACEM;AACAyQ;AAEIvF;AACA/D;AACAzH;AACF;AAEEwL;AACA/D;AACAzH;;AAIN;AACA;AACF;;ACZA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AAIL;AACEsa;AACAC;AACAC;AACAC;;;AAIA;AACF;;AAEA;AACA;;AAEA;;;AAKA;;AAEAvY;AAGAA;;;AAGEA;AACF;AAEA;AACF;;ACXO;AAEP;AAEA;AAEA;AACE;AACA;AACAiI;AACE3H;AACAxC;;AAGFwF;AACEhD;AACAC;AACAzC;AACA+P;;AAEFzF;AACE9H;AACAC;AACAzC;AACA+P;;AAEF1F;AACE7H;AACAC;AACAzC;AACA+P;;AAEFpK;AACEnD;AACAC;AACAzC;AACA+P;;AAEF1K;AACE7C;AACAC;AACAzC;;AAEFuK;AACE/H;AACAC;AACAzC;;AAGFoE;AACE5B;AACAC;AACAzC;;AAGFyK;AACEjI;AACAC;AACAzC;AACA+P;;AAEFrM;AACElB;AACAC;AACAzC;;AAGF0K;AACElI;AACAC;AACAzC;;AAEFqL;AACE7I;AACAC;AACAzC;;AAGFiD;AACET;AACAuN;AACA/P;;AAEFsL;AACE9I;AACAxC;;AAGF8G;AACEtE;;AAEAxC;;AAEF0a;AACElY;AACAC;AACAzC;;;AAIFsF;AACE9C;AACAC;AACAzC;AAEA+P;AACF;AACF;AAEO;;;AAGL5N;AACF;AAEA;AAGIC;AAA8B;AAEhC;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;;;;;;AAOII;AACF;;;;;AAME2C;;AAEAjB;;;AAGAV;;;;;;;;;;;;AAYAgX;AACApV;;;AA2BF;;;AAGA;AACA;AACE;;AAIA;AACA2F;AACF;;;;AAMEzF;AACAvC;AACAqI;;;AAQI;AAAW;AAMjB;;AAMA;;AAEA;AACA;;;;AAOE;AACEnB;AACF;AACF;;;;;AAKE;AACEF;AACF;AACF;;;;;AAKE;AACErH;AACF;AACF;AACA;;;;AAIE;AACE0I;AACF;AACF;;AAEA;AACA;;;AAGA;;;AAIEqP;AACAC;AACF;;AAEA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;;AAEI;;AAEE;AAEIva;AACAC;AACAC;AACF;AAEE6D;AACA0D;AACF;AAEF;AACF;AACA;AACE+S;AACF;AACAD;AACF;AACF;;;;AAOA;AAEA;AACEza;AAGAA;AACAA;AAGAA;AACAA;AACAA;AAGAA;AACF;;;AAIA;AACA;AAEA;;;;;AAgBA;;AAUA;;AAIMma;AACAC;AACAC;AACAC;;AAGN;AAGI3X;;AAEAxC;AACA0C;AACF;AAEED;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AAEA0C;AACF;AAEEF;AACAC;AACAzC;AAEA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AAEA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;AACEgK;AACAF;;;;;AAKAM;AACAnG;;;AAGAoG;AACAC;AACAC;AACEU;AACAL;AACAJ;AACAC;AACAC;AACAC;AACAE;;;;;AAKFK;;;;;;AAMF;AACF;;ACvjBO;;AAKGvL;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;;ACOO;AAEP;AAEA;AAEO;;;AAGLgC;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;AACA;AACA8B;AACE5B;AACAC;AACAzC;;AAGF0D;AACElB;AACAxC;AAEF;;AAEFkF;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;AAEwBsB;;;;;AAQxB;AAEA;AAMA;AAEA;;;AAKIpD;AACA0C;AACF;;AAGE1C;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;AACF;;ACxHO;;;AAGL0a;AAKF;AACE1a;AACAA;AACAA;AAEA;AAMF;;ACXO;;;AAKH2H;AAKF;AAEA;AACE5H;AACF;AAEA;;AAEIC;AACA;AACF;AACAA;AACA;AACF;AAEA;AACA;;AAIA;AACA;AACA;AACA;AACE;AACA;AACF;;AAGE;AACA;AACF;;AAEA;AACA;;AAEAA;;AAGI2a;AACAtT;AACAuT;AACAC;AACF;AAEF7a;AAGAA;AACF;AAEA;AAKE;AAEA;AACEA;;AAEE;AACEA;AACAA;AACF;;AAEA;AACAA;AACF;AACF;AACE;AACAA;AACAA;AACAA;AACF;AACF;AAEA;AAGE;AAEAA;AACAA;AACAA;AACAA;AAGAA;AACAA;;AAIAA;AACAA;AACAA;;AAEAA;;AAGEc;AACEd;AACF;;AAEEA;AACF;AACF;AAEAA;;AAEEc;AACEd;AACF;;AAEEA;AACF;AACF;AAEAA;;AAEEc;AACEd;AACF;;AAEEA;AACF;AACF;AAEAA;;AAEEc;AACEd;AACF;;AAEEA;AACF;AACF;;;AAIA;;AAEE;AACEA;AACF;AACA;;AAEA;AACF;AAEAA;;AAEAA;AACAA;AAGAA;AACA;AAAa;AAAQ;;AACnB;AACE;AACF;AACA;;;AAKA;AACF;AAEAA;;AAEAA;AACAA;AACAA;AACA;AAAa;AAAQ;;AACnB;AACE;AACF;AACA;;;AAKA;AACF;AAEAA;AACF;;AC/MO;;;;;;AAML2H;AAQF;AACE;;;AAGE+S;AACF;;;;AAKE/S;AACF;AACF;;ACXO;AAEP;AAEA;AAEO;;;AAGL3F;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;AACA;AACAkF;AACEhF;AACAC;AACAzC;;AAGFuC;AACEC;AACAuN;AACAtN;AACAzC;;AAGFoE;AACE5B;AACAC;AACAzC;;AAGF0D;AACElB;AACAxC;AAEF;;AAEFkF;AACJ;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;AAEA;AACA;;;;;;;AAQEsB;;;AAYF;AACA;AACA;AACEuX;AACF;AACA;AACEC;AACF;AAEA;;AAEQ;AAAW;AAMnB;AAEA;AAGInY;AACAzC;AAEA0C;AAMF;;AAGEF;AACAxC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;;;;;;AAMEoC;AACF;AACF;;ACpKO;;;;;;;;AAQL4Y;AAUF;;AAME;AACE;AACA;AACElB;AACAJ;AACF;AACA;AACE;AACF;AACAuB;AACF;AAEAA;;AAGAjb;;;AAIIE;AACAC;AACAC;;AAGJ;;AAEA;AACA;AACE;AACA;AACE;AACF;;AAEF;;AAEA;AACA;AACA;;AAEE;AACE;AACF;AACF;;AAGA;AACE;AACA;;;;;;AAME4a;AACF;;;AAEUE;;AACR;AACEC;AACF;AACF;AACF;;AAGAnb;;AAGEE;AACAY;;AAEJ;AAEA;;;;;;AAQIka;AAQF;;;AAMA;;;;;;;AASAhb;AACA;AACF;AAEA;;;;;AAOI2H;AAQF;AAEA;;;;AAIEyT;AACF;AACA;AACE;AACF;;;AACuBC;;AAEvBrb;AAEA;;;;;AAKEqb;AACF;AACA;AACE;AACF;AACA;AAEA;AACErb;;AAGSE;AAAUY;AAAQoa;AAAmB;;AAChD;AAEA;AACAzX;AAEA;;;;;;;AAOE2X;AACF;AACA;AACE;AACF;AAEA;;;;;AAKEA;AACF;AACA;AACE;AACF;;;;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GLxY;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;AACA;AACAgb;AACE9a;;AAEAxC;;AAEFoE;AACE5B;AACAC;AACAzC;;AAGF0D;AACElB;AACAxC;;AAGF8G;AACEtE;;AAEAxC;;AAEFsH;AACE9E;AACAC;AACAzC;;AAEFuW;AACE/T;AACAC;AACAzC;AACF;;AAEFkF;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;;;;AAEwBsB;;;AAIxB;;;AAMA;;AAIA;AAEA;;AAEQ;AAAW;AAMnB;AAEA;AAGIZ;;AAEAxC;AACA0C;AACF;;AAGE1C;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;;;;;;;;AAQE2G;AACF;AACF;;AChMO;AACL;AACE5G;AACF;AAEA;AACEC;AACA;AACF;AAEAA;AACAA;AACAA;AACF;;ACCO;;AAKL;;AAEA;;AAEA;AAEAA;AACAA;AAGAA;AACAA;AACAA;AACAA;AAGAA;AACAA;AACAA;AACAA;AACAA;AACAA;AAEA;AACA;AACE;AACF;AAEA;AACA;AACEmI;AACF;AACA;AACEA;AACF;AAEA;AACEhI;AACAyQ;AAEIvF;AACA/D;AACF;AAEE+D;AACA/D;AACF;AAEE+D;AACA/D;AACAzH;;AAGN;AACA;AACE;AAAe;;;AAGb;AACA;;AAEE;AACF;AACA;AACF;AACA;AAAe;;;AAGb;AACA;;AAEE;AACF;AACA;AACF;AACA;AAAS;;AAET;AACF;AAEAG;AACAA;AACAA;;;AAKI4Q;AAEIvF;AACA/D;AACAzH;AACF;AAEEwL;AACA/D;AACAzH;;AAGN;AAEA;AACF;;AAGF;AAEA;AAME;AACEM;;AAGAyS;AACA;AACF;;;AAGA;AACA;AACE;AACA;;AAEF;;AAEA;AAEA;AACEzS;AAEAmC;AAIAsQ;AACA;AACF;;;AAGA;AACA;AACE;AACA;;AAEF;;AAEA;AAEA;AACEzS;AAEAyQ;AAEIvF;AACA/D;AACAzH;AACF;AAEEwL;AACA/D;AACAzH;AAEF;AAEEwL;AACA/D;AACAzH;AACF;AAEFyC;AAMF;;;AAGA;;;AAGA;;AAEA;;AAEA;AAEA;AACEnC;AACAyQ;AAEIvF;AACA/D;AACAzH;AACF;AAEEwL;AACA/D;AACAzH;AAEF;AAEEwL;AACA/D;AACAzH;AACF;AAEFyC;AAEF;;;AAGA;;;AAGA;;AAEA;;AAEA;;AAGF;AAEA;AAKE;;AAEA;AACEnC;AAEAyQ;AAEIvF;AACA/D;AACAzH;AACF;AAEEwL;AACA/D;AACAzH;AACF;AAEEwL;AACA/D;AACAzH;AACF;AAEFyC;AACF;;;AAGA;;;AAGA;;AAEA;;AAEA;;AAGE;AACEnC;;AAGAyS;AACA;AACF;;;AAGA;AACA;;AAEA;;AAEA;AACF;AAEA;AACEzS;;AAIAyS;AACA;AACF;;;AAGA;;;AAMA;;AAEA;AAEA;AACEzS;AAEAmC;AACAsQ;AACA;AACF;;;AAGA;AACA;;AAEA;;AAEA;;AAGF;AAEA;AACE5S;AACAA;AACAA;;AACSE;AAAUY;AAAQ+R;AAAe;;AAC5C;AAEA;;AACW3S;AAAUY;AAAQ+R;AAAgB;;AAC7C;;ACpWO;;;AAOP;;ACKA;AACE3Q;AACArC;AAEAuL;AACAjJ;AACE;AACA2Q;AACEzQ;;AAEF;;AAEF0C;AACF;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEA;AAGIC;AAA8B;;;;;AAM9BM;AACF;;AAIA;AACEvC;AACA;AACF;;AAEQ8S;;;AAGR;AACA;AACA5N;;AAGF;;AC9EO;AAIL;AAKA;AACE;AACF;AAEA;;AAEA;AACA;;AAEA;;AAEI;;AAEAhF;AACAuD;;AACoB0C;AAAU9E;AAAK;AACnC;AACF;AACF;AAEA;;AACWnB;AAAUY;;AACrB;;AAGEZ;AACAC;AACAC;;AAGJ;;ACjCO;AAOL;AACEL;AACF;AAEA;;AAEIC;AACA;AACF;AACAA;AACA;AACF;AAEA;AAIE;AAEA;AACEA;;;;;;AAMEA;AACAA;AACAA;AAEIE;AACAC;AACAC;AACF;AAEJ;AACA;AACF;AAEAJ;AACA;AACF;;AAGE;;;;;;AAQE0W;;AAEJ;AAEA;AAQA;AAEF;;AAEA;;AAEA;;AAEA;;AAEA;AACA;AAEE;;;;;;AAMI1W;AACAA;AACF;AACF;AACEA;AACF;AACF;;ACxGO;;;AASP;;ACFO;;;AAKSL;AAAQ;AACpBC;;;AAGF;AACA;AACE;AACF;AACA;AAEAI;;AAEA;AACA;AAEIH;AAAsB;AAE5B;;ACZO;AAEP;AAEA;AAEO;;;AAGLmC;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;AACA;AACAib;AACE/a;AACAC;AACAzC;;AAGFoE;AACE5B;AACAC;AACAzC;;AAGF0D;AACElB;AACAxC;AAEF;;AAEFkF;AACJ;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;;;;;AAOI9C;AACF;;;;AAEwBsB;AAAc6Z;;;;AAMtC;AAEA;;AAEQ;AAAW;AAMnB;AAEA;AAGIza;;AAEAxC;AACA0C;AACF;;AAGE1C;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;;AAGE;AAAoCoC;AAAK;AAC3C;;AAEA;AACF;;AC3IA;AAEO;;AAEL;AAA8BH;AAAW;AACvC;;;;AAKI0J;AACEoO;AACAE;AACAoD;AACAC;AACAxR;AACAyR;AACAhT;AACAY;AACA8H;AACA+G;AACF;AACF;AAEE9G;AACEsK;;AAEEpS;;;AAGFqS;;AAEErS;;AAEF;;AAEFvL;AACF;AAEJ;AACF;;ACjDO;;;;;;;;AAQLyG;AAUF;AACE;;AAcF;;ACzBA;AAEO;AAIL;AACEvG;AACF;;AAGEC;AACA;AACF;AACA;AACEA;AACA;AACF;;AAGEA;AACA;AACF;;AAGA;;AAEA;AACA;AACA;AACE;AACF;AACA;AACA;AACA;AACA;AACAO;AAEA;;;;AAIA;AACEsD;AACAC;AACAC;AACAC;AACAC;AACAzD;AACAoB;AACAsC;;AAEAC;AACE9B;AACAyB;;AAEFM;AACA;AACA;AACAC;AACAC;AACF;AAEA;AACA;AACEC;AAA0B;AAC1BC;AACA5C;AACAR;AACE0C;AACAW;;AAEFC;AACAC;AACF;AACA;AACEJ;AACAC;AACA5C;AACAuC;AACE9B;AACAyB;;AAEFtD;AACAmE;AAEAvD;AACE0C;AACF;AACF;;AAGEc;AAQA9D;AACF;;AAEA;AACA;AACE;AACA;;AAEA;AACF;;AAEA;;;AAGA;AACAP;AACAA;AACAA;;AAEA;AACAsE;AACE;AACA;AACE;AACA;AACE;;;AAKF;AACF;AACF;;AAIAtE;AACE;;AAEA;AACAP;AACF;AACF;AAEA;AAKE;AASF;AAEA;AACE;;;AAGE;AAEA;;AAEA;;AASF;AACF;;AC1LO;;;;;;;;;AASLsG;AAWF;AACE;;;;;;;;AAQEA;AACF;AAEA;AACF;;AChBO;AAEP;AAEA;AAcA;AAEA;AAEO;;;AAGLtE;AACF;AAEA;AAGIC;AAA8B;AAEhC;AACEC;;;AAGAC;AACE;AACA;AACA2X;AACEzX;AACAC;AACAzC;;AAEF6d;AACErb;AACAC;AACAzC;;AAEF4R;AACEpP;AACAC;AACAzC;;AAEFoE;AACE5B;AACAC;AACAzC;;AAGF0D;AACElB;AACAxC;;AAGFqD;AACEb;AACAC;AACAzC;;AAEFkD;AACEV;AACAuN;AACAtN;AACAzC;;AAEF8d;AACEtb;AACAC;AACAzC;;AAEFyG;AACEjE;AACAC;AACAzC;AACF;;AAEFkF;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;;;;;AAMEsB;;AAEAlB;AACAiE;;;;AAOF;AACA;AACA;AACA;;AAGAsX;AACE;AACEC;AACAC;AACA;AACF;AACF;AAEAF;AACE;AACEG;AACAD;AACA;AACF;AACF;AAEAF;AACE;AACE5Y;AACA8Y;AACA;AACF;AACF;AAEA;AACAF;AACE;AACEI;AACAF;AACA;AACF;AACF;;AAGE9d;AAGF;AAEA;;AAEQ;AAAW;AAMnB;AAEA;AAGI2C;;AAEAxC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;AAEEF;AACAC;AACAzC;AACA0C;AACF;;AAGA;AACF;AAEA;AACE7C;AACA;AACF;AAEA;;AAEEie;AACAxM;;;;;AAKAkM;AACArX;AACF;AACF;;ACxRO;AAIL;AACEvG;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;;;;AAE4CiQ;;;AAE5C;AACA;;;AAOE;AACE;AACE;AAAA;AAEA;AAAA;;;AAOA/P;AACAY;AACEod;;;AAGF/d;;AAEJ;AACE;;AAEED;AACAY;AACEod;AACAC;;;;AAIN;AACF;;AAEIje;AAAU;AACVY;AAAQod;AAAqBC;;AAC7Bhe;;AAEJ;AACF;AAEA;;AAMA;;ACtEO;AACL;AACA;AACF;;ACOA;AACE+B;AACArC;AACAuL;AACAjJ;;;AAGA4C;AACF;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGL/C;AACF;AAEO;AAGHC;AAA8B;;;;;AAM9BM;AACF;;AAGA;AACEvC;AACA;AACF;;AAIA;AACF;;AChEA;AAEO;;AAELoL;AACA;AAA8BnJ;AAAW;AACvC;;;;AAKI0J;AACE2E;AACF;AACF;AACEzQ;AAAY;AAElB;AACF;;ACnBO;;AAKD;AACE;AACF;AACAG;AAGAA;AAGAA;AACAA;AAGAA;AAGA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","debugId":"abe9e0d9-90ff-4e73-99b1-648bc5ca3347"}
|