@socketsecurity/cli-with-sentry 0.14.59 → 0.14.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +5 -5
- package/dist/constants.d.ts +12 -13
- package/dist/constants.js +29 -37
- package/dist/constants.js.map +1 -1
- package/dist/instrument-with-sentry.js +5 -5
- package/dist/instrument-with-sentry.js.map +1 -1
- package/dist/module-sync/cli.js +197 -152
- package/dist/module-sync/cli.js.map +1 -1
- package/dist/module-sync/shadow-bin.d.ts +1 -1
- package/dist/module-sync/shadow-bin.js +16 -11
- package/dist/module-sync/shadow-bin.js.map +1 -1
- package/dist/module-sync/shadow-npm-inject.js +51 -19
- package/dist/module-sync/shadow-npm-inject.js.map +1 -1
- package/dist/module-sync/shadow-npm-paths.js +16 -12
- package/dist/module-sync/shadow-npm-paths.js.map +1 -1
- package/dist/require/cli.js +197 -152
- package/dist/require/cli.js.map +1 -1
- package/dist/require/vendor.js +90 -5
- package/dist/require/vendor.js.map +1 -1
- package/package.json +18 -18
package/dist/require/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sources":["../../src/commands/action/core/classes.ts","../../src/commands/action/core/index.ts","../../src/commands/action/core/scm_comments.ts","../../src/commands/action/core/github.ts","../../src/commands/action/core/messages.ts","../../src/commands/action/run-action.ts","../../src/utils/api.ts","../../src/utils/output-formatting.ts","../../src/flags.ts","../../src/utils/meow-with-subcommands.ts","../../src/commands/action/cmd-action.ts","../../src/commands/analytics/fetch-org-analytics.ts","../../src/commands/analytics/fetch-repo-analytics.ts","../../src/utils/markdown.ts","../../src/commands/analytics/display-analytics.ts","../../src/commands/analytics/cmd-analytics.ts","../../src/commands/audit-log/get-audit-log.ts","../../src/commands/audit-log/cmd-audit-log.ts","../../src/commands/cdxgen/run-cyclonedx.ts","../../src/commands/cdxgen/cmd-cdxgen.ts","../../src/commands/dependencies/find-dependencies.ts","../../src/commands/dependencies/cmd-dependencies.ts","../../src/commands/diff-scan/get-diff-scan.ts","../../src/commands/diff-scan/cmd-diff-scan-get.ts","../../src/commands/diff-scan/cmd-diff-scan.ts","../../src/commands/fix/npm-fix.ts","../../src/utils/lockfile/pnpm-lock-yaml.ts","../../src/utils/cmd.ts","../../src/utils/npm.ts","../../src/commands/optimize/run-agent.ts","../../src/commands/fix/pnpm-fix.ts","../../src/utils/package-environment.ts","../../src/commands/fix/run-fix.ts","../../src/commands/fix/cmd-fix.ts","../../src/commands/info/fetch-package-info.ts","../../src/commands/info/log-package-info.ts","../../src/commands/info/get-package-info.ts","../../src/commands/info/cmd-info.ts","../../src/commands/login/apply-login.ts","../../src/commands/login/attempt-login.ts","../../src/commands/login/cmd-login.ts","../../src/commands/logout/apply-logout.ts","../../src/commands/logout/attempt-logout.ts","../../src/commands/logout/cmd-logout.ts","../../src/commands/manifest/convert_gradle_to_maven.ts","../../src/commands/manifest/cmd-manifest-gradle.ts","../../src/commands/manifest/convert_sbt_to_maven.ts","../../src/commands/manifest/cmd-manifest-scala.ts","../../src/commands/manifest/cmd-manifest-auto.ts","../../src/commands/manifest/cmd-manifest-kotlin.ts","../../src/commands/manifest/cmd-manifest.ts","../../src/commands/npm/wrap-npm.ts","../../src/commands/npm/cmd-npm.ts","../../src/commands/npx/wrap-npx.ts","../../src/commands/npx/cmd-npx.ts","../../src/commands/oops/cmd-oops.ts","../../src/commands/optimize/deps-includes-by-agent.ts","../../src/commands/optimize/get-dependency-entries.ts","../../src/commands/optimize/get-overrides-by-agent.ts","../../src/commands/optimize/get-workspace-globs.ts","../../src/commands/optimize/lockfile-includes-by-agent.ts","../../src/commands/optimize/ls-by-agent.ts","../../src/commands/optimize/update-lockfile.ts","../../src/commands/optimize/update-manifest-by-agent.ts","../../src/commands/optimize/apply-optimization.ts","../../src/commands/optimize/cmd-optimize.ts","../../src/commands/organization/get-organization.ts","../../src/commands/organization/cmd-organization.ts","../../src/commands/raw-npm/run-raw-npm.ts","../../src/commands/raw-npm/cmd-raw-npm.ts","../../src/commands/raw-npx/run-raw-npx.ts","../../src/commands/raw-npx/cmd-raw-npx.ts","../../src/commands/report/create-report.ts","../../src/commands/report/get-socket-config.ts","../../src/commands/report/fetch-report-data.ts","../../src/commands/report/format-report-data.ts","../../src/commands/report/view-report.ts","../../src/commands/report/cmd-report-create.ts","../../src/commands/report/cmd-report-view.ts","../../src/commands/report/cmd-report.ts","../../src/commands/repos/create-repo.ts","../../src/commands/repos/cmd-repos-create.ts","../../src/commands/repos/delete-repo.ts","../../src/commands/repos/cmd-repos-del.ts","../../src/commands/repos/list-repos.ts","../../src/commands/repos/cmd-repos-list.ts","../../src/commands/repos/update-repo.ts","../../src/commands/repos/cmd-repos-update.ts","../../src/commands/repos/view-repo.ts","../../src/commands/repos/cmd-repos-view.ts","../../src/commands/repos/cmd-repos.ts","../../src/commands/scan/suggest-org-slug.ts","../../src/commands/scan/suggest-repo-slug.ts","../../src/commands/scan/suggest_branch_slug.ts","../../src/commands/scan/suggest_target.ts","../../src/commands/scan/create-full-scan.ts","../../src/commands/scan/cmd-scan-create.ts","../../src/commands/scan/delete-full-scan.ts","../../src/commands/scan/cmd-scan-del.ts","../../src/commands/scan/list-full-scans.ts","../../src/commands/scan/cmd-scan-list.ts","../../src/commands/scan/get-full-scan-metadata.ts","../../src/commands/scan/cmd-scan-metadata.ts","../../src/commands/scan/stream-full-scan.ts","../../src/commands/scan/get-full-scan.ts","../../src/commands/scan/view-full-scan.ts","../../src/commands/scan/cmd-scan-view.ts","../../src/commands/scan/cmd-scan.ts","../../src/commands/threat-feed/get-threat-feed.ts","../../src/commands/threat-feed/cmd-threat-feed.ts","../../src/commands/wrapper/add-socket-wrapper.ts","../../src/commands/wrapper/check-socket-wrapper-setup.ts","../../src/commands/wrapper/postinstall-wrapper.ts","../../src/commands/wrapper/remove-socket-wrapper.ts","../../src/commands/wrapper/cmd-wrapper.ts","../../src/cli.ts"],"sourcesContent":["// https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/core/classes.py\nimport { components } from '@socketsecurity/sdk/types/api'\n\ntype IntroducedBy = Array<[string, string]>\n\nexport class Alert {\n key = ''\n type = ''\n severity = ''\n category = ''\n props = {}\n\n constructor(arg: Partial<Alert> = {}) {\n this.key = arg.key ?? this.key\n this.type = arg.type ?? this.type\n this.severity = arg.severity ?? this.severity\n this.category = arg.category ?? this.category\n this.props = arg.props ?? this.props\n }\n}\n\nexport class Comment {\n id = 0\n body = ''\n body_list: string[] = []\n\n constructor(arg: Comment) {\n this.id = arg.id ?? this.id\n this.body = arg.body ?? this.body\n this.body_list = arg.body_list ?? this.body_list\n }\n}\n\nexport class Diff {\n newPackages: Purl[] = []\n newCapabilities: Record<string, any> = {}\n removedPackages: Purl[] = []\n newAlerts: Issue[] = []\n id = ''\n sbom = ''\n packages: Record<string, Package> = {}\n reportUrl = ''\n diffUrl = ''\n}\n\nexport class FullScan {\n id = ''\n created_at = ''\n updated_at = ''\n organizationId = ''\n repositoryId = ''\n branch = ''\n commit_message = ''\n commit_hash = ''\n pull_request = 0\n sbom_artifacts: Array<components['schemas']['SocketArtifact']> = []\n packages = {}\n\n constructor(obj: Partial<FullScan> = {}) {\n this.id = obj.id ?? this.id\n this.created_at = obj.created_at ?? this.created_at\n this.updated_at = obj.updated_at ?? this.updated_at\n this.organizationId = obj.organizationId ?? this.organizationId\n this.repositoryId = obj.repositoryId ?? this.repositoryId\n this.branch = obj.branch ?? this.branch\n this.commit_message = obj.commit_message ?? this.commit_message\n this.commit_hash = obj.commit_hash ?? this.commit_hash\n this.pull_request = obj.pull_request ?? this.pull_request\n this.sbom_artifacts = obj.sbom_artifacts ?? this.sbom_artifacts\n this.packages = obj.packages ?? this.packages\n }\n}\n\nexport class Issue {\n pkg_type = ''\n pkg_name = ''\n pkg_version = ''\n category = ''\n type = ''\n severity = ''\n pkg_id = ''\n props = {}\n key = ''\n error = false\n warn = false\n ignore = false\n monitor = false\n description = ''\n title = ''\n emoji = ''\n next_step_title = ''\n suggestion = ''\n introduced_by: IntroducedBy = []\n manifests = ''\n url = ''\n purl = ''\n\n constructor(arg: {\n pkg_type: string | undefined\n pkg_name: string | undefined\n pkg_version: string | undefined\n type: string | undefined\n severity: string | undefined\n pkg_id: string | undefined\n props: Record<string, any> | undefined\n key: string | undefined\n error: boolean | undefined\n warn: boolean | undefined\n ignore: boolean | undefined\n monitor: boolean | undefined\n description: string | undefined\n title: string | undefined\n next_step_title: string | undefined\n suggestion: string | undefined\n introduced_by: IntroducedBy | undefined\n url: string | undefined\n purl: string | undefined\n }) {\n this.pkg_type = arg.pkg_type ?? this.pkg_type\n this.pkg_name = arg.pkg_name ?? this.pkg_name\n this.pkg_version = arg.pkg_version ?? this.pkg_version\n this.type = arg.type ?? this.type\n this.severity = arg.severity ?? this.severity\n this.pkg_id = arg.pkg_id ?? this.pkg_id\n this.props = arg.props ?? this.props\n this.key = arg.key ?? this.key\n this.error = arg.error ?? this.error\n this.warn = arg.warn ?? this.warn\n this.ignore = arg.ignore ?? this.ignore\n this.monitor = arg.monitor ?? this.monitor\n this.description = arg.description ?? this.description\n this.title = arg.title ?? this.title\n this.next_step_title = arg.next_step_title ?? this.next_step_title\n this.suggestion = arg.suggestion ?? this.suggestion\n\n if (arg.introduced_by) {\n const arr = []\n for (const item of arg.introduced_by) {\n const [, manifest] = item\n arr.push(manifest)\n }\n this.manifests = arr.join(';')\n }\n }\n}\n\nexport class Package {\n type = ''\n name = ''\n version = ''\n release = ''\n id = ''\n direct = false\n manifestFiles: Array<{ file: string }> = []\n author: string[] = []\n size = 0\n score: Score\n scores = {}\n alerts: NonNullable<components['schemas']['SocketArtifact']['alerts']> = []\n alert_counts = {}\n topLevelAncestors: string[] = []\n url = ''\n transitives = 0\n license = 'NoLicenseFound'\n license_text = ''\n purl = ''\n\n constructor(arg: {\n type: string | undefined\n name: string | undefined\n version: string | undefined\n release: string | undefined\n id: string | undefined\n direct: boolean | undefined\n manifestFiles: Array<{ file: string }> | undefined\n author: string[] | undefined\n size: number | undefined\n score: Score | undefined\n alerts: components['schemas']['SocketArtifact']['alerts'] | undefined\n topLevelAncestors: string[] | undefined\n license: string | undefined\n }) {\n this.type = arg.type ?? this.type\n this.name = arg.name ?? this.name\n this.version = arg.version ?? this.version\n this.release = arg.release ?? this.release\n this.id = arg.id ?? this.id\n this.manifestFiles = arg.manifestFiles ?? this.manifestFiles\n this.author = arg.author ?? this.author\n this.size = arg.size ?? this.size\n this.alerts = arg.alerts ?? this.alerts\n this.topLevelAncestors = arg.topLevelAncestors ?? this.topLevelAncestors\n this.license = arg.license ?? this.license\n\n this.url = `https://socket.dev/${this.type}/package/${this.name}/overview/${this.version}`\n this.score = new Score(\n arg.score ?? {\n supplyChain: 0,\n quality: 0,\n license: 0,\n overall: 0,\n vulnerability: 0\n }\n )\n this.alert_counts = {\n critical: 0,\n high: 0,\n middle: 0,\n low: 0\n }\n this.purl = `${this.type}/${this.name}@${this.version}`\n }\n}\n\nexport class Purl {\n id = ''\n name = ''\n version = ''\n ecosystem = ''\n direct = false\n author: string[] = []\n size = 0\n transitives = 0\n introduced_by: IntroducedBy = []\n capabilities: string[] = []\n // is_new = false\n author_url = ''\n url = ''\n purl = ''\n\n constructor(arg: {\n id: string | undefined\n name: string | undefined\n version: string | undefined\n ecosystem: string | undefined\n direct: boolean | undefined\n introduced_by: IntroducedBy | undefined\n author: string[] | undefined\n size: number | undefined\n transitives: number | undefined\n url: string | undefined\n purl: string | undefined\n }) {\n this.id = arg.id ?? this.id\n this.name = arg.name ?? this.name\n this.version = arg.version ?? this.version\n this.ecosystem = arg.ecosystem ?? this.ecosystem\n this.direct = arg.direct ?? this.direct\n this.author = arg.author ?? this.author\n this.size = arg.size ?? this.size\n this.transitives = arg.transitives ?? this.transitives\n this.introduced_by = arg.introduced_by ?? this.introduced_by\n this.url = arg.url ?? this.url\n this.purl = arg.purl ?? this.purl\n\n this.author_url = this.generateAuthorData(this.author, this.ecosystem)\n }\n\n private generateAuthorData(authors: string[], ecosystem: string): string {\n const arr = []\n for (const author of authors) {\n const url = `https://socket.dev/${ecosystem}/user/${author}`\n arr.push(`[${author}](${url})`)\n }\n return arr.join(',')\n }\n}\n\nexport class Score {\n supplyChain = 0\n quality = 0\n license = 0\n overall = 0\n vulnerability = 0\n\n constructor(arg: Score) {\n this.supplyChain = (arg.supplyChain ?? 0) * 100\n this.quality = (arg.quality ?? 0) * 100\n this.license = (arg.license ?? 0) * 100\n this.overall = (arg.overall ?? 0) * 100\n this.vulnerability = (arg.vulnerability ?? 0) * 100\n }\n}\n","// https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/core/__init__.py\nimport { once } from 'node:events'\nimport fs from 'node:fs'\nimport path from 'node:path'\n\nimport ndjson from 'ndjson'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { SocketSdk } from '@socketsecurity/sdk'\n\nimport { Diff, FullScan, Issue, Package, Purl } from './classes'\n\nimport type { components, operations } from '@socketsecurity/sdk/types/api.d'\n\nexport class Core {\n socket: SocketSdk\n owner: string\n repo: string\n files: string[]\n securityPolicy: Record<\n string,\n { action: 'error' | 'ignore' | 'warn' | 'monitor' }\n > = {}\n\n constructor({\n owner,\n repo,\n socket\n }: Pick<Core, 'socket' | 'owner' | 'repo' | 'files'>) {\n this.socket = socket\n this.owner = owner\n this.repo = repo\n this.files = []\n }\n\n async getSbomData({\n fullScanId\n }: {\n fullScanId: string\n }): Promise<Array<components['schemas']['SocketArtifact']>> {\n const orgFullScanResponse = await this.socket.getOrgFullScan(\n this.owner,\n fullScanId,\n undefined\n )\n if (!orgFullScanResponse.success) {\n return []\n }\n\n const { data: readStream }: { data: any } = orgFullScanResponse\n const sbomArtifacts: any = []\n\n readStream\n .pipe(ndjson.parse())\n .on('data', (sbomArtifact: any) => sbomArtifacts.push(sbomArtifact))\n\n await once(readStream, 'end')\n\n return sbomArtifacts\n }\n\n async createFullScan({\n params\n }: {\n params: Omit<operations['CreateOrgFullScan']['parameters']['query'], 'repo'>\n }): Promise<FullScan> {\n const orgFullScanResponse = await this.socket.createOrgFullScan(\n this.owner,\n // Ignoring because pull_request is of type number but URLSearchParams will convert it to a string\n // @ts-ignore\n new URLSearchParams({ repo: this.repo, ...params }),\n this.files\n )\n\n if (!orgFullScanResponse.success) {\n return new FullScan()\n }\n\n const { id: fullScanId } = orgFullScanResponse.data\n const fullScan = new FullScan(orgFullScanResponse.data)\n if (fullScanId !== undefined) {\n fullScan.sbom_artifacts = await this.getSbomData({ fullScanId })\n }\n return fullScan\n }\n\n getSourceData({\n packages,\n pkg\n }: {\n pkg: Package\n packages: Record<string, Package>\n }): Array<[string, string]> {\n const introducedBy: Array<[string, string]> = []\n\n if (pkg.direct) {\n const manifests = pkg.manifestFiles.map(({ file }) => file).join(';')\n\n introducedBy.push(['direct', manifests])\n } else {\n for (const topId of pkg.topLevelAncestors) {\n const topPackage = packages[topId]\n\n if (!topPackage) {\n continue\n }\n\n const topPurl = `${topPackage.type}/${topPackage.name}@${topPackage.version}`\n const manifests = topPackage.manifestFiles\n .map(({ file }) => file)\n .join(';')\n\n introducedBy.push([topPurl, manifests])\n }\n }\n\n return introducedBy\n }\n\n createPurl({\n packageId,\n packages\n }: {\n packageId: string\n packages: Record<string, Package>\n }): { purl: Purl; pkg: Package } {\n const pkg = packages[packageId]!\n const introducedBy = this.getSourceData({ pkg, packages })\n const purl = new Purl({\n id: pkg.id,\n name: pkg.name,\n version: pkg.version,\n ecosystem: pkg.type,\n direct: pkg.direct,\n introduced_by: introducedBy,\n author: pkg.author,\n size: pkg.size,\n transitives: pkg.transitives,\n url: pkg.url,\n purl: pkg.purl\n })\n return { purl, pkg }\n }\n\n async createIssueAlerts({\n alerts,\n packages,\n pkg\n }: {\n pkg: Package\n alerts: Record<string, Issue[]>\n packages: Record<string, Package>\n }): Promise<Record<string, Issue[]>> {\n const issues = JSON.parse(\n fs.readFileSync(path.join(import.meta.dirname, 'issues.json'), 'utf8')\n ) as Record<string, Record<string, string>>\n\n for (const alert of pkg.alerts) {\n const issue = issues[alert.type]\n\n let description = ''\n let title = ''\n let suggestion = ''\n let nextStepTitle = ''\n\n if (issue !== undefined) {\n description = issue['description'] ?? ''\n title = issue['title'] ?? ''\n suggestion = issue['suggestion'] ?? ''\n nextStepTitle = issue['nextStepTitle'] ?? ''\n }\n\n const introducedBy = this.getSourceData({ pkg, packages })\n\n const issueAlert = new Issue({\n pkg_type: pkg.type,\n pkg_name: pkg.name,\n pkg_version: pkg.version,\n pkg_id: pkg.id,\n type: alert.type,\n severity: alert.severity,\n key: alert.key,\n props: alert.props,\n description,\n title,\n suggestion,\n next_step_title: nextStepTitle,\n introduced_by: introducedBy,\n purl: pkg.purl,\n url: pkg.url,\n error: false,\n ignore: false,\n warn: false,\n monitor: false\n })\n\n if (alert.type in this.securityPolicy) {\n const action = this.securityPolicy[alert.type]?.action\n if (action !== undefined) {\n issueAlert[action] = true\n }\n }\n\n if (issueAlert.type !== 'licenseSpdxDisj') {\n if (!(issueAlert.key in alerts)) {\n alerts[issueAlert.key] = [issueAlert]\n } else {\n alerts[issueAlert.key]!.push(issueAlert)\n }\n }\n }\n\n return alerts\n }\n\n compareIssueAlerts({\n alerts,\n headScanAlerts,\n newScanAlerts\n }: {\n newScanAlerts: Record<string, Issue[]>\n headScanAlerts: Record<string, Issue[]>\n alerts: Issue[]\n }) {\n const consolidatedAlerts = new Set()\n\n for (const alertKey in newScanAlerts) {\n if (!(alertKey in headScanAlerts)) {\n const newAlerts = newScanAlerts[alertKey]!\n\n for (const alert of newAlerts) {\n const alertStr = `${alert.purl},${alert.manifests},${alert.type}`\n\n if (alert.error || alert.warn) {\n if (!consolidatedAlerts.has(alertStr)) {\n alerts.push(alert)\n consolidatedAlerts.add(alertStr)\n }\n }\n }\n } else {\n const newAlerts = newScanAlerts[alertKey]!\n const headAlerts = headScanAlerts[alertKey]!\n\n for (const alert of newAlerts) {\n const alertStr = `${alert.purl},${alert.manifests},${alert.type}`\n if (\n !headAlerts.includes(alert) &&\n !consolidatedAlerts.has(alertStr)\n ) {\n if (alert.error || alert.warn) {\n alerts.push(alert)\n consolidatedAlerts.add(alertStr)\n }\n }\n }\n }\n }\n\n return alerts\n }\n\n checkAlertCapabilities({\n capabilities,\n headPackage,\n packageId,\n pkg\n }: {\n pkg: Package\n capabilities: Record<string, string[]>\n packageId: string\n headPackage?: Package | undefined\n }): Record<string, string[]> {\n const alertTypes = {\n envVars: 'Environment',\n networkAccess: 'Network',\n filesystemAccess: 'File System',\n shellAccess: 'Shell'\n }\n\n for (const alert of pkg.alerts) {\n let newAlert = true\n if (headPackage !== undefined && headPackage.alerts.includes(alert)) {\n newAlert = false\n }\n if (alert.type in alertTypes && newAlert) {\n const value = alertTypes[alert.type as keyof typeof alertTypes]\n if (!(packageId in capabilities)) {\n capabilities[packageId] = [value]\n } else {\n if (!capabilities[packageId]!.includes(value)) {\n capabilities[packageId]!.push(value)\n }\n }\n }\n }\n\n return capabilities\n }\n\n compareCapabilities({\n headPackages,\n newPackages\n }: {\n newPackages: Record<string, Package>\n headPackages: Record<string, Package>\n }) {\n let capabilities: Record<string, string[]> = {}\n\n for (const packageId in newPackages) {\n const pkg = newPackages[packageId]!\n\n if (packageId in headPackages) {\n const headPackage = headPackages[packageId]!\n for (const alert of pkg.alerts) {\n if (!headPackage.alerts.includes(alert)) {\n capabilities = this.checkAlertCapabilities({\n pkg,\n capabilities,\n packageId,\n headPackage\n })\n }\n }\n } else {\n capabilities = this.checkAlertCapabilities({\n pkg,\n capabilities,\n packageId\n })\n }\n }\n\n return capabilities\n }\n\n addCapabilitiesToPurl(diff: Diff): Diff {\n const newPackages: Purl[] = []\n\n for (const purl of diff.newPackages) {\n if (purl.id in diff.newCapabilities) {\n const capabilities =\n diff.newCapabilities[purl.id as keyof typeof diff.newCapabilities]!\n if (capabilities.length > 0) {\n purl.capabilities = capabilities\n newPackages.push(purl)\n }\n } else {\n newPackages.push(purl)\n }\n }\n diff.newPackages = newPackages\n\n return diff\n }\n\n async compareSBOMs({\n headScan,\n newScan\n }: {\n newScan: Awaited<ReturnType<Core['getSbomData']>>\n headScan: Awaited<ReturnType<Core['getSbomData']>>\n }): Promise<Diff> {\n let diff = new Diff()\n const newPackages = this.createSbomDict(newScan)\n const headPackages = this.createSbomDict(headScan)\n\n let newScanAlerts: Record<string, Issue[]> = {}\n let headScanAlerts: Record<string, Issue[]> = {}\n const consolidated = new Set()\n\n for (const packageId in newPackages) {\n const { pkg, purl } = this.createPurl({\n packageId,\n packages: newPackages\n })\n const basePurl = `${purl.ecosystem}/${purl.name}@${purl.version}`\n\n if (\n !(packageId in headPackages) &&\n pkg.direct &&\n !consolidated.has(basePurl)\n ) {\n diff.newPackages.push(purl)\n consolidated.add(basePurl)\n }\n // eslint-disable-next-line no-await-in-loop\n newScanAlerts = await this.createIssueAlerts({\n pkg,\n alerts: newScanAlerts,\n packages: newPackages\n })\n }\n\n for (const packageId in headPackages) {\n const { pkg, purl } = this.createPurl({\n packageId,\n packages: headPackages\n })\n\n if (!(packageId in newPackages) && pkg.direct) {\n diff.removedPackages.push(purl)\n }\n // eslint-disable-next-line no-await-in-loop\n headScanAlerts = await this.createIssueAlerts({\n pkg,\n alerts: headScanAlerts,\n packages: headPackages\n })\n }\n\n diff.newAlerts = this.compareIssueAlerts({\n newScanAlerts,\n headScanAlerts,\n alerts: diff.newAlerts\n })\n diff.newCapabilities = this.compareCapabilities({\n newPackages,\n headPackages\n })\n diff = this.addCapabilitiesToPurl(diff)\n\n return diff\n }\n\n createPackageFromSbomArtifact(\n sbomArtifact: Array<components['schemas']['SocketArtifact']>\n ): Package[] {\n return sbomArtifact.map(\n sbomArtifact =>\n new Package({\n type: sbomArtifact.type,\n name: sbomArtifact.name,\n version: sbomArtifact.version,\n release: sbomArtifact.release,\n id: sbomArtifact.id,\n direct: sbomArtifact.direct,\n manifestFiles: sbomArtifact.manifestFiles,\n author: sbomArtifact.author,\n size: sbomArtifact.size,\n score: sbomArtifact.score,\n alerts: sbomArtifact.alerts,\n topLevelAncestors: sbomArtifact.topLevelAncestors,\n license: sbomArtifact.license\n })\n )\n }\n\n getLicenseDetails({ package: pkg }: { package: Package }): Package {\n const licenseText = JSON.parse(\n fs.readFileSync(\n path.join(import.meta.dirname, 'license_texts.json'),\n 'utf8'\n )\n ) as Record<string, string>\n const licenseStr = licenseText[pkg.license]\n if (licenseStr !== undefined) {\n pkg.license_text = licenseStr\n }\n return pkg\n }\n\n createSbomDict(\n sbomArtifacts: Awaited<ReturnType<typeof this.getSbomData>>\n ): Record<string, Package> {\n const packages: Record<string, Package> = {}\n const topLevelCount: Record<string, number> = {}\n\n for (const sbomArtifact of sbomArtifacts) {\n let pkg = new Package({\n type: sbomArtifact.type,\n name: sbomArtifact.name,\n version: sbomArtifact.version,\n release: sbomArtifact.release,\n id: sbomArtifact.id,\n direct: sbomArtifact.direct,\n manifestFiles: sbomArtifact.manifestFiles,\n author: sbomArtifact.author,\n size: sbomArtifact.size,\n score: sbomArtifact.score,\n alerts: sbomArtifact.alerts,\n topLevelAncestors: sbomArtifact.topLevelAncestors,\n license: sbomArtifact.license\n })\n\n if (pkg.id in packages) {\n logger.log('Duplicate package?')\n } else {\n pkg = this.getLicenseDetails({ package: pkg })\n packages[pkg.id] = pkg\n\n for (const topId in sbomArtifact.topLevelAncestors ?? []) {\n if (!(topId in topLevelCount)) {\n topLevelCount[topId] = 1\n } else {\n topLevelCount[topId]! += 1\n }\n }\n }\n }\n\n if (Object.keys(topLevelCount).length > 0) {\n for (const packageId in topLevelCount) {\n const pkg = packages[packageId]\n if (pkg) {\n pkg.transitives = topLevelCount[packageId] ?? 0\n }\n }\n }\n\n return packages\n }\n\n async createNewDiff({\n params = {}\n }: {\n params?: Omit<\n operations['CreateOrgFullScan']['parameters']['query'],\n 'repo'\n >\n }): Promise<Diff> {\n let headFullScanId: string = ''\n let headFullScan: Awaited<ReturnType<typeof this.getSbomData>> = []\n\n try {\n const orgRepoResponse = await this.socket.getOrgRepo(\n this.owner,\n this.repo\n )\n if (orgRepoResponse.success) {\n headFullScanId = orgRepoResponse.data.head_full_scan_id ?? ''\n if (headFullScanId !== '') {\n headFullScan = await this.getSbomData({ fullScanId: headFullScanId })\n }\n }\n } catch (e) {\n logger.error(e)\n }\n\n const newFullScan = await this.createFullScan({ params })\n newFullScan.packages = this.createSbomDict(newFullScan.sbom_artifacts)\n\n const diffReport = await this.compareSBOMs({\n newScan: newFullScan.sbom_artifacts,\n headScan: headFullScan\n })\n diffReport.packages = newFullScan.packages\n\n const baseSocket = 'https://socket.dev/dashboard/org'\n diffReport.id = newFullScan.id\n diffReport.reportUrl = `${baseSocket}/${this.owner}/sbom/${diffReport.id}`\n if (headFullScanId !== '') {\n diffReport.diffUrl = `${baseSocket}/${this.owner}/diff/${diffReport.id}/${headFullScanId}`\n } else {\n diffReport.diffUrl = diffReport.reportUrl\n }\n\n return diffReport\n }\n}\n","// https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/core/scm_comments.py\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { Comment, Issue } from './classes'\n\nexport type SocketComments = {\n security: Comment | undefined\n overview: Comment | undefined\n ignore: Comment[]\n}\n\nexport function checkForSocketComments({\n comments\n}: {\n comments: Record<string, Comment>\n}): SocketComments {\n const socketComments: {\n security: Comment | undefined\n overview: Comment | undefined\n ignore: Comment[]\n } = {\n security: undefined,\n overview: undefined,\n ignore: []\n }\n\n for (const commentId in comments) {\n const comment = comments[commentId]!\n\n if (comment.body.includes('socket-security-comment-actions')) {\n socketComments.security = comment\n } else if (comment.body.includes('socket-overview-comment-actions')) {\n socketComments.overview = comment\n } else if (\n // Based on:\n // To ignore an alert, reply with a comment starting with @SocketSecurity ignore\n // followed by a space separated list of ecosystem/package-name@version specifiers.\n // e.g. @SocketSecurity ignore npm/foo@1.0.0 or ignore all packages with @SocketSecurity ignore-all\n comment.body\n .split('\\n')\n .at(0)\n ?.includes('SocketSecurity ignore')\n ) {\n socketComments.ignore.push(comment)\n }\n }\n\n return socketComments\n}\n\n// Parses the ignore command\n// @SocketSecurity ignore pkg1 pkg2 ...\n// @SocketSecurity ignore ignore-all\nexport function parseIgnoreCommand(line: string) {\n const result = { packages: [] as string[], ignoreAll: false }\n const words = line.trim().replace(/\\s+/g, ' ').split(' ')\n if (words.at(1) === 'ignore-all') {\n result.ignoreAll = true\n return result\n }\n if (words.at(1) === 'ignore') {\n for (let i = 2; i < words.length; i++) {\n const pkg = words[i] as string\n result.packages.push(pkg)\n }\n return result\n }\n return result\n}\n\n// Ref: https://github.com/socketdev-demo/javascript-threats/pull/89#issuecomment-2456015512\nexport function processSecurityComment({\n ignore: ignoreComments,\n security: securityComment\n}: Pick<SocketComments, 'security' | 'ignore'>): string {\n const result: string[] = []\n let start = false\n\n let ignoreAll = false\n const ignoredPackages = []\n for (const ignoreComment of ignoreComments) {\n const parsed = parseIgnoreCommand(\n ignoreComment.body?.split('\\n').at(0) ?? ''\n )\n if (parsed.ignoreAll) {\n ignoreAll = true\n break\n }\n ignoredPackages.push(parsed.packages)\n }\n\n // Split the comment body into lines and update them\n // to generate a new comment body\n for (let line of securityComment?.body?.split('\\n') ?? []) {\n line = line.trim()\n\n if (line.includes('start-socket-alerts-table')) {\n start = true\n result.push(line)\n } else if (\n start &&\n !line.includes('end-socket-alerts-table') &&\n // is not heading line?\n !(\n line === '|Alert|Package|Introduced by|Manifest File|CI|' ||\n line.includes(':---')\n ) &&\n line !== ''\n ) {\n // Parsing Markdown data colunms\n const [_, _title, packageLink, _introducedBy, _manifest, _ci] =\n line.split('|') as [string, string, string, string, string, string]\n\n // Parsing package link [npm/pkg](url)\n const [_ecosystem, pkg] = packageLink\n .slice(1, packageLink.indexOf(']'))\n .split('/', 2) as [string, string]\n const [pkgName, pkgVersion] = pkg.split('@')\n\n // Checking if this package should be ignored\n let ignore = false\n if (ignoreAll) {\n ignore = true\n } else {\n for (const [ignoredPkgName, ignorePkgVersion] of ignoredPackages) {\n if (\n pkgName === ignoredPkgName &&\n (ignorePkgVersion === '*' || pkgVersion === ignorePkgVersion)\n ) {\n ignore = true\n break\n }\n }\n }\n\n if (ignore) {\n break\n }\n result.push(line)\n } else if (line.includes('end-socket-alerts-table')) {\n start = false\n result.push(line)\n } else {\n result.push(line)\n }\n }\n\n return result.join('\\n')\n}\n\nexport function getIgnoreOptions({ comments }: { comments: SocketComments }) {\n const ignoreCommands: string[] = []\n let ignoreAll = false\n\n for (const comment of comments.ignore) {\n let firstLine = comment.body_list[0]!\n if (!ignoreAll && firstLine.includes('SocketSecurity ignore')) {\n try {\n firstLine = firstLine.replace(/@/, '')\n let [, command] = firstLine.split('SocketSecurity ')\n command = command!.trim()\n if (command === 'ignore-all') {\n ignoreAll = true\n } else {\n command = command.replace(/ignore/, '').trim()\n const [name, version] = command.split('@')\n const data = `${name}/${version}`\n ignoreCommands.push(data)\n }\n } catch (e) {\n logger.fail(`Unable to process ignore command for ${comment}`)\n logger.error(e)\n }\n }\n }\n return { ignoreAll, ignoreCommands }\n}\n\nexport function removeAlerts({\n comments,\n newAlerts\n}: {\n comments: SocketComments\n newAlerts: Issue[]\n}) {\n const alerts: Issue[] = []\n\n if (comments.ignore.length === 0) {\n return newAlerts\n }\n\n const { ignoreAll, ignoreCommands } = getIgnoreOptions({\n comments\n })\n\n for (const alert of newAlerts) {\n if (ignoreAll) {\n break\n } else {\n const fullName = `${alert.pkg_type}/${alert.pkg_name}`\n const purl = `${fullName}/${alert.pkg_version}`\n const purlStar = `${fullName}/*`\n if (ignoreCommands.includes(purl) || ignoreCommands.includes(purlStar)) {\n logger.log(`Alerts for ${alert.pkg_name}@${alert.pkg_version} ignored`)\n } else {\n logger.log(\n `Adding alert ${alert.type} for ${alert.pkg_name}@${alert.pkg_version}`\n )\n alerts.push(alert)\n }\n }\n }\n\n return alerts\n}\n","// https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/core/github.py\n/* eslint-disable no-await-in-loop */\nimport { Octokit } from '@octokit/rest'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { Comment } from './classes'\nimport * as SCMComments from './scm_comments'\n\nexport class GitHub {\n octokit: Octokit = new Octokit()\n owner: string\n repo: string\n prNumber: number\n\n constructor() {\n const [owner = '', repo = ''] = (\n process.env['GITHUB_REPOSITORY'] ?? ''\n ).split('/')\n // https://github.com/actions/checkout/issues/58#issuecomment-2264361099\n const prNumber = parseInt(\n process.env['GITHUB_REF']?.match(/refs\\/pull\\/(\\d+)\\/merge/)?.at(1) ?? ''\n )\n this.owner = owner\n this.repo = repo\n this.prNumber = prNumber\n }\n\n checkEventType(): 'main' | 'diff' | 'comment' | 'unsupported' {\n switch (process.env['GITHUB_EVENT_NAME']) {\n case 'push':\n return this.prNumber ? 'diff' : 'main'\n case 'pull_request':\n // This env variable needs to be set in the GitHub action.\n // Add this code below to GitHub action:\n // - steps:\n // - name: Get PR State\n // if: github.event_name == 'pull_request'\n // run: echo \"EVENT_ACTION=${{ github.event.action }}\" >> $GITHUB_ENV\n const eventAction = process.env['EVENT_ACTION']\n if (!eventAction) {\n throw new Error('Missing event action')\n }\n if (['opened', 'synchronize'].includes(eventAction)) {\n return 'diff'\n } else {\n logger.log(`Pull request action: ${eventAction} is not supported`)\n process.exit()\n }\n case 'issue_comment':\n return 'comment'\n default:\n throw new Error(\n `Unknown event type: ${process.env['GITHUB_EVENT_NAME']}`\n )\n }\n }\n\n async getCommentsForPR(): Promise<SCMComments.SocketComments> {\n const { data: githubComments } =\n await this.octokit.rest.issues.listComments({\n owner: this.owner,\n repo: this.repo,\n issue_number: this.prNumber\n })\n const comments: Record<string, Comment> = {}\n for (const githubComment of githubComments) {\n comments[githubComment.id] = new Comment({\n id: githubComment.id,\n body: githubComment.body ?? '',\n body_list: (githubComment.body ?? '').split('\\n')\n })\n }\n return SCMComments.checkForSocketComments({ comments })\n }\n\n async commentReactionExists({\n commentId\n }: {\n commentId: number\n }): Promise<boolean> {\n const { data } = await this.octokit.reactions.listForIssueComment({\n owner: this.owner,\n repo: this.repo,\n comment_id: commentId\n })\n return data.some(reaction => reaction.content === '+1')\n }\n\n async postReaction({ commentId }: { commentId: number }) {\n await this.octokit.reactions.createForIssueComment({\n owner: this.owner,\n repo: this.repo,\n comment_id: commentId,\n content: '+1'\n })\n }\n\n async handleIgnoreReactons({\n comments\n }: {\n comments: SCMComments.SocketComments\n }) {\n for (const ignoreComment of comments.ignore) {\n if (\n ignoreComment.body?.includes('SocketSecurity ignore') &&\n !(await this.commentReactionExists({\n commentId: ignoreComment.id\n }))\n ) {\n await this.postReaction({ commentId: ignoreComment.id })\n }\n }\n }\n\n async updateComment({ body, id }: { id: number; body: string }) {\n await this.octokit.issues.updateComment({\n owner: this.owner,\n repo: this.repo,\n comment_id: id,\n body\n })\n }\n\n async removeCommentAlerts({\n comments\n }: {\n comments: SCMComments.SocketComments\n }) {\n const securityAlert = comments.security\n if (securityAlert !== undefined) {\n const newBody = SCMComments.processSecurityComment({\n security: comments.security,\n ignore: comments.ignore\n })\n await this.handleIgnoreReactons({ comments })\n await this.updateComment({ id: securityAlert.id, body: newBody })\n }\n }\n\n async postComment({ body }: { body: string }) {\n await this.octokit.issues.createComment({\n owner: this.owner,\n repo: this.repo,\n issue_number: this.prNumber,\n body\n })\n }\n\n async addSocketComments({\n comments,\n newOverviewComment,\n newSecurityComment,\n overviewComment,\n securityComment\n }: {\n securityComment: string\n overviewComment: string\n comments: SCMComments.SocketComments\n newSecurityComment: boolean\n newOverviewComment: boolean\n }): Promise<void> {\n const {\n overview: existingOverviewComment,\n security: existingSecurityComment\n } = comments\n if (newOverviewComment) {\n logger.log('New Dependency Overview comment')\n if (existingOverviewComment !== undefined) {\n logger.log('Previous version of Dependency Overview, updating')\n await this.updateComment({\n body: overviewComment,\n id: existingOverviewComment.id\n })\n } else {\n logger.log('No previous version of Dependency Overview, posting')\n await this.postComment({ body: overviewComment })\n }\n }\n if (newSecurityComment) {\n logger.log('New Security Issue Comment')\n if (existingSecurityComment !== undefined) {\n logger.log('Previous version of Security Issue comment, updating')\n await this.updateComment({\n body: securityComment,\n id: existingSecurityComment.id\n })\n } else {\n logger.log('No Previous version of Security Issue comment, posting')\n await this.postComment({ body: securityComment })\n }\n }\n }\n}\n","// https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/core/messages.py\nimport { Diff, Issue, Purl } from './classes'\n\nexport function createSecurityCommentJSON({ diff }: { diff: Diff }) {\n const scanFailed = false\n\n // Not porting this code because it's unreachable\n // https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/core/messages.py#L13-L18\n\n const output: {\n scanFailed: boolean\n newAlerts: Issue[]\n fullScanId: string\n } = {\n scanFailed,\n newAlerts: [],\n fullScanId: diff.id\n }\n for (const alert of diff.newAlerts) {\n output.newAlerts.push(alert)\n }\n\n return output\n}\n\nexport function createPurlLink(purl: Purl): string {\n const packageUrl = `[${purl.purl}](${purl.url})`\n return packageUrl\n}\n\nexport function createAddedTable(diff: Diff): string {\n const overviewTable = [\n 'Package',\n 'Direct',\n 'Capabilities',\n 'Transitives',\n 'Size',\n 'Author'\n ]\n const rows = []\n for (const added of diff.newPackages) {\n const packageUrl = createPurlLink(added)\n const capabilities = added.capabilities.join(', ')\n const row = [\n packageUrl,\n added.direct,\n capabilities,\n added.transitives,\n `${added.size} KB`,\n added.author_url\n ]\n rows.push(row)\n }\n\n let md = ''\n md += `|${overviewTable.join('|')}|\\n`\n md += '|---|---|---|---|---|---|\\n'\n for (const row of rows) {\n md += `|${row.join('|')}|\\n`\n }\n\n return md\n}\n\nexport function createRemoveLine(diff: Diff): string {\n const removedLine = ['Removed packages:']\n for (const removed of diff.removedPackages) {\n const packageUrl = createPurlLink(removed)\n removedLine.push(packageUrl)\n }\n return removedLine.join(', ')\n}\n\nexport function dependencyOverviewTemplate(diff: Diff): string {\n let md = ''\n md += '<!-- socket-overview-comment-actions -->\\n'\n md += '# Socket Security: Dependency Overview\\n'\n md +=\n 'New and removed dependencies detected. Learn more about [socket.dev](https://socket.dev)\\n\\n'\n md += createAddedTable(diff)\n if (diff.removedPackages.length > 0) {\n md += createRemoveLine(diff)\n }\n return md\n}\n\nexport function createSources(alert: Issue): [string, string] {\n const sources: string[] = []\n const manifests: string[] = []\n for (const [source, manifest] of alert.introduced_by) {\n const addStr = `<li>${manifest}</li>`\n const sourceStr = `<li>${source}</li>`\n if (!sources.includes(sourceStr)) {\n sources.push(sourceStr)\n }\n if (!manifests.includes(addStr)) {\n manifests.push(addStr)\n }\n }\n const manifestList = manifests.join('')\n const sourceList = sources.join('')\n const manifestStr = `<ul>${manifestList}</ul>`\n const sourcesStr = `<ul>${sourceList}</ul>`\n return [manifestStr, sourcesStr]\n}\n\nexport function createSecurityAlertTable(diff: Diff): {\n ignoreCommands: string[]\n nextSteps: Record<string, string[]>\n mdTable: string\n} {\n const alertTable = [\n 'Alert',\n 'Package',\n 'Introduced by',\n 'Manifest File',\n 'CI'\n ]\n const nextSteps: Record<string, string[]> = {}\n const ignoreCommands: string[] = []\n\n const rows: string[][] = []\n for (const alert of diff.newAlerts) {\n if (!(alert.next_step_title in nextSteps)) {\n nextSteps[alert.next_step_title] = [alert.description, alert.suggestion]\n }\n const ignore = `\\`SocketSecurity ignore ${alert.purl}\\``\n if (!ignoreCommands.includes(ignore)) {\n ignoreCommands.push(ignore)\n }\n const [manifestStr, sourceStr] = createSources(alert)\n const purlUrl = `[${alert.purl}](${alert.url})`\n if (alert.error) {\n alert.emoji = ':no_entry_sign:'\n } else {\n alert.emoji = ':warning:'\n }\n const row = [alert.title, purlUrl, sourceStr, manifestStr, alert.emoji]\n if (!rows.some(r => r.join() === row.join())) {\n rows.push(row)\n }\n }\n\n let md = ''\n md += `|${alertTable.join('|')}|\\n`\n md += '|---|---|---|---|---|\\n'\n for (const row of rows) {\n md += `|${row.join('|')}|\\n`\n }\n\n return { ignoreCommands, nextSteps, mdTable: md }\n}\n\nexport function createNextSteps(nextSteps: Record<string, string[]>): string {\n let md = ''\n for (const step in nextSteps) {\n const detail = nextSteps[step]!\n md += '<details>\\n'\n md += `<summary>${step}</summary>\\n`\n for (const line of detail) {\n md += `${line}\\n`\n }\n md += '</details>\\n'\n }\n return md\n}\n\nexport function createDeeperLook(): string {\n let md = ''\n md += '<details>\\n'\n md += '<summary>Take a deeper look at the dependency</summary>\\n'\n md +=\n \"Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support [AT] socket [DOT] dev.\\n\"\n md += '</details>\\n'\n return md\n}\n\nexport function createRemovePackage(): string {\n let md = ''\n md += '<details>\\n'\n md += '<summary>Remove the package</summary>\\n'\n md +=\n 'If you happen to install a dependency that Socket reports as [https://socket.dev/npm/issue/malware](Known Malware) you should immediately remove it and select a different dependency. For other alert types, you may may wish to investigate alternative packages or consider if there are other ways to mitigate the specific risk posed by the dependency.\\n'\n md += '</details>\\n'\n return md\n}\n\nexport function createAcceptableRisk(ignoreCommands: string[]): string {\n let md = ''\n md += '<details>\\n'\n md += '<summary>Mark a package as acceptable risk</summary>\\n'\n md +=\n 'To ignore an alert, reply with a comment starting with `SocketSecurity ignore` followed by a space separated list of `ecosystem/package-name@version` specifiers. e.g. `SocketSecurity ignore npm/foo@1.0.0` or ignore all packages with `SocketSecurity ignore-all`\\n'\n md += '<ul>\\n'\n for (const ignore of ignoreCommands) {\n md += `<li>${ignore}</li>\\n`\n }\n md += '</ul>\\n'\n md += '</details>\\n'\n return md\n}\n\nexport function securityCommentTemplate(diff: Diff): string {\n let md = ''\n md += '<!-- socket-security-comment-actions -->\\n'\n md += '# Socket Security: Issues Report\\n'\n md +=\n 'Potential security issues detected. Learn more about [socket.dev](https://socket.dev)\\n'\n md +=\n 'To accept the risk, merge this PR and you will not be notified again.\\n\\n'\n md += '<!-- start-socket-alerts-table -->\\n'\n const { ignoreCommands, mdTable, nextSteps } = createSecurityAlertTable(diff)\n md += mdTable\n md += '<!-- end-socket-alerts-table -->\\n\\n'\n md += createNextSteps(nextSteps)\n md += createDeeperLook()\n md += createRemovePackage()\n md += createAcceptableRisk(ignoreCommands)\n return md.trim()\n}\n","// https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/socketcli.py\n\nimport micromatch from 'micromatch'\nimport { simpleGit } from 'simple-git'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { SocketSdk } from '@socketsecurity/sdk'\n\nimport { Core } from './core'\nimport { GitHub } from './core/github'\nimport * as Messages from './core/messages'\nimport * as SCMComments from './core/scm_comments'\nimport { getDefaultToken } from '../../utils/sdk'\n\n// TODO: is this a github action handler?\nexport async function runAction(\n githubEventBefore: string,\n githubEventAfter: string\n) {\n //TODO\n const socket = new SocketSdk(getDefaultToken()!)\n\n const git = simpleGit()\n const changedFiles = (\n await git.diff(\n process.env['GITHUB_EVENT_NAME'] === 'pull_request'\n ? ['--name-only', 'HEAD^1', 'HEAD']\n : ['--name-only', githubEventBefore, githubEventAfter]\n )\n ).split('\\n')\n\n logger.log({ changedFiles })\n // supportedFiles have 3-level deep globs\n const patterns = Object.values(await socket.getReportSupportedFiles())\n .flatMap((i: Record<string, any>) => Object.values(i))\n .flatMap((i: Record<string, any>) => Object.values(i))\n .flatMap((i: Record<string, any>) => Object.values(i))\n\n const files = micromatch(changedFiles, patterns)\n\n const scm = new GitHub()\n\n if (scm.checkEventType() === 'comment') {\n logger.log('Comment initiated flow')\n const comments = await scm.getCommentsForPR()\n await scm.removeCommentAlerts({ comments })\n } else if (scm.checkEventType() === 'diff') {\n logger.log('Push initiated flow')\n const core = new Core({ owner: scm.owner, repo: scm.repo, files, socket })\n const diff = await core.createNewDiff({})\n const comments = await scm.getCommentsForPR()\n diff.newAlerts = SCMComments.removeAlerts({\n comments,\n newAlerts: diff.newAlerts\n })\n const overviewComment = Messages.dependencyOverviewTemplate(diff)\n const securityComment = Messages.securityCommentTemplate(diff)\n let newSecurityComment = true\n let newOverviewComment = true\n const updateOldSecurityComment = comments.security !== undefined\n const updateOldOverviewComment = comments.overview !== undefined\n if (diff.newAlerts.length === 0) {\n if (!updateOldSecurityComment) {\n newSecurityComment = false\n logger.log('No new alerts or security issue comment disabled')\n } else {\n logger.log('Updated security comment with no new alerts')\n }\n }\n if (diff.newPackages.length === 0 && diff.removedPackages.length === 0) {\n if (!updateOldOverviewComment) {\n newOverviewComment = false\n logger.log(\n 'No new/removed packages or Dependency Overview comment disabled'\n )\n } else {\n logger.log('Updated overview comment with no dependencies')\n }\n }\n await scm.addSocketComments({\n securityComment,\n overviewComment,\n comments,\n newSecurityComment,\n newOverviewComment\n })\n }\n}\n","import process from 'node:process'\n\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { AuthError } from './errors'\nimport constants from '../constants'\n\nimport type {\n SocketSdkErrorType,\n SocketSdkOperations\n} from '@socketsecurity/sdk'\n\nconst { API_V0_URL } = constants\n\nexport function handleUnsuccessfulApiResponse<T extends SocketSdkOperations>(\n _name: T,\n result: SocketSdkErrorType<T>\n) {\n // SocketSdkErrorType['error'] is not typed.\n const resultErrorMessage = (result as { error?: Error }).error?.message\n const message =\n typeof resultErrorMessage === 'string'\n ? resultErrorMessage\n : 'No error message returned'\n if (result.status === 401 || result.status === 403) {\n throw new AuthError(message)\n }\n logger.fail(\n `${colors.bgRed(colors.white('API returned an error:'))} ${message}`\n )\n process.exit(1)\n}\n\nexport async function handleApiCall<T>(\n value: T,\n description: string\n): Promise<T> {\n let result: T\n try {\n result = await value\n } catch (cause) {\n throw new Error(`Failed ${description}`, { cause })\n }\n return result\n}\n\nexport async function handleAPIError(code: number) {\n if (code === 400) {\n return 'One of the options passed might be incorrect.'\n } else if (code === 403) {\n return 'You might be trying to access an organization that is not linked to the API key you are logged in with.'\n }\n}\n\nexport function getLastFiveOfApiToken(token: string): string {\n // Get the last 5 characters of the API token before the trailing \"_api\".\n return token.slice(-9, -4)\n}\n\nexport async function queryAPI(path: string, apiToken: string) {\n return await fetch(`${API_V0_URL}/${path}`, {\n method: 'GET',\n headers: {\n Authorization: `Basic ${btoa(`${apiToken}:${apiToken}`)}`\n }\n })\n}\n","type HelpListOptions = {\n keyPrefix: string\n padName: number\n}\n\ntype ListDescription = string | { description: string }\n\nexport function getFlagListOutput(\n list: Record<string, ListDescription>,\n indent: number,\n { keyPrefix = '--', padName } = {} as HelpListOptions\n): string {\n return getHelpListOutput(\n {\n ...list\n },\n indent,\n { keyPrefix, padName }\n )\n}\n\nexport function getHelpListOutput(\n list: Record<string, ListDescription>,\n indent: number,\n { keyPrefix = '', padName = 18 } = {} as HelpListOptions\n): string {\n let result = ''\n const names = Object.keys(list).sort()\n for (const name of names) {\n const rawDescription = list[name]\n const description =\n (typeof rawDescription === 'object'\n ? rawDescription.description\n : rawDescription) || ''\n result +=\n ''.padEnd(indent) +\n (keyPrefix + name).padEnd(padName) +\n description +\n '\\n'\n }\n return result.trim()\n}\n","import type { Flag } from 'meow'\n\n// TODO: not sure if I'm missing something but meow doesn't seem to expose this?\ntype StringFlag = Flag<'string', string> | Flag<'string', string[], true>\ntype BooleanFlag = Flag<'boolean', boolean> | Flag<'boolean', boolean[], true>\ntype NumberFlag = Flag<'number', number> | Flag<'number', number[], true>\ntype AnyFlag = StringFlag | BooleanFlag | NumberFlag\n\n// Note: we use this description in getFlagListOutput, meow doesn't care\nexport type MeowFlags = Record<string, AnyFlag & { description: string }>\n\nexport const commonFlags: MeowFlags = {\n help: {\n type: 'boolean',\n default: false,\n shortFlag: 'h',\n description: 'Print this help.'\n },\n dryRun: {\n type: 'boolean',\n default: false,\n description: 'Do input validation for a command and exit 0 when input is ok'\n }\n}\n\nexport const outputFlags: MeowFlags = {\n json: {\n type: 'boolean',\n shortFlag: 'j',\n default: false,\n description: 'Output result as json'\n },\n markdown: {\n type: 'boolean',\n shortFlag: 'm',\n default: false,\n description: 'Output result as markdown'\n }\n}\n\nexport const validationFlags: MeowFlags = {\n all: {\n type: 'boolean',\n default: false,\n description: 'Include all issues'\n },\n strict: {\n type: 'boolean',\n default: false,\n description: 'Exits with an error code if any matching issues are found'\n }\n}\n","import path from 'node:path'\n\nimport meow from 'meow'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { toSortedObject } from '@socketsecurity/registry/lib/objects'\nimport { normalizePath } from '@socketsecurity/registry/lib/path'\nimport { escapeRegExp } from '@socketsecurity/registry/lib/regexps'\n\nimport { getLastFiveOfApiToken } from './api'\nimport { getFlagListOutput, getHelpListOutput } from './output-formatting'\nimport { getSetting } from './settings'\nimport constants from '../constants'\nimport { MeowFlags, commonFlags } from '../flags'\n\nimport type { Options } from 'meow'\n\nconst { DRY_RUN_LABEL, REDACTED } = constants\n\ninterface CliAlias {\n description: string\n argv: readonly string[]\n hidden?: boolean | undefined\n}\n\ntype CliAliases = Record<string, CliAlias>\n\ntype CliSubcommandRun = (\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n context: { parentName: string }\n) => Promise<void> | void\n\nexport interface CliSubcommand {\n description: string\n hidden?: boolean | undefined\n run: CliSubcommandRun\n}\n\n// Property names are picked such that the name is at the top when the props\n// get ordered by alphabet while flags is near the bottom and the help text\n// at the bottom, because they tend ot occupy the most lines of code.\nexport interface CliCommandConfig {\n commandName: string // tmp optional while we migrate\n description: string\n hidden: boolean\n flags: MeowFlags // tmp optional while we migrate\n help: (command: string, config: CliCommandConfig) => string\n}\n\ninterface MeowOptions extends Options<any> {\n aliases?: CliAliases | undefined\n argv: readonly string[]\n name: string\n}\n\n// For debugging. Whenever you call meowOrExit it will store the command here\n// This module exports a getter that returns the current value.\nlet lastSeenCommand = ''\n\nexport function getLastSeenCommand(): string {\n return lastSeenCommand\n}\n\nexport async function meowWithSubcommands(\n subcommands: Record<string, CliSubcommand>,\n options: MeowOptions\n): Promise<void> {\n const {\n aliases = {},\n argv,\n importMeta,\n name,\n ...additionalOptions\n } = { __proto__: null, ...options }\n const [commandOrAliasName, ...rawCommandArgv] = argv\n // If we got at least some args, then lets find out if we can find a command.\n if (commandOrAliasName) {\n const alias = aliases[commandOrAliasName]\n // First: Resolve argv data from alias if its an alias that's been given.\n const [commandName, ...commandArgv] = alias\n ? [...alias.argv, ...rawCommandArgv]\n : [commandOrAliasName, ...rawCommandArgv]\n // Second: Find a command definition using that data.\n const commandDefinition = commandName ? subcommands[commandName] : undefined\n // Third: If a valid command has been found, then we run it...\n if (commandDefinition) {\n return await commandDefinition.run(commandArgv, importMeta, {\n parentName: name\n })\n }\n }\n const flags = {\n ...commonFlags,\n ...additionalOptions.flags\n }\n // ...else we provide basic instructions and help.\n\n emitBanner(name)\n\n const cli = meow(\n `\n Usage\n $ ${name} <command>\n\n Commands\n ${getHelpListOutput(\n {\n ...toSortedObject(\n Object.fromEntries(\n Object.entries(subcommands).filter(\n ({ 1: subcommand }) => !subcommand.hidden\n )\n )\n ),\n ...toSortedObject(\n Object.fromEntries(\n Object.entries(aliases).filter(({ 1: alias }) => {\n const { hidden } = alias\n const cmdName = hidden ? '' : alias.argv[0]\n const subcommand = cmdName ? subcommands[cmdName] : undefined\n return subcommand && !subcommand.hidden\n })\n )\n )\n },\n 6\n )}\n\n Options\n ${getFlagListOutput(flags, 6)}\n\n Examples\n $ ${name} --help\n `,\n {\n argv,\n importMeta,\n ...additionalOptions,\n flags,\n autoHelp: false // otherwise we can't exit(0)\n }\n )\n if (!cli.flags['help'] && cli.flags['dryRun']) {\n process.exitCode = 0\n logger.log(`${DRY_RUN_LABEL}: No-op, call a sub-command; ok`)\n } else {\n cli.showHelp()\n }\n}\n\n/**\n * Note: meow will exit immediately if it calls its .showHelp()\n */\nexport function meowOrExit({\n allowUnknownFlags, // commands that pass-through args need to allow this\n argv,\n config,\n importMeta,\n parentName\n}: {\n allowUnknownFlags?: boolean | undefined\n argv: readonly string[]\n config: CliCommandConfig\n parentName: string\n importMeta: ImportMeta\n}) {\n const command = `${parentName} ${config.commandName}`\n lastSeenCommand = command\n\n emitBanner(command)\n\n // This exits if .printHelp() is called either by meow itself or by us.\n const cli = meow({\n argv,\n description: config.description,\n help: config.help(command, config),\n importMeta,\n flags: config.flags,\n allowUnknownFlags: Boolean(allowUnknownFlags),\n autoHelp: false // otherwise we can't exit(0)\n })\n if (cli.flags['help']) {\n cli.showHelp()\n }\n return cli\n}\n\nexport function emitBanner(name: string) {\n // Print a banner at the top of each command.\n // This helps with brand recognition and marketing.\n // It also helps with debugging since it contains version and command details.\n // Note: print over stderr to preserve stdout for flags like --json and\n // --markdown. If we don't do this, you can't use --json in particular\n // and pipe the result to other tools. By emiting the banner over stderr\n // you can do something like `socket scan view xyz | jq | process`.\n // The spinner also emits over stderr for example.\n logger.error(getAsciiHeader(name))\n}\n\nfunction getAsciiHeader(command: string) {\n // Note: In tests we return <redacted> because otherwise snapshots will fail.\n // The '@rollup/plugin-replace' will replace \"process.env['SOCKET_CLI_VERSION_HASH']\".\n const redacting = process.env['VITEST']\n const cliVersion = redacting\n ? REDACTED\n : // The '@rollup/plugin-replace' will replace \"process.env['SOCKET_CLI_VERSION_HASH']\".\n process.env['SOCKET_CLI_VERSION_HASH']\n const nodeVersion = redacting ? REDACTED : process.version\n const apiToken = getSetting('apiToken')\n const shownToken = redacting\n ? REDACTED\n : apiToken\n ? getLastFiveOfApiToken(apiToken)\n : 'no'\n const relCwd = redacting\n ? REDACTED\n : normalizePath(\n process\n .cwd()\n .replace(\n new RegExp(\n `^${escapeRegExp(constants.homePath)}(?:${path.sep}|$)`,\n 'i'\n ),\n '~/'\n )\n )\n const body = `\n _____ _ _ /---------------\n | __|___ ___| |_ ___| |_ | Socket.dev CLI ver ${cliVersion}\n |__ | . | _| '_| -_| _| | Node: ${nodeVersion}, API token set: ${shownToken}\n |_____|___|___|_,_|___|_|.dev | Command: \\`${command}\\`, cwd: ${relCwd}`.trimStart()\n return ` ${body}\\n`\n}\n","// https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/socketcli.py\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { runAction } from './run-action'\nimport constants from '../../constants'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'action',\n description: 'Socket action command', // GitHub Action ?\n hidden: true,\n flags: {\n // This flag is unused\n // socketSecurityApiKey: { // deprecate this asap.\n // type: 'string',\n // default: 'env var SOCKET_SECURITY_API_KEY',\n // description: 'Socket API token'\n // },\n githubEventBefore: {\n type: 'string',\n default: '',\n description: 'Before marker'\n },\n githubEventAfter: {\n type: 'string',\n default: '',\n description: 'After marker'\n }\n },\n help: (command, { flags }) => `\n Usage\n $ ${command} [options]\n\n Options\n ${getFlagListOutput(flags, 6)}\n `\n}\n\nexport const cmdAction = {\n description: config.description,\n hidden: config.hidden,\n run: run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const githubEventBefore = String(cli.flags['githubEventBefore'] || '')\n const githubEventAfter = String(cli.flags['githubEventAfter'] || '')\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await runAction(githubEventBefore, githubEventAfter)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { setupSdk } from '../../utils/sdk'\n\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\nimport type { SocketSdkReturnType } from '@socketsecurity/sdk'\n\nexport async function fetchOrgAnalyticsData(\n time: number,\n spinner: Spinner,\n apiToken: string\n): Promise<SocketSdkReturnType<'getOrgAnalytics'>['data'] | undefined> {\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getOrgAnalytics(time.toString()),\n 'fetching analytics data'\n )\n\n if (result.success === false) {\n handleUnsuccessfulApiResponse('getOrgAnalytics', result)\n return undefined\n }\n\n spinner.stop()\n\n if (!result.data.length) {\n logger.log('No analytics data is available for this organization yet.')\n return undefined\n }\n\n return result.data\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { setupSdk } from '../../utils/sdk'\n\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\nimport type { SocketSdkReturnType } from '@socketsecurity/sdk'\n\nexport async function fetchRepoAnalyticsData(\n repo: string,\n time: number,\n spinner: Spinner,\n apiToken: string\n): Promise<SocketSdkReturnType<'getRepoAnalytics'>['data'] | undefined> {\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getRepoAnalytics(repo, time.toString()),\n 'fetching analytics data'\n )\n\n if (result.success === false) {\n handleUnsuccessfulApiResponse('getRepoAnalytics', result)\n return undefined\n }\n\n spinner.stop()\n\n if (!result.data.length) {\n logger.log('No analytics data is available for this organization yet.')\n return undefined\n }\n\n return result.data\n}\n","export function mdTableStringNumber(\n title1: string,\n title2: string,\n obj: Record<string, number | string>\n): string {\n // | Date | Counts |\n // | ----------- | ------ |\n // | Header | 201464 |\n // | Paragraph | 18 |\n let mw1 = title1.length\n let mw2 = title2.length\n for (const [key, value] of Object.entries(obj)) {\n mw1 = Math.max(mw1, key.length)\n mw2 = Math.max(mw2, String(value ?? '').length)\n }\n\n const lines = []\n lines.push(`| ${title1.padEnd(mw1, ' ')} | ${title2.padEnd(mw2)} |`)\n lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)\n for (const [key, value] of Object.entries(obj)) {\n lines.push(\n `| ${key.padEnd(mw1, ' ')} | ${String(value ?? '').padStart(mw2, ' ')} |`\n )\n }\n lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)\n\n return lines.join('\\n')\n}\n\nexport function mdTable<T extends Array<Record<string, string>>>(\n logs: T,\n // This is saying \"an array of strings and the strings are a valid key of elements of T\"\n // In turn, T is defined above as the audit log event type from our OpenAPI docs.\n cols: Array<string & keyof T[number]>\n): string {\n // Max col width required to fit all data in that column\n const cws = cols.map(col => col.length)\n\n for (const log of logs) {\n for (let i = 0; i < cols.length; ++i) {\n // @ts-ignore\n const val: unknown = log[cols[i] ?? ''] ?? ''\n cws[i] = Math.max(cws[i] ?? 0, String(val).length)\n }\n }\n\n let div = '|'\n for (const cw of cws) div += ' ' + '-'.repeat(cw) + ' |'\n\n let header = '|'\n for (let i = 0; i < cols.length; ++i)\n header += ' ' + String(cols[i]).padEnd(cws[i] ?? 0, ' ') + ' |'\n\n let body = ''\n for (const log of logs) {\n body += '|'\n for (let i = 0; i < cols.length; ++i) {\n // @ts-ignore\n const val: unknown = log[cols[i] ?? ''] ?? ''\n body += ' ' + String(val).padEnd(cws[i] ?? 0, ' ') + ' |'\n }\n body += '\\n'\n }\n\n return [div, header, div, body.trim(), div].filter(s => !!s.trim()).join('\\n')\n}\n","import fs from 'node:fs/promises'\n\n// @ts-ignore\nimport ScreenWidget from 'blessed/lib/widgets/screen'\nimport contrib from 'blessed-contrib'\nimport { stripIndents } from 'common-tags'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { fetchOrgAnalyticsData } from './fetch-org-analytics'\nimport { fetchRepoAnalyticsData } from './fetch-repo-analytics'\nimport constants from '../../constants'\nimport { AuthError } from '../../utils/errors'\nimport { mdTableStringNumber } from '../../utils/markdown'\nimport { getDefaultToken } from '../../utils/sdk'\n\nimport type { SocketSdkReturnType } from '@socketsecurity/sdk'\nimport type { Widgets } from 'blessed' // Note: Widgets does not seem to actually work as code :'(\n\ninterface 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\nconst METRICS = [\n 'total_critical_alerts',\n 'total_high_alerts',\n 'total_medium_alerts',\n 'total_low_alerts',\n 'total_critical_added',\n 'total_medium_added',\n 'total_low_added',\n 'total_high_added',\n 'total_critical_prevented',\n 'total_high_prevented',\n 'total_medium_prevented',\n 'total_low_prevented'\n] as const\n\n// Note: This maps `new Date(date).getMonth()` to English three letters\nconst Months = [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec'\n] as const\n\nexport async function displayAnalytics({\n filePath,\n outputKind,\n repo,\n scope,\n time\n}: {\n scope: string\n time: number\n repo: string\n outputKind: 'json' | 'markdown' | 'print'\n filePath: string\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API token.'\n )\n }\n\n await outputAnalyticsWithToken({\n apiToken,\n filePath,\n outputKind,\n repo,\n scope,\n time\n })\n}\n\nasync function outputAnalyticsWithToken({\n apiToken,\n filePath,\n outputKind,\n repo,\n scope,\n time\n}: {\n apiToken: string\n scope: string\n time: number\n repo: string\n outputKind: 'json' | 'markdown' | 'print'\n filePath: string\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Fetching analytics data')\n\n let data:\n | undefined\n | SocketSdkReturnType<'getOrgAnalytics'>['data']\n | SocketSdkReturnType<'getRepoAnalytics'>['data']\n if (scope === 'org') {\n data = await fetchOrgAnalyticsData(time, spinner, apiToken)\n } else if (repo) {\n data = await fetchRepoAnalyticsData(repo, time, spinner, apiToken)\n }\n\n // A message should already have been printed if we have no data here\n if (!data) return\n\n if (outputKind === 'json') {\n const serialized = renderJson(data)\n if (!serialized) return\n\n if (filePath && filePath !== '-') {\n try {\n await fs.writeFile(filePath, serialized, 'utf8')\n logger.log(`Data successfully written to ${filePath}`)\n } catch (e) {\n process.exitCode = 1\n logger.fail('There was an error trying to write the json to disk')\n logger.error(e)\n }\n } else {\n logger.log(serialized)\n }\n } else {\n const fdata = scope === 'org' ? formatDataOrg(data) : formatDataRepo(data)\n\n if (outputKind === 'markdown') {\n const serialized = renderMarkdown(fdata, time, repo)\n\n if (filePath && filePath !== '-') {\n try {\n await fs.writeFile(filePath, serialized, 'utf8')\n logger.log(`Data successfully written to ${filePath}`)\n } catch (e) {\n logger.error(e)\n }\n } else {\n logger.log(serialized)\n }\n } else {\n displayAnalyticsScreen(fdata)\n }\n }\n}\n\nfunction renderJson(data: unknown): string | undefined {\n try {\n return JSON.stringify(data, null, 2)\n } catch (e) {\n process.exitCode = 1\n // This could be caused by circular references, which is an \"us\" problem\n logger.fail(\n 'There was a problem converting the data set to JSON. Please try without --json or with --markdown'\n )\n return\n }\n}\n\nfunction renderMarkdown(\n data: FormattedData,\n days: number,\n repoSlug: string\n): string {\n return stripIndents`\n# Socket Alert Analytics\n\nThese are the Socket.dev stats are 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(\n ([title, table]) =>\n stripIndents`\n## ${title}\n\n${table}\n`\n )\n .join('\\n\\n')}\n\n## Top 5 alert types\n\n${mdTableStringNumber('Name', 'Counts', data['top_five_alert_types'])}\n`\n}\n\nfunction displayAnalyticsScreen(data: FormattedData): void {\n const screen: Widgets.Screen = new ScreenWidget({})\n const grid = new contrib.grid({ 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 bar = grid.set(4, 0, 1, 2, contrib.bar, {\n label: 'Top 5 alert types',\n barWidth: 10,\n barSpacing: 17,\n xOffset: 0,\n maxHeight: 9,\n barBgColor: 'magenta'\n })\n\n screen.append(bar) //must append before setting data\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\n screen.key(['escape', 'q', 'C-c'], () => process.exit(0))\n}\n\nfunction formatDataRepo(\n data: SocketSdkReturnType<'getRepoAnalytics'>['data']\n): FormattedData {\n const sortedTopFiveAlerts: Record<string, number> = {}\n const totalTopAlerts: Record<string, number> = {}\n\n const formattedData = {} as Omit<FormattedData, 'top_five_alert_types'>\n for (const metric of METRICS) {\n formattedData[metric] = {}\n }\n\n for (const entry of data) {\n const topFiveAlertTypes = entry['top_five_alert_types']\n for (const type of Object.keys(topFiveAlertTypes)) {\n const count = topFiveAlertTypes[type] ?? 0\n if (!totalTopAlerts[type]) {\n totalTopAlerts[type] = count\n } else if (count > (totalTopAlerts[type] ?? 0)) {\n totalTopAlerts[type] = count\n }\n }\n }\n for (const entry of data) {\n for (const metric of METRICS) {\n formattedData[metric]![formatDate(entry['created_at'])] = entry[metric]\n }\n }\n\n const topFiveAlertEntries = Object.entries(totalTopAlerts)\n .sort(([_keya, a], [_keyb, b]) => b - a)\n .slice(0, 5)\n for (const [key, value] of topFiveAlertEntries) {\n sortedTopFiveAlerts[key] = value\n }\n\n return {\n ...formattedData,\n top_five_alert_types: sortedTopFiveAlerts\n }\n}\n\nfunction formatDataOrg(\n data: SocketSdkReturnType<'getOrgAnalytics'>['data']\n): FormattedData {\n const sortedTopFiveAlerts: Record<string, number> = {}\n const totalTopAlerts: Record<string, number> = {}\n\n const formattedData = {} as Omit<FormattedData, 'top_five_alert_types'>\n for (const metric of METRICS) {\n formattedData[metric] = {}\n }\n\n for (const entry of data) {\n const topFiveAlertTypes = entry['top_five_alert_types']\n for (const type of Object.keys(topFiveAlertTypes)) {\n const count = topFiveAlertTypes[type] ?? 0\n if (!totalTopAlerts[type]) {\n totalTopAlerts[type] = count\n } else {\n totalTopAlerts[type] += count\n }\n }\n }\n\n for (const metric of METRICS) {\n const formatted = formattedData[metric]\n for (const entry of data) {\n const date = formatDate(entry['created_at'])\n if (!formatted[date]) {\n formatted[date] = entry[metric]!\n } else {\n formatted[date] += entry[metric]!\n }\n }\n }\n\n const topFiveAlertEntries = Object.entries(totalTopAlerts)\n .sort(([_keya, a], [_keyb, b]) => b - a)\n .slice(0, 5)\n for (const [key, value] of topFiveAlertEntries) {\n sortedTopFiveAlerts[key] = value\n }\n\n return {\n ...formattedData,\n top_five_alert_types: sortedTopFiveAlerts\n }\n}\n\nfunction formatDate(date: string): string {\n return `${Months[new Date(date).getMonth()]} ${new Date(date).getDate()}`\n}\n\nfunction renderLineCharts(\n grid: contrib.grid,\n screen: Widgets.Screen,\n title: string,\n coords: number[],\n data: Record<string, number>\n): void {\n const line = grid.set(...coords, contrib.line, {\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 { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { displayAnalytics } from './display-analytics'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'analytics',\n description: `Look up analytics data`,\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n file: {\n type: 'string',\n shortFlag: 'f',\n default: '-',\n description:\n 'Path to a local file to save the output. Only valid with --json/--markdown. Defaults to stdout.'\n },\n repo: {\n type: 'string',\n shortFlag: 'r',\n default: '',\n description: 'Name of the repository. Only valid when scope=repo'\n },\n scope: {\n type: 'string',\n shortFlag: 's',\n default: 'org',\n description:\n \"Scope of the analytics data - either 'org' or 'repo', default: org\"\n },\n time: {\n type: 'number',\n shortFlag: 't',\n default: 7,\n description: 'Time filter - either 7, 30 or 90, default: 7'\n }\n },\n help: (command, { flags }) => `\n Usage\n $ ${command} --scope=<scope> --time=<time filter>\n\n Default parameters are set to show the organization-level analytics over the\n last 7 days.\n\n Options\n ${getFlagListOutput(flags, 6)}\n\n Examples\n $ ${command} --scope=org --time=7\n $ ${command} --scope=org --time=30\n $ ${command} --scope=repo --repo=test-repo --time=30\n `\n}\n\nexport const cmdAnalytics = {\n description: config.description,\n hidden: config.hidden,\n run: run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const { file, json, markdown, repo, scope, time } = cli.flags\n\n const badScope = scope !== 'org' && scope !== 'repo'\n const badTime = time !== 7 && time !== 30 && time !== 90\n const badRepo = scope === 'repo' && !repo\n const badFile = file !== '-' && !json && !markdown\n const badFlags = json && markdown\n\n if (badScope || badTime || badRepo || badFile || badFlags) {\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 stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Scope must be \"repo\" or \"org\" ${badScope ? colors.red('(bad!)') : colors.green('(ok)')}\n\n - The time filter must either be 7, 30 or 90 ${badTime ? colors.red('(bad!)') : colors.green('(ok)')}\n\n ${scope === 'repo' ? `- Repository name using --repo when scope is \"repo\" ${badRepo ? colors.red('(bad!)') : colors.green('(ok)')}` : ''}\n\n ${badFlags ? `- The \\`--json\\` and \\`--markdown\\` flags can not be used at the same time ${badFlags ? colors.red('(bad!)') : colors.green('(ok)')}` : ''}\n\n ${badFile ? `- The \\`--file\\` flag is only valid when using \\`--json\\` or \\`--markdown\\` ${badFile ? colors.red('(bad!)') : colors.green('(ok)')}` : ''}\n `\n .split('\\n')\n .filter(s => !!s.trim())\n .join('\\n')\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n return await displayAnalytics({\n scope,\n time,\n repo: String(repo || ''),\n outputKind: json ? 'json' : markdown ? 'markdown' : 'print',\n filePath: String(file || '')\n })\n}\n","import { stripIndents } from 'common-tags'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { Separator, select } from '@socketsecurity/registry/lib/prompts'\nimport { SocketSdkReturnType } from '@socketsecurity/sdk'\n\nimport constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { mdTable } from '../../utils/markdown'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nimport type { Choice } from '@socketsecurity/registry/lib/prompts'\n\ntype AuditChoice = Choice<string>\n\ntype AuditChoices = Array<Separator | AuditChoice>\n\nexport async function getAuditLog({\n logType,\n orgSlug,\n outputKind,\n page,\n perPage\n}: {\n outputKind: 'json' | 'markdown' | 'print'\n orgSlug: string\n page: number\n perPage: number\n logType: string\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n const auditLogs = await getAuditLogWithToken({\n apiToken,\n orgSlug,\n outputKind,\n page,\n perPage,\n logType\n })\n if (!auditLogs) return\n\n if (outputKind === 'json')\n await outputAsJson(auditLogs.results, orgSlug, logType, page, perPage)\n else if (outputKind === 'markdown')\n await outputAsMarkdown(auditLogs.results, orgSlug, logType, page, perPage)\n else await outputAsPrint(auditLogs.results, orgSlug, logType)\n}\n\nasync function outputAsJson(\n auditLogs: SocketSdkReturnType<'getAuditLogEvents'>['data']['results'],\n orgSlug: string,\n logType: string,\n page: number,\n perPage: number\n): Promise<void> {\n let json\n try {\n json = JSON.stringify(\n {\n desc: 'Audit logs for given query',\n generated: new Date().toISOString(),\n org: orgSlug,\n logType,\n page,\n perPage,\n logs: auditLogs.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 null,\n 2\n )\n } catch (e) {\n process.exitCode = 1\n logger.fail(\n 'There was a problem converting the logs to JSON, please try without the `--json` flag'\n )\n return\n }\n\n logger.log(json)\n}\n\nasync function outputAsMarkdown(\n auditLogs: SocketSdkReturnType<'getAuditLogEvents'>['data']['results'],\n orgSlug: string,\n logType: string,\n page: number,\n perPage: number\n): Promise<void> {\n try {\n const table = mdTable<any>(auditLogs, [\n 'event_id',\n 'created_at',\n 'type',\n 'user_email',\n 'ip_address',\n 'user_agent'\n ])\n\n logger.log(\n stripIndents`\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- per page: ${perPage}\n- generated: ${new Date().toISOString()}\n\n${table}\n`\n )\n } catch (e) {\n process.exitCode = 1\n logger.fail(\n 'There was a problem converting the logs to JSON, please try without the `--json` flag'\n )\n logger.error(e)\n return\n }\n}\n\nasync function outputAsPrint(\n auditLogs: SocketSdkReturnType<'getAuditLogEvents'>['data']['results'],\n orgSlug: string,\n logType: string\n): Promise<void> {\n const data: AuditChoices = []\n const logDetails: { [key: string]: string } = {}\n\n for (const d of auditLogs) {\n const { created_at } = d\n if (created_at) {\n const name = `${new Date(created_at).toLocaleDateString('en-us', { year: 'numeric', month: 'numeric', day: 'numeric' })} - ${d.user_email} - ${d.type} - ${d.ip_address} - ${d.user_agent}`\n data.push({ name } as AuditChoice, new Separator())\n logDetails[name] = JSON.stringify(d.payload)\n }\n }\n\n logger.log(\n logDetails[\n (await select({\n message: logType\n ? `\\n Audit log for: ${orgSlug} with type: ${logType}\\n`\n : `\\n Audit log for: ${orgSlug}\\n`,\n choices: data,\n pageSize: 30\n })) as any\n ]\n )\n}\n\nasync function getAuditLogWithToken({\n apiToken,\n logType,\n orgSlug,\n outputKind,\n page,\n perPage\n}: {\n apiToken: string\n outputKind: 'json' | 'markdown' | 'print'\n orgSlug: string\n page: number\n perPage: number\n logType: string\n}): Promise<SocketSdkReturnType<'getAuditLogEvents'>['data'] | void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start(`Looking up audit log for ${orgSlug}`)\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getAuditLogEvents(orgSlug, {\n outputJson: outputKind === 'json', // I'm not sure this is used at all\n outputMarkdown: outputKind === 'markdown', // I'm not sure this is used at all\n orgSlug,\n type: logType,\n page,\n per_page: perPage\n }),\n `Looking up audit log for ${orgSlug}\\n`\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('getAuditLogEvents', result)\n return\n }\n\n spinner.stop()\n\n return result.data\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getAuditLog } from './get-audit-log'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'audit-log',\n description: 'Look up the audit log for an organization',\n hidden: false,\n flags: {\n type: {\n type: 'string',\n shortFlag: 't',\n default: '',\n description: 'Type of log event'\n },\n perPage: {\n type: 'number',\n shortFlag: 'pp',\n default: 30,\n description: 'Results per page - default is 30'\n },\n page: {\n type: 'number',\n shortFlag: 'p',\n default: 1,\n description: 'Page number - default is 1'\n },\n ...commonFlags,\n ...outputFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <org slug>\n\n This feature requires an Enterprise Plan. To learn more about getting access\n to this feature and many more, please visit https://socket.dev/pricing\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg\n `\n}\n\nexport const cmdAuditLog = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const { json, markdown, page, perPage, type } = cli.flags\n\n const logType = String(type || '')\n const [orgSlug = ''] = cli.input\n\n if (!orgSlug) {\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 stripIndents`\n ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n `\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await getAuditLog({\n orgSlug,\n outputKind: json ? 'json' : markdown ? 'markdown' : 'print',\n page: Number(page || 0),\n perPage: Number(perPage || 0),\n logType: logType.charAt(0).toUpperCase() + logType.slice(1)\n })\n}\n","import { promises as fs } from 'fs'\nimport { existsSync } from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\n\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport shadowBin from '../../shadow/npm/bin'\n\nconst { NPM, NPX, PNPM } = 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 async function runCycloneDX(yargv: any) {\n let cleanupPackageLock = false\n if (\n yargv.type !== 'yarn' &&\n nodejsPlatformTypes.has(yargv.type) &&\n existsSync('./yarn.lock')\n ) {\n if (existsSync('./package-lock.json')) {\n yargv.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 await shadowBin(\n NPX,\n ['synp@1.9.14', '--', '--source-file', './yarn.lock'],\n 2\n )\n yargv.type = NPM\n cleanupPackageLock = true\n } catch {}\n }\n }\n await shadowBin(\n NPX,\n ['@cyclonedx/cdxgen@11.2.0', '--', ...argvToArray(yargv)],\n 2\n )\n if (cleanupPackageLock) {\n try {\n await fs.rm('./package-lock.json')\n } catch {}\n }\n const fullOutputPath = path.join(process.cwd(), yargv.output)\n if (existsSync(fullOutputPath)) {\n logger.log(colors.cyanBright(`${yargv.output} created!`))\n }\n}\n\nfunction argvToArray(argv: {\n [key: string]: boolean | null | number | string | Array<string | number>\n}): string[] {\n if (argv['help']) return ['--help']\n const result = []\n for (const { 0: key, 1: value } of Object.entries(argv)) {\n if (key === '_' || key === '--') continue\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 if (argv['--']) {\n result.push('--', ...(argv as any)['--'])\n }\n return result\n}\n","// import { meowOrExit } from '../../utils/meow-with-subcommands'\nimport process from 'node:process'\n\nimport yargsParse from 'yargs-parser'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { runCycloneDX } from './run-cyclonedx'\nimport constants from '../../constants'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\n// TODO: convert yargs to meow. Or convert all the other things to yargs.\nconst toLower = (arg: string) => arg.toLowerCase()\nconst arrayToLower = (arg: string[]) => arg.map(toLower)\n\nconst yargsConfig = {\n configuration: {\n 'camel-case-expansion': false,\n 'strip-aliased': true,\n 'parse-numbers': false,\n 'populate--': true,\n 'unknown-options-as-args': true\n },\n coerce: {\n author: arrayToLower,\n filter: arrayToLower,\n only: arrayToLower,\n profile: toLower,\n standard: arrayToLower,\n type: toLower\n },\n default: {\n //author: ['OWASP Foundation'],\n //'auto-compositions': true,\n //babel: true,\n //evidence: false,\n //'include-crypto': false,\n //'include-formulation': false,\n\n // Default 'install-deps' to `false` and 'lifecycle' to 'pre-build' to\n // sidestep arbitrary code execution during a cdxgen scan.\n // https://github.com/CycloneDX/cdxgen/issues/1328\n 'install-deps': false,\n lifecycle: 'pre-build',\n\n //output: 'bom.json',\n //profile: 'generic',\n //'project-version': '',\n //recurse: true,\n //'server-host': '127.0.0.1',\n //'server-port': '9090',\n //'spec-version': '1.5',\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: 'filter', type: 'string' },\n { key: 'only', type: 'string' },\n { key: 'standard', type: 'string' }\n ],\n boolean: [\n 'auto-compositions',\n 'babel',\n 'deep',\n 'evidence',\n 'fail-on-error',\n 'generate-key-and-sign',\n 'help',\n 'include-formulation',\n 'include-crypto',\n 'install-deps',\n 'print',\n 'required-only',\n 'server',\n 'validate',\n 'version'\n ],\n string: [\n 'api-key',\n 'lifecycle',\n 'output',\n 'parent-project-id',\n 'profile',\n 'project-group',\n 'project-name',\n 'project-version',\n 'project-id',\n 'server-host',\n 'server-port',\n 'server-url',\n 'spec-version'\n ]\n}\n\nconst config: CliCommandConfig = {\n commandName: 'cdxgen',\n description: 'Create an SBOM with CycloneDX generator (cdxgen)',\n hidden: false,\n flags: {\n // TODO: convert from yargsConfig\n },\n help: (command, config) => `\n Usage\n $ ${command} [options]\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n `\n}\n\nexport const cmdCdxgen = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n allowUnknownFlags: true,\n argv: argv.filter(s => s !== '--help' && s !== '-h'), // Don't let meow take over --help\n config,\n importMeta,\n parentName\n })\n //\n //\n // if (cli.input.length)\n // logger.fail(\n // stripIndents`\n // ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n //\n // - Unexpected arguments\n // `)\n // config.help(parentName, config)\n // return\n // }\n\n // TODO: convert to meow\n const yargv = {\n ...yargsParse(argv as string[], yargsConfig)\n } as any // as Record<string, unknown>;\n\n const unknown: string[] = yargv._\n const { length: unknownLength } = unknown\n if (unknownLength) {\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', unknownLength)}: ${yargv._.join(', ')}`\n )\n return\n }\n\n if (yargv.output === undefined) {\n yargv.output = 'socket-cdx.json'\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await runCycloneDX(yargv)\n}\n","// @ts-ignore\nimport chalkTable from 'chalk-table'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function findDependencies({\n limit,\n offset,\n outputJson\n}: {\n outputJson: boolean\n limit: number\n offset: number\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Searching dependencies...')\n\n const socketSdk = await setupSdk(apiToken)\n\n const result = await handleApiCall(\n socketSdk.searchDependencies({ limit, offset }),\n 'Searching dependencies'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('searchDependencies', result)\n return\n }\n\n spinner.stop('Organization dependencies:')\n\n if (outputJson) {\n logger.log(result.data)\n return\n }\n\n logger.log(\n 'Request details: Offset:',\n offset,\n ', limit:',\n limit,\n ', is there more data after this?',\n result.data.end ? 'no' : 'yes'\n )\n\n const options = {\n columns: [\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: 'type', name: colors.cyan('Type') },\n { field: 'direct', name: colors.cyan('Direct') }\n ]\n }\n\n logger.log(chalkTable(options, result.data.rows))\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { findDependencies } from './find-dependencies'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'dependencies',\n description:\n 'Search for any dependency that is being used in your organization',\n hidden: false,\n flags: {\n ...commonFlags,\n limit: {\n type: 'number',\n shortFlag: 'l',\n default: 50,\n description: 'Maximum number of dependencies returned'\n },\n offset: {\n type: 'number',\n shortFlag: 'o',\n default: 0,\n description: 'Page number'\n },\n ...outputFlags\n },\n help: (command, config) => `\n Usage\n ${command}\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n ${command} --limit 20 --offset 10\n `\n}\n\nexport const cmdScanCreate = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n // TODO: markdown flag is ignored\n await findDependencies({\n limit: Number(cli.flags['limit'] || 0) || 0,\n offset: Number(cli.flags['offset'] || 0) || 0,\n outputJson: Boolean(cli.flags['json'])\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'\nimport { SocketSdkReturnType } from '@socketsecurity/sdk'\n\nimport constants from '../../constants'\nimport { handleAPIError, handleApiCall, queryAPI } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken } from '../../utils/sdk'\n\nexport async function getDiffScan({\n after,\n before,\n depth,\n file,\n orgSlug,\n outputJson\n}: {\n after: string\n before: string\n depth: number\n file: string\n orgSlug: string\n outputJson: boolean\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await getDiffScanWithToken({\n after,\n before,\n depth,\n file,\n orgSlug,\n outputJson,\n apiToken\n })\n}\nexport async function getDiffScanWithToken({\n after,\n apiToken,\n before,\n depth,\n file,\n orgSlug,\n outputJson\n}: {\n after: string\n apiToken: string\n depth: number\n before: string\n file: string\n orgSlug: string\n outputJson: boolean\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Getting diff scan...')\n\n const response = await queryAPI(\n `orgs/${orgSlug}/full-scans/diff?before=${encodeURIComponent(before)}&after=${encodeURIComponent(after)}`,\n apiToken\n )\n\n if (!response.ok) {\n const err = await handleAPIError(response.status)\n spinner.errorAndStop(\n `${colors.bgRed(colors.white(response.statusText))}: ${err}`\n )\n return\n }\n\n const result = await handleApiCall(\n (await response.json()) as Promise<\n SocketSdkReturnType<'GetOrgDiffScan'>['data']\n >,\n 'Deserializing json'\n )\n\n spinner.stop()\n\n const dashboardUrl = (result as any)?.['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 (outputJson || file) {\n let json\n try {\n json = JSON.stringify(result, null, 2)\n } catch (e) {\n process.exitCode = 1\n // Most likely caused by a circular reference (or OOM)\n logger.fail('There was a problem converting the data to JSON')\n logger.error(e)\n return\n }\n\n if (file && file !== '-') {\n logger.log(`Writing json to \\`${file}\\``)\n fs.writeFile(file, JSON.stringify(result, null, 2), err => {\n if (err) {\n logger.fail(`Writing to \\`${file}\\` failed...`)\n logger.error(err)\n } else {\n logger.log(`Data successfully written to \\`${file}\\``)\n }\n logger.error(dashboardMessage)\n })\n } else {\n // TODO: expose different method for writing to stderr when simply dodging stdout\n logger.error(`\\n Diff scan result: \\n`)\n logger.log(json)\n logger.error(dashboardMessage)\n }\n\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, {\n showHidden: false,\n depth: depth > 0 ? depth : null,\n colors: true,\n maxArrayLength: null\n })\n )\n logger.log(\n `\\n 📝 To display the detailed report in the terminal, use the --json flag \\n`\n )\n logger.log(dashboardMessage)\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getDiffScan } from './get-diff-scan'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'get',\n description: 'Get a diff scan for an organization',\n hidden: false,\n flags: {\n ...commonFlags,\n after: {\n type: 'string',\n shortFlag: 'a',\n default: '',\n description: 'The full scan ID of the head scan'\n },\n before: {\n type: 'string',\n shortFlag: 'b',\n default: '',\n description: 'The full scan ID of the base scan'\n },\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 json: {\n type: 'boolean',\n shortFlag: 'j',\n default: false,\n description:\n 'Output result as json. This can be big. Use --file to store it to disk without truncation.'\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 },\n help: (command, config) => `\n Usage\n $ ${command} <org slug> --before=<before> --after=<after>\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 to be further analyzed by other tools.\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeCorp --before=aaa0aa0a-aaaa-0000-0a0a-0000000a00a0 --after=aaa1aa1a-aaaa-1111-1a1a-1111111a11a1\n `\n}\n\nexport const cmdDiffScanGet = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const before = String(cli.flags['before'] || '')\n const after = String(cli.flags['after'] || '')\n const [orgSlug = ''] = cli.input\n\n if (!before || !after || cli.input.length < 1) {\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(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\\n\n - Specify a before and after full scan ID ${!before && !after ? colors.red('(missing before and after!)') : !before ? colors.red('(missing before!)') : !after ? colors.red('(missing after!)') : colors.green('(ok)')}\\n\n - To get full scans IDs, you can run the command \"socket scan list <your org slug>\".\n The args are expecting a full \\`aaa0aa0a-aaaa-0000-0a0a-0000000a00a0\\` ID.\\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\\n`)\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await getDiffScan({\n outputJson: Boolean(cli.flags['json']),\n before,\n after,\n depth: Number(cli.flags['depth']),\n orgSlug,\n file: String(cli.flags['file'] || '')\n })\n}\n","import { cmdDiffScanGet } from './cmd-diff-scan-get'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands'\n\nconst description = 'Diff scans related commands'\n\nexport const cmdDiffScan: 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: true,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n get: cmdDiffScanGet\n },\n {\n argv,\n description,\n importMeta,\n name: parentName + ' diff-scan'\n }\n )\n }\n}\n","import { getManifestData } from '@socketsecurity/registry'\nimport { runScript } from '@socketsecurity/registry/lib/npm'\nimport {\n fetchPackagePackument,\n readPackageJson\n} from '@socketsecurity/registry/lib/packages'\n\nimport constants from '../../constants'\nimport {\n Arborist,\n SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES,\n SafeArborist\n} from '../../shadow/npm/arborist/lib/arborist'\nimport {\n findPackageNodes,\n getAlertsMapFromArborist,\n updateNode\n} from '../../utils/lockfile/package-lock-json'\nimport { getCveInfoByAlertsMap } from '../../utils/socket-package-alert'\n\nimport type { SafeNode } from '../../shadow/npm/arborist/lib/node'\nimport type { EnvDetails } from '../../utils/package-environment'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nconst { NPM } = constants\n\nfunction isTopLevel(tree: SafeNode, node: SafeNode): boolean {\n return tree.children.get(node.name) === node\n}\n\ntype NpmFixOptions = {\n spinner?: Spinner | undefined\n}\n\nexport async function npmFix(\n _pkgEnvDetails: EnvDetails,\n cwd: string,\n options?: NpmFixOptions | undefined\n) {\n const { spinner } = { __proto__: null, ...options } as NpmFixOptions\n\n spinner?.start()\n\n const arb = new SafeArborist({\n path: cwd,\n ...SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES\n })\n\n await arb.reify()\n\n const alertsMap = await getAlertsMapFromArborist(arb, {\n consolidate: true,\n include: {\n existing: true,\n unfixable: false,\n upgrade: false\n }\n })\n\n const infoByPkg = getCveInfoByAlertsMap(alertsMap)\n if (!infoByPkg) {\n spinner?.stop()\n return\n }\n\n await arb.buildIdealTree()\n\n const editablePkgJson = await readPackageJson(cwd, { editable: true })\n\n for (const { 0: name, 1: infos } of infoByPkg) {\n const revertToIdealTree = arb.idealTree!\n arb.idealTree = null\n // eslint-disable-next-line no-await-in-loop\n await arb.buildIdealTree()\n\n const tree = arb.idealTree!\n\n const hasUpgrade = !!getManifestData(NPM, name)\n if (hasUpgrade) {\n spinner?.info(`Skipping ${name}. Socket Optimize package exists.`)\n continue\n }\n\n const nodes = findPackageNodes(tree, name)\n\n const packument =\n nodes.length && infos.length\n ? // eslint-disable-next-line no-await-in-loop\n await fetchPackagePackument(name)\n : null\n if (!packument) {\n continue\n }\n\n for (let i = 0, { length: nodesLength } = nodes; i < nodesLength; i += 1) {\n const node = nodes[i]!\n for (\n let j = 0, { length: infosLength } = infos;\n j < infosLength;\n j += 1\n ) {\n const { firstPatchedVersionIdentifier, vulnerableVersionRange } =\n infos[j]!\n const { version: oldVersion } = node\n if (\n updateNode(\n node,\n packument,\n vulnerableVersionRange,\n firstPatchedVersionIdentifier\n )\n ) {\n try {\n // eslint-disable-next-line no-await-in-loop\n await runScript('test', [], { spinner, stdio: 'ignore' })\n\n spinner?.info(`Patched ${name} ${oldVersion} -> ${node.version}`)\n\n if (isTopLevel(tree, node)) {\n for (const depField of [\n 'dependencies',\n 'optionalDependencies',\n 'peerDependencies'\n ]) {\n const { content: pkgJson } = editablePkgJson\n const oldVersion = (pkgJson[depField] as any)?.[name]\n if (oldVersion) {\n const decorator = /^[~^]/.exec(oldVersion)?.[0] ?? ''\n ;(pkgJson as any)[depField][name] =\n `${decorator}${node.version}`\n }\n }\n }\n // eslint-disable-next-line no-await-in-loop\n await editablePkgJson.save()\n } catch {\n spinner?.error(`Reverting ${name} to ${oldVersion}`)\n arb.idealTree = revertToIdealTree\n }\n } else {\n spinner?.error(`Could not patch ${name} ${oldVersion}`)\n }\n }\n }\n }\n\n const arb2 = new Arborist({ path: cwd })\n arb2.idealTree = arb.idealTree\n await arb2.reify()\n\n spinner?.stop()\n}\n","import { detectDepTypes } from '@pnpm/lockfile.detect-dep-types'\n\nimport { batchScan } from '../alert/artifact'\nimport { AlertsByPkgId, addArtifactToAlertsMap } from '../socket-package-alert'\n\nimport type { Lockfile } from '@pnpm/lockfile-file'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\ntype AlertIncludeFilter = {\n critical?: boolean | undefined\n cve?: boolean | undefined\n existing?: boolean | undefined\n unfixable?: boolean | undefined\n upgrade?: boolean | undefined\n}\n\ntype GetAlertsMapFromPnpmLockfileOptions = {\n consolidate?: boolean | undefined\n include?: AlertIncludeFilter | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function getAlertsMapFromPnpmLockfile(\n lockfile: Lockfile,\n options?: GetAlertsMapFromPnpmLockfileOptions | undefined\n): Promise<AlertsByPkgId> {\n const { include: _include, spinner } = {\n __proto__: null,\n ...options\n } as GetAlertsMapFromPnpmLockfileOptions\n\n const depTypes = detectDepTypes(lockfile)\n const pkgIds = Object.keys(depTypes)\n let { length: remaining } = pkgIds\n const alertsByPkgId: AlertsByPkgId = new Map()\n if (!remaining) {\n return alertsByPkgId\n }\n const getText = () => `Looking up data for ${remaining} packages`\n\n spinner?.start(getText())\n\n const toAlertsMapOptions = {\n overrides: lockfile.overrides,\n ...options\n }\n for await (const artifact of batchScan(pkgIds)) {\n await addArtifactToAlertsMap(artifact, alertsByPkgId, toAlertsMapOptions)\n remaining -= 1\n if (spinner && remaining > 0) {\n spinner.start()\n spinner.setText(getText())\n }\n }\n\n spinner?.stop()\n\n return alertsByPkgId\n}\n","export function cmdFlagsToString(args: string[]) {\n const result = []\n for (let i = 0, { length } = args; i < length; i += 1) {\n if (args[i]!.startsWith('--')) {\n // Check if the next item exists and is NOT another flag.\n if (i + 1 < length && !args[i + 1]!.startsWith('--')) {\n result.push(`${args[i]}=${args[i + 1]}`)\n i += 1\n } else {\n result.push(args[i])\n }\n }\n }\n return result.join(' ')\n}\n\nexport function cmdPrefixMessage(cmdName: string, text: string): string {\n const cmdPrefix = cmdName ? `${cmdName}: ` : ''\n return `${cmdPrefix}${text}`\n}\n","import process from 'node:process'\n\nimport { isDebug } from '@socketsecurity/registry/lib/debug'\nimport {\n isAuditFlag,\n isFundFlag,\n isLoglevelFlag,\n isProgressFlag\n} from '@socketsecurity/registry/lib/npm'\nimport { isObject } from '@socketsecurity/registry/lib/objects'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../constants'\nimport { getNpmBinPath } from '../shadow/npm/paths'\n\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nconst { SOCKET_CLI_SENTRY_BUILD, SOCKET_IPC_HANDSHAKE } = constants\n\ntype SpawnOption = Exclude<Parameters<typeof spawn>[2], undefined>\n\ntype SafeNpmInstallOptions = SpawnOption & {\n agentExecPath?: string | undefined\n args?: string[] | readonly string[] | undefined\n ipc?: object | undefined\n spinner?: Spinner | undefined\n}\n\nexport function safeNpmInstall(options?: SafeNpmInstallOptions) {\n const {\n agentExecPath = getNpmBinPath(),\n args = [],\n ipc,\n spinner,\n ...spawnOptions\n } = { __proto__: null, ...options } as SafeNpmInstallOptions\n const useIpc = isObject(ipc)\n const useDebug = isDebug()\n const terminatorPos = args.indexOf('--')\n const npmArgs = (\n terminatorPos === -1 ? args : args.slice(0, terminatorPos)\n ).filter(a => !isAuditFlag(a) && !isFundFlag(a) && !isProgressFlag(a))\n const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos)\n const isSilent = !useDebug && !npmArgs.some(isLoglevelFlag)\n const logLevelArgs = isSilent ? ['--loglevel', 'error'] : []\n const spawnPromise = spawn(\n // Lazily access constants.execPath.\n constants.execPath,\n [\n // Lazily access constants.nodeHardenFlags.\n ...constants.nodeHardenFlags,\n // Lazily access constants.nodeNoWarningsFlags.\n ...constants.nodeNoWarningsFlags,\n // Lazily access constants.ENV[SOCKET_CLI_SENTRY_BUILD].\n ...(constants.ENV[SOCKET_CLI_SENTRY_BUILD]\n ? [\n '--require',\n // Lazily access constants.distInstrumentWithSentryPath.\n constants.distInstrumentWithSentryPath\n ]\n : []),\n '--require',\n // Lazily access constants.distShadowNpmInjectPath.\n constants.distShadowNpmInjectPath,\n agentExecPath,\n 'install',\n // Avoid code paths for 'audit' and 'fund'.\n '--no-audit',\n '--no-fund',\n // Add `--no-progress` flag to fix input being swallowed by the spinner\n // when running the command with recent versions of npm.\n '--no-progress',\n // Add '--loglevel=error' if a loglevel flag is not provided and the\n // SOCKET_CLI_DEBUG environment variable is not truthy.\n ...logLevelArgs,\n ...npmArgs,\n ...otherArgs\n ],\n {\n spinner,\n // Set stdio to include 'ipc'.\n // See https://github.com/nodejs/node/blob/v23.6.0/lib/child_process.js#L161-L166\n // and https://github.com/nodejs/node/blob/v23.6.0/lib/internal/child_process.js#L238.\n stdio: useIpc ? [0, 1, 2, 'ipc'] : 'inherit',\n ...spawnOptions,\n env: {\n ...process.env,\n ...spawnOptions.env\n }\n }\n )\n if (useIpc) {\n spawnPromise.process.send({ [SOCKET_IPC_HANDSHAKE]: ipc })\n }\n return spawnPromise\n}\n","import { isDebug } from '@socketsecurity/registry/lib/debug'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\nimport { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nimport constants from '../../constants'\nimport { cmdFlagsToString } from '../../utils/cmd'\nimport { safeNpmInstall } from '../../utils/npm'\n\nimport type { EnvDetails } from '../../utils/package-environment'\n\nconst { NPM } = constants\n\ntype SpawnOption = Exclude<Parameters<typeof spawn>[2], undefined>\ntype SpawnResult = ReturnType<typeof spawn>\n\nexport type AgentInstallOptions = SpawnOption & {\n args?: string[] | readonly string[] | undefined\n spinner?: Spinner | undefined\n}\n\nexport function runAgentInstall(\n pkgEnvDetails: EnvDetails,\n options?: AgentInstallOptions | undefined\n): SpawnResult {\n const { agent, agentExecPath } = pkgEnvDetails\n // All package managers support the \"install\" command.\n if (agent === NPM) {\n return safeNpmInstall({\n agentExecPath,\n ...options\n })\n }\n const {\n args = [],\n spinner,\n ...spawnOptions\n } = { __proto__: null, ...options } as AgentInstallOptions\n return spawn(agentExecPath, ['install', ...args], {\n spinner,\n stdio: isDebug() ? 'inherit' : 'ignore',\n ...spawnOptions,\n env: {\n ...process.env,\n NODE_OPTIONS: cmdFlagsToString([\n // Lazily access constants.nodeHardenFlags.\n ...constants.nodeHardenFlags,\n // Lazily access constants.nodeNoWarningsFlags.\n ...constants.nodeNoWarningsFlags\n ]),\n ...spawnOptions.env\n }\n })\n}\n","import { readWantedLockfile } from '@pnpm/lockfile-file'\n\nimport { getManifestData } from '@socketsecurity/registry'\nimport {\n fetchPackagePackument,\n readPackageJson\n} from '@socketsecurity/registry/lib/packages'\n\nimport constants from '../../constants'\nimport {\n SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES,\n SafeArborist\n} from '../../shadow/npm/arborist/lib/arborist'\nimport {\n findBestPatchVersion,\n findPackageNodes\n} from '../../utils/lockfile/package-lock-json'\nimport { getAlertsMapFromPnpmLockfile } from '../../utils/lockfile/pnpm-lock-yaml'\nimport { getCveInfoByAlertsMap } from '../../utils/socket-package-alert'\nimport { runAgentInstall } from '../optimize/run-agent'\n\nimport type { EnvDetails } from '../../utils/package-environment'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nconst { NPM, OVERRIDES, PNPM } = constants\n\ntype PnpmFixOptions = {\n spinner?: Spinner | undefined\n}\n\nexport async function pnpmFix(\n pkgEnvDetails: EnvDetails,\n cwd: string,\n options?: PnpmFixOptions | undefined\n) {\n const { spinner } = { __proto__: null, ...options } as PnpmFixOptions\n\n spinner?.start()\n\n const lockfile = await readWantedLockfile(cwd, { ignoreIncompatible: false })\n if (!lockfile) {\n spinner?.stop()\n return\n }\n\n const alertsMap = await getAlertsMapFromPnpmLockfile(lockfile, {\n consolidate: true,\n include: {\n existing: true,\n unfixable: false,\n upgrade: false\n }\n })\n\n const infoByPkg = getCveInfoByAlertsMap(alertsMap)\n if (!infoByPkg) {\n spinner?.stop()\n return\n }\n\n const arb = new SafeArborist({\n path: cwd,\n ...SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES\n })\n\n await arb.loadActual()\n\n const editablePkgJson = await readPackageJson(cwd, { editable: true })\n const { content: pkgJson } = editablePkgJson\n\n for (const { 0: name, 1: infos } of infoByPkg) {\n const tree = arb.actualTree!\n\n const hasUpgrade = !!getManifestData(NPM, name)\n if (hasUpgrade) {\n spinner?.info(`Skipping ${name}. Socket Optimize package exists.`)\n continue\n }\n\n const nodes = findPackageNodes(tree, name)\n\n const packument =\n nodes.length && infos.length\n ? // eslint-disable-next-line no-await-in-loop\n await fetchPackagePackument(name)\n : null\n if (!packument) {\n continue\n }\n\n for (let i = 0, { length: nodesLength } = nodes; i < nodesLength; i += 1) {\n const node = nodes[i]!\n for (\n let j = 0, { length: infosLength } = infos;\n j < infosLength;\n j += 1\n ) {\n const { firstPatchedVersionIdentifier, vulnerableVersionRange } =\n infos[j]!\n const { version: oldVersion } = node\n const availableVersions = Object.keys(packument.versions)\n // Find the highest non-vulnerable version within the same major range\n const targetVersion = findBestPatchVersion(\n node,\n availableVersions,\n vulnerableVersionRange,\n firstPatchedVersionIdentifier\n )\n const targetPackument = targetVersion\n ? packument.versions[targetVersion]\n : undefined\n if (targetPackument) {\n const oldPnpm = (pkgJson as any)[PNPM]\n const oldOverrides = oldPnpm?.[OVERRIDES] as\n | { [key: string]: string }\n | undefined\n try {\n editablePkgJson.update({\n [PNPM]: {\n ...oldPnpm,\n [OVERRIDES]: {\n [`${node.name}@${vulnerableVersionRange}`]: `^${targetVersion}`,\n ...oldOverrides\n }\n }\n })\n\n spinner?.info(`Patched ${name} ${oldVersion} -> ${node.version}`)\n\n // eslint-disable-next-line no-await-in-loop\n await editablePkgJson.save()\n // eslint-disable-next-line no-await-in-loop\n await runAgentInstall(pkgEnvDetails, { spinner })\n } catch {\n spinner?.error(`Reverting ${name} to ${oldVersion}`)\n }\n } else {\n spinner?.error(`Could not patch ${name} ${oldVersion}`)\n }\n }\n }\n }\n\n spinner?.stop()\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\n\nimport browserslist from 'browserslist'\nimport semver from 'semver'\nimport which from 'which'\n\nimport { parse as parseBunLockb } from '@socketregistry/hyrious__bun.lockb/index.cjs'\nimport { Logger } from '@socketsecurity/registry/lib/logger'\nimport { isObjectObject } from '@socketsecurity/registry/lib/objects'\nimport { readPackageJson } from '@socketsecurity/registry/lib/packages'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { cmdPrefixMessage } from './cmd'\nimport { findUp, readFileBinary, readFileUtf8 } from './fs'\nimport constants from '../constants'\n\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\nimport type { EditablePackageJson } from '@socketsecurity/registry/lib/packages'\nimport type { SemVer } from 'semver'\n\nconst {\n BINARY_LOCK_EXT,\n BUN,\n LOCK_EXT,\n NPM,\n NPM_BUGGY_OVERRIDES_PATCHED_VERSION,\n PNPM,\n VLT,\n YARN,\n YARN_BERRY,\n YARN_CLASSIC\n} = constants\n\nexport const AGENTS = [BUN, NPM, PNPM, YARN_BERRY, YARN_CLASSIC, VLT] as const\nexport type Agent = (typeof AGENTS)[number]\nexport type StringKeyValueObject = { [key: string]: string }\n\nconst binByAgent = {\n __proto__: null,\n [BUN]: BUN,\n [NPM]: NPM,\n [PNPM]: PNPM,\n [YARN_BERRY]: YARN,\n [YARN_CLASSIC]: YARN,\n [VLT]: VLT\n}\n\nasync function getAgentExecPath(agent: Agent): Promise<string> {\n const binName = binByAgent[agent]\n return (await which(binName, { nothrow: true })) ?? binName\n}\n\nasync function getAgentVersion(\n agentExecPath: string,\n cwd: string\n): Promise<SemVer | undefined> {\n let result\n try {\n result =\n semver.coerce(\n // All package managers support the \"--version\" flag.\n (await spawn(agentExecPath, ['--version'], { cwd })).stdout\n ) ?? undefined\n } catch {}\n return result\n}\n\n// The order of LOCKS properties IS significant as it affects iteration order.\nconst LOCKS: Record<string, Agent> = {\n [`bun${LOCK_EXT}`]: BUN,\n [`bun${BINARY_LOCK_EXT}`]: BUN,\n // If both package-lock.json and npm-shrinkwrap.json are present in the root\n // of a project, npm-shrinkwrap.json will take precedence and package-lock.json\n // will be ignored.\n // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#package-lockjson-vs-npm-shrinkwrapjson\n 'npm-shrinkwrap.json': NPM,\n 'package-lock.json': NPM,\n 'pnpm-lock.yaml': PNPM,\n 'pnpm-lock.yml': PNPM,\n [`yarn${LOCK_EXT}`]: YARN_CLASSIC,\n 'vlt-lock.json': VLT,\n // Lastly, look for a hidden lock file which is present if .npmrc has package-lock=false:\n // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#hidden-lockfiles\n //\n // Unlike the other LOCKS keys this key contains a directory AND filename so\n // it has to be handled differently.\n 'node_modules/.package-lock.json': NPM\n}\n\ntype ReadLockFile =\n | ((lockPath: string) => Promise<string | undefined>)\n | ((lockPath: string, agentExecPath: string) => Promise<string | undefined>)\n\nconst readLockFileByAgent: Record<Agent, ReadLockFile> = (() => {\n function wrapReader<T extends (...args: any[]) => Promise<any>>(\n reader: T\n ): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>> | undefined> {\n return async (...args: any[]): Promise<any> => {\n try {\n return await reader(...args)\n } catch {}\n return undefined\n }\n }\n\n const binaryReader = wrapReader(readFileBinary)\n\n const defaultReader = wrapReader(\n async (lockPath: string) => await readFileUtf8(lockPath)\n )\n\n return {\n [BUN]: wrapReader(async (lockPath: string, agentExecPath: string) => {\n const ext = path.extname(lockPath)\n if (ext === LOCK_EXT) {\n return await defaultReader(lockPath)\n }\n if (ext === BINARY_LOCK_EXT) {\n const lockBuffer = await binaryReader(lockPath)\n if (lockBuffer) {\n try {\n return parseBunLockb(lockBuffer)\n } catch {}\n }\n // To print a Yarn lockfile to your console without writing it to disk\n // use `bun bun.lockb`.\n // https://bun.sh/guides/install/yarnlock\n return (await spawn(agentExecPath, [lockPath])).stdout.trim()\n }\n return undefined\n }),\n [NPM]: defaultReader,\n [PNPM]: defaultReader,\n [VLT]: defaultReader,\n [YARN_BERRY]: defaultReader,\n [YARN_CLASSIC]: defaultReader\n }\n})()\n\nexport type DetectOptions = {\n cwd?: string | undefined\n onUnknown?: (pkgManager: string | undefined) => void\n}\n\ntype EnvBase = {\n agent: Agent\n agentExecPath: string\n features: {\n // Fixed by https://github.com/npm/cli/pull/8089.\n // Landed in npm v11.2.0.\n npmBuggyOverrides: boolean\n }\n minimumNodeVersion: string\n npmExecPath: string\n pkgSupported: boolean\n targets: {\n browser: boolean\n node: boolean\n }\n}\n\nexport type EnvDetails = Readonly<\n Remap<\n EnvBase & {\n agentVersion: SemVer\n lockName: string\n lockPath: string\n lockSrc: string\n pkgJson: EditablePackageJson\n pkgPath: string\n }\n >\n>\n\nexport type PartialEnvDetails = Readonly<\n Remap<\n EnvBase & {\n agentVersion: SemVer | undefined\n lockName: string | undefined\n lockPath: string | undefined\n lockSrc: string | undefined\n pkgJson: EditablePackageJson | undefined\n pkgPath: string | undefined\n }\n >\n>\n\nexport async function detectPackageEnvironment({\n cwd = process.cwd(),\n onUnknown\n}: DetectOptions = {}): Promise<EnvDetails | PartialEnvDetails> {\n let lockPath = await findUp(Object.keys(LOCKS), { cwd })\n let lockName = lockPath ? path.basename(lockPath) : undefined\n const isHiddenLockFile = lockName === '.package-lock.json'\n const pkgJsonPath = lockPath\n ? path.resolve(lockPath, `${isHiddenLockFile ? '../' : ''}../package.json`)\n : await findUp('package.json', { cwd })\n const pkgPath =\n pkgJsonPath && existsSync(pkgJsonPath)\n ? path.dirname(pkgJsonPath)\n : undefined\n const editablePkgJson = pkgPath\n ? await readPackageJson(pkgPath, { editable: true })\n : undefined\n const pkgJson = editablePkgJson?.content\n // Read Corepack `packageManager` field in package.json:\n // https://nodejs.org/api/packages.html#packagemanager\n const pkgManager = isNonEmptyString(pkgJson?.packageManager)\n ? pkgJson.packageManager\n : undefined\n\n let agent: Agent | undefined\n let agentVersion: SemVer | undefined\n if (pkgManager) {\n const atSignIndex = pkgManager.lastIndexOf('@')\n if (atSignIndex !== -1) {\n const name = pkgManager.slice(0, atSignIndex) as Agent\n const version = pkgManager.slice(atSignIndex + 1)\n if (version && AGENTS.includes(name)) {\n agent = name\n agentVersion = semver.coerce(version) ?? undefined\n }\n }\n }\n if (\n agent === undefined &&\n !isHiddenLockFile &&\n typeof pkgJsonPath === 'string' &&\n typeof lockName === 'string'\n ) {\n agent = LOCKS[lockName] as Agent\n }\n if (agent === undefined) {\n agent = NPM\n onUnknown?.(pkgManager)\n }\n const agentExecPath = await getAgentExecPath(agent)\n\n const npmExecPath =\n agent === NPM ? agentExecPath : await getAgentExecPath(NPM)\n if (agentVersion === undefined) {\n agentVersion = await getAgentVersion(agentExecPath, cwd)\n }\n if (agent === YARN_CLASSIC && (agentVersion?.major ?? 0) > 1) {\n agent = YARN_BERRY\n }\n const targets = {\n browser: false,\n node: true\n }\n let lockSrc: string | undefined\n // Lazily access constants.maintainedNodeVersions.\n let minimumNodeVersion = constants.maintainedNodeVersions.last\n if (pkgJson) {\n const browserField = pkgJson.browser\n if (isNonEmptyString(browserField) || isObjectObject(browserField)) {\n targets.browser = true\n }\n const nodeRange = pkgJson.engines?.['node']\n if (isNonEmptyString(nodeRange)) {\n const coerced = semver.coerce(nodeRange)\n if (coerced && semver.lt(coerced, minimumNodeVersion)) {\n minimumNodeVersion = coerced.version\n }\n }\n const browserslistQuery = pkgJson['browserslist'] as string[] | undefined\n if (Array.isArray(browserslistQuery)) {\n const browserslistTargets = browserslist(browserslistQuery)\n .map(s => s.toLowerCase())\n .sort(naturalCompare)\n const browserslistNodeTargets = browserslistTargets\n .filter(v => v.startsWith('node '))\n .map(v => v.slice(5 /*'node '.length*/))\n if (!targets.browser && browserslistTargets.length) {\n targets.browser =\n browserslistTargets.length !== browserslistNodeTargets.length\n }\n if (browserslistNodeTargets.length) {\n const coerced = semver.coerce(browserslistNodeTargets[0])\n if (coerced && semver.lt(coerced, minimumNodeVersion)) {\n minimumNodeVersion = coerced.version\n }\n }\n }\n // Lazily access constants.maintainedNodeVersions.\n targets.node = constants.maintainedNodeVersions.some(v =>\n semver.satisfies(v, `>=${minimumNodeVersion}`)\n )\n lockSrc =\n typeof lockPath === 'string'\n ? await readLockFileByAgent[agent](lockPath, agentExecPath)\n : undefined\n } else {\n lockName = undefined\n lockPath = undefined\n }\n const pkgSupported = targets.browser || targets.node\n const npmBuggyOverrides =\n agent === NPM &&\n !!agentVersion &&\n semver.lt(agentVersion, NPM_BUGGY_OVERRIDES_PATCHED_VERSION)\n return {\n agent,\n agentExecPath,\n agentVersion,\n lockName,\n lockPath,\n lockSrc,\n minimumNodeVersion,\n npmExecPath,\n pkgJson: editablePkgJson,\n pkgPath,\n pkgSupported,\n features: {\n npmBuggyOverrides\n },\n targets\n }\n}\n\nexport type DetectAndValidateOptions = {\n cmdName?: string | undefined\n logger?: Logger | undefined\n prod?: boolean | undefined\n}\nexport async function detectAndValidatePackageEnvironment(\n cwd: string,\n options?: DetectAndValidateOptions | undefined\n): Promise<void | EnvDetails> {\n const {\n cmdName = '',\n logger,\n prod\n } = {\n __proto__: null,\n ...options\n } as DetectAndValidateOptions\n const details = await detectPackageEnvironment({\n cwd,\n onUnknown(pkgManager: string | undefined) {\n logger?.warn(\n cmdPrefixMessage(\n cmdName,\n `Unknown package manager${pkgManager ? ` ${pkgManager}` : ''}, defaulting to npm`\n )\n )\n }\n })\n if (!details.pkgSupported) {\n logger?.fail(\n cmdPrefixMessage(cmdName, 'No supported Node or browser range detected')\n )\n return\n }\n if (details.agent === VLT) {\n logger?.fail(\n cmdPrefixMessage(\n cmdName,\n `${details.agent} does not support overrides. Soon, though ⚡`\n )\n )\n return\n }\n const lockName = details.lockName ?? 'lock file'\n if (details.lockName === undefined || details.lockSrc === undefined) {\n logger?.fail(cmdPrefixMessage(cmdName, `No ${lockName} found`))\n return\n }\n if (details.lockSrc.trim() === '') {\n logger?.fail(cmdPrefixMessage(cmdName, `${lockName} is empty`))\n return\n }\n if (details.pkgPath === undefined) {\n logger?.fail(cmdPrefixMessage(cmdName, 'No package.json found'))\n return\n }\n if (prod && (details.agent === BUN || details.agent === YARN_BERRY)) {\n logger?.fail(\n cmdPrefixMessage(\n cmdName,\n `--prod not supported for ${details.agent}${details.agentVersion ? `@${details.agentVersion.version}` : ''}`\n )\n )\n return\n }\n if (\n details.lockPath &&\n path.relative(cwd, details.lockPath).startsWith('.')\n ) {\n logger?.warn(\n cmdPrefixMessage(\n cmdName,\n `Package ${lockName} found at ${details.lockPath}`\n )\n )\n }\n return details as EnvDetails\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { npmFix } from './npm-fix'\nimport { pnpmFix } from './pnpm-fix'\nimport constants from '../../constants'\nimport { detectAndValidatePackageEnvironment } from '../../utils/package-environment'\n\nconst { NPM, PNPM } = constants\n\nconst CMD_NAME = 'socket fix'\n\nexport async function runFix() {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start()\n\n const cwd = process.cwd()\n\n const pkgEnvDetails = await detectAndValidatePackageEnvironment(cwd, {\n cmdName: CMD_NAME,\n logger\n })\n if (!pkgEnvDetails) {\n spinner.stop()\n return\n }\n\n switch (pkgEnvDetails.agent) {\n case NPM: {\n await npmFix(pkgEnvDetails, cwd)\n break\n }\n case PNPM: {\n await pnpmFix(pkgEnvDetails, cwd)\n break\n }\n }\n spinner.successAndStop('Socket.dev fix successful')\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { runFix } from './run-fix'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'fix',\n description: 'Fix \"fixable\" Socket alerts',\n hidden: true,\n flags: {\n ...commonFlags\n },\n help: (command, config) => `\n Usage\n $ ${command}\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n `\n}\n\nexport const cmdFix = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await runFix()\n}\n","import { PackageData } from './get-package-info'\nimport { getSeverityCount } from '../../utils/alert/severity'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { getPublicToken, setupSdk } from '../../utils/sdk'\n\nexport async function fetchPackageInfo(\n pkgName: string,\n pkgVersion: string,\n includeAllIssues: boolean\n): Promise<void | PackageData> {\n const socketSdk = await setupSdk(getPublicToken())\n const result = await handleApiCall(\n socketSdk.getIssuesByNPMPackage(pkgName, pkgVersion),\n 'looking up package'\n )\n const scoreResult = await handleApiCall(\n socketSdk.getScoreByNPMPackage(pkgName, pkgVersion),\n 'looking up package score'\n )\n\n if (result.success === false) {\n return handleUnsuccessfulApiResponse('getIssuesByNPMPackage', result)\n }\n\n if (scoreResult.success === false) {\n return handleUnsuccessfulApiResponse('getScoreByNPMPackage', scoreResult)\n }\n\n const severityCount = getSeverityCount(\n result.data,\n includeAllIssues ? undefined : 'high'\n )\n\n return {\n data: result.data,\n severityCount,\n score: scoreResult.data\n }\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport constants from '@socketsecurity/registry/lib/constants'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { hasKeys } from '@socketsecurity/registry/lib/objects'\n\nimport { PackageData } from './get-package-info'\nimport { SEVERITY, formatSeverityCount } from '../../utils/alert/severity'\nimport { ColorOrMarkdown } from '../../utils/color-or-markdown'\nimport {\n getSocketDevAlertUrl,\n getSocketDevPackageOverviewUrl\n} from '../../utils/socket-url'\n\nimport type { SocketSdkReturnType } from '@socketsecurity/sdk'\n\nconst { NPM } = constants\n\nfunction formatScore(score: number): string {\n if (score > 80) {\n return colors.green(`${score}`)\n } else if (score < 80 && score > 60) {\n return colors.yellow(`${score}`)\n }\n return colors.red(`${score}`)\n}\n\nfunction logPackageIssuesDetails(\n packageData: SocketSdkReturnType<'getIssuesByNPMPackage'>['data'],\n outputMarkdown: boolean\n) {\n const issueDetails = packageData.filter(\n d =>\n d.value?.severity === SEVERITY.critical ||\n d.value?.severity === SEVERITY.high\n )\n const uniqueIssueDetails = issueDetails.reduce((acc, issue) => {\n const { type } = issue\n if (type) {\n const details = acc.get(type)\n if (details) {\n details.count += 1\n } else {\n acc.set(type, {\n label: issue.value?.label ?? '',\n count: 1\n })\n }\n }\n return acc\n }, new Map<string, { count: number; label: string }>())\n const format = new ColorOrMarkdown(outputMarkdown)\n for (const [type, details] of uniqueIssueDetails.entries()) {\n const issueWithLink = format.hyperlink(\n details.label,\n getSocketDevAlertUrl(type),\n { fallbackToUrl: true }\n )\n if (details.count === 1) {\n logger.log(`- ${issueWithLink}`)\n } else {\n logger.log(`- ${issueWithLink}: ${details.count}`)\n }\n }\n}\n\nexport function logPackageInfo(\n { data, score, severityCount }: PackageData,\n {\n name,\n outputKind,\n pkgName,\n pkgVersion\n }: {\n includeAllIssues: boolean\n name: string\n outputKind: 'json' | 'markdown' | 'print'\n pkgName: string\n pkgVersion: string\n }\n): void {\n if (outputKind === 'json') {\n logger.log(JSON.stringify(data, undefined, 2))\n return\n }\n if (outputKind === 'markdown') {\n logger.log(stripIndents`\n # Package report for ${pkgName}\n\n Package report card:\n `)\n } else {\n logger.log(`Package report card for ${pkgName}:`)\n }\n const scoreResult = {\n 'Supply Chain Risk': Math.floor(score.supplyChainRisk.score * 100),\n Maintenance: Math.floor(score.maintenance.score * 100),\n Quality: Math.floor(score.quality.score * 100),\n Vulnerabilities: Math.floor(score.vulnerability.score * 100),\n License: Math.floor(score.license.score * 100)\n }\n logger.log('\\n')\n Object.entries(scoreResult).map(score =>\n logger.log(`- ${score[0]}: ${formatScore(score[1])}`)\n )\n logger.log('\\n')\n if (hasKeys(severityCount)) {\n if (outputKind === 'markdown') {\n logger.log('# Issues\\n')\n }\n logger.log(\n `Package has these issues: ${formatSeverityCount(severityCount)}\\n`\n )\n logPackageIssuesDetails(data, outputKind === 'markdown')\n } else {\n logger.log('Package has no issues')\n }\n\n const format = new ColorOrMarkdown(outputKind === 'markdown')\n const url = getSocketDevPackageOverviewUrl(NPM, pkgName, pkgVersion)\n\n logger.log('\\n')\n if (pkgVersion === 'latest') {\n logger.log(\n `Detailed info on socket.dev: ${format.hyperlink(`${pkgName}`, url, { fallbackToUrl: true })}`\n )\n } else {\n logger.log(\n `Detailed info on socket.dev: ${format.hyperlink(`${pkgName} v${pkgVersion}`, url, { fallbackToUrl: true })}`\n )\n }\n if (outputKind !== 'markdown') {\n logger.log(\n colors.dim(\n `\\nOr rerun ${colors.italic(name)} using the ${colors.italic('--json')} flag to get full JSON output`\n )\n )\n } else {\n logger.log('')\n }\n}\n","import process from 'node:process'\n\nimport { hasKeys } from '@socketsecurity/registry/lib/objects'\n\nimport { fetchPackageInfo } from './fetch-package-info'\nimport { logPackageInfo } from './log-package-info'\nimport constants from '../../constants'\n\nimport type { SocketSdkAlert } from '../../utils/alert/severity'\nimport type { SocketSdkReturnType } from '@socketsecurity/sdk'\n\nexport interface PackageData {\n data: SocketSdkReturnType<'getIssuesByNPMPackage'>['data']\n severityCount: Record<SocketSdkAlert['severity'], number>\n score: SocketSdkReturnType<'getScoreByNPMPackage'>['data']\n}\n\nexport async function getPackageInfo({\n commandName,\n includeAllIssues,\n outputKind,\n pkgName,\n pkgVersion,\n strict\n}: {\n commandName: string\n includeAllIssues: boolean\n outputKind: 'json' | 'markdown' | 'print'\n pkgName: string\n pkgVersion: string\n strict: boolean\n}) {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start(\n pkgVersion === 'latest'\n ? `Looking up data for the latest version of ${pkgName}`\n : `Looking up data for version ${pkgVersion} of ${pkgName}`\n )\n\n const packageData = await fetchPackageInfo(\n pkgName,\n pkgVersion,\n includeAllIssues\n )\n\n spinner.successAndStop('Data fetched')\n\n if (packageData) {\n logPackageInfo(packageData, {\n name: commandName,\n includeAllIssues,\n outputKind,\n pkgName,\n pkgVersion\n })\n\n if (strict && hasKeys(packageData.severityCount)) {\n // Let NodeJS exit gracefully but with exit(1)\n process.exitCode = 1\n }\n }\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getPackageInfo } from './get-package-info'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags, validationFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'info',\n description: 'Look up info regarding a package',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n ...validationFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <name>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} webtorrent\n $ ${command} webtorrent@1.9.1\n `\n}\n\nexport const cmdInfo = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const { all, json, markdown, strict } = cli.flags\n const [rawPkgName = ''] = cli.input\n\n if (!rawPkgName || cli.input.length > 1) {\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(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\\n\n - Expecting a package name ${!rawPkgName ? colors.red('(missing!)') : colors.green('(ok)')}\\n\n - Can only accept one package at a time ${cli.input.length > 1 ? colors.red('(got ' + cli.input.length + '!)') : colors.green('(ok)')}\\n`)\n return\n }\n\n const versionSeparator = rawPkgName.lastIndexOf('@')\n const pkgName =\n versionSeparator < 1 ? rawPkgName : rawPkgName.slice(0, versionSeparator)\n const pkgVersion =\n versionSeparator < 1 ? 'latest' : rawPkgName.slice(versionSeparator + 1)\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await getPackageInfo({\n commandName: `${parentName} ${config.commandName}`,\n includeAllIssues: Boolean(all),\n outputKind: json ? 'json' : markdown ? 'markdown' : 'print',\n pkgName,\n pkgVersion,\n strict: Boolean(strict)\n })\n}\n","import { updateSetting } from '../../utils/settings'\n\nexport function applyLogin(\n apiToken: string,\n enforcedOrgs: string[],\n apiBaseUrl: string | undefined,\n apiProxy: string | undefined\n) {\n updateSetting('enforcedOrgs', enforcedOrgs)\n updateSetting('apiToken', apiToken)\n updateSetting('apiBaseUrl', apiBaseUrl)\n updateSetting('apiProxy', apiProxy)\n}\n","import terminalLink from 'terminal-link'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { confirm, password, select } from '@socketsecurity/registry/lib/prompts'\n\nimport { applyLogin } from './apply-login'\nimport constants from '../../constants'\nimport { AuthError } from '../../utils/errors'\nimport { setupSdk } from '../../utils/sdk'\nimport { getSetting } from '../../utils/settings'\n\nimport type { Choice, Separator } from '@socketsecurity/registry/lib/prompts'\nimport type { SocketSdkReturnType } from '@socketsecurity/sdk'\n\ntype OrgChoice = Choice<string>\ntype OrgChoices = Array<Separator | OrgChoice>\nconst { SOCKET_PUBLIC_API_TOKEN } = constants\n\nexport async function attemptLogin(\n apiBaseUrl: string | undefined,\n apiProxy: string | undefined\n) {\n apiBaseUrl ??= getSetting('apiBaseUrl') ?? undefined\n apiProxy ??= getSetting('apiProxy') ?? undefined\n const apiToken =\n (await password({\n message: `Enter your ${terminalLink(\n 'Socket.dev API key',\n 'https://docs.socket.dev/docs/api-keys'\n )} (leave blank for a public key)`\n })) || SOCKET_PUBLIC_API_TOKEN\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Verifying API key...')\n\n let orgs: SocketSdkReturnType<'getOrganizations'>['data']\n try {\n const sdk = await setupSdk(apiToken, apiBaseUrl, apiProxy)\n const result = await sdk.getOrganizations()\n if (!result.success) {\n throw new AuthError()\n }\n orgs = result.data\n spinner.success('API key verified')\n } catch {\n spinner.errorAndStop('Invalid API key')\n return\n }\n\n const enforcedChoices: OrgChoices = Object.values(orgs.organizations)\n .filter(org => org?.plan === 'enterprise')\n .map(org => ({\n name: org.name,\n value: org.id\n }))\n\n let enforcedOrgs: string[] = []\n if (enforcedChoices.length > 1) {\n const id = (await select(\n {\n message:\n \"Which organization's policies should Socket enforce system-wide?\",\n choices: enforcedChoices.concat({\n name: 'None',\n value: '',\n description: 'Pick \"None\" if this is a personal device'\n })\n },\n {\n spinner\n }\n )) as string | null\n if (id) {\n enforcedOrgs = [id]\n }\n } else if (enforcedChoices.length) {\n const confirmOrg = await confirm(\n {\n message: `Should Socket enforce ${(enforcedChoices[0] as OrgChoice)?.name}'s security policies system-wide?`,\n default: true\n },\n {\n spinner\n }\n )\n if (confirmOrg) {\n const existing = enforcedChoices[0] as OrgChoice\n if (existing) {\n enforcedOrgs = [existing.value]\n }\n }\n }\n\n spinner.stop()\n\n const oldToken = getSetting('apiToken')\n try {\n applyLogin(apiToken, enforcedOrgs, apiBaseUrl, apiProxy)\n logger.success(`API credentials ${oldToken ? 'updated' : 'set'}`)\n } catch {\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'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { InputError } from '../../utils/errors'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'login',\n description: 'Socket API login',\n hidden: false,\n flags: {\n ...commonFlags,\n apiBaseUrl: {\n type: 'string',\n description: 'API server to connect to for login'\n },\n apiProxy: {\n type: 'string',\n description: 'Proxy to use when making connection to API server'\n }\n },\n help: (command, config) => `\n Usage\n $ ${command}\n\n Logs into the Socket API by prompting for an API key\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command}\n $ ${command} --api-proxy=http://localhost:1234\n `\n}\n\nexport const cmdLogin = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const apiBaseUrl = cli.flags['apiBaseUrl'] as string | undefined\n const apiProxy = cli.flags['apiProxy'] as string | undefined\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n if (!isInteractive()) {\n throw new InputError(\n 'Cannot prompt for credentials in a non-interactive shell'\n )\n }\n\n await attemptLogin(apiBaseUrl, apiProxy)\n}\n","import { updateSetting } from '../../utils/settings'\n\nexport function applyLogout() {\n updateSetting('apiToken', null)\n updateSetting('apiBaseUrl', null)\n updateSetting('apiProxy', null)\n updateSetting('enforcedOrgs', null)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { applyLogout } from './apply-logout'\n\nexport function attemptLogout() {\n try {\n applyLogout()\n logger.success('Successfully logged out')\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'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\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}\n\n Logs out of the Socket API and clears all Socket credentials from disk\n `\n}\n\nexport const cmdLogout = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n attemptLogout()\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants'\n\nexport async function convertGradleToMaven(\n target: string,\n bin: string,\n _out: string,\n verbose: boolean,\n gradleOpts: string[]\n) {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n const rbin = path.resolve(bin)\n const rtarget = path.resolve(target)\n\n if (verbose) {\n logger.group('gradle2maven:')\n logger.log(`[VERBOSE] - Absolute bin path: \\`${rbin}\\``)\n logger.log(`[VERBOSE] - Absolute target path: \\`${rtarget}\\``)\n logger.groupEnd()\n } else {\n logger.group('gradle2maven:')\n logger.log(`- executing: \\`${bin}\\``)\n logger.log(`- src dir: \\`${target}\\``)\n logger.groupEnd()\n }\n\n try {\n // Run sbt with the init script we provide which should yield zero or more pom files.\n // We have to figure out where to store those pom files such that we can upload them and predict them through the GitHub API.\n // We could do a .socket folder. We could do a socket.pom.gz with all the poms, although I'd prefer something plain-text if it is to be committed.\n\n // Note: init.gradle will be exported by .config/rollup.dist.config.mjs\n const initLocation = path.join(constants.rootDistPath, 'init.gradle')\n const commandArgs = ['--init-script', initLocation, ...gradleOpts, 'pom']\n\n if (verbose) {\n logger.log('[VERBOSE] Executing:', bin, commandArgs)\n }\n\n spinner.start(\n `Converting gradle to maven from \\`${bin}\\` on \\`${target}\\`...`\n )\n\n const output = await spawn(bin, commandArgs, {\n cwd: target || '.'\n })\n\n spinner.stop()\n\n if (verbose) {\n logger.group('[VERBOSE] gradle 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 gradle')\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 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\n // const loc = output.stdout?.match(/Wrote (.*?.pom)\\n/)?.[1]?.trim()\n // if (!loc) {\n // logger.fail(\n // 'There were no errors from sbt but could not find the location of resulting .pom file either'\n // )\n // process.exit(1)\n // }\n //\n // // Move the pom file to ...? initial cwd? loc will be an absolute path, or dump to stdout\n // if (out === '-') {\n // spinner.start('Result:\\n```')\n // spinner.log(await safeReadFile(loc, 'utf8'))\n // spinner.log('```')\n // spinner.successAndStop(`OK`)\n // } else {\n // spinner.start()\n // if (verbose) {\n // spinner.log(\n // `Moving manifest file from \\`${loc.replace(/^\\/home\\/[^/]*?\\//, '~/')}\\` to \\`${out}\\``\n // )\n // } else {\n // spinner.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 // spinner.successAndStop(`OK. File should be available in \\`${out}\\``)\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 path from 'node:path'\n\nimport { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { convertGradleToMaven } from './convert_gradle_to_maven'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\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 cwd: {\n type: 'string',\n description: 'Set the cwd, defaults to process.cwd()'\n },\n gradleOpts: {\n type: 'string',\n default: '',\n description:\n 'Additional options to pass on to ./gradlew, see `./gradlew --help`'\n },\n out: {\n type: 'string',\n default: './socket.pom.xml',\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 task: {\n type: 'string',\n default: 'all',\n description: 'Task to target. By default targets all.'\n },\n verbose: {\n type: 'boolean',\n description: 'Print debug messages'\n }\n },\n help: (command, config) => `\n Usage\n $ ${command} [--gradle=path/to/gradle/binary] [--out=path/to/result] DIR\n\n Options\n ${getFlagListOutput(config.flags, 6)}\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.\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} --gradlew=../gradlew .\n `\n}\n\nexport const cmdManifestGradle = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const verbose = Boolean(cli.flags['verbose'])\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 const target = cli.input[0]\n\n // TODO: I'm 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 if (!target || target === '-' || cli.input.length > 1) {\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(stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - The DIR arg is required ${!target ? colors.red('(missing!)') : target === '-' ? colors.red('(stdin is not supported)') : colors.green('(ok)')}\n\n - Can only accept one DIR (make sure to escape spaces!) ${cli.input.length > 1 ? colors.red(`(received ${cli.input.length}!)`) : colors.green('(ok)')}`)\n return\n }\n\n let bin: string\n if (cli.flags['bin']) {\n bin = cli.flags['bin'] as string\n } else {\n bin = path.join(target, 'gradlew')\n }\n\n let out: string = './socket.pom.xml'\n if (cli.flags['out']) {\n out = cli.flags['out'] as string\n }\n if (cli.flags['stdout']) {\n out = '-'\n }\n\n if (verbose) {\n logger.group()\n logger.log('- target:', target)\n logger.log('- gradle bin:', bin)\n logger.log('- out:', out)\n logger.groupEnd()\n }\n\n let gradleOpts: string[] = []\n if (cli.flags['gradleOpts']) {\n gradleOpts = (cli.flags['gradleOpts'] as string)\n .split(' ')\n .map(s => s.trim())\n .filter(Boolean)\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await convertGradleToMaven(target, bin, out, verbose, gradleOpts)\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants'\nimport { safeReadFile } from '../../utils/fs'\n\nexport async function convertSbtToMaven(\n target: string,\n bin: string,\n out: string,\n verbose: boolean,\n sbtOpts: string[]\n) {\n // Lazily access constants.spinner.\n const { spinner } = constants\n const rbin = path.resolve(bin)\n const rtarget = path.resolve(target)\n\n if (verbose) {\n logger.group('sbt2maven:')\n logger.log(`[VERBOSE] - Absolute bin path: \\`${rbin}\\``)\n logger.log(`[VERBOSE] - Absolute target path: \\`${rtarget}\\``)\n // logger.log(`[VERBOSE] - Absolute out path: \\`${rout}\\``)\n logger.groupEnd()\n } else {\n logger.group('sbt2maven:')\n logger.log(`- executing: \\`${bin}\\``)\n logger.log(`- src dir: \\`${target}\\``)\n // logger.log(`- dst dir: \\`${out}\\``)\n logger.groupEnd()\n }\n\n try {\n spinner.start(`Converting sbt to maven from \\`${bin}\\` on \\`${target}\\`...`)\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'].concat(sbtOpts), {\n cwd: target || '.'\n })\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 to 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] as string, 'utf8'))\n logger.log('```')\n logger.success(`OK`)\n } else if (out === '-') {\n process.exitCode = 1\n logger.fail(\n 'Requested out target was stdout but there are multiple generated files'\n )\n poms.forEach(fn => logger.error('-', fn))\n logger.error('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 { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { convertSbtToMaven } from './convert_sbt_to_maven'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\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 default: 'sbt',\n description: 'Location of sbt binary to use'\n },\n cwd: {\n type: 'string',\n description: 'Set the cwd, defaults to process.cwd()'\n },\n out: {\n type: 'string',\n default: './socket.pom.xml',\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 default: '',\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} [--sbt=path/to/sbt/binary] [--out=path/to/result] FILE|DIR\n\n Options\n ${getFlagListOutput(config.flags, 6)}\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 optionally configure the path to the \\`sbt\\` bin 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} ./build.sbt\n $ ${command} --bin=/usr/bin/sbt ./build.sbt\n `\n}\n\nexport const cmdManifestScala = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const verbose = Boolean(cli.flags['verbose'])\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 const target = cli.input[0]\n\n // TODO: I'm 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 if (!target || target === '-' || cli.input.length > 1) {\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 stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - The DIR or FILE arg is required ${!target ? colors.red('(missing!)') : target === '-' ? colors.red('(stdin is not supported)') : colors.green('(ok)')}\n\n - Can only accept one DIR or FILE (make sure to escape spaces!) ${cli.input.length > 1 ? colors.red(`(received ${cli.input.length}!)`) : colors.green('(ok)')}`\n )\n return\n }\n\n let bin: string = 'sbt'\n if (cli.flags['bin']) {\n bin = cli.flags['bin'] as string\n }\n\n let out: string = './socket.pom.xml'\n if (cli.flags['out']) {\n out = cli.flags['out'] as string\n }\n if (cli.flags['stdout']) {\n out = '-'\n }\n\n if (verbose) {\n logger.group()\n logger.log('- target:', target)\n logger.log('- gradle bin:', bin)\n logger.log('- out:', out)\n logger.groupEnd()\n }\n\n let sbtOpts: string[] = []\n if (cli.flags['sbtOpts']) {\n sbtOpts = (cli.flags['sbtOpts'] as string)\n .split(' ')\n .map(s => s.trim())\n .filter(Boolean)\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await convertSbtToMaven(target, bin, out, verbose, sbtOpts)\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport meow from 'meow'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { cmdManifestGradle } from './cmd-manifest-gradle'\nimport { cmdManifestScala } from './cmd-manifest-scala'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\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 cwd: {\n type: 'string',\n description: 'Set the cwd, defaults to process.cwd()'\n },\n verbose: {\n type: 'boolean',\n default: false,\n description: 'Enable debug output, may help when running into errors'\n }\n // TODO: support output flags\n },\n help: (command, config) => `\n Usage\n $ ${command}\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Tries to figure out what language your current 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}\n\nexport const cmdManifestAuto = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n const verbose = !!cli.flags['verbose']\n const cwd = (cli.flags['cwd'] as string) ?? process.cwd()\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 subArgs = []\n if (verbose) {\n subArgs.push('--verbose')\n }\n\n const dir = cwd\n\n if (existsSync(path.join(dir, 'build.sbt'))) {\n logger.log('Detected a Scala sbt build, running default Scala generator...')\n if (cwd) {\n subArgs.push('--cwd', cwd)\n }\n subArgs.push(dir)\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n await cmdManifestScala.run(subArgs, importMeta, { parentName })\n return\n }\n\n if (existsSync(path.join(dir, 'gradlew'))) {\n logger.log('Detected a gradle build, running default gradle generator...')\n if (cwd) {\n // This command takes the cwd as first arg.\n subArgs.push(cwd)\n }\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n await cmdManifestGradle.run(subArgs, importMeta, { parentName })\n return\n }\n\n // Show new help screen and exit.\n meow(\n `\n $ ${parentName} ${config.commandName}\n\n Unfortunately this script did not discover a supported language in the\n current folder.\n\n - Make sure this script would work with your target build\n - Make sure to run it from the correct folder\n - Make sure the necessary build tools are available (\\`PATH\\`)\n\n If that doesn't work, see \\`${parentName} <lang> --help\\` for config details for\n your target language.\n `,\n {\n argv: [],\n description: config.description,\n importMeta\n }\n ).showHelp()\n}\n","import path from 'node:path'\n\nimport { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { convertGradleToMaven } from './convert_gradle_to_maven'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\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 cwd: {\n type: 'string',\n description: 'Set the cwd, defaults to process.cwd()'\n },\n gradleOpts: {\n type: 'string',\n default: '',\n description:\n 'Additional options to pass on to ./gradlew, see `./gradlew --help`'\n },\n out: {\n type: 'string',\n default: './socket.pom.xml',\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 task: {\n type: 'string',\n default: 'all',\n description: 'Task to target. By default targets all.'\n },\n verbose: {\n type: 'boolean',\n description: 'Print debug messages'\n }\n },\n help: (command, config) => `\n Usage\n $ ${command} [--gradle=path/to/gradle/binary] [--out=path/to/result] DIR\n\n Options\n ${getFlagListOutput(config.flags, 6)}\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} --gradlew=../gradlew .\n `\n}\n\nexport const cmdManifestKotlin = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const verbose = Boolean(cli.flags['verbose'])\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 const target = cli.input[0]\n\n // TODO: I'm 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 if (!target || target === '-' || cli.input.length > 1) {\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 stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - The DIR arg is required ${!target ? colors.red('(missing!)') : target === '-' ? colors.red('(stdin is not supported)') : colors.green('(ok)')}\n\n - Can only accept one DIR (make sure to escape spaces!) ${cli.input.length > 1 ? colors.red(`(received ${cli.input.length}!)`) : colors.green('(ok)')}`\n )\n return\n }\n\n let bin: string\n if (cli.flags['bin']) {\n bin = cli.flags['bin'] as string\n } else {\n bin = path.join(target, 'gradlew')\n }\n\n let out: string = './socket.pom.xml'\n if (cli.flags['out']) {\n out = cli.flags['out'] as string\n }\n if (cli.flags['stdout']) {\n out = '-'\n }\n\n if (verbose) {\n logger.group()\n logger.log('- target:', target)\n logger.log('- gradle bin:', bin)\n logger.log('- out:', out)\n logger.groupEnd()\n }\n\n let gradleOpts: string[] = []\n if (cli.flags['gradleOpts']) {\n gradleOpts = (cli.flags['gradleOpts'] as string)\n .split(' ')\n .map(s => s.trim())\n .filter(Boolean)\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await convertGradleToMaven(target, bin, out, verbose, gradleOpts)\n}\n","import { cmdManifestAuto } from './cmd-manifest-auto'\nimport { cmdManifestGradle } from './cmd-manifest-gradle'\nimport { cmdManifestKotlin } from './cmd-manifest-kotlin'\nimport { cmdManifestScala } from './cmd-manifest-scala'\nimport { commonFlags } from '../../flags'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst config: CliCommandConfig = {\n commandName: 'manifest',\n description: 'Generate a dependency manifest for given file or dir',\n hidden: false,\n flags: {\n ...commonFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <language> <target>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\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} yolo\n `\n}\n\nexport const cmdManifest = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n await meowWithSubcommands(\n {\n auto: cmdManifestAuto,\n scala: cmdManifestScala,\n gradle: cmdManifestGradle,\n kotlin: cmdManifestKotlin\n },\n {\n argv,\n aliases: {\n yolo: {\n description: config.description,\n hidden: true,\n argv: ['auto']\n }\n },\n description: config.description,\n importMeta,\n flags: config.flags,\n name: `${parentName} ${config.commandName}`\n }\n )\n}\n","import constants from '../../constants'\n\nconst { NPM } = constants\n\nexport async function wrapNpm(argv: readonly string[]) {\n // Lazily access constants.distShadowNpmBinPath.\n const shadowBin = require(constants.distShadowNpmBinPath)\n await shadowBin(NPM, argv)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { wrapNpm } from './wrap-npm'\nimport constants from '../../constants'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT, NPM } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'npm',\n description: `${NPM} wrapper functionality`,\n hidden: false,\n flags: {},\n help: (command, _config) => `\n Usage\n $ ${command}\n `\n}\n\nexport const cmdNpm = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n allowUnknownFlags: true,\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await wrapNpm(argv)\n}\n","import constants from '../../constants'\n\nconst { NPX } = constants\n\nexport async function wrapNpx(argv: readonly string[]) {\n // Lazily access constants.distShadowNpmBinPath.\n const shadowBin = require(constants.distShadowNpmBinPath)\n await shadowBin(NPX, argv)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { wrapNpx } from './wrap-npx'\nimport constants from '../../constants'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT, NPX } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'npx',\n description: `${NPX} wrapper functionality`,\n hidden: false,\n flags: {},\n help: (command, _config) => `\n Usage\n $ ${command}\n `\n}\n\nexport const cmdNpx = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n allowUnknownFlags: true,\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await wrapNpx(argv)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'oops',\n description: 'Trigger an intentional error (for development)',\n hidden: true,\n flags: {\n ...commonFlags\n },\n help: (parentName, config) => `\n Usage\n $ ${parentName} ${config.commandName}\n\n Don't run me.\n `\n}\n\nexport const cmdOops = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n throw new Error('This error was intentionally left blank')\n}\n","import constants from '../../constants'\n\nimport type { Agent } from '../../utils/package-environment'\n\ntype AgentDepsIncludesFn = (stdout: string, name: string) => boolean\n\nconst { BUN, NPM, PNPM, VLT, YARN_BERRY, YARN_CLASSIC } = constants\n\nfunction matchLsCmdViewHumanStdout(stdout: string, name: string) {\n return stdout.includes(` ${name}@`)\n}\n\nfunction matchQueryCmdStdout(stdout: string, name: string) {\n return stdout.includes(`\"${name}\"`)\n}\n\nexport const depsIncludesByAgent = new Map<Agent, AgentDepsIncludesFn>([\n [BUN, matchLsCmdViewHumanStdout],\n [NPM, matchQueryCmdStdout],\n [PNPM, matchQueryCmdStdout],\n [VLT, matchQueryCmdStdout],\n [YARN_BERRY, matchLsCmdViewHumanStdout],\n [YARN_CLASSIC, matchLsCmdViewHumanStdout]\n])\n","import { readPackageJson } from '@socketsecurity/registry/lib/packages'\n\ntype PackageJson = Awaited<ReturnType<typeof readPackageJson>>\n\nexport function getDependencyEntries(pkgJson: PackageJson) {\n const {\n dependencies,\n devDependencies,\n optionalDependencies,\n peerDependencies\n } = pkgJson\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 { readPackageJson } from '@socketsecurity/registry/lib/packages'\n\nimport constants from '../../constants'\n\nimport type {\n Agent,\n StringKeyValueObject\n} from '../../utils/package-environment'\n\ntype PackageJson = Awaited<ReturnType<typeof readPackageJson>>\ntype NpmOverrides = { [key: string]: string | StringKeyValueObject }\ntype PnpmOrYarnOverrides = { [key: string]: string }\ntype Overrides = NpmOverrides | PnpmOrYarnOverrides\ntype GetOverrides = (pkgJson: PackageJson) => GetOverridesResult\ntype GetOverridesResult = { type: Agent; overrides: Overrides }\n\nconst {\n BUN,\n NPM,\n OVERRIDES,\n PNPM,\n RESOLUTIONS,\n VLT,\n YARN_BERRY,\n YARN_CLASSIC\n} = constants\n\nfunction getOverridesDataBun(pkgJson: PackageJson) {\n const overrides = (pkgJson as any)?.[RESOLUTIONS] ?? {}\n return { type: YARN_BERRY, overrides }\n}\n\n// npm overrides documentation:\n// https://docs.npmjs.com/cli/v10/configuring-npm/package-json#overrides\nfunction getOverridesDataNpm(pkgJson: PackageJson) {\n const overrides = (pkgJson as any)?.[OVERRIDES] ?? {}\n return { type: NPM, overrides }\n}\n\n// pnpm overrides documentation:\n// https://pnpm.io/package_json#pnpmoverrides\nfunction getOverridesDataPnpm(pkgJson: PackageJson) {\n const overrides = (pkgJson as any)?.pnpm?.[OVERRIDES] ?? {}\n return { type: PNPM, overrides }\n}\n\nfunction getOverridesDataVlt(pkgJson: PackageJson) {\n const overrides = (pkgJson as any)?.[OVERRIDES] ?? {}\n return { type: VLT, overrides }\n}\n\n// Yarn resolutions documentation:\n// https://yarnpkg.com/configuration/manifest#resolutions\nfunction getOverridesDataYarn(pkgJson: PackageJson) {\n const overrides = (pkgJson as any)?.[RESOLUTIONS] ?? {}\n return { type: YARN_BERRY, overrides }\n}\n\n// Yarn resolutions documentation:\n// https://classic.yarnpkg.com/en/docs/selective-version-resolutions\nfunction getOverridesDataClassic(pkgJson: PackageJson) {\n const overrides = (pkgJson as any)?.[RESOLUTIONS] ?? {}\n return { type: YARN_CLASSIC, overrides }\n}\n\nexport const overridesDataByAgent = new Map<Agent, GetOverrides>([\n [BUN, getOverridesDataBun],\n [NPM, getOverridesDataNpm],\n [PNPM, getOverridesDataPnpm],\n [VLT, getOverridesDataVlt],\n [YARN_BERRY, getOverridesDataYarn],\n [YARN_CLASSIC, getOverridesDataClassic]\n])\n","import path from 'node:path'\n\nimport { parse as yamlParse } from 'yaml'\n\nimport { readPackageJson } from '@socketsecurity/registry/lib/packages'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport constants from '../../constants'\nimport { safeReadFile } from '../../utils/fs'\n\nimport type { Agent } from '../../utils/package-environment'\n\ntype PackageJson = Awaited<ReturnType<typeof readPackageJson>>\n\nconst { PNPM } = constants\n\nconst PNPM_WORKSPACE = `${PNPM}-workspace`\n\nexport async function getWorkspaceGlobs(\n agent: Agent,\n pkgPath: string,\n pkgJson: PackageJson\n): Promise<string[] | undefined> {\n let workspacePatterns\n if (agent === PNPM) {\n for (const workspacePath of [\n path.join(pkgPath, `${PNPM_WORKSPACE}.yaml`),\n path.join(pkgPath, `${PNPM_WORKSPACE}.yml`)\n ]) {\n // eslint-disable-next-line no-await-in-loop\n const yml = (await safeReadFile(workspacePath, 'utf8')) as\n | string\n | undefined\n if (yml) {\n try {\n workspacePatterns = yamlParse(yml)?.packages\n } catch {}\n if (workspacePatterns) {\n break\n }\n }\n }\n } else {\n workspacePatterns = pkgJson['workspaces']\n }\n return Array.isArray(workspacePatterns)\n ? workspacePatterns\n .filter(isNonEmptyString)\n .map(workspacePatternToGlobPattern)\n : undefined\n}\n\nfunction workspacePatternToGlobPattern(workspace: string): string {\n const { length } = workspace\n if (!length) {\n return ''\n }\n // If the workspace ends with \"/\"\n if (workspace.charCodeAt(length - 1) === 47 /*'/'*/) {\n return `${workspace}/*/package.json`\n }\n // If the workspace ends with \"/**\"\n if (\n workspace.charCodeAt(length - 1) === 42 /*'*'*/ &&\n workspace.charCodeAt(length - 2) === 42 /*'*'*/ &&\n workspace.charCodeAt(length - 3) === 47 /*'/'*/\n ) {\n return `${workspace}/*/**/package.json`\n }\n // Things like \"packages/a\" or \"packages/*\"\n return `${workspace}/package.json`\n}\n","import { escapeRegExp } from '@socketsecurity/registry/lib/regexps'\n\nimport constants from '../../constants'\n\nimport type { Agent } from '../../utils/package-environment'\n\nexport type AgentLockIncludesFn = (\n lockSrc: string,\n name: string,\n ext?: string | undefined\n) => boolean\n\nconst { BUN, LOCK_EXT, NPM, PNPM, VLT, YARN_BERRY, YARN_CLASSIC } = constants\n\nfunction includesNpm(lockSrc: string, name: string) {\n // Detects the package name in the following cases:\n // \"name\":\n return lockSrc.includes(`\"${name}\":`)\n}\n\nfunction includesBun(lockSrc: string, name: string, lockName?: string) {\n // This is a bit counterintuitive. When lockName ends with a .lockb\n // we treat it as a yarn.lock. When lockName ends with a .lock we\n // treat it as a package-lock.json. The bun.lock format is not identical\n // package-lock.json, however it close enough for npmLockIncludes to work.\n const lockfileScanner = lockName?.endsWith(LOCK_EXT)\n ? includesNpm\n : includesYarn\n return lockfileScanner(lockSrc, name)\n}\n\nfunction includesPnpm(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*)(?:(['/])${escapedName}\\\\1|${escapedName}(?=[:@]))`,\n 'm'\n ).test(lockSrc)\n}\n\nfunction includesVlt(lockSrc: string, name: string) {\n // Detects the package name in the following cases:\n // \"name\"\n return lockSrc.includes(`\"${name}\"`)\n}\n\nfunction includesYarn(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 const lockfileIncludesByAgent = new Map<Agent, AgentLockIncludesFn>([\n [BUN, includesBun],\n [NPM, includesNpm],\n [PNPM, includesPnpm],\n [VLT, includesVlt],\n [YARN_BERRY, includesYarn],\n [YARN_CLASSIC, includesYarn]\n])\n","import { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants'\n\nimport type { Agent } from '../../utils/package-environment'\n\ntype AgentListDepsOptions = { npmExecPath?: string }\n\ntype AgentListDepsFn = (\n agentExecPath: string,\n cwd: string,\n options?: AgentListDepsOptions | undefined\n) => Promise<string>\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)) {\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([...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([...names], null, 2)\n}\n\nasync function npmQuery(npmExecPath: string, cwd: string): Promise<string> {\n let stdout = ''\n try {\n stdout = (await spawn(npmExecPath, ['query', ':not(.dev)'], { cwd })).stdout\n } catch {}\n return cleanupQueryStdout(stdout)\n}\n\nasync function lsBun(agentExecPath: string, cwd: string): Promise<string> {\n try {\n // Bun does not support filtering by production packages yet.\n // https://github.com/oven-sh/bun/issues/8283\n return (await spawn(agentExecPath!, ['pm', 'ls', '--all'], { cwd })).stdout\n } catch {}\n return ''\n}\n\nasync function lsNpm(agentExecPath: string, cwd: string): Promise<string> {\n return await npmQuery(agentExecPath, cwd)\n}\n\nasync function lsPnpm(\n agentExecPath: string,\n cwd: string,\n options?: AgentListDepsOptions | undefined\n): Promise<string> {\n const npmExecPath = options?.npmExecPath\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 agentExecPath,\n // Pnpm uses the alternative spelling of parsable.\n // https://en.wiktionary.org/wiki/parsable\n ['ls', '--parseable', '--prod', '--depth', 'Infinity'],\n { cwd }\n )\n ).stdout\n } catch {}\n return parsableToQueryStdout(stdout)\n}\n\nasync function lsVlt(agentExecPath: string, cwd: string): Promise<string> {\n let stdout = ''\n try {\n // See https://docs.vlt.sh/cli/commands/list#options.\n stdout = (\n await spawn(agentExecPath, ['ls', '--view', 'human', ':not(.dev)'], {\n cwd\n })\n ).stdout\n } catch {}\n return cleanupQueryStdout(stdout)\n}\n\nasync function lsYarnBerry(\n agentExecPath: string,\n cwd: string\n): Promise<string> {\n try {\n return (\n // Yarn Berry does not support filtering by production packages yet.\n // https://github.com/yarnpkg/berry/issues/5117\n (\n await spawn(agentExecPath, ['info', '--recursive', '--name-only'], {\n cwd\n })\n ).stdout.trim()\n )\n } catch {}\n return ''\n}\n\nasync function lsYarnClassic(\n agentExecPath: string,\n cwd: string\n): Promise<string> {\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(agentExecPath, ['list', '--prod'], { cwd })\n ).stdout.trim()\n } catch {}\n return ''\n}\n\nexport const lsByAgent = new Map<Agent, AgentListDepsFn>([\n [BUN, lsBun],\n [NPM, lsNpm],\n [PNPM, lsPnpm],\n [VLT, lsVlt],\n [YARN_BERRY, lsYarnBerry],\n [YARN_CLASSIC, lsYarnClassic]\n])\n","import { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nimport { runAgentInstall } from './run-agent'\nimport constants from '../../constants'\nimport { cmdPrefixMessage } from '../../utils/cmd'\n\nimport type { EnvDetails } from '../../utils/package-environment'\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}\nexport async function updateLockfile(\n pkgEnvDetails: EnvDetails,\n options: UpdateLockfileOptions\n) {\n const {\n cmdName = '',\n logger,\n spinner\n } = {\n __proto__: null,\n ...options\n } as UpdateLockfileOptions\n spinner?.start(`Updating ${pkgEnvDetails.lockName}...`)\n try {\n await runAgentInstall(pkgEnvDetails, { spinner })\n spinner?.stop()\n if (pkgEnvDetails.features.npmBuggyOverrides) {\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 logger?.fail(\n cmdPrefixMessage(\n cmdName,\n `${pkgEnvDetails.agent} install failed to update ${pkgEnvDetails.lockName}`\n )\n )\n logger?.error(e)\n }\n}\n","import { hasKeys, isObject } from '@socketsecurity/registry/lib/objects'\n\nimport constants from '../../constants'\n\nimport type {\n Agent,\n StringKeyValueObject\n} from '../../utils/package-environment'\nimport type { EditablePackageJson } from '@socketsecurity/registry/lib/packages'\n\ntype NpmOverrides = { [key: string]: string | StringKeyValueObject }\ntype PnpmOrYarnOverrides = { [key: string]: string }\ntype Overrides = NpmOverrides | PnpmOrYarnOverrides\ntype AgentModifyManifestFn = (\n pkgJson: EditablePackageJson,\n overrides: Overrides\n) => void\n\nconst {\n BUN,\n NPM,\n OVERRIDES,\n PNPM,\n RESOLUTIONS,\n VLT,\n YARN_BERRY,\n YARN_CLASSIC\n} = constants\n\nconst PNPM_FIELD_NAME = PNPM\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 updatePkgJson(\n editablePkgJson: EditablePackageJson,\n field: string,\n value: any\n) {\n const { content: pkgJson } = editablePkgJson\n const oldValue = pkgJson[field]\n if (oldValue) {\n // The field already exists so we simply update the field value.\n if (field === PNPM_FIELD_NAME) {\n if (hasKeys(value)) {\n editablePkgJson.update({\n [field]: {\n ...(isObject(oldValue) ? oldValue : {}),\n overrides: value\n }\n })\n } else {\n // Properties with undefined values are omitted when saved as JSON.\n editablePkgJson.update(\n (hasKeys(pkgJson[field])\n ? {\n [field]: {\n ...(isObject(oldValue) ? oldValue : {}),\n overrides: undefined\n }\n }\n : { [field]: undefined }) as typeof pkgJson\n )\n }\n } else if (field === OVERRIDES || field === RESOLUTIONS) {\n // Properties with undefined values are omitted when saved as JSON.\n editablePkgJson.update({\n [field]: hasKeys(value) ? value : undefined\n } as typeof pkgJson)\n } else {\n editablePkgJson.update({ [field]: value })\n }\n return\n }\n if (\n (field === OVERRIDES ||\n field === PNPM_FIELD_NAME ||\n 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(pkgJson)\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_FIELD_NAME) {\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, [field, value])\n editablePkgJson.fromJSON(\n `${JSON.stringify(Object.fromEntries(entries), null, 2)}\\n`\n )\n}\n\nfunction updateOverrides(\n editablePkgJson: EditablePackageJson,\n overrides: Overrides\n) {\n updatePkgJson(editablePkgJson, OVERRIDES, overrides)\n}\n\nfunction updateResolutions(\n editablePkgJson: EditablePackageJson,\n overrides: Overrides\n) {\n updatePkgJson(editablePkgJson, RESOLUTIONS, overrides as PnpmOrYarnOverrides)\n}\n\nfunction pnpmUpdatePkgJson(\n editablePkgJson: EditablePackageJson,\n overrides: Overrides\n) {\n updatePkgJson(editablePkgJson, PNPM_FIELD_NAME, overrides)\n}\n\nexport const updateManifestByAgent = new Map<Agent, AgentModifyManifestFn>([\n [BUN, updateResolutions],\n [NPM, updateOverrides],\n [PNPM, pnpmUpdatePkgJson],\n [VLT, updateOverrides],\n [YARN_BERRY, updateResolutions],\n [YARN_CLASSIC, updateResolutions]\n])\n","import path from 'node:path'\n\nimport npa from 'npm-package-arg'\nimport semver from 'semver'\nimport { glob as tinyGlob } from 'tinyglobby'\n\nimport { getManifestData } from '@socketsecurity/registry'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { hasOwn, toSortedObject } from '@socketsecurity/registry/lib/objects'\nimport {\n fetchPackageManifest,\n readPackageJson\n} from '@socketsecurity/registry/lib/packages'\nimport { pEach } from '@socketsecurity/registry/lib/promises'\nimport { Spinner } from '@socketsecurity/registry/lib/spinner'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { depsIncludesByAgent } from './deps-includes-by-agent'\nimport { getDependencyEntries } from './get-dependency-entries'\nimport { overridesDataByAgent } from './get-overrides-by-agent'\nimport { getWorkspaceGlobs } from './get-workspace-globs'\nimport { lockfileIncludesByAgent } from './lockfile-includes-by-agent'\nimport { lsByAgent } from './ls-by-agent'\nimport { updateLockfile } from './update-lockfile'\nimport { updateManifestByAgent } from './update-manifest-by-agent'\nimport constants from '../../constants'\nimport { cmdPrefixMessage } from '../../utils/cmd'\nimport { detectAndValidatePackageEnvironment } from '../../utils/package-environment'\n\nimport type { AgentLockIncludesFn } from './lockfile-includes-by-agent'\nimport type {\n Agent,\n EnvDetails,\n StringKeyValueObject\n} from '../../utils/package-environment'\nimport type { Logger } from '@socketsecurity/registry/lib/logger'\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}\ntype GetOverridesResult = { type: Agent; overrides: Overrides }\ntype NpmOverrides = { [key: string]: string | StringKeyValueObject }\ntype PackageJson = Awaited<ReturnType<typeof readPackageJson>>\ntype PnpmOrYarnOverrides = { [key: string]: string }\ntype Overrides = NpmOverrides | PnpmOrYarnOverrides\n\nconst { NPM, PNPM, YARN_CLASSIC } = constants\n\nconst CMD_NAME = 'socket optimize'\n\nconst manifestNpmOverrides = getManifestData(NPM)\n\nasync function addOverrides(\n pkgPath: string,\n pkgEnvDetails: EnvDetails,\n options?: AddOverridesOptions | undefined\n): Promise<AddOverridesState> {\n const {\n agent,\n agentExecPath,\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 let { pkgJson: editablePkgJson } = pkgEnvDetails\n if (editablePkgJson === undefined) {\n editablePkgJson = await readPackageJson(pkgPath, { editable: true })\n }\n const { content: pkgJson } = editablePkgJson\n const isRoot = pkgPath === rootPath\n const isLockScanned = isRoot && !prod\n const workspaceName = path.relative(rootPath, pkgPath)\n const workspaceGlobs = await getWorkspaceGlobs(agent, pkgPath, pkgJson)\n const isWorkspace = !!workspaceGlobs\n if (\n isWorkspace &&\n agent === PNPM &&\n npmExecPath === NPM &&\n !state.warnedPnpmWorkspaceRequiresNpm\n ) {\n state.warnedPnpmWorkspaceRequiresNpm = true\n logger?.warn(\n cmdPrefixMessage(\n CMD_NAME,\n 'pnpm workspace support requires `npm ls`, falling back to `pnpm list`'\n )\n )\n }\n const thingToScan = isLockScanned\n ? lockSrc\n : await lsByAgent.get(agent)!(agentExecPath, pkgPath, { npmExecPath })\n // The AgentDepsIncludesFn and AgentLockIncludesFn types overlap in their\n // first two parameters. AgentLockIncludesFn accepts an optional third\n // parameter which AgentDepsIncludesFn will ignore so we cast thingScanner\n // as an AgentLockIncludesFn type.\n const thingScanner = (\n isLockScanned\n ? lockfileIncludesByAgent.get(agent)\n : depsIncludesByAgent.get(agent)\n ) as AgentLockIncludesFn\n const depEntries = getDependencyEntries(pkgJson)\n\n const overridesDataObjects = [] as GetOverridesResult[]\n if (pkgJson['private'] || isWorkspace) {\n overridesDataObjects.push(overridesDataByAgent.get(agent)!(pkgJson))\n } else {\n overridesDataObjects.push(\n overridesDataByAgent.get(NPM)!(pkgJson),\n overridesDataByAgent.get(YARN_CLASSIC)!(pkgJson)\n )\n }\n spinner?.setText(\n `Adding overrides${workspaceName ? ` to ${workspaceName}` : ''}...`\n )\n\n const depAliasMap = new Map<string, string>()\n\n const nodeRange = `>=${pkgEnvDetails.minimumNodeVersion}`\n const manifestEntries = manifestNpmOverrides.filter(({ 1: data }) =>\n semver.satisfies(semver.coerce(data.engines.node)!, nodeRange)\n )\n // Chunk package names to process them in parallel 3 at a time.\n await pEach(manifestEntries, 3, async ({ 1: data }) => {\n const { name: sockRegPkgName, package: origPkgName, version } = data\n const major = semver.major(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 errors.\n // https://docs.npmjs.com/cli/v8/using-npm/package-spec#aliases\n if (\n !(\n thisSpec.startsWith(sockOverridePrefix) &&\n semver.coerce(npa(thisSpec).rawSpec)?.version\n )\n ) {\n thisSpec = sockOverrideSpec\n depObj[origPkgName] = thisSpec\n state.added.add(sockRegPkgName)\n if (workspaceName) {\n state.addedInWorkspaces.add(workspaceName)\n }\n }\n depAliasMap.set(origPkgName, thisSpec)\n }\n }\n if (isRoot) {\n // Chunk package names to process them in parallel 3 at a time.\n await pEach(overridesDataObjects, 3, async ({ overrides, type }) => {\n const overrideExists = hasOwn(overrides, origPkgName)\n if (\n overrideExists ||\n thingScanner(thingToScan, origPkgName, lockName)\n ) {\n const oldSpec = overrideExists ? overrides[origPkgName]! : 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 semver.major(\n semver.coerce(npa(thisSpec).rawSpec)?.version ?? version\n ) !== major\n ) {\n const otherVersion = (await fetchPackageManifest(thisSpec))\n ?.version\n if (otherVersion && otherVersion !== version) {\n newSpec = `${sockOverridePrefix}${pin ? otherVersion : `^${semver.major(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 }\n }\n })\n }\n })\n if (workspaceGlobs) {\n const workspacePkgJsonPaths = await tinyGlob(workspaceGlobs, {\n absolute: true,\n cwd: pkgPath!,\n ignore: ['**/node_modules/**', '**/bower_components/**']\n })\n // Chunk package names to process them in parallel 3 at a time.\n await pEach(workspacePkgJsonPaths, 3, async workspacePkgJsonPath => {\n const otherState = await addOverrides(\n path.dirname(workspacePkgJsonPath),\n pkgEnvDetails,\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. This allows\n // us to do the SetA.add(setB.get) pump type-safe without casts.\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 }\n if (state.added.size > 0 || state.updated.size > 0) {\n editablePkgJson.update(Object.fromEntries(depEntries) as PackageJson)\n for (const { overrides, type } of overridesDataObjects) {\n updateManifestByAgent.get(type)!(\n editablePkgJson,\n toSortedObject(overrides)\n )\n }\n await editablePkgJson.save()\n }\n return state\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\nexport async function applyOptimization(\n cwd: string,\n pin: boolean,\n prod: boolean\n) {\n const pkgEnvDetails = await detectAndValidatePackageEnvironment(cwd, {\n cmdName: CMD_NAME,\n logger,\n prod\n })\n if (!pkgEnvDetails) {\n return\n }\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Socket optimizing...')\n\n const state = await addOverrides(pkgEnvDetails.pkgPath, pkgEnvDetails, {\n logger,\n pin,\n prod,\n spinner\n })\n\n spinner.stop()\n\n const addedCount = state.added.size\n const updatedCount = state.updated.size\n const pkgJsonChanged = addedCount > 0 || updatedCount > 0\n if (pkgJsonChanged) {\n if (updatedCount > 0) {\n logger?.log(\n `${createActionMessage('Updated', updatedCount, state.updatedInWorkspaces.size)}${addedCount ? '.' : '🚀'}`\n )\n }\n if (addedCount > 0) {\n logger?.log(\n `${createActionMessage('Added', addedCount, state.addedInWorkspaces.size)} 🚀`\n )\n }\n } else {\n logger?.log('Congratulations! Already Socket.dev optimized 🎉')\n }\n\n if (pkgJsonChanged || pkgEnvDetails.features.npmBuggyOverrides) {\n await updateLockfile(pkgEnvDetails, { cmdName: CMD_NAME, logger, spinner })\n }\n}\n","import process from 'node:process'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { applyOptimization } from './apply-optimization'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'optimize',\n description: 'Optimize dependencies with @socketregistry overrides',\n hidden: false,\n flags: {\n ...commonFlags,\n pin: {\n type: 'boolean',\n default: false,\n description: 'Pin overrides to their latest version'\n },\n prod: {\n type: 'boolean',\n default: false,\n description: 'Only add overrides for production dependencies'\n }\n },\n help: (command, config) => `\n Usage\n $ ${command}\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command}\n $ ${command} --pin\n `\n}\n\nexport const cmdOptimize = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const cwd = process.cwd()\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await applyOptimization(\n cwd,\n Boolean(cli.flags['pin']),\n Boolean(cli.flags['prod'])\n )\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport {\n getLastFiveOfApiToken,\n handleApiCall,\n handleUnsuccessfulApiResponse\n} from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function getOrganization(\n format: 'text' | 'json' | 'markdown' = 'text'\n): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n await printOrganizationsFromToken(apiToken, format)\n}\n\nasync function printOrganizationsFromToken(\n apiToken: string,\n format: 'text' | 'json' | 'markdown' = 'text'\n) {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Fetching organizations...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getOrganizations(),\n 'looking up organizations'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('getOrganizations', result)\n return\n }\n\n spinner.stop()\n\n const organizations = Object.values(result.data.organizations)\n const lastFiveOfApiToken = getLastFiveOfApiToken(apiToken)\n\n switch (format) {\n case 'json': {\n logger.log(\n JSON.stringify(\n organizations.map(o => ({\n name: o.name,\n id: o.id,\n plan: o.plan\n })),\n null,\n 2\n )\n )\n return\n }\n case 'markdown': {\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)\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 key, ending with: ${colors.italic(lastFiveOfApiToken)}\\n`\n )\n logger.log(\n `| Name${' '.repeat(mw1 - 4)} | ID${' '.repeat(mw2 - 2)} | Plan${' '.repeat(mw3 - 4)} |`\n )\n logger.log(\n `| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} | ${'-'.repeat(mw3)} |`\n )\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(\n `| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} | ${'-'.repeat(mw3)} |`\n )\n return\n }\n default: {\n logger.log(\n `List of organizations associated with your API key, ending with: ${colors.italic(lastFiveOfApiToken)}\\n`\n )\n // Just dump\n for (const o of organizations) {\n logger.log(\n `- Name: ${colors.bold(o.name)}, ID: ${colors.bold(o.id)}, Plan: ${colors.bold(o.plan)}`\n )\n }\n }\n }\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getOrganization } from './get-organization'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'organizations',\n description: 'List organizations associated with the API key used',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags\n },\n help: (command, _config) => `\n Usage\n $ ${command}\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n `\n}\n\nexport const cmdOrganization = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const json = Boolean(cli.flags['json'])\n const markdown = Boolean(cli.flags['markdown'])\n if (json && markdown) {\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(stripIndents`\n${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - The json and markdown flags cannot be both set, pick one\n `)\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await getOrganization(json ? 'json' : markdown ? 'markdown' : 'text')\n}\n","import process from 'node:process'\n\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport { getNpmBinPath } from '../../shadow/npm/paths'\n\nexport async function runRawNpm(\n argv: string[] | readonly string[]\n): Promise<void> {\n const spawnPromise = spawn(getNpmBinPath(), argv as string[], {\n stdio: 'inherit'\n })\n // See https://nodejs.org/api/all.html#all_child_process_event-exit.\n spawnPromise.process.on('exit', (code, signalName) => {\n if (signalName) {\n process.kill(process.pid, signalName)\n } else if (code !== null) {\n process.exit(code)\n }\n })\n await spawnPromise\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { runRawNpm } from './run-raw-npm'\nimport constants from '../../constants'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT, NPM } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'raw-npm',\n description: `Temporarily disable the Socket ${NPM} wrapper`,\n hidden: false,\n flags: {},\n help: command => `\n Usage\n $ ${command} <command>\n\n Examples\n $ ${command} install\n `\n}\n\nexport const cmdRawNpm = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n allowUnknownFlags: true,\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await runRawNpm(argv)\n}\n","import process from 'node:process'\n\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport { getNpxBinPath } from '../../shadow/npm/paths'\n\nexport async function runRawNpx(\n argv: string[] | readonly string[]\n): Promise<void> {\n const spawnPromise = spawn(getNpxBinPath(), argv as string[], {\n stdio: 'inherit'\n })\n // See https://nodejs.org/api/all.html#all_child_process_event-exit.\n spawnPromise.process.on('exit', (code, signalName) => {\n if (signalName) {\n process.kill(process.pid, signalName)\n } else if (code !== null) {\n process.exit(code)\n }\n })\n await spawnPromise\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { runRawNpx } from './run-raw-npx'\nimport constants from '../../constants'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT, NPX } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'raw-npx',\n description: `Temporarily disable the Socket ${NPX} wrapper`,\n hidden: false,\n flags: {},\n help: command => `\n Usage\n $ ${command} <command>\n\n Examples\n $ ${command} install\n `\n}\n\nexport const cmdRawNpx = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n allowUnknownFlags: true,\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await runRawNpx(argv)\n}\n","import { debugLog, isDebug } from '@socketsecurity/registry/lib/debug'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { getPackageFilesFullScans } from '../../utils/path-resolve'\nimport { setupSdk } from '../../utils/sdk'\n\nimport type { SocketYml } from '@socketsecurity/config'\nimport type {\n SocketSdkResultType,\n SocketSdkReturnType\n} from '@socketsecurity/sdk'\n\nconst { DRY_RUN_LABEL } = constants\n\nexport async function createReport(\n socketConfig: SocketYml | undefined,\n inputPaths: string[],\n {\n cwd,\n dryRun\n }: {\n cwd: string\n dryRun: boolean\n }\n): Promise<undefined | SocketSdkResultType<'createReport'>> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n const socketSdk = await setupSdk()\n const supportedFiles = await socketSdk\n .getReportSupportedFiles()\n .then(res => {\n if (!res.success)\n handleUnsuccessfulApiResponse('getReportSupportedFiles', res)\n return (res as SocketSdkReturnType<'getReportSupportedFiles'>).data\n })\n .catch((cause: Error) => {\n throw new Error('Failed getting supported files for report', {\n cause\n })\n })\n const packagePaths = await getPackageFilesFullScans(\n cwd,\n inputPaths,\n supportedFiles,\n socketConfig\n )\n const packagePathsCount = packagePaths.length\n if (packagePathsCount && isDebug()) {\n for (const pkgPath of packagePaths) {\n debugLog(`Uploading: ${pkgPath}`)\n }\n }\n if (dryRun) {\n debugLog(`${DRY_RUN_LABEL}: Skipped actual upload`)\n return undefined\n }\n spinner.start(\n `Creating report with ${packagePathsCount} package ${pluralize('file', packagePathsCount)}`\n )\n const apiCall = socketSdk.createReportFromFilePaths(\n packagePaths,\n cwd,\n socketConfig?.issueRules\n )\n const result = await handleApiCall(apiCall, 'creating report')\n if (!result.success) {\n handleUnsuccessfulApiResponse('createReport', result)\n return undefined\n }\n spinner.successAndStop()\n return result\n}\n","import { betterAjvErrors } from '@apideck/better-ajv-errors'\n\nimport { SocketValidationError, readSocketConfig } from '@socketsecurity/config'\n\nimport { InputError } from '../../utils/errors'\n\nexport async function getSocketConfig(absoluteConfigPath: string) {\n const socketConfig = await readSocketConfig(absoluteConfigPath).catch(\n (cause: unknown) => {\n if (\n cause &&\n typeof cause === 'object' &&\n cause instanceof SocketValidationError\n ) {\n // Inspired by workbox-build:\n // https://github.com/GoogleChrome/workbox/blob/95f97a207fd51efb3f8a653f6e3e58224183a778/packages/workbox-build/src/lib/validate-options.ts#L68-L71\n const betterErrors = betterAjvErrors({\n basePath: 'config',\n data: cause.data,\n errors: cause.validationErrors,\n schema: cause.schema as Parameters<\n typeof betterAjvErrors\n >[0]['schema']\n })\n throw new InputError(\n 'The socket.yml config is not valid',\n betterErrors\n .map(\n err =>\n `[${err.path}] ${err.message}.${err.suggestion ? err.suggestion : ''}`\n )\n .join('\\n')\n )\n } else {\n throw new Error('Failed to read socket.yml config', { cause })\n }\n }\n )\n\n return socketConfig\n}\n","import constants from '../../constants'\nimport {\n formatSeverityCount,\n getSeverityCount\n} from '../../utils/alert/severity'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { setupSdk } from '../../utils/sdk'\n\nimport type {\n SocketSdkResultType,\n SocketSdkReturnType\n} from '@socketsecurity/sdk'\n\nexport type ReportData = SocketSdkReturnType<'getReport'>['data']\n\nconst MAX_TIMEOUT_RETRY = 5\nconst HTTP_CODE_TIMEOUT = 524\n\nexport async function fetchReportData(\n reportId: string,\n includeAllIssues: boolean,\n strict: boolean\n): Promise<void | ReportData> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start(`Fetching report with ID ${reportId} (this could take a while)`)\n\n const socketSdk = await setupSdk()\n let result: SocketSdkResultType<'getReport'> | undefined\n for (let retry = 1; !result; ++retry) {\n try {\n // eslint-disable-next-line no-await-in-loop\n result = await handleApiCall(\n socketSdk.getReport(reportId),\n 'fetching report'\n )\n } catch (err) {\n if (\n retry >= MAX_TIMEOUT_RETRY ||\n !(err instanceof Error) ||\n (err.cause as any)?.cause?.response?.statusCode !== HTTP_CODE_TIMEOUT\n ) {\n spinner.stop()\n throw err\n }\n }\n }\n\n if (!result.success) {\n return handleUnsuccessfulApiResponse('getReport', result)\n }\n\n // Conclude the status of the API call.\n if (strict) {\n if (result.data.healthy) {\n spinner.success('Report result is healthy and great!')\n } else {\n spinner.error('Report result deemed unhealthy for project')\n }\n } else if (!result.data.healthy) {\n const severityCount = getSeverityCount(\n result.data.issues,\n includeAllIssues ? undefined : 'high'\n )\n const issueSummary = formatSeverityCount(severityCount)\n spinner.success(`Report has these issues: ${issueSummary}`)\n } else {\n spinner.success('Report has no issues')\n }\n spinner.stop()\n\n return result.data\n}\n","import process from 'node:process'\n\nimport { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { ColorOrMarkdown } from '../../utils/color-or-markdown'\n\nimport type { ReportData } from './fetch-report-data'\n\nexport function formatReportDataOutput(\n reportId: string,\n data: ReportData,\n commandName: string,\n outputJson: boolean,\n outputMarkdown: boolean,\n strict: boolean\n): void {\n if (outputJson) {\n logger.log(JSON.stringify(data, undefined, 2))\n } else {\n const format = new ColorOrMarkdown(outputMarkdown)\n logger.log(stripIndents`\n Detailed info on socket.dev: ${format.hyperlink(reportId, data.url, {\n fallbackToUrl: true\n })}`)\n if (!outputMarkdown) {\n logger.log(\n colors.dim(\n `Or rerun ${colors.italic(commandName)} using the ${colors.italic('--json')} flag to get full JSON output`\n )\n )\n }\n }\n\n if (strict && !data.healthy) {\n process.exit(1)\n }\n}\n","import { fetchReportData } from './fetch-report-data'\nimport { formatReportDataOutput } from './format-report-data'\n\nexport async function viewReport(\n reportId: string,\n {\n all,\n commandName,\n json,\n markdown,\n strict\n }: {\n commandName: string\n all: boolean\n json: boolean\n markdown: boolean\n strict: boolean\n }\n) {\n const result = await fetchReportData(reportId, all, strict)\n if (result) {\n formatReportDataOutput(\n reportId,\n result,\n commandName,\n json,\n markdown,\n strict\n )\n }\n}\n","import path from 'node:path'\nimport process from 'node:process'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { createReport } from './create-report'\nimport { getSocketConfig } from './get-socket-config'\nimport { viewReport } from './view-report'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags, validationFlags } from '../../flags'\nimport { ColorOrMarkdown } from '../../utils/color-or-markdown'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'create',\n description: '[Deprecated] Create a project report',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n ...validationFlags,\n dryRun: {\n type: 'boolean',\n default: false,\n description: 'Only output what will be done without actually doing it'\n },\n view: {\n type: 'boolean',\n shortFlag: 'v',\n default: false,\n description: 'Will wait for and return the created report'\n }\n },\n help: () => `\n This command is deprecated in favor of \\`socket scan create\\`.\n It will be removed in the next major release of the CLI.\n `\n}\n\nexport const cmdReportCreate = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n // TODO: Allow setting a custom cwd and/or configFile path?\n const cwd = process.cwd()\n const absoluteConfigPath = path.join(cwd, 'socket.yml')\n\n const dryRun = Boolean(cli.flags['dryRun'])\n const json = Boolean(cli.flags['json'])\n const markdown = Boolean(cli.flags['markdown'])\n const strict = Boolean(cli.flags['strict'])\n const includeAllIssues = Boolean(cli.flags['all'])\n const view = Boolean(cli.flags['view'])\n\n // Note exiting earlier to skirt a hidden auth requirement\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n const socketConfig = await getSocketConfig(absoluteConfigPath)\n\n const result = await createReport(socketConfig, cli.input, { cwd, dryRun })\n\n const commandName = `${parentName} ${config.commandName}`\n\n if (result?.success) {\n if (view) {\n const reportId = result.data.id\n await viewReport(reportId, {\n all: includeAllIssues,\n commandName,\n json,\n markdown,\n strict\n })\n } else if (json) {\n logger.log(JSON.stringify(result.data, undefined, 2))\n } else {\n const format = new ColorOrMarkdown(markdown)\n logger.log(\n `New report: ${format.hyperlink(result.data.id, result.data.url, { fallbackToUrl: true })}`\n )\n }\n }\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { viewReport } from './view-report'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags, validationFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'view',\n description: '[Deprecated] View a project report',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n ...validationFlags\n },\n help: () => `\n This command is deprecated in favor of \\`socket scan view\\`.\n It will be removed in the next major release of the CLI.\n `\n}\n\nexport const cmdReportView = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const [reportId, ...extraInput] = cli.input\n\n // Validate the input.\n if (extraInput.length || !reportId) {\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(stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Need at least one report ID ${!reportId ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Can only handle a single report ID ${extraInput.length < 2 ? colors.red(`(received ${extraInput.length}!)`) : colors.green('(ok)')}`)\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await viewReport(reportId, {\n all: Boolean(cli.flags['all']),\n commandName: `${parentName} ${config.commandName}`,\n json: Boolean(cli.flags['json']),\n markdown: Boolean(cli.flags['markdown']),\n strict: Boolean(cli.flags['strict'])\n })\n}\n","import { cmdReportCreate } from './cmd-report-create'\nimport { cmdReportView } from './cmd-report-view'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands'\n\nconst description = '[Deprecated] Project report related commands'\n\nexport const cmdReport: CliSubcommand = {\n description,\n hidden: true, // Deprecated in favor of `scan`\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n create: cmdReportCreate,\n view: cmdReportView\n },\n {\n argv,\n description,\n importMeta,\n name: parentName + ' report'\n }\n )\n }\n}\n","import constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function createRepo({\n default_branch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility\n}: {\n orgSlug: string\n repoName: string\n description: string\n homepage: string\n default_branch: string\n visibility: string\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await createRepoWithToken({\n apiToken,\n default_branch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility\n })\n}\n\nasync function createRepoWithToken({\n apiToken,\n default_branch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility\n}: {\n apiToken: string\n orgSlug: string\n repoName: string\n description: string\n homepage: string\n default_branch: string\n visibility: string\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Creating repository...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.createOrgRepo(orgSlug, {\n name: repoName,\n description,\n homepage,\n default_branch,\n visibility\n }),\n 'creating repository'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('createOrgRepo', result)\n return\n }\n\n spinner.successAndStop('Repository created successfully')\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { createRepo } from './create-repo'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'create',\n description: 'Create a repository in an organization',\n hidden: false,\n flags: {\n ...commonFlags,\n repoName: {\n type: 'string',\n shortFlag: 'n',\n default: '',\n description: 'Repository name'\n },\n repoDescription: {\n type: 'string',\n shortFlag: 'd',\n default: '',\n description: 'Repository description'\n },\n homepage: {\n type: 'string',\n shortFlag: 'h',\n default: '',\n description: 'Repository url'\n },\n defaultBranch: {\n type: 'string',\n shortFlag: 'b',\n default: 'main',\n description: 'Repository default branch'\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} <org slug>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg --repoName=test-repo\n `\n}\n\nexport const cmdReposCreate = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const repoName = cli.flags['repoName']\n const [orgSlug = ''] = cli.input\n\n if (!repoName || typeof repoName !== 'string' || !orgSlug) {\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(stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Repository name using --repoName ${!repoName ? colors.red('(missing!)') : typeof repoName !== 'string' ? colors.red('(invalid!)') : colors.green('(ok)')}`)\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await createRepo({\n orgSlug,\n repoName,\n description: String(cli.flags['repoDescription'] || ''),\n homepage: String(cli.flags['homepage'] || ''),\n default_branch: String(cli.flags['defaultBranch'] || ''),\n visibility: String(cli.flags['visibility'] || 'private')\n })\n}\n","import constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function deleteRepo(\n orgSlug: string,\n repoName: string\n): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await deleteRepoWithToken(orgSlug, repoName, apiToken)\n}\n\nasync function deleteRepoWithToken(\n orgSlug: string,\n repoName: string,\n apiToken: string\n): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Deleting repository...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.deleteOrgRepo(orgSlug, repoName),\n 'deleting repository'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('deleteOrgRepo', result)\n return\n }\n\n spinner.successAndStop('Repository deleted successfully')\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { deleteRepo } from './delete-repo'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'del',\n description: 'Delete a repository in an organization',\n hidden: false,\n flags: {\n ...commonFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <org slug> <repo slug>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg test-repo\n `\n}\n\nexport const cmdReposDel = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const [orgSlug = '', repoName = ''] = cli.input\n\n if (!orgSlug || !repoName) {\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(stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Repository name as the second argument ${!repoName ? colors.red('(missing!)') : typeof repoName !== 'string' ? colors.red('(invalid!)') : colors.green('(ok)')}\n\n - At least one TARGET (e.g. \\`.\\` or \\`./package.json\\``)\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await deleteRepo(orgSlug, repoName)\n}\n","// @ts-ignore\nimport chalkTable from 'chalk-table'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function listRepos({\n direction,\n orgSlug,\n outputKind,\n page,\n per_page,\n sort\n}: {\n direction: string\n orgSlug: string\n outputKind: 'json' | 'markdown' | 'print'\n page: number\n per_page: number\n sort: string\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await listReposWithToken({\n apiToken,\n direction,\n orgSlug,\n outputKind,\n page,\n per_page,\n sort\n })\n}\n\nasync function listReposWithToken({\n apiToken,\n direction,\n orgSlug,\n outputKind,\n page,\n per_page,\n sort\n}: {\n apiToken: string\n direction: string\n orgSlug: string\n outputKind: 'json' | 'markdown' | 'print'\n page: number\n per_page: number\n sort: string\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Fetching list of repositories...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getOrgRepoList(orgSlug, {\n sort,\n direction,\n per_page,\n page\n }),\n 'listing repositories'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('getOrgRepoList', result)\n return\n }\n\n spinner.stop('Fetch complete.')\n\n if (outputKind === 'json') {\n const data = result.data.results.map(o => ({\n id: o.id,\n name: o.name,\n visibility: o.visibility,\n defaultBranch: o.default_branch,\n archived: o.archived\n }))\n logger.log(JSON.stringify(data, null, 2))\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: 'archived', name: colors.magenta('Archived') }\n ]\n }\n\n logger.log(chalkTable(options, result.data.results))\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { listRepos } from './list-repos'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'list',\n description: 'List repositories in an organization',\n hidden: false,\n flags: {\n ...commonFlags,\n sort: {\n type: 'string',\n shortFlag: 's',\n default: 'created_at',\n description: 'Sorting option'\n },\n direction: {\n type: 'string',\n default: 'desc',\n description: 'Direction option'\n },\n perPage: {\n type: 'number',\n shortFlag: 'pp',\n default: 30,\n description: 'Number of results per page'\n },\n page: {\n type: 'number',\n shortFlag: 'p',\n default: 1,\n description: 'Page number'\n },\n ...outputFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <org slug>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg\n `\n}\n\nexport const cmdReposList = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const [orgSlug = ''] = cli.input\n\n if (!orgSlug) {\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(stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - At least one TARGET (e.g. \\`.\\` or \\`./package.json\\``)\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await listRepos({\n direction: cli.flags['direction'] === 'asc' ? 'asc' : 'desc',\n orgSlug,\n outputKind: cli.flags['json']\n ? 'json'\n : cli.flags['markdown']\n ? 'markdown'\n : 'print',\n page: Number(cli.flags['page']) || 1,\n per_page: Number(cli.flags['perPage']) || 30,\n sort: String(cli.flags['sort'] || 'created_at')\n })\n}\n","import constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function updateRepo({\n default_branch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility\n}: {\n orgSlug: string\n repoName: string\n description: string\n homepage: string\n default_branch: string\n visibility: string\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await updateRepoWithToken({\n apiToken,\n default_branch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility\n })\n}\n\nasync function updateRepoWithToken({\n apiToken,\n default_branch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility\n}: {\n apiToken: string\n orgSlug: string\n repoName: string\n description: string\n homepage: string\n default_branch: string\n visibility: string\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Updating repository...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.updateOrgRepo(orgSlug, repoName, {\n orgSlug,\n name: repoName,\n description,\n homepage,\n default_branch,\n visibility\n }),\n 'updating repository'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('updateOrgRepo', result)\n return\n }\n\n spinner.successAndStop('Repository updated successfully')\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { updateRepo } from './update-repo'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'update',\n description: 'Update a repository in an organization',\n hidden: false,\n flags: {\n ...commonFlags,\n repoName: {\n type: 'string',\n shortFlag: 'n',\n default: '',\n description: 'Repository name'\n },\n repoDescription: {\n type: 'string',\n shortFlag: 'd',\n default: '',\n description: 'Repository description'\n },\n homepage: {\n type: 'string',\n shortFlag: 'h',\n default: '',\n description: 'Repository url'\n },\n defaultBranch: {\n type: 'string',\n shortFlag: 'b',\n default: 'main',\n description: 'Repository default branch'\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} <org slug>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg\n `\n}\n\nexport const cmdReposUpdate = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const repoName = cli.flags['repoName']\n const [orgSlug = ''] = cli.input\n\n if (!repoName || typeof repoName !== 'string' || !orgSlug) {\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(stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Repository name using --repoName ${!repoName ? colors.red('(missing!)') : typeof repoName !== 'string' ? colors.red('(invalid!)') : colors.green('(ok)')}\n\n - At least one TARGET (e.g. \\`.\\` or \\`./package.json\\``)\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await updateRepo({\n orgSlug,\n repoName,\n description: String(cli.flags['repoDescription'] || ''),\n homepage: String(cli.flags['homepage'] || ''),\n default_branch: String(cli.flags['defaultBranch'] || ''),\n visibility: String(cli.flags['visibility'] || 'private')\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 constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function viewRepo(\n orgSlug: string,\n repoName: string,\n outputKind: 'json' | 'markdown' | 'print'\n): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n await viewRepoWithToken(orgSlug, repoName, apiToken, outputKind)\n}\n\nasync function viewRepoWithToken(\n orgSlug: string,\n repoName: string,\n apiToken: string,\n outputKind: 'json' | 'markdown' | 'print'\n): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Fetching repository data...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getOrgRepo(orgSlug, repoName),\n 'fetching repository'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('getOrgRepo', result)\n return\n }\n\n spinner.stop('Fetched repository data.')\n\n if (outputKind === 'json') {\n const {\n archived,\n created_at,\n default_branch,\n homepage,\n id,\n name,\n visibility\n } = result.data\n logger.log(\n JSON.stringify(\n {\n id,\n name,\n visibility,\n default_branch,\n homepage,\n archived,\n created_at\n },\n null,\n 2\n )\n )\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 { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { viewRepo } from './view-repo'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'view',\n description: 'View repositories in an organization',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n repoName: {\n description: 'The repository to check',\n default: '',\n type: 'string'\n }\n },\n help: (command, config) => `\n Usage\n $ ${command} <org slug>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg\n `\n}\n\nexport const cmdReposView = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const repoName = cli.flags['repoName']\n const [orgSlug = ''] = cli.input\n\n if (!repoName || typeof repoName !== 'string' || !orgSlug) {\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 stripIndents`\n ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${\n !orgSlug ? colors.red('(missing!)') : colors.green('(ok)')\n }\n\n - Repository name using --repoName ${\n !repoName\n ? colors.red('(missing!)')\n : typeof repoName !== 'string'\n ? colors.red('(invalid!)')\n : colors.green('(ok)')\n }\n `\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await viewRepo(\n orgSlug,\n repoName,\n cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print'\n )\n}\n","import { cmdReposCreate } from './cmd-repos-create'\nimport { cmdReposDel } from './cmd-repos-del'\nimport { cmdReposList } from './cmd-repos-list'\nimport { cmdReposUpdate } from './cmd-repos-update'\nimport { cmdReposView } from './cmd-repos-view'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands'\n\nconst description = 'Repositories related commands'\n\nexport const cmdRepos: CliSubcommand = {\n description,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n create: cmdReposCreate,\n view: cmdReposView,\n list: cmdReposList,\n del: cmdReposDel,\n update: cmdReposUpdate\n },\n {\n argv,\n description,\n importMeta,\n name: `${parentName} repos`\n }\n )\n }\n}\n","import { select } from '@socketsecurity/registry/lib/prompts'\nimport { SocketSdk } from '@socketsecurity/sdk'\n\nimport { handleApiCall } from '../../utils/api'\n\nexport async function suggestOrgSlug(\n socketSdk: SocketSdk\n): Promise<string | void> {\n const result = await handleApiCall(\n socketSdk.getOrganizations(),\n 'looking up organizations'\n )\n // Ignore a failed request here. It was not the primary goal of\n // running this command and reporting it only leads to end-user confusion.\n if (result.success) {\n const proceed = await select<string>({\n message:\n 'Missing org name; do you want to use any of these orgs for this scan?',\n choices: Array.from(Object.values(result.data.organizations))\n .map(({ name: slug }) => ({\n name: 'Yes [' + slug + ']',\n value: slug,\n description: `Use \"${slug}\" as the organization`\n }))\n .concat({\n name: 'No',\n value: '',\n description:\n 'Do not use any of these organizations (will end in a no-op)'\n })\n })\n if (proceed) {\n return proceed\n }\n } else {\n // TODO: in verbose mode, report this error to stderr\n }\n}\n","import path from 'node:path'\nimport process from 'node:process'\n\nimport { select } from '@socketsecurity/registry/lib/prompts'\nimport { SocketSdk } from '@socketsecurity/sdk'\n\nimport { handleApiCall } from '../../utils/api'\n\nexport async function suggestRepoSlug(\n socketSdk: SocketSdk,\n orgSlug: string\n): Promise<{\n slug: string\n defaultBranch: string\n} | void> {\n // Same as above, but if there's a repo with the same name as cwd then\n // default the selection to that name.\n const result = await handleApiCall(\n socketSdk.getOrgRepoList(orgSlug, {\n orgSlug,\n sort: 'name',\n direction: 'asc',\n // There's no guarantee that the cwd is part of this page. If it's not\n // then do an additional request and specific search for it instead.\n // This way we can offer the tip of \"do you want to create [cwd]?\".\n perPage: 10,\n page: 0\n }),\n 'looking up known repos'\n )\n // Ignore a failed request here. It was not the primary goal of\n // running this command and reporting it only leads to end-user confusion.\n if (result.success) {\n const currentDirName = dirNameToSlug(path.basename(process.cwd()))\n\n let cwdIsKnown =\n !!currentDirName &&\n result.data.results.some(obj => obj.slug === currentDirName)\n if (!cwdIsKnown && currentDirName) {\n // Do an explicit request so we can assert that the cwd exists or not\n const result = await handleApiCall(\n socketSdk.getOrgRepo(orgSlug, currentDirName),\n 'checking if current cwd is a known repo'\n )\n if (result.success) {\n cwdIsKnown = true\n }\n }\n\n const proceed = await select<string>({\n message:\n 'Missing repo name; do you want to use any of these known repo names for this scan?',\n choices:\n // Put the CWD suggestion at the top, whether it exists or not\n (currentDirName\n ? [\n {\n name: `Yes, current dir [${cwdIsKnown ? currentDirName : `create repo for ${currentDirName}`}]`,\n value: currentDirName,\n description: cwdIsKnown\n ? 'Register a new repo name under the given org and use it'\n : 'Use current dir as repo'\n }\n ]\n : []\n ).concat(\n result.data.results\n .filter(({ slug }) => !!slug && slug !== currentDirName)\n .map(({ slug }) => ({\n name: 'Yes [' + slug + ']',\n value: slug || '', // Filtered above but TS is like nah.\n description: `Use \"${slug}\" as the repo name`\n })),\n {\n name: 'No',\n value: '',\n description: 'Do not use any of these repos (will end in a no-op)'\n }\n )\n })\n\n if (proceed) {\n const repoName = proceed\n let repoDefaultBranch = ''\n // Store the default branch to help with the branch name question next\n result.data.results.some(obj => {\n if (obj.slug === proceed && obj.default_branch) {\n repoDefaultBranch = obj.default_branch\n return\n }\n })\n return { slug: repoName, defaultBranch: repoDefaultBranch }\n }\n } else {\n // TODO: in verbose mode, report this error to stderr\n }\n}\n\nfunction dirNameToSlug(name: string): string {\n // Uses slug specs asserted by our servers\n // Note: this can lead to collisions; eg. slug for `x--y` and `x---y` is `x-y`\n return name\n .toLowerCase()\n .replace(/[^[a-zA-Z0-9_.-]/g, '_')\n .replace(/--+/g, '-')\n .replace(/__+/g, '_')\n .replace(/\\.\\.+/g, '.')\n .replace(/[._-]+$/, '')\n}\n","import { select } from '@socketsecurity/registry/lib/prompts'\nimport { spawnSync } from '@socketsecurity/registry/lib/spawn'\n\nexport async function suggestBranchSlug(\n repoDefaultBranch: string | undefined\n): Promise<string | void> {\n const spawnResult = spawnSync('git', ['branch', '--show-current'])\n const currentBranch = spawnResult.stdout.toString('utf8').trim()\n if (currentBranch && spawnResult.status === 0) {\n const proceed = await select<string>({\n message: 'Use the current git branch as target branch name?',\n choices: [\n {\n name: `Yes [${currentBranch}]`,\n value: currentBranch,\n description: 'Use the current git branch for branch name'\n },\n ...(repoDefaultBranch && repoDefaultBranch !== currentBranch\n ? [\n {\n name: `No, use the default branch [${repoDefaultBranch}]`,\n value: repoDefaultBranch,\n description:\n 'Use the default branch for target repo as the target branch name'\n }\n ]\n : []),\n {\n name: 'No',\n value: '',\n description:\n 'Do not use the current git branch as name (will end in a no-op)'\n }\n ].filter(Boolean)\n })\n if (proceed) {\n return proceed\n }\n }\n}\n","import { select } from '@socketsecurity/registry/lib/prompts'\n\nexport async function suggestTarget(): Promise<string[] | void> {\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 if (proceed) {\n return ['.']\n }\n}\n","import assert from 'node:assert'\nimport process from 'node:process'\nimport readline from 'node:readline/promises'\n\nimport { stripIndents } from 'common-tags'\nimport open from 'open'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { suggestOrgSlug } from './suggest-org-slug'\nimport { suggestRepoSlug } from './suggest-repo-slug'\nimport { suggestBranchSlug } from './suggest_branch_slug'\nimport { suggestTarget } from './suggest_target'\nimport constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getPackageFilesFullScans } from '../../utils/path-resolve'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function createFullScan({\n branchName,\n commitHash: _commitHash,\n commitMessage,\n committers: _committers,\n cwd,\n defaultBranch,\n orgSlug,\n pendingHead,\n pullRequest: _pullRequest,\n readOnly,\n repoName,\n targets,\n tmp\n}: {\n branchName: string\n commitHash: string\n commitMessage: string\n committers: string\n cwd: string\n defaultBranch: boolean\n orgSlug: string\n pendingHead: boolean\n pullRequest: number | undefined\n readOnly: boolean\n repoName: string\n targets: string[]\n tmp: boolean\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n const socketSdk = await setupSdk()\n const supportedFiles = await socketSdk\n .getReportSupportedFiles()\n .then(res => {\n if (!res.success) {\n handleUnsuccessfulApiResponse('getReportSupportedFiles', res)\n assert(\n false,\n 'handleUnsuccessfulApiResponse should unconditionally throw'\n )\n }\n\n return res.data\n })\n .catch((cause: Error) => {\n throw new Error('Failed getting supported files for report', { cause })\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 if (!targets.length) {\n const received = await suggestTarget()\n targets = received ?? []\n updatedInput = true\n }\n\n // // TODO: we'll probably use socket.json or something else soon...\n // const absoluteConfigPath = path.join(cwd, 'socket.yml')\n // const socketConfig = await getSocketConfig(absoluteConfigPath)\n\n const packagePaths = await getPackageFilesFullScans(\n cwd,\n targets,\n supportedFiles\n // socketConfig\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 apiToken = getDefaultToken()\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 let repoDefaultBranch = ''\n\n if (apiToken) {\n if (!orgSlug) {\n const suggestion = await suggestOrgSlug(socketSdk)\n if (suggestion) orgSlug = suggestion\n updatedInput = true\n }\n\n // (Don't bother asking for the rest if we didn't get an org slug above)\n if (orgSlug && !repoName) {\n const suggestion = await suggestRepoSlug(socketSdk, orgSlug)\n if (suggestion) {\n repoDefaultBranch = suggestion.defaultBranch\n repoName = suggestion.slug\n }\n updatedInput = true\n }\n\n // (Don't bother asking for the rest if we didn't get an org/repo above)\n if (orgSlug && repoName && !branchName) {\n const suggestion = await suggestBranchSlug(repoDefaultBranch)\n if (suggestion) branchName = suggestion\n updatedInput = true\n }\n }\n\n if (!orgSlug || !repoName || !branchName || !packagePaths.length) {\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 stripIndents`\n ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Repository name using --repo ${!repoName ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Branch name using --branch ${!branchName ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - At least one TARGET (e.g. \\`.\\` or \\`./package.json\\`) ${\n !packagePaths.length\n ? colors.red(\n targets.length > 0\n ? '(TARGET' +\n (targets.length ? 's' : '') +\n ' contained no matching/supported files!)'\n : '(missing)'\n )\n : colors.green('(ok)')\n }\n\n ${!apiToken ? 'Note: was unable to make suggestions because no API Token was found; this would make command fail regardless' : ''}\n `\n )\n return\n }\n\n if (updatedInput) {\n logger.log(\n 'Note: You can invoke this command next time to skip the interactive questions:'\n )\n logger.log('```')\n logger.log(\n ` socket scan create [other flags...] --repo ${repoName} --branch ${branchName} ${orgSlug} ${targets.join(' ')}`\n )\n logger.log('```')\n }\n\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n if (readOnly) {\n logger.log('[ReadOnly] Bailing now')\n return\n }\n\n spinner.start(`Creating a scan with ${packagePaths.length} packages...`)\n\n const result = await handleApiCall(\n socketSdk.createOrgFullScan(\n orgSlug,\n {\n repo: repoName,\n branch: branchName,\n commit_message: commitMessage,\n make_default_branch: defaultBranch,\n set_as_pending_head: pendingHead,\n tmp\n },\n packagePaths,\n cwd\n ),\n 'Creating scan'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('CreateOrgFullScan', result)\n return\n }\n\n spinner.successAndStop('Scan created successfully')\n\n const link = colors.underline(colors.cyan(`${result.data.html_report_url}`))\n logger.log(`Available at: ${link}`)\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout\n })\n\n const answer = await rl.question(\n 'Would you like to open it in your browser? (y/n)'\n )\n\n if (answer.toLowerCase() === 'y') {\n await open(`${result.data.html_report_url}`)\n }\n rl.close()\n}\n","import process from 'node:process'\n\nimport { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { createFullScan } from './create-full-scan'\nimport constants from '../../constants'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\nimport { getDefaultToken } from '../../utils/sdk'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'create',\n description: 'Create a scan',\n hidden: false,\n flags: {\n repo: {\n type: 'string',\n shortFlag: 'r',\n default: '',\n description: 'Repository name'\n },\n branch: {\n type: 'string',\n shortFlag: 'b',\n default: '',\n description: 'Branch name'\n },\n commitMessage: {\n type: 'string',\n shortFlag: 'm',\n default: '',\n description: 'Commit message'\n },\n commitHash: {\n type: 'string',\n shortFlag: 'ch',\n default: '',\n description: 'Commit hash'\n },\n cwd: {\n type: 'string',\n description: 'working directory, defaults to process.cwd()'\n },\n dryRun: {\n type: 'boolean',\n description:\n 'run input validation part of command without any concrete side effects'\n },\n pullRequest: {\n type: 'number',\n shortFlag: 'pr',\n description: 'Commit hash'\n },\n committers: {\n type: 'string',\n shortFlag: 'c',\n default: '',\n description: 'Committers'\n },\n defaultBranch: {\n type: 'boolean',\n shortFlag: 'db',\n default: false,\n description: 'Make default branch'\n },\n pendingHead: {\n type: 'boolean',\n shortFlag: 'ph',\n default: false,\n description: 'Set as pending head'\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 tmp: {\n type: 'boolean',\n shortFlag: 't',\n default: false,\n description:\n 'Set the visibility (true/false) of the scan in your dashboard'\n },\n view: {\n type: 'boolean',\n shortFlag: 'v',\n default: true,\n description:\n 'Will wait for and return the created report. Use --no-view to disable.'\n }\n },\n // TODO: your project's \"socket.yml\" file's \"projectIgnorePaths\"\n help: (command, config) => `\n Usage\n $ ${command} [...options] <org> <TARGET> [TARGET...]\n\n Uploads the specified \"package.json\" and lock files for JavaScript, Python,\n Go, Scala, Gradle, and Kotlin dependency manifests.\n If any folder is specified, the ones found in there recursively are uploaded.\n\n Supports globbing such as \"**/package.json\", \"**/requirements.txt\", etc.\n\n Ignores any file specified in your project's \".gitignore\" and also has a\n sensible set of default ignores from the \"ignore-by-default\" module.\n\n TARGET should be a FILE or DIR that _must_ be inside the CWD.\n\n When a FILE is given only that FILE is targeted. Otherwise any eligible\n files in the given DIR will be considered.\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} --repo=test-repo --branch=main FakeOrg ./package.json\n `\n}\n\nexport const cmdScanCreate = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const [orgSlug = '', ...targets] = cli.input\n\n const cwd =\n cli.flags['cwd'] && cli.flags['cwd'] !== 'process.cwd()'\n ? String(cli.flags['cwd'])\n : process.cwd()\n\n const { branch: branchName, repo: repoName } = cli.flags\n\n const apiToken = getDefaultToken() // This checks if we _can_ suggest anything\n\n if (!apiToken && (!orgSlug || !repoName || !branchName || !targets.length)) {\n // Without api token we cannot recover because we can't request more info\n // from the server, to match and help with the current cwd/git status.\n //\n // Use exit status of 2 to indicate incorrect usage, generally invalid\n // options or missing arguments.\n // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html\n process.exitCode = 2\n logger.fail(\n stripIndents`\n ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Repository name using --repo ${!repoName ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Branch name using --branch ${!branchName ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - At least one TARGET (e.g. \\`.\\` or \\`./package.json\\`) ${!targets.length ? '(missing)' : colors.green('(ok)')}\n\n (Additionally, no API Token was set so we cannot auto-discover these details)\n `\n )\n return\n }\n\n // Note exiting earlier to skirt a hidden auth requirement\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await createFullScan({\n branchName: branchName as string,\n commitHash: (cli.flags['commitHash'] as string) ?? '',\n commitMessage: (cli.flags['commitMessage'] as string) ?? '',\n committers: (cli.flags['committers'] as string) ?? '',\n cwd,\n defaultBranch: Boolean(cli.flags['defaultBranch']),\n orgSlug,\n pendingHead: Boolean(cli.flags['pendingHead']),\n pullRequest: (cli.flags['pullRequest'] as number) ?? undefined,\n readOnly: Boolean(cli.flags['readOnly']),\n repoName: repoName as string,\n targets,\n tmp: Boolean(cli.flags['tmp'])\n })\n}\n","import constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function deleteOrgFullScan(\n orgSlug: string,\n fullScanId: string\n): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await deleteOrgFullScanWithToken(orgSlug, fullScanId, apiToken)\n}\nexport async function deleteOrgFullScanWithToken(\n orgSlug: string,\n fullScanId: string,\n apiToken: string\n): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Deleting scan...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.deleteOrgFullScan(orgSlug, fullScanId),\n 'Deleting scan'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('deleteOrgFullScan', result)\n return\n }\n\n spinner.successAndStop('Scan deleted successfully')\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { deleteOrgFullScan } from './delete-full-scan'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'del',\n description: 'Delete a scan',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <org slug> <scan ID>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0\n `\n}\n\nexport const cmdScanDel = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const [orgSlug = '', fullScanId = ''] = cli.input\n\n if (!orgSlug || !fullScanId) {\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 stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Full Scan ID to delete as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}`\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await deleteOrgFullScan(orgSlug, fullScanId)\n}\n","// @ts-ignore\nimport chalkTable from 'chalk-table'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function listFullScans({\n direction,\n from_time,\n orgSlug,\n outputKind,\n page,\n per_page,\n sort\n}: {\n direction: string\n from_time: string\n orgSlug: string\n outputKind: 'json' | 'markdown' | 'print'\n page: number\n per_page: number\n sort: string\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await listFullScansWithToken({\n apiToken,\n direction,\n from_time,\n orgSlug,\n outputKind,\n page,\n per_page,\n sort\n })\n}\n\nasync function listFullScansWithToken({\n apiToken,\n direction,\n from_time,\n orgSlug,\n outputKind,\n page,\n per_page,\n sort\n}: {\n apiToken: string\n direction: string\n from_time: string // seconds\n orgSlug: string\n outputKind: 'json' | 'markdown' | 'print'\n page: number\n per_page: number\n sort: string\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Fetching list of scans...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getOrgFullScanList(orgSlug, {\n sort,\n direction,\n per_page,\n page,\n from: from_time\n }),\n 'Listing scans'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('getOrgFullScanList', result)\n return\n }\n\n spinner.stop(`Fetch complete`)\n\n if (outputKind === 'json') {\n logger.log(result.data)\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: '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 branch: d.branch\n }\n })\n\n logger.log(chalkTable(options, formattedResults))\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { listFullScans } from './list-full-scans'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type {\n CliCommandConfig,\n CliSubcommand\n} from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'list',\n description: 'List the full scans for an organization',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\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 direction: {\n type: 'string',\n shortFlag: 'd',\n default: 'desc',\n description: 'Direction option (`desc` or `asc`) - Default is `desc`'\n },\n perPage: {\n type: 'number',\n shortFlag: 'pp',\n default: 30,\n description: 'Results per page - Default is 30'\n },\n page: {\n type: 'number',\n shortFlag: 'p',\n default: 1,\n description: 'Page number - Default is 1'\n },\n fromTime: {\n type: 'string',\n shortFlag: 'f',\n default: '',\n description: 'From time - as a unix timestamp'\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} <org slug>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg\n `\n}\n\nexport const cmdScanList: CliSubcommand = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n) {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const orgSlug = cli.input[0]\n\n if (!orgSlug) {\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 stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}`\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await listFullScans({\n direction: String(cli.flags['direction'] || ''),\n from_time: String(cli.flags['fromTime'] || ''),\n orgSlug,\n outputKind: cli.flags['json']\n ? 'json'\n : cli.flags['markdown']\n ? 'markdown'\n : 'print',\n page: Number(cli.flags['page'] || 1),\n per_page: Number(cli.flags['perPage'] || 30),\n sort: String(cli.flags['sort'] || '')\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function getOrgScanMetadata(\n orgSlug: string,\n scanId: string,\n outputKind: 'json' | 'markdown' | 'print'\n): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await getOrgScanMetadataWithToken(orgSlug, scanId, apiToken, outputKind)\n}\nexport async function getOrgScanMetadataWithToken(\n orgSlug: string,\n scanId: string,\n apiToken: string,\n outputKind: 'json' | 'markdown' | 'print'\n): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Fetching meta data for a full scan...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getOrgFullScanMetadata(orgSlug, scanId),\n 'Listing scans'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('getOrgFullScanMetadata', result)\n return\n }\n\n spinner?.successAndStop('Fetched the meta data\\n')\n\n if (outputKind === 'json') {\n logger.log(result.data)\n } else {\n // Markdown = print\n if (outputKind === 'markdown') {\n logger.log('# Scan meta data\\n')\n }\n logger.log(`Scan ID: ${scanId}\\n`)\n for (const [key, value] of Object.entries(result.data)) {\n if (\n [\n 'id',\n 'updated_at',\n 'organization_id',\n 'repository_id',\n 'commit_hash',\n 'html_report_url'\n ].includes(key)\n )\n continue\n 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}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getOrgScanMetadata } from './get-full-scan-metadata'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type {\n CliCommandConfig,\n CliSubcommand\n} from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'metadata',\n description: \"Get a full scan's metadata\",\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <org slug> <scan id>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0\n `\n}\n\nexport const cmdScanMetadata: CliSubcommand = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const [orgSlug = '', fullScanId = ''] = cli.input\n\n if (!orgSlug || !fullScanId) {\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 stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Full Scan ID to inspect as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}`\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await getOrgScanMetadata(\n orgSlug,\n fullScanId,\n cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print'\n )\n}\n","import constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nimport type { SocketSdkResultType } from '@socketsecurity/sdk'\n\nexport async function streamFullScan(\n orgSlug: string,\n fullScanId: string,\n file: string | undefined\n): Promise<SocketSdkResultType<'getOrgFullScan'> | undefined> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n spinner.start('Fetching scan...')\n\n const socketSdk = await setupSdk(apiToken)\n const data = await handleApiCall(\n socketSdk.getOrgFullScan(\n orgSlug,\n fullScanId,\n file === '-' ? undefined : file\n ),\n 'Fetching a scan'\n )\n\n if (!data?.success) {\n handleUnsuccessfulApiResponse('getOrgFullScan', data)\n return\n }\n\n spinner?.successAndStop(\n file ? `Full scan details written to ${file}` : 'stdout'\n )\n\n return data\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { components } from '@socketsecurity/sdk/types/api'\n\nimport constants from '../../constants'\nimport { handleAPIError, queryAPI } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken } from '../../utils/sdk'\n\nexport async function getFullScan(\n orgSlug: string,\n fullScanId: string\n): Promise<Array<components['schemas']['SocketArtifact']> | undefined> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n spinner.start('Fetching full-scan...')\n\n const response = await queryAPI(\n `orgs/${orgSlug}/full-scans/${encodeURIComponent(fullScanId)}`,\n apiToken\n )\n\n spinner.stop('Fetch complete.')\n\n if (!response.ok) {\n const err = await handleAPIError(response.status)\n logger.fail(\n `${colors.bgRed(colors.white(response.statusText))}: Fetch error: ${err}`\n )\n return\n }\n\n // This is nd-json; each line is a json object\n const jsons = await response.text()\n const lines = jsons.split('\\n').filter(Boolean)\n const data = lines.map(line => {\n try {\n return JSON.parse(line)\n } catch {\n console.error(\n 'At least one line item was returned that could not be parsed as JSON...'\n )\n return {}\n }\n }) as unknown as Array<components['schemas']['SocketArtifact']>\n\n return data\n}\n","import fs from 'node:fs/promises'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { components } from '@socketsecurity/sdk/types/api'\n\nimport { getFullScan } from './get-full-scan'\nimport { mdTable } from '../../utils/markdown'\n\nexport async function viewFullScan(\n orgSlug: string,\n fullScanId: string,\n filePath: string\n): Promise<void> {\n const artifacts: Array<components['schemas']['SocketArtifact']> | undefined =\n await getFullScan(orgSlug, fullScanId)\n if (!artifacts) return\n\n const display = artifacts.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\nSscan ID: ${fullScanId}\n\n${md}\n\nView this report at: https://socket.dev/dashboard/org/${orgSlug}/sbom/${fullScanId}\n `.trim() + '\\n'\n\n if (filePath && filePath !== '-') {\n try {\n await fs.writeFile(filePath, report, 'utf8')\n logger.log(`Data successfully written to ${filePath}`)\n } catch (e) {\n process.exitCode = 1\n logger.fail('There was an error trying to write the json to disk')\n logger.error(e)\n }\n } else {\n logger.log(report)\n }\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { streamFullScan } from './stream-full-scan'\nimport { viewFullScan } from './view-full-scan'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type {\n CliCommandConfig,\n CliSubcommand\n} from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'view',\n description: 'View the raw results of a scan',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <org slug> <scan ID> [path to output file]\n\n When no output path is given the contents is sent to stdout.\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0 ./stream.txt\n `\n}\n\nexport const cmdScanView: CliSubcommand = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const [orgSlug = '', fullScanId = '', file = '-'] = cli.input\n\n if (!orgSlug || !fullScanId) {\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 stripIndents`\n ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Full Scan ID to fetch as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}\n `\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n if (cli.flags['json']) {\n await streamFullScan(orgSlug, fullScanId, file)\n } else {\n await viewFullScan(orgSlug, fullScanId, file)\n }\n}\n","import { cmdScanCreate } from './cmd-scan-create'\nimport { cmdScanDel } from './cmd-scan-del'\nimport { cmdScanList } from './cmd-scan-list'\nimport { cmdScanMetadata } from './cmd-scan-metadata'\nimport { cmdScanView } from './cmd-scan-view'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands'\n\nconst description = 'Full Scan related commands'\n\nexport const cmdScan: CliSubcommand = {\n description,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n create: cmdScanCreate,\n list: cmdScanList,\n del: cmdScanDel,\n metadata: cmdScanMetadata,\n view: cmdScanView\n },\n {\n aliases: {\n // Backwards compat. TODO: Drop next major bump\n stream: {\n description: cmdScanView.description,\n hidden: true,\n argv: ['view'] // Original args will be appended (!)\n }\n },\n argv,\n description,\n importMeta,\n name: parentName + ' scan'\n }\n )\n }\n}\n","import process from 'node:process'\n\n// @ts-ignore\nimport BoxWidget from 'blessed/lib/widgets/box'\n// @ts-ignore\nimport ScreenWidget from 'blessed/lib/widgets/screen'\n// @ts-ignore\nimport TableWidget from 'blessed-contrib/lib/widget/table'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport { queryAPI } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken } from '../../utils/sdk'\n\nimport type { Widgets } from 'blessed' // Note: Widgets does not seem to actually work as code :'(\n\ntype ThreatResult = {\n createdAt: string\n description: string\n id: number\n locationHtmlUrl: string\n packageHtmlUrl: string\n purl: string\n removedAt: string\n threatType: string\n}\n\nexport async function getThreatFeed({\n direction,\n ecosystem,\n filter,\n outputKind,\n page,\n perPage\n}: {\n direction: string\n ecosystem: string\n filter: string\n outputKind: 'json' | 'markdown' | 'print'\n page: string\n perPage: number\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await getThreatFeedWithToken({\n apiToken,\n direction,\n ecosystem,\n filter,\n outputKind,\n page,\n perPage\n })\n}\n\nasync function getThreatFeedWithToken({\n apiToken,\n direction,\n ecosystem,\n filter,\n outputKind,\n page,\n perPage\n}: {\n apiToken: string\n direction: string\n ecosystem: string\n filter: string\n outputKind: 'json' | 'markdown' | 'print'\n page: string\n perPage: number\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n const queryParams = new URLSearchParams([\n ['direction', direction],\n ['ecosystem', ecosystem],\n ['filter', filter],\n ['page', page],\n ['per_page', String(perPage)]\n ])\n\n spinner.start('Fetching Threat Feed data...')\n\n const response = await queryAPI(`threat-feed?${queryParams}`, apiToken)\n const data = (await response.json()) as {\n results: ThreatResult[]\n nextPage: string\n }\n\n spinner.stop('Threat feed data fetched')\n\n if (outputKind === 'json') {\n logger.log(data)\n return\n }\n\n const screen: Widgets.Screen = new ScreenWidget()\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 height: '70%', // Changed from 100% to 70%\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 // Create details box at the bottom\n const detailsBox: Widgets.BoxElement = new BoxWidget({\n bottom: 0,\n height: '30%',\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 // allow control the table with the keyboard\n table.focus()\n\n screen.append(table)\n screen.append(detailsBox)\n\n const formattedOutput = formatResults(data.results)\n const descriptions = data.results.map(d => d.description)\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 // 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 `Ecosystem: ${selectedRow[0]}\\n` +\n `Name: ${selectedRow[1]}\\n` +\n `Version:${selectedRow[2]}\\n` +\n `Threat type:${selectedRow[3]}\\n` +\n `Detected at:${selectedRow[4]}\\n` +\n `Details: ${selectedRow[5]}\\n` +\n `Description: ${descriptions[selectedIndex]}`\n )\n screen.render()\n }\n }\n })\n\n screen.render()\n\n screen.key(['escape', 'q', 'C-c'], () => process.exit(0))\n screen.key(['return'], () => {\n const selectedIndex = table.rows.selected\n screen.destroy()\n const selectedRow = formattedOutput[selectedIndex]\n console.log(selectedRow)\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\nfunction msAtHome(isoTimeStamp: string): string {\n const timeStart = Date.parse(isoTimeStamp)\n const timeEnd = Date.now()\n\n const rtf = new Intl.RelativeTimeFormat('en', {\n numeric: 'always',\n style: 'short'\n })\n\n const delta = timeEnd - timeStart\n if (delta < 60 * 60 * 1000) {\n return rtf.format(-Math.round(delta / (60 * 1000)), 'minute')\n // return Math.round(delta / (60 * 1000)) + ' min ago'\n } else if (delta < 24 * 60 * 60 * 1000) {\n return rtf.format(-(delta / (60 * 60 * 1000)).toFixed(1), 'hour')\n // return (delta / (60 * 60 * 1000)).toFixed(1) + ' hr ago'\n } else if (delta < 7 * 24 * 60 * 60 * 1000) {\n return rtf.format(-(delta / (24 * 60 * 60 * 1000)).toFixed(1), 'day')\n // return (delta / (24 * 60 * 60 * 1000)).toFixed(1) + ' day ago'\n } else {\n return isoTimeStamp.slice(0, 10)\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getThreatFeed } from './get-threat-feed'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'threat-feed',\n description: '[beta] View the threat feed',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n perPage: {\n type: 'number',\n shortFlag: 'pp',\n default: 30,\n description: 'Number of items per page'\n },\n page: {\n type: 'string',\n shortFlag: 'p',\n default: '1',\n description: 'Page token'\n },\n direction: {\n type: 'string',\n shortFlag: 'd',\n default: 'desc',\n description: 'Order asc or desc by the createdAt attribute.'\n },\n eco: {\n type: 'string',\n shortFlag: 'e',\n default: '',\n description: 'Only show threats for a particular ecosystem'\n },\n filter: {\n type: 'string',\n shortFlag: 'f',\n default: 'mal',\n description: 'Filter what type of threats to return'\n }\n },\n help: (command, config) => `\n Usage\n $ ${command}\n\n This feature requires a Threat Feed license. Please contact\n sales@socket.dev if you are interested in purchasing this access.\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Valid 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 Valid ecosystems:\n\n - gem\n - golang\n - maven\n - npm\n - nuget\n - pypi\n\n Examples\n $ ${command}\n $ ${command} --perPage=5 --page=2 --direction=asc --filter=joke\n `\n}\n\nexport const cmdThreatFeed = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await getThreatFeed({\n direction: String(cli.flags['direction'] || 'desc'),\n ecosystem: String(cli.flags['eco'] || ''),\n filter: String(cli.flags['filter'] || 'mal'),\n outputKind: cli.flags['json']\n ? 'json'\n : cli.flags['markdown']\n ? 'markdown'\n : 'print',\n page: String(cli.flags['page'] || '1'),\n perPage: Number(cli.flags['perPage']) || 30\n })\n}\n","import fs from 'node:fs'\n\nimport { stripIndents } from 'common-tags'\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 // TODO: pretty sure you need to source the file or restart\n // any terminal session before changes are reflected.\n logger.log(\n stripIndents`\nThe alias was added to ${file}. Running 'npm install' will now be wrapped in Socket's \"safe npm\" 🎉\nIf you want to disable it at any time, run \\`socket wrapper --disable\\`\n`\n )\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 return true\n }\n return false\n}\n","import { existsSync } from 'node:fs'\nimport process from 'node:process'\nimport readline from 'node:readline'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { addSocketWrapper } from './add-socket-wrapper'\nimport { checkSocketWrapperSetup } from './check-socket-wrapper-setup'\nimport constants from '../../constants'\n\nexport function postinstallWrapper() {\n // Lazily access constants.bashRcPath and constants.zshRcPath.\n const { bashRcPath, zshRcPath } = constants\n const socketWrapperEnabled =\n (existsSync(bashRcPath) && checkSocketWrapperSetup(bashRcPath)) ||\n (existsSync(zshRcPath) && checkSocketWrapperSetup(zshRcPath))\n\n if (!socketWrapperEnabled) {\n installSafeNpm(`The Socket CLI is now successfully installed! 🎉\n\n To better protect yourself against supply-chain attacks, our \"safe npm\" wrapper can warn you about malicious packages whenever you run 'npm install'.\n\n Do you want to install \"safe npm\" (this will create an alias to the socket-npm command)? (y/n)`)\n }\n}\n\nfunction installSafeNpm(query: string): void {\n logger.log(`\n _____ _ _\n| __|___ ___| |_ ___| |_\n|__ | . | _| '_| -_| _|\n|_____|___|___|_,_|___|_|\n\n`)\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout\n })\n return askQuestion(rl, query)\n}\n\nfunction askQuestion(rl: readline.Interface, query: string): void {\n rl.question(query, (ans: string) => {\n if (ans.toLowerCase() === 'y') {\n // Lazily access constants.bashRcPath and constants.zshRcPath.\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(`There was an issue setting up the alias: ${e}`)\n }\n rl.close()\n } else if (ans.toLowerCase() !== 'n') {\n askQuestion(\n rl,\n 'Incorrect input: please enter either y (yes) or n (no): '\n )\n } else {\n rl.close()\n }\n })\n}\n","import fs from 'node:fs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nexport function removeSocketWrapper(file: string): void {\n return fs.readFile(file, 'utf8', function (err, data) {\n if (err) {\n logger.fail('There was an error removing the alias:')\n logger.error(err)\n return\n }\n const linesWithoutSocketAlias = data\n .split('\\n')\n .filter(\n l => l !== 'alias npm=\"socket npm\"' && l !== 'alias npx=\"socket npx\"'\n )\n\n const updatedFileContent = linesWithoutSocketAlias.join('\\n')\n\n fs.writeFile(file, updatedFileContent, function (err) {\n if (err) {\n logger.error(err)\n return\n }\n // TODO: pretty sure you need to source the file or restart\n // any terminal session before changes are reflected.\n logger.log(\n `The alias was removed from ${file}. Running 'npm install' will now run the standard npm command.`\n )\n })\n })\n}\n","import { existsSync } from 'node:fs'\n\nimport { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { addSocketWrapper } from './add-socket-wrapper'\nimport { checkSocketWrapperSetup } from './check-socket-wrapper-setup'\nimport { postinstallWrapper } from './postinstall-wrapper'\nimport { removeSocketWrapper } from './remove-socket-wrapper'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\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 enable: {\n type: 'boolean',\n default: false,\n description: 'Enables the Socket npm/npx wrapper'\n },\n disable: {\n type: 'boolean',\n default: false,\n description: 'Disables the Socket npm/npx wrapper'\n }\n },\n help: (command, config) => `\n Usage\n $ ${command} <flag>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} --enable\n $ ${command} --disable\n `\n}\n\nexport const cmdWrapper = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n // I don't think meow would mess with this but ...\n if (argv[0] === '--postinstall') {\n postinstallWrapper()\n return\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const { enable } = cli.flags\n if (!enable && !cli.flags['disable']) {\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 stripIndents`\n ${colors.bgRed(colors.white('Input error'))}: Please provide the required flags:\n\n - Must use --enabled or --disabled\n `\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n // Lazily access constants.bashRcPath and constants.zshRcPath.\n const { bashRcPath, zshRcPath } = constants\n if (enable) {\n if (existsSync(bashRcPath) && !checkSocketWrapperSetup(bashRcPath)) {\n addSocketWrapper(bashRcPath)\n }\n if (existsSync(zshRcPath) && !checkSocketWrapperSetup(zshRcPath)) {\n addSocketWrapper(zshRcPath)\n }\n } else {\n if (existsSync(bashRcPath)) {\n removeSocketWrapper(bashRcPath)\n }\n if (existsSync(zshRcPath)) {\n removeSocketWrapper(zshRcPath)\n }\n }\n if (!existsSync(bashRcPath) && !existsSync(zshRcPath)) {\n logger.fail('There was an issue setting up the alias in your bash profile')\n }\n}\n","#!/usr/bin/env node\n\nimport process from 'node:process'\nimport { pathToFileURL } from 'node:url'\n\nimport { messageWithCauses, stackWithCauses } from 'pony-cause'\nimport updateNotifier from 'tiny-updater'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { cmdAction } from './commands/action/cmd-action'\nimport { cmdAnalytics } from './commands/analytics/cmd-analytics'\nimport { cmdAuditLog } from './commands/audit-log/cmd-audit-log'\nimport { cmdCdxgen } from './commands/cdxgen/cmd-cdxgen'\nimport { cmdScanCreate } from './commands/dependencies/cmd-dependencies'\nimport { cmdDiffScan } from './commands/diff-scan/cmd-diff-scan'\nimport { cmdFix } from './commands/fix/cmd-fix'\nimport { cmdInfo } from './commands/info/cmd-info'\nimport { cmdLogin } from './commands/login/cmd-login'\nimport { cmdLogout } from './commands/logout/cmd-logout'\nimport { cmdManifest } from './commands/manifest/cmd-manifest'\nimport { cmdNpm } from './commands/npm/cmd-npm'\nimport { cmdNpx } from './commands/npx/cmd-npx'\nimport { cmdOops } from './commands/oops/cmd-oops'\nimport { cmdOptimize } from './commands/optimize/cmd-optimize'\nimport { cmdOrganization } from './commands/organization/cmd-organization'\nimport { cmdRawNpm } from './commands/raw-npm/cmd-raw-npm'\nimport { cmdRawNpx } from './commands/raw-npx/cmd-raw-npx'\nimport { cmdReport } from './commands/report/cmd-report'\nimport { cmdRepos } from './commands/repos/cmd-repos'\nimport { cmdScan } from './commands/scan/cmd-scan'\nimport { cmdThreatFeed } from './commands/threat-feed/cmd-threat-feed'\nimport { cmdWrapper } from './commands/wrapper/cmd-wrapper'\nimport constants from './constants'\nimport { AuthError, InputError, captureException } from './utils/errors'\nimport { meowWithSubcommands } from './utils/meow-with-subcommands'\n\nconst { SOCKET_CLI_BIN_NAME, rootPkgJsonPath } = constants\n\n// TODO: Add autocompletion using https://socket.dev/npm/package/omelette\nvoid (async () => {\n await updateNotifier({\n name: SOCKET_CLI_BIN_NAME,\n version: require(rootPkgJsonPath).version,\n ttl: 86_400_000 /* 24 hours in milliseconds */\n })\n\n try {\n await meowWithSubcommands(\n {\n action: cmdAction,\n cdxgen: cmdCdxgen,\n fix: cmdFix,\n info: cmdInfo,\n login: cmdLogin,\n logout: cmdLogout,\n npm: cmdNpm,\n npx: cmdNpx,\n oops: cmdOops,\n optimize: cmdOptimize,\n organization: cmdOrganization,\n 'raw-npm': cmdRawNpm,\n 'raw-npx': cmdRawNpx,\n report: cmdReport,\n wrapper: cmdWrapper,\n scan: cmdScan,\n 'audit-log': cmdAuditLog,\n repos: cmdRepos,\n dependencies: cmdScanCreate,\n analytics: cmdAnalytics,\n 'diff-scan': cmdDiffScan,\n 'threat-feed': cmdThreatFeed,\n manifest: cmdManifest\n },\n {\n aliases: {\n ci: {\n description: 'Alias for \"report create --view --strict\"',\n argv: ['report', 'create', '--view', '--strict']\n }\n },\n argv: process.argv.slice(2),\n name: SOCKET_CLI_BIN_NAME,\n importMeta: { url: `${pathToFileURL(__filename)}` } as ImportMeta\n }\n )\n } catch (e) {\n process.exitCode = 1\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 logger.fail(\n `${colors.bgRed(colors.white(`${errorTitle}:`))} ${errorMessage}`\n )\n if (errorBody) {\n logger.error(`\\n${errorBody}`)\n }\n await captureException(e)\n }\n})()\n"],"names":["id","body","body_list","newPackages","removedPackages","newAlerts","sbom","reportUrl","diffUrl","created_at","updated_at","organizationId","repositoryId","branch","commit_message","commit_hash","pull_request","sbom_artifacts","constructor","pkg_type","pkg_name","pkg_version","category","type","severity","pkg_id","key","error","warn","ignore","monitor","description","title","emoji","next_step_title","suggestion","introduced_by","manifests","url","purl","arr","name","version","release","direct","manifestFiles","author","size","alerts","topLevelAncestors","transitives","license","license_text","supplyChain","quality","overall","vulnerability","critical","high","middle","low","ecosystem","capabilities","author_url","generateAuthorData","socket","fullScanId","data","params","fullScan","getSourceData","pkg","file","createPurl","packages","nextStepTitle","issueAlert","compareIssueAlerts","newScanAlerts","consolidatedAlerts","checkAlertCapabilities","envVars","networkAccess","filesystemAccess","shellAccess","newAlert","compareCapabilities","headPackage","packageId","newScan","diff","consolidated","headScanAlerts","headPackages","getLicenseDetails","package","logger","topLevelCount","headFullScanId","headFullScan","headScan","diffReport","comments","security","overview","comment","socketComments","ignoreAll","result","ignoredPackages","line","start","command","ignoreCommands","octokit","checkEventType","commentId","comment_id","content","securityComment","rows","md","removedLine","sources","nextSteps","mdTable","changedFiles","newSecurityComment","newOverviewComment","API_V0_URL","process","cause","method","headers","keyPrefix","padName","help","default","shortFlag","dryRun","json","markdown","all","strict","REDACTED","__proto__","parentName","hidden","allowUnknownFlags","DRY_RUN_BAIL_TEXT","commandName","flags","githubEventBefore","githubEventAfter","run","handleUnsuccessfulApiResponse","mw2","lines","cols","time","spinner","screen","label","barWidth","barSpacing","xOffset","maxHeight","barBgColor","formattedData","totalTopAlerts","sortedTopFiveAlerts","top_five_alert_types","formatted","style","text","baseline","xLabelPadding","xPadding","wholeNumbersOnly","legend","width","x","y","repo","scope","filePath","perPage","logType","desc","org","logs","user_email","year","month","day","choices","pageSize","per_page","page","PNPM","cleanupPackageLock","configuration","coerce","filter","only","profile","standard","lifecycle","alias","array","boolean","argv","length","outputJson","offset","columns","field","limit","apiToken","fs","showHidden","depth","colors","maxArrayLength","after","before","get","NPM","path","consolidate","include","existing","unfixable","upgrade","editable","vulnerableVersionRange","stdio","pkgJson","arb2","remaining","i","SOCKET_IPC_HANDSHAKE","args","constants","env","spawnPromise","agentExecPath","ignoreIncompatible","YARN_CLASSIC","nothrow","cwd","onUnknown","agent","agentVersion","browser","node","lockSrc","lockName","lockPath","features","npmBuggyOverrides","targets","cmdName","prod","acc","count","fallbackToUrl","severityCount","pkgVersion","Maintenance","Quality","Vulnerabilities","Object","logPackageIssuesDetails","includeAllIssues","updateSetting","SOCKET_PUBLIC_API_TOKEN","apiBaseUrl","apiProxy","message","value","enforcedOrgs","applyLogout","attemptLogout","bin","gradleOpts","out","stdout","task","verbose","poms","sbtOpts","subArgs","meow","importMeta","auto","scala","gradle","kotlin","aliases","yolo","NPX","peerDependencies","overrides","workspacePatterns","pkgs","pkgid","names","NPM_BUGGY_OVERRIDES_PATCHED_VERSION","isPlacingHigher","insertIndex","entries","updatePkgJson","pkgPath","state","added","addedInWorkspaces","updated","updatedInWorkspaces","warnedPnpmWorkspaceRequiresNpm","editablePkgJson","npmExecPath","overridesDataObjects","depAliasMap","thisSpec","depObj","newSpec","absolute","updateManifestByAgent","pin","mw1","mw3","DRY_RUN_LABEL","debugLog","basePath","formatReportDataOutput","view","create","visibility","repoName","repoDescription","homepage","defaultBranch","sort","direction","outputKind","list","del","update","cwdIsKnown","slug","commitHash","committers","pullRequest","tmp","assert","updatedInput","make_default_branch","set_as_pending_head","commitMessage","pendingHead","readOnly","branchName","from","fromTime","untilTime","console","score","metadata","stream","keys","fg","selectedFg","selectedBg","interactive","height","border","columnWidth","columnSpacing","truncate","bottom","table","numeric","eco","zshRcPath","installSafeNpm","rl","askQuestion","enable","disable","postinstallWrapper","rootPkgJsonPath","action","cdxgen","fix","info","login","logout","npm","npx","oops","optimize","organization","report","wrapper","scan","repos","dependencies","analytics","manifest","ci","errorTitle","errorMessage","errorBody"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;AAqBO;AACLA;AACAC;AACAC;;;;;AAMA;AACF;AAEO;AACLC;;AAEAC;AACAC;AACAL;AACAM;;AAEAC;AACAC;AACF;AAEO;AACLR;AACAS;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;;AAGAC;;;;;;;;;;;;AAYA;AACF;AAEO;AACLC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;;AAEAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;;;;;;;;;;;;;;;;;;;;AA0CI;AACE;AACAC;AACF;;AAEF;AACF;AACF;AAEO;AACLjB;AACAkB;AACAC;AACAC;AACA3C;AACA4C;AACAC;AACAC;AACAC;;AAGAC;;AAEAC;AACAX;AACAY;AACAC;AACAC;AACAb;;;;;;;;;;;;;AA6BE;;AAGIc;AACAC;AACAH;AACAI;AACAC;AACF;;AAGAC;AACAC;AACAC;AACAC;;AAEF;AACF;AACF;AAEO;AACL5D;AACAyC;AACAC;AACAmB;AACAjB;AACAE;AACAC;AACAG;AACAd;AACA0B;AACA;AACAC;AACAzB;AACAC;;;;;;;;;;;;;AA2BE;AACF;AAEQyB;;AAEN;AACE;;AAEF;AACA;AACF;AACF;AAEO;AACLX;AACAC;AACAH;AACAI;AACAC;;;;;;;AAQA;AACF;;AC1RA;AAcO;;AAULtC;;;AAGE+C;AACiD;;;;;AAKnD;AAEA;AACEC;AAGF;AACE;AAKA;AACE;AACF;;AAEQC;AAAgC;;;AAOxC;AAEA;AACF;AAEA;AACEC;AAGF;;AAGI;AACA;AACA;;;AAAiD;AAInD;;AAEA;;AAEQpE;;;;AAGNqE;AAAmDH;AAAW;AAChE;AACA;AACF;AAEAI;;AAEEC;AAIF;;;;AAI+CC;AAAK;;AAGlD;AACE;AACE;;AAGE;AACF;AAEA;;AAEUA;AAAK;;AAIjB;AACF;AAEA;AACF;AAEAC;;AAEEC;AAIF;AACE;AACA;;AAA+CA;AAAS;AACxD;;;;;;AAMEtC;;;;;;AAMF;;;AACemC;;AACjB;AAEA;;;AAGEA;AAKF;;AAKE;AACE;;;;;;AAQExC;AACAC;AACAG;AACAwC;AACF;AAEA;;AAA+CD;AAAS;AAExD;;;;;;;;;;;;AAYExC;AACAE;;;AAGAT;AACAE;AACAD;AACAE;AACF;AAEA;;;AAGI8C;AACF;AACF;AAEA;AACE;;AAEA;;AAEA;AACF;AACF;AAEA;AACF;AAEAC;;;AAGEC;AAKF;AACE;AAEA;AACE;AACE;AAEA;AACE;AAEA;AACE;AACE9B;AACA+B;AACF;AACF;AACF;AACF;AACE;AACA;AAEA;AACE;AACA;AAIE;AACE/B;AACA+B;AACF;AACF;AACF;AACF;AACF;AAEA;AACF;AAEAC;;;;AAIET;AAMF;AACE;AACEU;AACAC;AACAC;AACAC;;AAGF;;AAEE;AACEC;AACF;AACA;AACE;AACA;AACEvB;AACF;;AAEIA;AACF;AACF;AACF;AACF;AAEA;AACF;AAEAwB;;AAEEnF;AAIF;;AAGE;AACE;;AAGE;AACA;;AAEI2D;;;;AAIEyB;AACF;AACF;AACF;AACF;AACEzB;;;AAGE0B;AACF;AACF;AACF;AAEA;AACF;;;AAKE;AACE;;AAGE;;AAEErF;AACF;AACF;AACEA;AACF;AACF;;AAGA;AACF;AAEA;;AAEEsF;AAIF;AACE;AACA;AACA;;;AAIA;AAEA;;;AACelD;AAAK;;AAEhBmC;AACF;AACA;AAEA;AAKEgB;AACAC;AACF;AACA;AACAb;;AAEE9B;AACA0B;AACF;AACF;AAEA;;;AACenC;AAAK;;AAEhBmC;AACF;;AAGEgB;AACF;AACA;AACAE;;AAEE5C;AACA0B;AACF;AACF;AAEAgB;;;;AAIA;AACAA;;AAEEG;AACF;AACAH;AAEA;AACF;;;;;;;;;;;;;;;;AAqBM;AAEN;AAEAI;AAAoBC;AAAmC;;AAOrD;;;AAGA;AACA;AACF;;;;AAQE;AACE;;;;;;;;;;;;;;AAcA;AAEA;AACEC;AACF;AACEzB;AAA+BwB;AAAa;AAC5CrB;;AAGE;AACEuB;AACF;AACEA;AACF;AACF;AACF;AACF;;AAGE;AACE;AACA;;AAEA;AACF;AACF;AAEA;AACF;AAEA;AACE7B;AAMF;;;;AAKI;;AAKE8B;;AAEEC;AAAwCjC;AAA2B;AACrE;AACF;;AAEA8B;AACF;AAEA;AAAgD5B;AAAO;;AAGvD;;AAEEgC;AACF;AACAC;;AAGAA;AACAA;;AAEEA;AACF;AACEA;AACF;AAEA;AACF;AACF;;AC/iBA;AAWO;AACLC;AAGF;AACE;AAKEC;AACAC;AACA3E;;AAGF;AACE;;;;;AAMA;AACE;AACA;AACA;AACA;AACA4E;AAKAC;AACF;AACF;AAEA;AACF;;AAEA;AACA;AACA;AACO;AACL;AAAiBhC;AAA0BiC;;AAC3C;;;AAGE;AACF;;AAEE;AACE;AACAC;AACF;AACA;AACF;AACA;AACF;;AAEA;AACO;AACL/E;AACA0E;AAC2C;;;;;AAM3C;AACE;;AAIEI;AACA;AACF;AACAE;AACF;;AAEA;AACA;AACA;AACEC;AAEA;AACEC;AACAH;;AAIA;AACA;AAMA;AACA;;AAGA;;;;AAMA;;AAEA;AACE/E;AACF;;AAEI;AAIEA;AACA;AACF;AACF;AACF;AAEA;AACE;AACF;AACA+E;;AAEAG;AACAH;AACF;AACEA;AACF;AACF;AAEA;AACF;AAEO;AAA4BN;AAAuC;;;AAIxE;AACE;;;;;AAKIU;;AAEEL;AACF;AACEK;;AAEA;AACAC;AACF;;AAEAjB;AACAA;AACF;AACF;AACF;;;AACoBiB;;AACtB;AAEO;;AAEL5G;AAIF;;AAGE;AACE;AACF;;;AAEmB4G;;AACjBX;AACF;AAEA;AACE;AACE;AACF;;;AAGE;AACA;AACEN;AACF;AACEA;AAGAhD;AACF;AACF;AACF;AAEA;AACF;;ACtNA;AACA;AAQO;AACLkE;AAKAhG;;AAIE;;;;;AAOF;AAEAiG;AACE;AACE;AACE;AACF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;;AAEE;AACF;;AAEE;AACF;AACEnB;;AAEF;AACF;AACE;AACF;;AAIF;AACF;;;AAGU7B;;;;;AAKN;;AAEF;;;AAGIlE;;AAEF;AACF;;AAC4CqG;AAAS;AACvD;AAEA;AACEc;AAGF;;AACUjD;;;;AAGNkD;AACF;;AAEF;AAEA;AAAqBD;AAAiC;AACpD;;;AAGEC;AACAC;AACF;AACF;AAEA;AACEhB;AAGF;AACE;AACE;;;;;AAMwD;AACxD;AACF;AACF;AAEA;;AAA4BtG;AAAiC;AAC3D;;;AAGEqH;AACApH;AACF;AACF;AAEA;AACEqG;AAGF;AACE;;AAEE;;;AAGA;;AACkCA;AAAS;;;AACMrG;AAAc;AACjE;AACF;AAEA;AAAoBA;AAAuB;AACzC;;;;AAIEA;AACF;AACF;AAEA;;;;;AAKEsH;AAOF;;AAEIf;AACAD;AACF;AACA;AACEP;;AAEEA;;AAEE/F;;AAEF;AACF;AACE+F;;AACyB/F;AAAsB;AACjD;AACF;AACA;AACE+F;;AAEEA;;AAEE/F;;AAEF;AACF;AACE+F;;AACyB/F;AAAsB;AACjD;AACF;AACF;AACF;;ACjMA;;AAyBO;;AAEL;AACF;AAEO;AACL;;AASA;AACE;;;AAUAuH;AACF;;;AAIAC;AACA;;AAEA;AAEA;AACF;AAEO;AACL;AACA;AACE;AACAC;AACF;AACA;AACF;AAEO;;AAELD;AACAA;AACAA;AAEAA;AACA;AACEA;AACF;AACA;AACF;AAEO;;;;AAIH;AACA;AACA;AACEE;AACF;AACA;AACEtF;AACF;AACF;AACA;AACA;AACA;AACA;AACA;AACF;AAEO;AAKL;;;;AAWA;AACE;AACEuF;AACF;AACA;AACA;AACEX;AACF;;;;;AAKA;;AAEA;AACA;AACA;AACEO;AACF;AACF;;;AAIAC;AACA;;AAEA;;;;AAEoCI;;AACtC;AAEO;;AAEL;AACE;AACAJ;;AAEA;;AAEA;AACAA;AACF;AACA;AACF;AAEO;;AAELA;AACAA;AACAA;AAEAA;AACA;AACF;AAEO;;AAELA;AACAA;AACAA;AAEAA;AACA;AACF;AAEO;;AAELA;AACAA;AACAA;AAEAA;AACA;;AAEA;AACAA;AACAA;AACA;AACF;AAEO;;AAELA;AACAA;AACAA;AAEAA;AAEAA;;;;AACiCG;AAAU;AAC3CH;AACAA;AACAA;;;AAGAA;AACA;AACF;;AC3NA;;;AAcA;AACO;AAIL;;AAGA;AACA;;AAQaK;AAAa;AAC1B;;AAMA;AAEA;AAEA;AACE9B;AACA;;AACgCM;AAAS;;AAEzCN;AACA;;;;AAAiE/B;AAAO;;AAExE;AACAyB;;;AAGA;AACA;AACA;;;AAGA;AACA;AACA;;AAEIqC;AACA/B;AACF;AACEA;AACF;AACF;AACA;;AAEIgC;AACAhC;AAGF;AACEA;AACF;AACF;;;;;;AAMEgC;AACF;AACF;AACF;;ACzEA;AAAQC;AAAW;AAEZ;AAIL;AACA;;;AAME;AACF;AACAjC;AAGAkC;AACF;AAEO;AAIL;;;;AAIE;AAA2CC;AAAM;AACnD;AACA;AACF;AAEO;;AAEH;AACF;AACE;AACF;AACF;AAEO;AACL;;AAEF;AAEO;;AAEHC;AACAC;;AAEA;AACF;AACF;;AC7DO;AAGHC;AAAkBC;AAAQ;AAE5B;;;;AAKeA;AAAQ;AAEzB;AAEO;AAGHD;AAAgBC;AAAa;;;AAI/B;AACE;AACA;;AASF;AACA;AACF;;ACvCA;;AAMA;;AAGO;AACLC;AACEjH;AACAkH;AACAC;AACA3G;;AAEF4G;AACEpH;AACAkH;AACA1G;AACF;AACF;AAEO;AACL6G;AACErH;AACAmH;AACAD;AACA1G;;AAEF8G;AACEtH;AACAmH;AACAD;AACA1G;AACF;AACF;AAEO;AACL+G;AACEvH;AACAkH;AACA1G;;AAEFgH;AACExH;AACAkH;AACA1G;AACF;AACF;;AClCA;;AAAuBiH;AAAS;AA+CzB;;;;;;;AAUL;AAAMC;;;AACN;AACA;AACA;AACE;AACA;;AAIA;;AAEA;AACA;;AAEIC;AACF;AACF;AACF;AACA;AACE;AACA;;AAEF;;;;AAMF;AACA;;AAEA;AACA;AAEU;AAGS;AAAc;AAIvB;AAEsC;AAAS;;AACjCC;AAAO;;;AAGf;AACF;;;AAOd;AACA;;AAEA;AACA;AACA;;;AAIM;;;AAGF;AAEF;;AAEEnD;AACF;;AAEA;AACF;;AAEA;AACA;AACA;AACO;;AACc;;;;AAInBkD;AAOF;;;;AAME;;;;;;;AAOEE;;AAEF;AACA;;AAEA;AACA;AACF;AAEO;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACApD;AACF;AAEA;AAIE;AAGIkC;;AAEJ;AACA;AAKA;AAaA;AACF;AACA;AACA;AACA;;AAEA;;AC1OA;;AAWA;AAAQmB;AAAkB;AAE1B;AACEC;AACAvH;AAAsC;AACtCoH;AACAI;AACE;AACA;AACA;AACA;AACA;AACA;AACAC;AACEjI;AACAkH;AACA1G;;AAEF0H;AACElI;AACAkH;AACA1G;AACF;;;AAEgBwH;AAAM;AAC1B;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGLG;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;AAEA;AACA;AAEA;AACElD;AACA;AACF;AAEA;AACF;;AC/DO;AAKL;AACA;AAKA;AACE2D;AACA;AACF;;AAIA;AACE3D;AACA;AACF;;AAGF;;ACxBO;AAML;AACA;AAKA;AACE2D;AACA;AACF;;AAIA;AACE3D;AACA;AACF;;AAGF;;ACjCO;AAKL;AACA;AACA;AACA;AACA;AACA;AACA;;AAEE4D;AACF;;;AAIAC;AACA;;AAIA;AACAA;AAEA;AACF;AAEO;AAEL;AACA;AACAC;AAEA;;AAGA;AACE;AACE;AACA;;AAEF;AACF;;AAGA;;AAGA;;AAIA;AACE7J;AACA;AACE;AACA;;AAEF;AACAA;AACF;AAEA;AACF;;AChDuC;;AAkBvC;;AAeA;AACA;AAeO;;;;;AAKL8J;AAOF;AACE;;AAEE;AAGF;AAEA;;;;;;AAMEA;AACF;AACF;AAEA;;;;;;AAMEA;AAQF;AACE;;AACQC;AAAQ;AAEhBA;AAEA;;;;;AAQA;;AAEA;;;AAIE;;AAGA;;;AAGIhE;;;AAGAA;AACAA;AACF;AACF;AACEA;AACF;AACF;AACE;;;AAKE;;;AAGIA;;AAEAA;AACF;AACF;AACEA;AACF;AACF;;AAEA;AACF;AACF;AAEA;;;;;AAKI;AACAA;AAGA;AACF;AACF;AAEA;AAKE;AACF;;AAEA;;AAEA;AAqCA;;AAEA;AACA;;AAIA;;AAEA;AACA;AACA;AAEA;AACE;AACA;AAAgCwB;AAASsC;AAASG;AAAO;;;;;;;;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CAA;AACA;AACF;AAEA;AACEA;AACA2H;AACE;AACE3H;AACAA;AACF;AACEA;AACF;AACAA;AACF;AACF;AACE;AACAA;AACAA;AACAA;AACF;AAEA;AACF;;AAEA;AACA;;AAEAA;;AAGI4H;AACAC;AACAC;AACAC;AACF;AAEF/H;AAGAA;AACF;;ACtIA;AAAQqD;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;AACE;AACAyE;AACEzM;AACAmH;AACAD;AACA1G;;AAEFkM;AACE1M;AACAmH;AACAD;AACA1G;;AAEF8L;AACEtM;AACAkH;AACA1G;;AAGF6G;AACErH;AACAmH;AACAD;AACA1G;;AAGFyC;AACEjD;AACAmH;AACAD;AACA1G;AAEF;;AAEFyG;AACF;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;AAEA;AACA;;AAGA;AACE;AACA;AACA;;AAEAlD;AACJ;AACA;AACA;AACA;AACI;AACF;AAEA;AACEA;AACA;AACF;AAEA;;;;;;;AAOA;AACF;;AChHA;AAEO;;AAEL;AACA;AACA;AACA;AACAmD;AACA;AAA8BD;AAAW;AACvC;AAEIgF;AACF;;;;;AAMA;AAEJ;AACF;;ACHA;AAAQC;AAAI;AAEZ;;AAEA;AAMO;;AAKGnE;AAAQ;AAAMf;;;;AAItB;AACEmF;;AAEF;AAEA;AAEA;AACEC;AACAC;AACEC;AACAC;AACAC;AACF;AACF;AAEA;;;AAGE;AACF;AAEA;AAEA;AAAqDC;AAAe;AAEpE;AAAa;AAAS;;AACpB;;AAEA;AACA;AAEA;;AAGA;AACE1E;AACA;AACF;AAEA;;AAIM;AACA;;AAGJ;AACF;AAEA;AAAkBoD;;AAChB;AACA;AACeA;;;;AAI0BuB;AAAuB;;AAEtDjM;AAAoB;;;AAUxB;AACA;;AAAuCkM;AAAgB;AAEvD5E;AAEA;;;AAMY1C;AAAiB;;AAEzB;AACE;AACEuH;AAEJ;AACF;AACF;AACA;AACA;AACF;;;AAGA;AACF;;AAEA;AACF;AACF;AACF;AAEA;AAA4BT;AAAU;AACtCU;AACA;;AAGF;;ACjIO;;AAIGR;AAA2B;AACjCrF;;;AAIF;AACA;;AACMmE;AAAkB;AACxB;;AAEE;AACF;AACA;AAEApD;AAEA;;;;AAIA;AACE;AACA+E;AACA;;AAEE/E;AACF;AACF;;AAIA;AACF;;AC1DO;;AAEL;AAAkBoD;;;AAEd;AACA;AACExG;AACAoI;AACF;AACEpI;AACF;AACF;AACF;AACA;AACF;AAEO;;AAEL;AACF;;ACFA;;AAAiCqI;AAAqB;AAW/C;;;AAGHC;;;;AAIF;AAAMjG;;;AACN;AACA;AACA;AACA;AAGA;;;;AAIE;;AAGE;;AAEA;;AAEA;;AAIM;AACAkG;AAIN;AACAA;AAGA;AACA;AAEA;AACA;;AAEA;AACA;;;AAOA;AACA;AACA;AACAP;AACA;AACAQ;;AAEE;AACF;AACF;AAEF;AACEC;AAA4B;AAA4B;AAC1D;AACA;AACF;;ACrFA;AAAQlB;AAAI;AAUL;;;AAIUmB;AAAc;AAC7B;;AAEE;;;AAGA;AACF;;AAEEJ;;;AAGF;AAAMjG;;;;;AAGJ2F;AACA;AACAQ;;;AAGI;;AAEA;AACA;AAEF;AACF;AACF;AACF;;AC5BA;;;AAAwB7C;AAAK;AAMtB;;AAKGvC;AAAQ;AAAMf;;;;AAItB;AAAiDsG;AAA0B;;;AAGzE;AACF;AAEA;AACElB;AACAC;AACEC;AACAC;AACAC;AACF;AACF;AAEA;;;AAGE;AACF;AAEA;AACEL;;AAEF;AAEA;AAEA;AAAqDM;AAAe;;AAC5DpH;AAAiB;AAEzB;AAAa;AAAS;;AACpB;;AAGA;AACE0C;AACA;AACF;AAEA;;AAIM;AACA;;AAGJ;AACF;AAEA;AAAkBoD;;AAChB;AACA;AACeA;;;;AAI0BuB;AAAuB;;AAEtDjM;AAAoB;;AAE5B;;;AAUA;AACE;AACA;;;AAKI;AACE;AACA;;;AAGA;AACF;AACF;AAEAsH;;AAEA;AACA;AACA;;AACuCA;AAAQ;AACjD;;AAEA;AACF;;AAEA;AACF;AACF;AACF;;AAGF;;ACxHA;;;;;;;;;;AAUEwF;AACF;AAEO;AAIP;AACEvG;;;;;;AAMA;AACF;AAEA;AACE;AACA;AAA+BwG;;AACjC;AAEA;AAIE;;;AAIM;;AAC6CC;AAAI;;AAGvD;AACF;;AAEA;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAMA;;;;AAMQ;;AAEF;;AAEJ;AAEA;AAEA;;;AAMI;;AAEE;AACF;;AAEE;AACA;;;;AAIA;AACA;AACA;AACA;AACA;AACF;AACA;AACF;;;;;AAKA;;AAEJ;AAkDO;AACLA;AACAC;AACa;;AACqCD;AAAI;;AAEtD;;AAGmCA;AAAI;AACvC;;AAKqChB;;AAErC;AACA;AACA;AACA;AAIA;AACA;AACA;AACE;AACA;;;;AAIIkB;;AAEF;AACF;AACF;AACA;AAMEA;AACF;;AAEEA;;AAEF;AACA;AAEA;;AAGEC;AACF;AACA;AACED;AACF;AACA;AACEE;AACAC;;AAEF;AACA;AACA;AACA;AACE;;;AAGA;AACA;AACA;AACE;;;AAGA;AACF;AACA;AACA;;AAIE;;;AAMA;;;;;AAKE;AACF;AACF;AACA;;AAIAC;AAIF;AACEC;AACAC;AACF;;AAEA;;;;;;;;;;AAaErB;;;AAGAsB;AACEC;;AAEFC;;AAEJ;AAOO;;AAKHC;;AAEAC;AACF;AACEtH;;;AAGF;;;AAGIjD;AAMF;AACF;AACA;;AAIE;AACF;AACA;AACEA;AAMA;AACF;AACA;;;AAGE;AACF;;;AAGE;AACF;AACA;;AAEE;AACF;AACA;;AAOE;AACF;AACA;AAIEA;AAMF;AACA;AACF;;AC1YA;;AAAauG;AAAK;AAElB;AAEO;AACL;;AACQvC;AAAQ;;AAIhB;AAEA;AACEsG;AACAtK;AACF;;;AAGE;AACF;;AAGE;AAAU;AACR;AACA;AACF;AACA;AAAW;AACT;AACA;AACF;AACF;AACAgE;AACF;;AC7BA;AAAQX;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;;;AAGAf;AACF;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;AAEA;AACElD;AACA;AACF;;AAGF;;AC/CO;;AAML;AAIA;AAKA;AACE;AACF;AAEA;AACE;AACF;AAEA;;;;;;AAUF;;ACrBA;AAAQmI;AAAI;AAEZ;;AAEI;;AAEA;AACF;AACA;AACF;AAEA;;;;AAUY5M;AAAK;AACb;AACE;AACA;;AAEA;AACEiP;AACEtG;AACAuG;AACF;AACF;AACF;AACA;AACF;AACA;AACA;AACE;AAGIC;AAAoB;AAExB;AACE1K;AACF;;AAEA;AACF;AACF;AAEO;;;AACU2K;AAA2B;;;;AAKxCC;AAOF;;AAGE5K;AACA;AACF;;;AAGF;;AAEA;AACA;AACE;AACEA;AACF;AACA;AACE;AACA6K;AACAC;AACAC;;;AAGF/K;AACAgL;AAGAhL;AACA;;AAEIA;AACF;;AAIAiL;AACF;AACEjL;AACF;;;AAKAA;;AAEEA;AACwE0K;;AAE1E;AACE1K;AACuF0K;;AAEzF;;;AAOA;AACE1K;AACF;AACF;;AC5HO;;;;;;AAML+C;AAQF;AACE;;AACQiB;AAAQ;AAEhBA;;AAYAA;AAEA;;AAEIvH;;;AAIAmO;AACF;;AAGE;;AAEF;AACF;AACF;;ACnDA;AAAQvH;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;AACE;AACA;;;AAGFf;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;;;;;AAE6BH;;;;AAI3B;AACA;AACA;;AAEA/C;AACJ;AACA;AACI;AACF;AAEA;AACA;AAEA;AAGA;AACEA;AACA;AACF;AAEA;AACEsD;AACA4H;;;;;AAKF;AACF;;ACrFO;AAMLC;AACAA;AACAA;AACAA;AACF;;ACIA;AAAQC;AAAwB;AAEzB;AAILC;AACAC;AACA;AAEIC;;AAKJ;;AACQvH;AAAQ;AAEhBA;AAEA;;;AAGE;AACA;;AAEA;;AAEAA;AACF;AACEA;AACA;AACF;;;;AAOE;;AAGF;AACE;AAEIuH;AAEApF;AACE1J;AACA+O;AACAzP;;AAEJ;AAEEiI;AACF;AAEF;;AAEA;AACF;AACE;;AAGIvB;AACF;AAEEuB;AACF;AAEF;AACE;AACA;AACEyH;AACF;AACF;AACF;;AAIA;;;;AAIA;AACEzL;AACF;AACF;;AC3FA;AAAQqD;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;AACE;AACA8H;AACE9P;AACAQ;;AAEFuP;AACE/P;AACAQ;AACF;;AAEFyG;AACF;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;AAEA;AACA;AAEA;AACElD;AACA;AACF;AAEA;AACE;AAGF;AAEA;AACF;;AC3EO;AACLmL;AACAA;AACAA;AACAA;AACF;;ACHO;;AAEHO;AACA1L;AACF;AACEA;AACF;AACF;;ACFA;AAAQqD;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;;;AAGAf;AACF;AACA;;AAEA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;AAEA;AACElD;AACA;AACF;AAEA2L;AACF;;AC3CO;AAOL;;AACQ3H;AAAQ;AAEhB;AACA;AAEA;AACEhE;AACAA;AACAA;;AAEF;AACEA;AACAA;AACAA;;AAEF;;AAGE;AACA;AACA;;AAEA;;;AAIA;;AAEA;;;;AAQA;;AAIA;AACEA;AACAA;;AAEF;;;AAGEA;AACA;;AAEEA;AACAA;;AAEF;AACA;AACF;AACAA;AACAA;;AAIIA;AACA;AACF;;AAGF;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;;;;;AAQA;AACEA;AACAA;;AAEF;AACF;AACF;;AC1GA;AAAQqD;AAAkB;AAE1B;AACEC;AACAvH;AAEAoH;AACAI;AACE;AACAqI;AACErQ;AACAQ;;AAEF2N;AACEnO;AACAQ;;AAEF8P;AACEtQ;AACAkH;AACA1G;;AAGF+P;AACEvQ;AACAkH;AACA1G;;AAGFgQ;AACExQ;AACAQ;;AAEFiQ;AACEzQ;AACAkH;AACA1G;;AAEFkQ;AACE1Q;AACAQ;AACF;;AAEFyG;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;;AAEA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;;AAIA;AACElD;;;;;AAKF;AAEA;;AAEA;AACA;AACA;AACA;AACE;AACA;AACA;;AAEAA;;AAEJ;;AAEA;AACI;AACF;AAEA;AACA;AACE4L;AACF;;AAEA;;AAGA;AACEE;AACF;AACA;AACEA;AACF;AAEA;;AAEE9L;AACAA;AACAA;;AAEF;;AAGA;;AAKA;AAEA;AACEA;AACA;AACF;;AAGF;;ACvKO;AAOL;;AACQgE;AAAQ;AAChB;AACA;AAEA;AACEhE;AACAA;AACAA;AACA;;AAEF;AACEA;AACAA;AACAA;AACA;;AAEF;;;;AAKE;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAIA;AACEA;AACAA;;AAEF;;;AAGEA;AACA;;AAEEA;AACAA;;AAEF;AACA;AACF;;;AAGEkM;AACA;AACF;AACA;;AAEElM;AAGA;AACF;AACA;AACA;AACA;;AAEEA;AACAA;AACAA;AACAA;AACF;;AAEEA;AAGAkM;AACAlM;AACA;AACF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEAkM;AACAlM;AACF;;;;;AAQA;AACEA;AACAA;;AAEF;AACF;AACF;;AC1GA;AAAQqD;AAAkB;AAE1B;AACEC;AACAvH;AAEAoH;AACAI;AACE;AACAqI;AACErQ;AACAkH;AACA1G;;AAEF2N;AACEnO;AACAQ;;AAEF+P;AACEvQ;AACAkH;AACA1G;;AAGFgQ;AACExQ;AACAQ;;AAEFoQ;AACE5Q;AACAkH;AACA1G;;AAEFkQ;AACE1Q;AACAQ;AACF;;AAEFyG;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;;AAEA;;AAEA;AACA;;AA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A;AASA;AAIE;AAIF;AAEA;;AAKA;AAEA;AAIE;AACF;AAEA;;AAKUlI;AAAiB;AACzB;AACA;AACE;;AAEE;;AAEI;;AAEEyL;AACF;AACF;AACF;AACE;;AAIQ;;AAEEA;AACF;AACF;AACE;AAAmB;AAE7B;;AAEA;;;AAGA;AACF;;AAC2B;AAAe;AAC1C;AACA;AACF;AACA;AAME;AACF;AACA;AACA;AACA;AACA;;;;;AAKE;AACEM;;AAEF;AACF;AACEA;AACAC;AACF;;AAEE;AACED;AACAC;AACF;AACF;AACA;;AAEA;AACA;AACED;AACAC;AACF;AACA;;;AAGEA;AACF;AACAC;;AAIF;AAEA;AAIEC;AACF;AAEA;AAIEA;AACF;AAEA;AAIEA;AACF;AAEO;;ACpHP;;;AAAmBhE;AAAa;AAEhC;AAEA;AAEA;;;;;;;AAWIiE;AACF;;;;;;AAMEC;AACEC;AACAC;AACAC;AACAC;AACAC;AACF;AACF;AAAM9K;;;;AACA4F;AAAyB;;AAE7BmF;AAAmDtF;AAAe;AACpE;;AACQpH;AAAiB;AACzB;AACA;;;AAGA;AACA;;;AAaA;AACA;AAE0D2M;AAAY;AACtE;AACA;AACA;AACA;AACA;AAKA;;AAGA;AACEC;AACF;;AAKA;AACAlK;AAIA;AAEA;AACA;AAAuD;AAAQ;AAG/D;AACA;AAAyC;AAAQ;;AACvCvH;AAAsBsD;AAAsBrD;AAAQ;AAC5D;AACA;AACA;AACA;AAAa;;AACX;AAGA;AACEyR;AACF;AACA;AAGA;;AAEE;AACA;;AAOEC;AACAC;AACAX;AACA;AACEA;AACF;AACF;AACAS;AACF;AACF;AACA;AACE;AACA;;AAAyD5S;AAAK;AAC5D;;;AAME;AACA;AACA;;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA+S;AACF;AACE;AAGA;;;AASI;AACEA;AACF;AACF;AACF;AACEA;AACF;AACF;;AAEEvB;AACA;AACAW;AACF;AACF;AACF;AACF;AACF;AACA;AACE;AACEa;AACA7E;AACA7N;AACF;AACA;;AAEE;;;;AAOImI;AACF;AAEF;AAeE;AACE0J;AACF;AAC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;AAEA;AACA;AAEE;;;AAGI/Q;;;AAGAA;AACAA;AACF;AACF;AACEA;AACF;AACF;;AC9CA;AAAQqD;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;AACE;;;AAGFf;AACF;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;AAEA;AAEA;AACE;AACA;AACA;;;AAIJ;;AAEA;;AAEA;AACA;AAEI;AACF;AAEA;AACElD;AACA;AACF;AAEA;AACE;AACF;AACE;AACF;AACF;;AC/EA;AAEO;;AAEL;AAA8BkD;AAAW;AACvC;AAEI+L;AACAS;AACAC;AACAqB;AACAhC;AACF;AAEErC;AACE;AACAsE;;AAEE9N;AACAgE;AACF;;;;;;AAMJ;AAEJ;AACF;;ACtBuC;;AAahC;;;;;;AAMLzB;AAQF;AACE;;AAEE;AAGF;AAEA;;;;;;;AAOEA;AACF;AACF;AAEA;;;;;;;AAOEA;AASF;AACE;;AACQ1B;AAAQ;AAEhB;AAQAA;;AAGA;AAKAA;;AAGEhE;AACA;AACF;AAEA;AAEA;AACEkR;AACAC;AACAC;AACAC;AACAC;AACApN;AACAkB;AACAmM;AAAe;AACfC;AACEjW;AACA4V;;AAEFM;AACA;AACA;AACAC;AACAC;AACF;;AAEA;AACA;AACEC;AACAL;AACAnM;AACAoM;AACEjW;AACA4V;;AAEFjN;AACA5C;AAEAuD;AACEsM;AACF;AACF;;AAEA;;AAGAlN;AACAA;AAEA;AACA;;AAGE5B;AAQAlE;AACF;;AAEA;AACA0T;AACE;AACA;AACE;AACA;AACE;;;AAWF;AACF;AACF;;AAIA5N;AACAA;AACE;;AAEA;AACA6M;AACF;AACF;AAEA;AACE;;;AAGE;AAEA;;AAEA;;AASF;AACF;AAEA;AACE;AACA;;AAGEgB;AACAjN;AACF;AAEA;AACA;AACE;AACA;;;AAGA;AACF;;AAEE;AACF;AACE;AACF;AACF;;ACrOA;AAAQxB;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;AACE;AACA;AACAmC;AACEnK;AACAmH;AACAD;AACA1G;;AAEFuK;AACE/K;AACAmH;AACAD;AACA1G;;AAEFyT;AACEjU;AACAmH;AACAD;AACA1G;;AAEFgW;AACExW;AACAmH;AACAD;AACA1G;;AAEF4K;AACEpL;AACAmH;AACAD;AACA1G;AACF;;AAEFyG;AACF;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;AAEA;AACElD;AACA;AACF;AAEA;;;;AAIEyP;;;AAOF;AACF;;ACtHO;;AAKD;AACE;AACF;AACA;AACA;;AAGN;AACA;AACA;AAEI;AAEJ;;ACpBO;;;;AASHzP;AAGA;AACF;AACA;AACF;;ACTO;AACL;;;AACoBgS;AAAU;;;AAM5BC;;AAEJ;;AAEA;AACE;AACF;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AAEE;;;AAGA;AACA;AACF;AAEA;AACEC;AACE;AACE;;;AACoBF;AAAU;;AAE5B;;AAEA;AACA;;AAEA;;AAEA;AACF;;;AAGAG;AAIF;;AAEA;AACF;AACF;;AC/DO;AACL;AACE;AACEnS;AACAA;AACA;AACF;;AAOA;;AAGE;AACEA;AACA;AACF;AACA;AACA;AACAA;AAGF;AACF;AACF;;ACbA;AAAQqD;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;AACE;AACA6O;AACE7W;AACAkH;AACA1G;;AAEFsW;AACE9W;AACAkH;AACA1G;AACF;;AAEFyG;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;AAErC;AACA;AACEoP;AACA;AACF;;;;;AAMEpP;AACF;;AAEQkP;;;AAEN;AACA;AACA;;;AAIJ;;AAEA;AACA;AAEI;AACF;AAEA;AACEpS;AACA;AACF;;AAEA;;;AACoBgS;AAAU;AAC9B;;;AAGE;;;AAGA;AACF;AACE;;AAEA;AACA;;AAEA;AACF;;AAEEhS;AACF;AACF;;AC7EA;;AAA6BuS;AAAgB;;AAE7C;AACA;AACE;AACE9V;AACAC;;AAEF;;AAGE;AAEI8V;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACA;AACA;AACAC;AACAC;AACAC;AACA;AACAC;AACAC;AACAC;AACA;AACA;AACAC;AACF;AAEE9G;AACE+G;AACE3X;;AAEF;;;AAGFU;AACA6P;AAAchQ;AAAoC;AACpD;;;AAIF;AACA;;;AAGEqX;;AAEF;AACEA;;;AAGF;AACEA;AACAC;AACAC;AACF;AACEF;AACF;;AAIA;AACE3T;AACF;;AAEF;AACF","debugId":"2e8d9d04-20b0-4f47-8b02-b404f72bd2d"}
|
|
1
|
+
{"version":3,"file":"cli.js","sources":["../../src/commands/action/core/classes.ts","../../src/commands/action/core/index.ts","../../src/commands/action/core/scm_comments.ts","../../src/commands/action/core/github.ts","../../src/commands/action/core/messages.ts","../../src/commands/action/run-action.ts","../../src/utils/api.ts","../../src/utils/output-formatting.ts","../../src/flags.ts","../../src/utils/meow-with-subcommands.ts","../../src/commands/action/cmd-action.ts","../../src/commands/analytics/fetch-org-analytics.ts","../../src/commands/analytics/fetch-repo-analytics.ts","../../src/utils/markdown.ts","../../src/commands/analytics/display-analytics.ts","../../src/commands/analytics/cmd-analytics.ts","../../src/commands/audit-log/get-audit-log.ts","../../src/commands/audit-log/cmd-audit-log.ts","../../src/commands/cdxgen/run-cyclonedx.ts","../../src/utils/cmd.ts","../../src/commands/cdxgen/cmd-cdxgen.ts","../../src/commands/dependencies/find-dependencies.ts","../../src/commands/dependencies/cmd-dependencies.ts","../../src/commands/diff-scan/get-diff-scan.ts","../../src/commands/diff-scan/cmd-diff-scan-get.ts","../../src/commands/diff-scan/cmd-diff-scan.ts","../../src/commands/fix/npm-fix.ts","../../src/utils/lockfile/pnpm-lock-yaml.ts","../../src/utils/npm.ts","../../src/commands/optimize/run-agent.ts","../../src/commands/fix/pnpm-fix.ts","../../src/utils/package-environment.ts","../../src/commands/fix/run-fix.ts","../../src/commands/fix/cmd-fix.ts","../../src/commands/info/fetch-package-info.ts","../../src/commands/info/log-package-info.ts","../../src/commands/info/get-package-info.ts","../../src/commands/info/cmd-info.ts","../../src/commands/login/apply-login.ts","../../src/commands/login/attempt-login.ts","../../src/commands/login/cmd-login.ts","../../src/commands/logout/apply-logout.ts","../../src/commands/logout/attempt-logout.ts","../../src/commands/logout/cmd-logout.ts","../../src/commands/manifest/convert_gradle_to_maven.ts","../../src/commands/manifest/cmd-manifest-gradle.ts","../../src/commands/manifest/convert_sbt_to_maven.ts","../../src/commands/manifest/cmd-manifest-scala.ts","../../src/commands/manifest/cmd-manifest-auto.ts","../../src/commands/manifest/cmd-manifest-kotlin.ts","../../src/commands/manifest/cmd-manifest.ts","../../src/commands/npm/wrap-npm.ts","../../src/commands/npm/cmd-npm.ts","../../src/commands/npx/wrap-npx.ts","../../src/commands/npx/cmd-npx.ts","../../src/commands/oops/cmd-oops.ts","../../src/commands/optimize/deps-includes-by-agent.ts","../../src/commands/optimize/get-dependency-entries.ts","../../src/commands/optimize/get-overrides-by-agent.ts","../../src/commands/optimize/get-workspace-globs.ts","../../src/commands/optimize/lockfile-includes-by-agent.ts","../../src/commands/optimize/ls-by-agent.ts","../../src/commands/optimize/update-manifest-by-agent.ts","../../src/commands/optimize/add-overrides.ts","../../src/commands/optimize/update-lockfile.ts","../../src/commands/optimize/apply-optimization.ts","../../src/commands/optimize/cmd-optimize.ts","../../src/commands/organization/get-organization.ts","../../src/commands/organization/cmd-organization.ts","../../src/commands/raw-npm/run-raw-npm.ts","../../src/commands/raw-npm/cmd-raw-npm.ts","../../src/commands/raw-npx/run-raw-npx.ts","../../src/commands/raw-npx/cmd-raw-npx.ts","../../src/commands/report/create-report.ts","../../src/commands/report/get-socket-config.ts","../../src/commands/report/fetch-report-data.ts","../../src/commands/report/format-report-data.ts","../../src/commands/report/view-report.ts","../../src/commands/report/cmd-report-create.ts","../../src/commands/report/cmd-report-view.ts","../../src/commands/report/cmd-report.ts","../../src/commands/repos/create-repo.ts","../../src/commands/repos/cmd-repos-create.ts","../../src/commands/repos/delete-repo.ts","../../src/commands/repos/cmd-repos-del.ts","../../src/commands/repos/list-repos.ts","../../src/commands/repos/cmd-repos-list.ts","../../src/commands/repos/update-repo.ts","../../src/commands/repos/cmd-repos-update.ts","../../src/commands/repos/view-repo.ts","../../src/commands/repos/cmd-repos-view.ts","../../src/commands/repos/cmd-repos.ts","../../src/commands/scan/suggest-org-slug.ts","../../src/commands/scan/suggest-repo-slug.ts","../../src/commands/scan/suggest_branch_slug.ts","../../src/commands/scan/suggest_target.ts","../../src/commands/scan/create-full-scan.ts","../../src/commands/scan/cmd-scan-create.ts","../../src/commands/scan/delete-full-scan.ts","../../src/commands/scan/cmd-scan-del.ts","../../src/commands/scan/list-full-scans.ts","../../src/commands/scan/cmd-scan-list.ts","../../src/commands/scan/get-full-scan-metadata.ts","../../src/commands/scan/cmd-scan-metadata.ts","../../src/commands/scan/stream-full-scan.ts","../../src/commands/scan/get-full-scan.ts","../../src/commands/scan/view-full-scan.ts","../../src/commands/scan/cmd-scan-view.ts","../../src/commands/scan/cmd-scan.ts","../../src/commands/threat-feed/get-threat-feed.ts","../../src/commands/threat-feed/cmd-threat-feed.ts","../../src/commands/wrapper/add-socket-wrapper.ts","../../src/commands/wrapper/check-socket-wrapper-setup.ts","../../src/commands/wrapper/postinstall-wrapper.ts","../../src/commands/wrapper/remove-socket-wrapper.ts","../../src/commands/wrapper/cmd-wrapper.ts","../../src/cli.ts"],"sourcesContent":["// https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/core/classes.py\nimport { components } from '@socketsecurity/sdk/types/api'\n\ntype IntroducedBy = Array<[string, string]>\n\nexport class Alert {\n key = ''\n type = ''\n severity = ''\n category = ''\n props = {}\n\n constructor(arg: Partial<Alert> = {}) {\n this.key = arg.key ?? this.key\n this.type = arg.type ?? this.type\n this.severity = arg.severity ?? this.severity\n this.category = arg.category ?? this.category\n this.props = arg.props ?? this.props\n }\n}\n\nexport class Comment {\n id = 0\n body = ''\n body_list: string[] = []\n\n constructor(arg: Comment) {\n this.id = arg.id ?? this.id\n this.body = arg.body ?? this.body\n this.body_list = arg.body_list ?? this.body_list\n }\n}\n\nexport class Diff {\n newPackages: Purl[] = []\n newCapabilities: Record<string, any> = {}\n removedPackages: Purl[] = []\n newAlerts: Issue[] = []\n id = ''\n sbom = ''\n packages: Record<string, Package> = {}\n reportUrl = ''\n diffUrl = ''\n}\n\nexport class FullScan {\n id = ''\n created_at = ''\n updated_at = ''\n organizationId = ''\n repositoryId = ''\n branch = ''\n commit_message = ''\n commit_hash = ''\n pull_request = 0\n sbom_artifacts: Array<components['schemas']['SocketArtifact']> = []\n packages = {}\n\n constructor(obj: Partial<FullScan> = {}) {\n this.id = obj.id ?? this.id\n this.created_at = obj.created_at ?? this.created_at\n this.updated_at = obj.updated_at ?? this.updated_at\n this.organizationId = obj.organizationId ?? this.organizationId\n this.repositoryId = obj.repositoryId ?? this.repositoryId\n this.branch = obj.branch ?? this.branch\n this.commit_message = obj.commit_message ?? this.commit_message\n this.commit_hash = obj.commit_hash ?? this.commit_hash\n this.pull_request = obj.pull_request ?? this.pull_request\n this.sbom_artifacts = obj.sbom_artifacts ?? this.sbom_artifacts\n this.packages = obj.packages ?? this.packages\n }\n}\n\nexport class Issue {\n pkg_type = ''\n pkg_name = ''\n pkg_version = ''\n category = ''\n type = ''\n severity = ''\n pkg_id = ''\n props = {}\n key = ''\n error = false\n warn = false\n ignore = false\n monitor = false\n description = ''\n title = ''\n emoji = ''\n next_step_title = ''\n suggestion = ''\n introduced_by: IntroducedBy = []\n manifests = ''\n url = ''\n purl = ''\n\n constructor(arg: {\n pkg_type: string | undefined\n pkg_name: string | undefined\n pkg_version: string | undefined\n type: string | undefined\n severity: string | undefined\n pkg_id: string | undefined\n props: Record<string, any> | undefined\n key: string | undefined\n error: boolean | undefined\n warn: boolean | undefined\n ignore: boolean | undefined\n monitor: boolean | undefined\n description: string | undefined\n title: string | undefined\n next_step_title: string | undefined\n suggestion: string | undefined\n introduced_by: IntroducedBy | undefined\n url: string | undefined\n purl: string | undefined\n }) {\n this.pkg_type = arg.pkg_type ?? this.pkg_type\n this.pkg_name = arg.pkg_name ?? this.pkg_name\n this.pkg_version = arg.pkg_version ?? this.pkg_version\n this.type = arg.type ?? this.type\n this.severity = arg.severity ?? this.severity\n this.pkg_id = arg.pkg_id ?? this.pkg_id\n this.props = arg.props ?? this.props\n this.key = arg.key ?? this.key\n this.error = arg.error ?? this.error\n this.warn = arg.warn ?? this.warn\n this.ignore = arg.ignore ?? this.ignore\n this.monitor = arg.monitor ?? this.monitor\n this.description = arg.description ?? this.description\n this.title = arg.title ?? this.title\n this.next_step_title = arg.next_step_title ?? this.next_step_title\n this.suggestion = arg.suggestion ?? this.suggestion\n\n if (arg.introduced_by) {\n const arr = []\n for (const item of arg.introduced_by) {\n const [, manifest] = item\n arr.push(manifest)\n }\n this.manifests = arr.join(';')\n }\n }\n}\n\nexport class Package {\n type = ''\n name = ''\n version = ''\n release = ''\n id = ''\n direct = false\n manifestFiles: Array<{ file: string }> = []\n author: string[] = []\n size = 0\n score: Score\n scores = {}\n alerts: NonNullable<components['schemas']['SocketArtifact']['alerts']> = []\n alert_counts = {}\n topLevelAncestors: string[] = []\n url = ''\n transitives = 0\n license = 'NoLicenseFound'\n license_text = ''\n purl = ''\n\n constructor(arg: {\n type: string | undefined\n name: string | undefined\n version: string | undefined\n release: string | undefined\n id: string | undefined\n direct: boolean | undefined\n manifestFiles: Array<{ file: string }> | undefined\n author: string[] | undefined\n size: number | undefined\n score: Score | undefined\n alerts: components['schemas']['SocketArtifact']['alerts'] | undefined\n topLevelAncestors: string[] | undefined\n license: string | undefined\n }) {\n this.type = arg.type ?? this.type\n this.name = arg.name ?? this.name\n this.version = arg.version ?? this.version\n this.release = arg.release ?? this.release\n this.id = arg.id ?? this.id\n this.manifestFiles = arg.manifestFiles ?? this.manifestFiles\n this.author = arg.author ?? this.author\n this.size = arg.size ?? this.size\n this.alerts = arg.alerts ?? this.alerts\n this.topLevelAncestors = arg.topLevelAncestors ?? this.topLevelAncestors\n this.license = arg.license ?? this.license\n\n this.url = `https://socket.dev/${this.type}/package/${this.name}/overview/${this.version}`\n this.score = new Score(\n arg.score ?? {\n supplyChain: 0,\n quality: 0,\n license: 0,\n overall: 0,\n vulnerability: 0\n }\n )\n this.alert_counts = {\n critical: 0,\n high: 0,\n middle: 0,\n low: 0\n }\n this.purl = `${this.type}/${this.name}@${this.version}`\n }\n}\n\nexport class Purl {\n id = ''\n name = ''\n version = ''\n ecosystem = ''\n direct = false\n author: string[] = []\n size = 0\n transitives = 0\n introduced_by: IntroducedBy = []\n capabilities: string[] = []\n // is_new = false\n author_url = ''\n url = ''\n purl = ''\n\n constructor(arg: {\n id: string | undefined\n name: string | undefined\n version: string | undefined\n ecosystem: string | undefined\n direct: boolean | undefined\n introduced_by: IntroducedBy | undefined\n author: string[] | undefined\n size: number | undefined\n transitives: number | undefined\n url: string | undefined\n purl: string | undefined\n }) {\n this.id = arg.id ?? this.id\n this.name = arg.name ?? this.name\n this.version = arg.version ?? this.version\n this.ecosystem = arg.ecosystem ?? this.ecosystem\n this.direct = arg.direct ?? this.direct\n this.author = arg.author ?? this.author\n this.size = arg.size ?? this.size\n this.transitives = arg.transitives ?? this.transitives\n this.introduced_by = arg.introduced_by ?? this.introduced_by\n this.url = arg.url ?? this.url\n this.purl = arg.purl ?? this.purl\n\n this.author_url = this.generateAuthorData(this.author, this.ecosystem)\n }\n\n private generateAuthorData(authors: string[], ecosystem: string): string {\n const arr = []\n for (const author of authors) {\n const url = `https://socket.dev/${ecosystem}/user/${author}`\n arr.push(`[${author}](${url})`)\n }\n return arr.join(',')\n }\n}\n\nexport class Score {\n supplyChain = 0\n quality = 0\n license = 0\n overall = 0\n vulnerability = 0\n\n constructor(arg: Score) {\n this.supplyChain = (arg.supplyChain ?? 0) * 100\n this.quality = (arg.quality ?? 0) * 100\n this.license = (arg.license ?? 0) * 100\n this.overall = (arg.overall ?? 0) * 100\n this.vulnerability = (arg.vulnerability ?? 0) * 100\n }\n}\n","// https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/core/__init__.py\nimport { once } from 'node:events'\nimport fs from 'node:fs'\nimport path from 'node:path'\n\nimport ndjson from 'ndjson'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { SocketSdk } from '@socketsecurity/sdk'\n\nimport { Diff, FullScan, Issue, Package, Purl } from './classes'\n\nimport type { components, operations } from '@socketsecurity/sdk/types/api.d'\n\nexport class Core {\n socket: SocketSdk\n owner: string\n repo: string\n files: string[]\n securityPolicy: Record<\n string,\n { action: 'error' | 'ignore' | 'warn' | 'monitor' }\n > = {}\n\n constructor({\n owner,\n repo,\n socket\n }: Pick<Core, 'socket' | 'owner' | 'repo' | 'files'>) {\n this.socket = socket\n this.owner = owner\n this.repo = repo\n this.files = []\n }\n\n async getSbomData({\n fullScanId\n }: {\n fullScanId: string\n }): Promise<Array<components['schemas']['SocketArtifact']>> {\n const orgFullScanResponse = await this.socket.getOrgFullScan(\n this.owner,\n fullScanId,\n undefined\n )\n if (!orgFullScanResponse.success) {\n return []\n }\n\n const { data: readStream }: { data: any } = orgFullScanResponse\n const sbomArtifacts: any = []\n\n readStream\n .pipe(ndjson.parse())\n .on('data', (sbomArtifact: any) => sbomArtifacts.push(sbomArtifact))\n\n await once(readStream, 'end')\n\n return sbomArtifacts\n }\n\n async createFullScan({\n params\n }: {\n params: Omit<operations['CreateOrgFullScan']['parameters']['query'], 'repo'>\n }): Promise<FullScan> {\n const orgFullScanResponse = await this.socket.createOrgFullScan(\n this.owner,\n // Ignoring because pull_request is of type number but URLSearchParams will convert it to a string\n // @ts-ignore\n new URLSearchParams({ repo: this.repo, ...params }),\n this.files\n )\n\n if (!orgFullScanResponse.success) {\n return new FullScan()\n }\n\n const { id: fullScanId } = orgFullScanResponse.data\n const fullScan = new FullScan(orgFullScanResponse.data)\n if (fullScanId !== undefined) {\n fullScan.sbom_artifacts = await this.getSbomData({ fullScanId })\n }\n return fullScan\n }\n\n getSourceData({\n packages,\n pkg\n }: {\n pkg: Package\n packages: Record<string, Package>\n }): Array<[string, string]> {\n const introducedBy: Array<[string, string]> = []\n\n if (pkg.direct) {\n const manifests = pkg.manifestFiles.map(({ file }) => file).join(';')\n\n introducedBy.push(['direct', manifests])\n } else {\n for (const topId of pkg.topLevelAncestors) {\n const topPackage = packages[topId]\n\n if (!topPackage) {\n continue\n }\n\n const topPurl = `${topPackage.type}/${topPackage.name}@${topPackage.version}`\n const manifests = topPackage.manifestFiles\n .map(({ file }) => file)\n .join(';')\n\n introducedBy.push([topPurl, manifests])\n }\n }\n\n return introducedBy\n }\n\n createPurl({\n packageId,\n packages\n }: {\n packageId: string\n packages: Record<string, Package>\n }): { purl: Purl; pkg: Package } {\n const pkg = packages[packageId]!\n const introducedBy = this.getSourceData({ pkg, packages })\n const purl = new Purl({\n id: pkg.id,\n name: pkg.name,\n version: pkg.version,\n ecosystem: pkg.type,\n direct: pkg.direct,\n introduced_by: introducedBy,\n author: pkg.author,\n size: pkg.size,\n transitives: pkg.transitives,\n url: pkg.url,\n purl: pkg.purl\n })\n return { purl, pkg }\n }\n\n async createIssueAlerts({\n alerts,\n packages,\n pkg\n }: {\n pkg: Package\n alerts: Record<string, Issue[]>\n packages: Record<string, Package>\n }): Promise<Record<string, Issue[]>> {\n const issues = JSON.parse(\n fs.readFileSync(path.join(import.meta.dirname, 'issues.json'), 'utf8')\n ) as Record<string, Record<string, string>>\n\n for (const alert of pkg.alerts) {\n const issue = issues[alert.type]\n\n let description = ''\n let title = ''\n let suggestion = ''\n let nextStepTitle = ''\n\n if (issue !== undefined) {\n description = issue['description'] ?? ''\n title = issue['title'] ?? ''\n suggestion = issue['suggestion'] ?? ''\n nextStepTitle = issue['nextStepTitle'] ?? ''\n }\n\n const introducedBy = this.getSourceData({ pkg, packages })\n\n const issueAlert = new Issue({\n pkg_type: pkg.type,\n pkg_name: pkg.name,\n pkg_version: pkg.version,\n pkg_id: pkg.id,\n type: alert.type,\n severity: alert.severity,\n key: alert.key,\n props: alert.props,\n description,\n title,\n suggestion,\n next_step_title: nextStepTitle,\n introduced_by: introducedBy,\n purl: pkg.purl,\n url: pkg.url,\n error: false,\n ignore: false,\n warn: false,\n monitor: false\n })\n\n if (alert.type in this.securityPolicy) {\n const action = this.securityPolicy[alert.type]?.action\n if (action !== undefined) {\n issueAlert[action] = true\n }\n }\n\n if (issueAlert.type !== 'licenseSpdxDisj') {\n if (!(issueAlert.key in alerts)) {\n alerts[issueAlert.key] = [issueAlert]\n } else {\n alerts[issueAlert.key]!.push(issueAlert)\n }\n }\n }\n\n return alerts\n }\n\n compareIssueAlerts({\n alerts,\n headScanAlerts,\n newScanAlerts\n }: {\n newScanAlerts: Record<string, Issue[]>\n headScanAlerts: Record<string, Issue[]>\n alerts: Issue[]\n }) {\n const consolidatedAlerts = new Set()\n\n for (const alertKey in newScanAlerts) {\n if (!(alertKey in headScanAlerts)) {\n const newAlerts = newScanAlerts[alertKey]!\n\n for (const alert of newAlerts) {\n const alertStr = `${alert.purl},${alert.manifests},${alert.type}`\n\n if (alert.error || alert.warn) {\n if (!consolidatedAlerts.has(alertStr)) {\n alerts.push(alert)\n consolidatedAlerts.add(alertStr)\n }\n }\n }\n } else {\n const newAlerts = newScanAlerts[alertKey]!\n const headAlerts = headScanAlerts[alertKey]!\n\n for (const alert of newAlerts) {\n const alertStr = `${alert.purl},${alert.manifests},${alert.type}`\n if (\n !headAlerts.includes(alert) &&\n !consolidatedAlerts.has(alertStr)\n ) {\n if (alert.error || alert.warn) {\n alerts.push(alert)\n consolidatedAlerts.add(alertStr)\n }\n }\n }\n }\n }\n\n return alerts\n }\n\n checkAlertCapabilities({\n capabilities,\n headPackage,\n packageId,\n pkg\n }: {\n pkg: Package\n capabilities: Record<string, string[]>\n packageId: string\n headPackage?: Package | undefined\n }): Record<string, string[]> {\n const alertTypes = {\n envVars: 'Environment',\n networkAccess: 'Network',\n filesystemAccess: 'File System',\n shellAccess: 'Shell'\n }\n\n for (const alert of pkg.alerts) {\n let newAlert = true\n if (headPackage !== undefined && headPackage.alerts.includes(alert)) {\n newAlert = false\n }\n if (alert.type in alertTypes && newAlert) {\n const value = alertTypes[alert.type as keyof typeof alertTypes]\n if (!(packageId in capabilities)) {\n capabilities[packageId] = [value]\n } else {\n if (!capabilities[packageId]!.includes(value)) {\n capabilities[packageId]!.push(value)\n }\n }\n }\n }\n\n return capabilities\n }\n\n compareCapabilities({\n headPackages,\n newPackages\n }: {\n newPackages: Record<string, Package>\n headPackages: Record<string, Package>\n }) {\n let capabilities: Record<string, string[]> = {}\n\n for (const packageId in newPackages) {\n const pkg = newPackages[packageId]!\n\n if (packageId in headPackages) {\n const headPackage = headPackages[packageId]!\n for (const alert of pkg.alerts) {\n if (!headPackage.alerts.includes(alert)) {\n capabilities = this.checkAlertCapabilities({\n pkg,\n capabilities,\n packageId,\n headPackage\n })\n }\n }\n } else {\n capabilities = this.checkAlertCapabilities({\n pkg,\n capabilities,\n packageId\n })\n }\n }\n\n return capabilities\n }\n\n addCapabilitiesToPurl(diff: Diff): Diff {\n const newPackages: Purl[] = []\n\n for (const purl of diff.newPackages) {\n if (purl.id in diff.newCapabilities) {\n const capabilities =\n diff.newCapabilities[purl.id as keyof typeof diff.newCapabilities]!\n if (capabilities.length > 0) {\n purl.capabilities = capabilities\n newPackages.push(purl)\n }\n } else {\n newPackages.push(purl)\n }\n }\n diff.newPackages = newPackages\n\n return diff\n }\n\n async compareSBOMs({\n headScan,\n newScan\n }: {\n newScan: Awaited<ReturnType<Core['getSbomData']>>\n headScan: Awaited<ReturnType<Core['getSbomData']>>\n }): Promise<Diff> {\n let diff = new Diff()\n const newPackages = this.createSbomDict(newScan)\n const headPackages = this.createSbomDict(headScan)\n\n let newScanAlerts: Record<string, Issue[]> = {}\n let headScanAlerts: Record<string, Issue[]> = {}\n const consolidated = new Set()\n\n for (const packageId in newPackages) {\n const { pkg, purl } = this.createPurl({\n packageId,\n packages: newPackages\n })\n const basePurl = `${purl.ecosystem}/${purl.name}@${purl.version}`\n\n if (\n !(packageId in headPackages) &&\n pkg.direct &&\n !consolidated.has(basePurl)\n ) {\n diff.newPackages.push(purl)\n consolidated.add(basePurl)\n }\n // eslint-disable-next-line no-await-in-loop\n newScanAlerts = await this.createIssueAlerts({\n pkg,\n alerts: newScanAlerts,\n packages: newPackages\n })\n }\n\n for (const packageId in headPackages) {\n const { pkg, purl } = this.createPurl({\n packageId,\n packages: headPackages\n })\n\n if (!(packageId in newPackages) && pkg.direct) {\n diff.removedPackages.push(purl)\n }\n // eslint-disable-next-line no-await-in-loop\n headScanAlerts = await this.createIssueAlerts({\n pkg,\n alerts: headScanAlerts,\n packages: headPackages\n })\n }\n\n diff.newAlerts = this.compareIssueAlerts({\n newScanAlerts,\n headScanAlerts,\n alerts: diff.newAlerts\n })\n diff.newCapabilities = this.compareCapabilities({\n newPackages,\n headPackages\n })\n diff = this.addCapabilitiesToPurl(diff)\n\n return diff\n }\n\n createPackageFromSbomArtifact(\n sbomArtifact: Array<components['schemas']['SocketArtifact']>\n ): Package[] {\n return sbomArtifact.map(\n sbomArtifact =>\n new Package({\n type: sbomArtifact.type,\n name: sbomArtifact.name,\n version: sbomArtifact.version,\n release: sbomArtifact.release,\n id: sbomArtifact.id,\n direct: sbomArtifact.direct,\n manifestFiles: sbomArtifact.manifestFiles,\n author: sbomArtifact.author,\n size: sbomArtifact.size,\n score: sbomArtifact.score,\n alerts: sbomArtifact.alerts,\n topLevelAncestors: sbomArtifact.topLevelAncestors,\n license: sbomArtifact.license\n })\n )\n }\n\n getLicenseDetails({ package: pkg }: { package: Package }): Package {\n const licenseText = JSON.parse(\n fs.readFileSync(\n path.join(import.meta.dirname, 'license_texts.json'),\n 'utf8'\n )\n ) as Record<string, string>\n const licenseStr = licenseText[pkg.license]\n if (licenseStr !== undefined) {\n pkg.license_text = licenseStr\n }\n return pkg\n }\n\n createSbomDict(\n sbomArtifacts: Awaited<ReturnType<typeof this.getSbomData>>\n ): Record<string, Package> {\n const packages: Record<string, Package> = {}\n const topLevelCount: Record<string, number> = {}\n\n for (const sbomArtifact of sbomArtifacts) {\n let pkg = new Package({\n type: sbomArtifact.type,\n name: sbomArtifact.name,\n version: sbomArtifact.version,\n release: sbomArtifact.release,\n id: sbomArtifact.id,\n direct: sbomArtifact.direct,\n manifestFiles: sbomArtifact.manifestFiles,\n author: sbomArtifact.author,\n size: sbomArtifact.size,\n score: sbomArtifact.score,\n alerts: sbomArtifact.alerts,\n topLevelAncestors: sbomArtifact.topLevelAncestors,\n license: sbomArtifact.license\n })\n\n if (pkg.id in packages) {\n logger.log('Duplicate package?')\n } else {\n pkg = this.getLicenseDetails({ package: pkg })\n packages[pkg.id] = pkg\n\n for (const topId in sbomArtifact.topLevelAncestors ?? []) {\n if (!(topId in topLevelCount)) {\n topLevelCount[topId] = 1\n } else {\n topLevelCount[topId]! += 1\n }\n }\n }\n }\n\n if (Object.keys(topLevelCount).length > 0) {\n for (const packageId in topLevelCount) {\n const pkg = packages[packageId]\n if (pkg) {\n pkg.transitives = topLevelCount[packageId] ?? 0\n }\n }\n }\n\n return packages\n }\n\n async createNewDiff({\n params = {}\n }: {\n params?: Omit<\n operations['CreateOrgFullScan']['parameters']['query'],\n 'repo'\n >\n }): Promise<Diff> {\n let headFullScanId: string = ''\n let headFullScan: Awaited<ReturnType<typeof this.getSbomData>> = []\n\n try {\n const orgRepoResponse = await this.socket.getOrgRepo(\n this.owner,\n this.repo\n )\n if (orgRepoResponse.success) {\n headFullScanId = orgRepoResponse.data.head_full_scan_id ?? ''\n if (headFullScanId !== '') {\n headFullScan = await this.getSbomData({ fullScanId: headFullScanId })\n }\n }\n } catch (e) {\n logger.error(e)\n }\n\n const newFullScan = await this.createFullScan({ params })\n newFullScan.packages = this.createSbomDict(newFullScan.sbom_artifacts)\n\n const diffReport = await this.compareSBOMs({\n newScan: newFullScan.sbom_artifacts,\n headScan: headFullScan\n })\n diffReport.packages = newFullScan.packages\n\n const baseSocket = 'https://socket.dev/dashboard/org'\n diffReport.id = newFullScan.id\n diffReport.reportUrl = `${baseSocket}/${this.owner}/sbom/${diffReport.id}`\n if (headFullScanId !== '') {\n diffReport.diffUrl = `${baseSocket}/${this.owner}/diff/${diffReport.id}/${headFullScanId}`\n } else {\n diffReport.diffUrl = diffReport.reportUrl\n }\n\n return diffReport\n }\n}\n","// https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/core/scm_comments.py\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { Comment, Issue } from './classes'\n\nexport type SocketComments = {\n security: Comment | undefined\n overview: Comment | undefined\n ignore: Comment[]\n}\n\nexport function checkForSocketComments({\n comments\n}: {\n comments: Record<string, Comment>\n}): SocketComments {\n const socketComments: {\n security: Comment | undefined\n overview: Comment | undefined\n ignore: Comment[]\n } = {\n security: undefined,\n overview: undefined,\n ignore: []\n }\n\n for (const commentId in comments) {\n const comment = comments[commentId]!\n\n if (comment.body.includes('socket-security-comment-actions')) {\n socketComments.security = comment\n } else if (comment.body.includes('socket-overview-comment-actions')) {\n socketComments.overview = comment\n } else if (\n // Based on:\n // To ignore an alert, reply with a comment starting with @SocketSecurity ignore\n // followed by a space separated list of ecosystem/package-name@version specifiers.\n // e.g. @SocketSecurity ignore npm/foo@1.0.0 or ignore all packages with @SocketSecurity ignore-all\n comment.body\n .split('\\n')\n .at(0)\n ?.includes('SocketSecurity ignore')\n ) {\n socketComments.ignore.push(comment)\n }\n }\n\n return socketComments\n}\n\n// Parses the ignore command\n// @SocketSecurity ignore pkg1 pkg2 ...\n// @SocketSecurity ignore ignore-all\nexport function parseIgnoreCommand(line: string) {\n const result = { packages: [] as string[], ignoreAll: false }\n const words = line.trim().replace(/\\s+/g, ' ').split(' ')\n if (words.at(1) === 'ignore-all') {\n result.ignoreAll = true\n return result\n }\n if (words.at(1) === 'ignore') {\n for (let i = 2; i < words.length; i++) {\n const pkg = words[i] as string\n result.packages.push(pkg)\n }\n return result\n }\n return result\n}\n\n// Ref: https://github.com/socketdev-demo/javascript-threats/pull/89#issuecomment-2456015512\nexport function processSecurityComment({\n ignore: ignoreComments,\n security: securityComment\n}: Pick<SocketComments, 'security' | 'ignore'>): string {\n const result: string[] = []\n let start = false\n\n let ignoreAll = false\n const ignoredPackages = []\n for (const ignoreComment of ignoreComments) {\n const parsed = parseIgnoreCommand(\n ignoreComment.body?.split('\\n').at(0) ?? ''\n )\n if (parsed.ignoreAll) {\n ignoreAll = true\n break\n }\n ignoredPackages.push(parsed.packages)\n }\n\n // Split the comment body into lines and update them\n // to generate a new comment body\n for (let line of securityComment?.body?.split('\\n') ?? []) {\n line = line.trim()\n\n if (line.includes('start-socket-alerts-table')) {\n start = true\n result.push(line)\n } else if (\n start &&\n !line.includes('end-socket-alerts-table') &&\n // is not heading line?\n !(\n line === '|Alert|Package|Introduced by|Manifest File|CI|' ||\n line.includes(':---')\n ) &&\n line !== ''\n ) {\n // Parsing Markdown data colunms\n const [_, _title, packageLink, _introducedBy, _manifest, _ci] =\n line.split('|') as [string, string, string, string, string, string]\n\n // Parsing package link [npm/pkg](url)\n const [_ecosystem, pkg] = packageLink\n .slice(1, packageLink.indexOf(']'))\n .split('/', 2) as [string, string]\n const [pkgName, pkgVersion] = pkg.split('@')\n\n // Checking if this package should be ignored\n let ignore = false\n if (ignoreAll) {\n ignore = true\n } else {\n for (const [ignoredPkgName, ignorePkgVersion] of ignoredPackages) {\n if (\n pkgName === ignoredPkgName &&\n (ignorePkgVersion === '*' || pkgVersion === ignorePkgVersion)\n ) {\n ignore = true\n break\n }\n }\n }\n\n if (ignore) {\n break\n }\n result.push(line)\n } else if (line.includes('end-socket-alerts-table')) {\n start = false\n result.push(line)\n } else {\n result.push(line)\n }\n }\n\n return result.join('\\n')\n}\n\nexport function getIgnoreOptions({ comments }: { comments: SocketComments }) {\n const ignoreCommands: string[] = []\n let ignoreAll = false\n\n for (const comment of comments.ignore) {\n let firstLine = comment.body_list[0]!\n if (!ignoreAll && firstLine.includes('SocketSecurity ignore')) {\n try {\n firstLine = firstLine.replace(/@/, '')\n let [, command] = firstLine.split('SocketSecurity ')\n command = command!.trim()\n if (command === 'ignore-all') {\n ignoreAll = true\n } else {\n command = command.replace(/ignore/, '').trim()\n const [name, version] = command.split('@')\n const data = `${name}/${version}`\n ignoreCommands.push(data)\n }\n } catch (e) {\n logger.fail(`Unable to process ignore command for ${comment}`)\n logger.error(e)\n }\n }\n }\n return { ignoreAll, ignoreCommands }\n}\n\nexport function removeAlerts({\n comments,\n newAlerts\n}: {\n comments: SocketComments\n newAlerts: Issue[]\n}) {\n const alerts: Issue[] = []\n\n if (comments.ignore.length === 0) {\n return newAlerts\n }\n\n const { ignoreAll, ignoreCommands } = getIgnoreOptions({\n comments\n })\n\n for (const alert of newAlerts) {\n if (ignoreAll) {\n break\n } else {\n const fullName = `${alert.pkg_type}/${alert.pkg_name}`\n const purl = `${fullName}/${alert.pkg_version}`\n const purlStar = `${fullName}/*`\n if (ignoreCommands.includes(purl) || ignoreCommands.includes(purlStar)) {\n logger.log(`Alerts for ${alert.pkg_name}@${alert.pkg_version} ignored`)\n } else {\n logger.log(\n `Adding alert ${alert.type} for ${alert.pkg_name}@${alert.pkg_version}`\n )\n alerts.push(alert)\n }\n }\n }\n\n return alerts\n}\n","// https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/core/github.py\n/* eslint-disable no-await-in-loop */\nimport { Octokit } from '@octokit/rest'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { Comment } from './classes'\nimport * as SCMComments from './scm_comments'\n\nexport class GitHub {\n octokit: Octokit = new Octokit()\n owner: string\n repo: string\n prNumber: number\n\n constructor() {\n const [owner = '', repo = ''] = (\n process.env['GITHUB_REPOSITORY'] ?? ''\n ).split('/')\n // https://github.com/actions/checkout/issues/58#issuecomment-2264361099\n const prNumber = parseInt(\n process.env['GITHUB_REF']?.match(/refs\\/pull\\/(\\d+)\\/merge/)?.at(1) ?? ''\n )\n this.owner = owner\n this.repo = repo\n this.prNumber = prNumber\n }\n\n checkEventType(): 'main' | 'diff' | 'comment' | 'unsupported' {\n switch (process.env['GITHUB_EVENT_NAME']) {\n case 'push':\n return this.prNumber ? 'diff' : 'main'\n case 'pull_request': {\n // This env variable needs to be set in the GitHub action.\n // Add this code below to GitHub action:\n // - steps:\n // - name: Get PR State\n // if: github.event_name == 'pull_request'\n // run: echo \"EVENT_ACTION=${{ github.event.action }}\" >> $GITHUB_ENV\n const eventAction = process.env['EVENT_ACTION']\n if (eventAction === 'opened' || eventAction === 'synchronize') {\n return 'diff'\n }\n if (!eventAction) {\n throw new Error('Missing event action')\n }\n logger.log(`Pull request action: ${eventAction} is not supported`)\n process.exit()\n }\n case 'issue_comment':\n return 'comment'\n default:\n throw new Error(\n `Unknown event type: ${process.env['GITHUB_EVENT_NAME']}`\n )\n }\n }\n\n async getCommentsForPR(): Promise<SCMComments.SocketComments> {\n const { data: githubComments } =\n await this.octokit.rest.issues.listComments({\n owner: this.owner,\n repo: this.repo,\n issue_number: this.prNumber\n })\n const comments: Record<string, Comment> = {}\n for (const githubComment of githubComments) {\n comments[githubComment.id] = new Comment({\n id: githubComment.id,\n body: githubComment.body ?? '',\n body_list: (githubComment.body ?? '').split('\\n')\n })\n }\n return SCMComments.checkForSocketComments({ comments })\n }\n\n async commentReactionExists({\n commentId\n }: {\n commentId: number\n }): Promise<boolean> {\n const { data } = await this.octokit.reactions.listForIssueComment({\n owner: this.owner,\n repo: this.repo,\n comment_id: commentId\n })\n return data.some(reaction => reaction.content === '+1')\n }\n\n async postReaction({ commentId }: { commentId: number }) {\n await this.octokit.reactions.createForIssueComment({\n owner: this.owner,\n repo: this.repo,\n comment_id: commentId,\n content: '+1'\n })\n }\n\n async handleIgnoreReactons({\n comments\n }: {\n comments: SCMComments.SocketComments\n }) {\n for (const ignoreComment of comments.ignore) {\n if (\n ignoreComment.body?.includes('SocketSecurity ignore') &&\n !(await this.commentReactionExists({\n commentId: ignoreComment.id\n }))\n ) {\n await this.postReaction({ commentId: ignoreComment.id })\n }\n }\n }\n\n async updateComment({ body, id }: { id: number; body: string }) {\n await this.octokit.issues.updateComment({\n owner: this.owner,\n repo: this.repo,\n comment_id: id,\n body\n })\n }\n\n async removeCommentAlerts({\n comments\n }: {\n comments: SCMComments.SocketComments\n }) {\n const securityAlert = comments.security\n if (securityAlert !== undefined) {\n const newBody = SCMComments.processSecurityComment({\n security: comments.security,\n ignore: comments.ignore\n })\n await this.handleIgnoreReactons({ comments })\n await this.updateComment({ id: securityAlert.id, body: newBody })\n }\n }\n\n async postComment({ body }: { body: string }) {\n await this.octokit.issues.createComment({\n owner: this.owner,\n repo: this.repo,\n issue_number: this.prNumber,\n body\n })\n }\n\n async addSocketComments({\n comments,\n newOverviewComment,\n newSecurityComment,\n overviewComment,\n securityComment\n }: {\n securityComment: string\n overviewComment: string\n comments: SCMComments.SocketComments\n newSecurityComment: boolean\n newOverviewComment: boolean\n }): Promise<void> {\n const {\n overview: existingOverviewComment,\n security: existingSecurityComment\n } = comments\n if (newOverviewComment) {\n logger.log('New Dependency Overview comment')\n if (existingOverviewComment !== undefined) {\n logger.log('Previous version of Dependency Overview, updating')\n await this.updateComment({\n body: overviewComment,\n id: existingOverviewComment.id\n })\n } else {\n logger.log('No previous version of Dependency Overview, posting')\n await this.postComment({ body: overviewComment })\n }\n }\n if (newSecurityComment) {\n logger.log('New Security Issue Comment')\n if (existingSecurityComment !== undefined) {\n logger.log('Previous version of Security Issue comment, updating')\n await this.updateComment({\n body: securityComment,\n id: existingSecurityComment.id\n })\n } else {\n logger.log('No Previous version of Security Issue comment, posting')\n await this.postComment({ body: securityComment })\n }\n }\n }\n}\n","// https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/core/messages.py\nimport { Diff, Issue, Purl } from './classes'\n\nexport function createSecurityCommentJSON({ diff }: { diff: Diff }) {\n const scanFailed = false\n\n // Not porting this code because it's unreachable\n // https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/core/messages.py#L13-L18\n\n const output: {\n scanFailed: boolean\n newAlerts: Issue[]\n fullScanId: string\n } = {\n scanFailed,\n newAlerts: [],\n fullScanId: diff.id\n }\n for (const alert of diff.newAlerts) {\n output.newAlerts.push(alert)\n }\n\n return output\n}\n\nexport function createPurlLink(purl: Purl): string {\n const packageUrl = `[${purl.purl}](${purl.url})`\n return packageUrl\n}\n\nexport function createAddedTable(diff: Diff): string {\n const overviewTable = [\n 'Package',\n 'Direct',\n 'Capabilities',\n 'Transitives',\n 'Size',\n 'Author'\n ]\n const rows = []\n for (const added of diff.newPackages) {\n const packageUrl = createPurlLink(added)\n const capabilities = added.capabilities.join(', ')\n const row = [\n packageUrl,\n added.direct,\n capabilities,\n added.transitives,\n `${added.size} KB`,\n added.author_url\n ]\n rows.push(row)\n }\n\n let md = ''\n md += `|${overviewTable.join('|')}|\\n`\n md += '|---|---|---|---|---|---|\\n'\n for (const row of rows) {\n md += `|${row.join('|')}|\\n`\n }\n\n return md\n}\n\nexport function createRemoveLine(diff: Diff): string {\n const removedLine = ['Removed packages:']\n for (const removed of diff.removedPackages) {\n const packageUrl = createPurlLink(removed)\n removedLine.push(packageUrl)\n }\n return removedLine.join(', ')\n}\n\nexport function dependencyOverviewTemplate(diff: Diff): string {\n let md = ''\n md += '<!-- socket-overview-comment-actions -->\\n'\n md += '# Socket Security: Dependency Overview\\n'\n md +=\n 'New and removed dependencies detected. Learn more about [socket.dev](https://socket.dev)\\n\\n'\n md += createAddedTable(diff)\n if (diff.removedPackages.length > 0) {\n md += createRemoveLine(diff)\n }\n return md\n}\n\nexport function createSources(alert: Issue): [string, string] {\n const sources: string[] = []\n const manifests: string[] = []\n for (const [source, manifest] of alert.introduced_by) {\n const addStr = `<li>${manifest}</li>`\n const sourceStr = `<li>${source}</li>`\n if (!sources.includes(sourceStr)) {\n sources.push(sourceStr)\n }\n if (!manifests.includes(addStr)) {\n manifests.push(addStr)\n }\n }\n const manifestList = manifests.join('')\n const sourceList = sources.join('')\n const manifestStr = `<ul>${manifestList}</ul>`\n const sourcesStr = `<ul>${sourceList}</ul>`\n return [manifestStr, sourcesStr]\n}\n\nexport function createSecurityAlertTable(diff: Diff): {\n ignoreCommands: string[]\n nextSteps: Record<string, string[]>\n mdTable: string\n} {\n const alertTable = [\n 'Alert',\n 'Package',\n 'Introduced by',\n 'Manifest File',\n 'CI'\n ]\n const nextSteps: Record<string, string[]> = {}\n const ignoreCommands: string[] = []\n\n const rows: string[][] = []\n for (const alert of diff.newAlerts) {\n if (!(alert.next_step_title in nextSteps)) {\n nextSteps[alert.next_step_title] = [alert.description, alert.suggestion]\n }\n const ignore = `\\`SocketSecurity ignore ${alert.purl}\\``\n if (!ignoreCommands.includes(ignore)) {\n ignoreCommands.push(ignore)\n }\n const [manifestStr, sourceStr] = createSources(alert)\n const purlUrl = `[${alert.purl}](${alert.url})`\n if (alert.error) {\n alert.emoji = ':no_entry_sign:'\n } else {\n alert.emoji = ':warning:'\n }\n const row = [alert.title, purlUrl, sourceStr, manifestStr, alert.emoji]\n if (!rows.some(r => r.join() === row.join())) {\n rows.push(row)\n }\n }\n\n let md = ''\n md += `|${alertTable.join('|')}|\\n`\n md += '|---|---|---|---|---|\\n'\n for (const row of rows) {\n md += `|${row.join('|')}|\\n`\n }\n\n return { ignoreCommands, nextSteps, mdTable: md }\n}\n\nexport function createNextSteps(nextSteps: Record<string, string[]>): string {\n let md = ''\n for (const step in nextSteps) {\n const detail = nextSteps[step]!\n md += '<details>\\n'\n md += `<summary>${step}</summary>\\n`\n for (const line of detail) {\n md += `${line}\\n`\n }\n md += '</details>\\n'\n }\n return md\n}\n\nexport function createDeeperLook(): string {\n let md = ''\n md += '<details>\\n'\n md += '<summary>Take a deeper look at the dependency</summary>\\n'\n md +=\n \"Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support [AT] socket [DOT] dev.\\n\"\n md += '</details>\\n'\n return md\n}\n\nexport function createRemovePackage(): string {\n let md = ''\n md += '<details>\\n'\n md += '<summary>Remove the package</summary>\\n'\n md +=\n 'If you happen to install a dependency that Socket reports as [https://socket.dev/npm/issue/malware](Known Malware) you should immediately remove it and select a different dependency. For other alert types, you may may wish to investigate alternative packages or consider if there are other ways to mitigate the specific risk posed by the dependency.\\n'\n md += '</details>\\n'\n return md\n}\n\nexport function createAcceptableRisk(ignoreCommands: string[]): string {\n let md = ''\n md += '<details>\\n'\n md += '<summary>Mark a package as acceptable risk</summary>\\n'\n md +=\n 'To ignore an alert, reply with a comment starting with `SocketSecurity ignore` followed by a space separated list of `ecosystem/package-name@version` specifiers. e.g. `SocketSecurity ignore npm/foo@1.0.0` or ignore all packages with `SocketSecurity ignore-all`\\n'\n md += '<ul>\\n'\n for (const ignore of ignoreCommands) {\n md += `<li>${ignore}</li>\\n`\n }\n md += '</ul>\\n'\n md += '</details>\\n'\n return md\n}\n\nexport function securityCommentTemplate(diff: Diff): string {\n let md = ''\n md += '<!-- socket-security-comment-actions -->\\n'\n md += '# Socket Security: Issues Report\\n'\n md +=\n 'Potential security issues detected. Learn more about [socket.dev](https://socket.dev)\\n'\n md +=\n 'To accept the risk, merge this PR and you will not be notified again.\\n\\n'\n md += '<!-- start-socket-alerts-table -->\\n'\n const { ignoreCommands, mdTable, nextSteps } = createSecurityAlertTable(diff)\n md += mdTable\n md += '<!-- end-socket-alerts-table -->\\n\\n'\n md += createNextSteps(nextSteps)\n md += createDeeperLook()\n md += createRemovePackage()\n md += createAcceptableRisk(ignoreCommands)\n return md.trim()\n}\n","// https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/socketcli.py\n\nimport micromatch from 'micromatch'\nimport { simpleGit } from 'simple-git'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { SocketSdk } from '@socketsecurity/sdk'\n\nimport { Core } from './core'\nimport { GitHub } from './core/github'\nimport * as Messages from './core/messages'\nimport * as SCMComments from './core/scm_comments'\nimport { getDefaultToken } from '../../utils/sdk'\n\n// TODO: is this a github action handler?\nexport async function runAction(\n githubEventBefore: string,\n githubEventAfter: string\n) {\n //TODO\n const socket = new SocketSdk(getDefaultToken()!)\n\n const git = simpleGit()\n const changedFiles = (\n await git.diff(\n process.env['GITHUB_EVENT_NAME'] === 'pull_request'\n ? ['--name-only', 'HEAD^1', 'HEAD']\n : ['--name-only', githubEventBefore, githubEventAfter]\n )\n ).split('\\n')\n\n logger.log({ changedFiles })\n // supportedFiles have 3-level deep globs\n const patterns = Object.values(await socket.getReportSupportedFiles())\n .flatMap((i: Record<string, any>) => Object.values(i))\n .flatMap((i: Record<string, any>) => Object.values(i))\n .flatMap((i: Record<string, any>) => Object.values(i))\n\n const files = micromatch(changedFiles, patterns)\n\n const scm = new GitHub()\n\n if (scm.checkEventType() === 'comment') {\n logger.log('Comment initiated flow')\n const comments = await scm.getCommentsForPR()\n await scm.removeCommentAlerts({ comments })\n } else if (scm.checkEventType() === 'diff') {\n logger.log('Push initiated flow')\n const core = new Core({ owner: scm.owner, repo: scm.repo, files, socket })\n const diff = await core.createNewDiff({})\n const comments = await scm.getCommentsForPR()\n diff.newAlerts = SCMComments.removeAlerts({\n comments,\n newAlerts: diff.newAlerts\n })\n const overviewComment = Messages.dependencyOverviewTemplate(diff)\n const securityComment = Messages.securityCommentTemplate(diff)\n let newSecurityComment = true\n let newOverviewComment = true\n const updateOldSecurityComment = comments.security !== undefined\n const updateOldOverviewComment = comments.overview !== undefined\n if (diff.newAlerts.length === 0) {\n if (!updateOldSecurityComment) {\n newSecurityComment = false\n logger.log('No new alerts or security issue comment disabled')\n } else {\n logger.log('Updated security comment with no new alerts')\n }\n }\n if (diff.newPackages.length === 0 && diff.removedPackages.length === 0) {\n if (!updateOldOverviewComment) {\n newOverviewComment = false\n logger.log(\n 'No new/removed packages or Dependency Overview comment disabled'\n )\n } else {\n logger.log('Updated overview comment with no dependencies')\n }\n }\n await scm.addSocketComments({\n securityComment,\n overviewComment,\n comments,\n newSecurityComment,\n newOverviewComment\n })\n }\n}\n","import process from 'node:process'\n\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { AuthError } from './errors'\nimport constants from '../constants'\n\nimport type {\n SocketSdkErrorType,\n SocketSdkOperations\n} from '@socketsecurity/sdk'\n\nconst { API_V0_URL } = constants\n\nexport function handleUnsuccessfulApiResponse<T extends SocketSdkOperations>(\n _name: T,\n result: SocketSdkErrorType<T>\n) {\n // SocketSdkErrorType['error'] is not typed.\n const resultErrorMessage = (result as { error?: Error }).error?.message\n const message =\n typeof resultErrorMessage === 'string'\n ? resultErrorMessage\n : 'No error message returned'\n if (result.status === 401 || result.status === 403) {\n throw new AuthError(message)\n }\n logger.fail(\n `${colors.bgRed(colors.white('API returned an error:'))} ${message}`\n )\n process.exit(1)\n}\n\nexport async function handleApiCall<T>(\n value: T,\n description: string\n): Promise<T> {\n let result: T\n try {\n result = await value\n } catch (cause) {\n throw new Error(`Failed ${description}`, { cause })\n }\n return result\n}\n\nexport async function handleAPIError(code: number) {\n if (code === 400) {\n return 'One of the options passed might be incorrect.'\n } else if (code === 403) {\n return 'You might be trying to access an organization that is not linked to the API key you are logged in with.'\n }\n}\n\nexport function getLastFiveOfApiToken(token: string): string {\n // Get the last 5 characters of the API token before the trailing \"_api\".\n return token.slice(-9, -4)\n}\n\nexport async function queryAPI(path: string, apiToken: string) {\n return await fetch(`${API_V0_URL}/${path}`, {\n method: 'GET',\n headers: {\n Authorization: `Basic ${btoa(`${apiToken}:${apiToken}`)}`\n }\n })\n}\n","type HelpListOptions = {\n keyPrefix: string\n padName: number\n}\n\ntype ListDescription = string | { description: string }\n\nexport function getFlagListOutput(\n list: Record<string, ListDescription>,\n indent: number,\n { keyPrefix = '--', padName } = {} as HelpListOptions\n): string {\n return getHelpListOutput(\n {\n ...list\n },\n indent,\n { keyPrefix, padName }\n )\n}\n\nexport function getHelpListOutput(\n list: Record<string, ListDescription>,\n indent: number,\n { keyPrefix = '', padName = 18 } = {} as HelpListOptions\n): string {\n let result = ''\n const names = Object.keys(list).sort()\n for (const name of names) {\n const rawDescription = list[name]\n const description =\n (typeof rawDescription === 'object'\n ? rawDescription.description\n : rawDescription) || ''\n result +=\n ''.padEnd(indent) +\n (keyPrefix + name).padEnd(padName) +\n description +\n '\\n'\n }\n return result.trim()\n}\n","import type { Flag } from 'meow'\n\n// TODO: not sure if I'm missing something but meow doesn't seem to expose this?\ntype StringFlag = Flag<'string', string> | Flag<'string', string[], true>\ntype BooleanFlag = Flag<'boolean', boolean> | Flag<'boolean', boolean[], true>\ntype NumberFlag = Flag<'number', number> | Flag<'number', number[], true>\ntype AnyFlag = StringFlag | BooleanFlag | NumberFlag\n\n// Note: we use this description in getFlagListOutput, meow doesn't care\nexport type MeowFlags = Record<string, AnyFlag & { description: string }>\n\nexport const commonFlags: MeowFlags = {\n help: {\n type: 'boolean',\n default: false,\n shortFlag: 'h',\n description: 'Print this help.'\n },\n dryRun: {\n type: 'boolean',\n default: false,\n description: 'Do input validation for a command and exit 0 when input is ok'\n }\n}\n\nexport const outputFlags: MeowFlags = {\n json: {\n type: 'boolean',\n shortFlag: 'j',\n default: false,\n description: 'Output result as json'\n },\n markdown: {\n type: 'boolean',\n shortFlag: 'm',\n default: false,\n description: 'Output result as markdown'\n }\n}\n\nexport const validationFlags: MeowFlags = {\n all: {\n type: 'boolean',\n default: false,\n description: 'Include all issues'\n },\n strict: {\n type: 'boolean',\n default: false,\n description: 'Exits with an error code if any matching issues are found'\n }\n}\n","import path from 'node:path'\n\nimport meow from 'meow'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { toSortedObject } from '@socketsecurity/registry/lib/objects'\nimport { normalizePath } from '@socketsecurity/registry/lib/path'\nimport { escapeRegExp } from '@socketsecurity/registry/lib/regexps'\n\nimport { getLastFiveOfApiToken } from './api'\nimport { getFlagListOutput, getHelpListOutput } from './output-formatting'\nimport { getSetting } from './settings'\nimport constants from '../constants'\nimport { MeowFlags, commonFlags } from '../flags'\n\nimport type { Options } from 'meow'\n\nconst { DRY_RUN_LABEL, REDACTED } = constants\n\ninterface CliAlias {\n description: string\n argv: readonly string[]\n hidden?: boolean | undefined\n}\n\ntype CliAliases = Record<string, CliAlias>\n\ntype CliSubcommandRun = (\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n context: { parentName: string }\n) => Promise<void> | void\n\nexport interface CliSubcommand {\n description: string\n hidden?: boolean | undefined\n run: CliSubcommandRun\n}\n\n// Property names are picked such that the name is at the top when the props\n// get ordered by alphabet while flags is near the bottom and the help text\n// at the bottom, because they tend ot occupy the most lines of code.\nexport interface CliCommandConfig {\n commandName: string // tmp optional while we migrate\n description: string\n hidden: boolean\n flags: MeowFlags // tmp optional while we migrate\n help: (command: string, config: CliCommandConfig) => string\n}\n\ninterface MeowOptions extends Options<any> {\n aliases?: CliAliases | undefined\n argv: readonly string[]\n name: string\n}\n\n// For debugging. Whenever you call meowOrExit it will store the command here\n// This module exports a getter that returns the current value.\nlet lastSeenCommand = ''\n\nexport function getLastSeenCommand(): string {\n return lastSeenCommand\n}\n\nexport async function meowWithSubcommands(\n subcommands: Record<string, CliSubcommand>,\n options: MeowOptions\n): Promise<void> {\n const {\n aliases = {},\n argv,\n importMeta,\n name,\n ...additionalOptions\n } = { __proto__: null, ...options }\n const [commandOrAliasName, ...rawCommandArgv] = argv\n // If we got at least some args, then lets find out if we can find a command.\n if (commandOrAliasName) {\n const alias = aliases[commandOrAliasName]\n // First: Resolve argv data from alias if its an alias that's been given.\n const [commandName, ...commandArgv] = alias\n ? [...alias.argv, ...rawCommandArgv]\n : [commandOrAliasName, ...rawCommandArgv]\n // Second: Find a command definition using that data.\n const commandDefinition = commandName ? subcommands[commandName] : undefined\n // Third: If a valid command has been found, then we run it...\n if (commandDefinition) {\n return await commandDefinition.run(commandArgv, importMeta, {\n parentName: name\n })\n }\n }\n const flags = {\n ...commonFlags,\n ...additionalOptions.flags\n }\n // ...else we provide basic instructions and help.\n\n emitBanner(name)\n\n const cli = meow(\n `\n Usage\n $ ${name} <command>\n\n Commands\n ${getHelpListOutput(\n {\n ...toSortedObject(\n Object.fromEntries(\n Object.entries(subcommands).filter(\n ({ 1: subcommand }) => !subcommand.hidden\n )\n )\n ),\n ...toSortedObject(\n Object.fromEntries(\n Object.entries(aliases).filter(({ 1: alias }) => {\n const { hidden } = alias\n const cmdName = hidden ? '' : alias.argv[0]\n const subcommand = cmdName ? subcommands[cmdName] : undefined\n return subcommand && !subcommand.hidden\n })\n )\n )\n },\n 6\n )}\n\n Options\n ${getFlagListOutput(flags, 6)}\n\n Examples\n $ ${name} --help\n `,\n {\n argv,\n importMeta,\n ...additionalOptions,\n flags,\n autoHelp: false // otherwise we can't exit(0)\n }\n )\n if (!cli.flags['help'] && cli.flags['dryRun']) {\n process.exitCode = 0\n logger.log(`${DRY_RUN_LABEL}: No-op, call a sub-command; ok`)\n } else {\n cli.showHelp()\n }\n}\n\n/**\n * Note: meow will exit immediately if it calls its .showHelp()\n */\nexport function meowOrExit({\n allowUnknownFlags, // commands that pass-through args need to allow this\n argv,\n config,\n importMeta,\n parentName\n}: {\n allowUnknownFlags?: boolean | undefined\n argv: readonly string[]\n config: CliCommandConfig\n parentName: string\n importMeta: ImportMeta\n}) {\n const command = `${parentName} ${config.commandName}`\n lastSeenCommand = command\n\n emitBanner(command)\n\n // This exits if .printHelp() is called either by meow itself or by us.\n const cli = meow({\n argv,\n description: config.description,\n help: config.help(command, config),\n importMeta,\n flags: config.flags,\n allowUnknownFlags: Boolean(allowUnknownFlags),\n autoHelp: false // otherwise we can't exit(0)\n })\n if (cli.flags['help']) {\n cli.showHelp()\n }\n return cli\n}\n\nexport function emitBanner(name: string) {\n // Print a banner at the top of each command.\n // This helps with brand recognition and marketing.\n // It also helps with debugging since it contains version and command details.\n // Note: print over stderr to preserve stdout for flags like --json and\n // --markdown. If we don't do this, you can't use --json in particular\n // and pipe the result to other tools. By emitting the banner over stderr\n // you can do something like `socket scan view xyz | jq | process`.\n // The spinner also emits over stderr for example.\n logger.error(getAsciiHeader(name))\n}\n\nfunction getAsciiHeader(command: string) {\n // Note: In tests we return <redacted> because otherwise snapshots will fail.\n // The '@rollup/plugin-replace' will replace \"process.env['VITEST']\".\n const redacting = process.env['VITEST']\n const cliVersion = redacting\n ? REDACTED\n : // The '@rollup/plugin-replace' will replace \"process.env['INLINED_SOCKET_CLI_VERSION_HASH']\".\n process.env['INLINED_SOCKET_CLI_VERSION_HASH']\n const nodeVersion = redacting ? REDACTED : process.version\n const apiToken = getSetting('apiToken')\n const shownToken = redacting\n ? REDACTED\n : apiToken\n ? getLastFiveOfApiToken(apiToken)\n : 'no'\n const relCwd = redacting\n ? REDACTED\n : normalizePath(\n process\n .cwd()\n .replace(\n new RegExp(\n `^${escapeRegExp(constants.homePath)}(?:${path.sep}|$)`,\n 'i'\n ),\n '~/'\n )\n )\n const body = `\n _____ _ _ /---------------\n | __|___ ___| |_ ___| |_ | Socket.dev CLI ver ${cliVersion}\n |__ | . | _| '_| -_| _| | Node: ${nodeVersion}, API token set: ${shownToken}\n |_____|___|___|_,_|___|_|.dev | Command: \\`${command}\\`, cwd: ${relCwd}`.trimStart()\n return ` ${body}\\n`\n}\n","// https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/socketcli.py\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { runAction } from './run-action'\nimport constants from '../../constants'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'action',\n description: 'Socket action command', // GitHub Action ?\n hidden: true,\n flags: {\n // This flag is unused\n // socketSecurityApiKey: { // deprecate this asap.\n // type: 'string',\n // default: 'env var SOCKET_SECURITY_API_KEY',\n // description: 'Socket API token'\n // },\n githubEventBefore: {\n type: 'string',\n default: '',\n description: 'Before marker'\n },\n githubEventAfter: {\n type: 'string',\n default: '',\n description: 'After marker'\n }\n },\n help: (command, { flags }) => `\n Usage\n $ ${command} [options]\n\n Options\n ${getFlagListOutput(flags, 6)}\n `\n}\n\nexport const cmdAction = {\n description: config.description,\n hidden: config.hidden,\n run: run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const githubEventBefore = String(cli.flags['githubEventBefore'] || '')\n const githubEventAfter = String(cli.flags['githubEventAfter'] || '')\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await runAction(githubEventBefore, githubEventAfter)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { setupSdk } from '../../utils/sdk'\n\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\nimport type { SocketSdkReturnType } from '@socketsecurity/sdk'\n\nexport async function fetchOrgAnalyticsData(\n time: number,\n spinner: Spinner,\n apiToken: string\n): Promise<SocketSdkReturnType<'getOrgAnalytics'>['data'] | undefined> {\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getOrgAnalytics(time.toString()),\n 'fetching analytics data'\n )\n\n if (result.success === false) {\n handleUnsuccessfulApiResponse('getOrgAnalytics', result)\n return undefined\n }\n\n spinner.stop()\n\n if (!result.data.length) {\n logger.log('No analytics data is available for this organization yet.')\n return undefined\n }\n\n return result.data\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { setupSdk } from '../../utils/sdk'\n\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\nimport type { SocketSdkReturnType } from '@socketsecurity/sdk'\n\nexport async function fetchRepoAnalyticsData(\n repo: string,\n time: number,\n spinner: Spinner,\n apiToken: string\n): Promise<SocketSdkReturnType<'getRepoAnalytics'>['data'] | undefined> {\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getRepoAnalytics(repo, time.toString()),\n 'fetching analytics data'\n )\n\n if (result.success === false) {\n handleUnsuccessfulApiResponse('getRepoAnalytics', result)\n return undefined\n }\n\n spinner.stop()\n\n if (!result.data.length) {\n logger.log('No analytics data is available for this organization yet.')\n return undefined\n }\n\n return result.data\n}\n","export function mdTableStringNumber(\n title1: string,\n title2: string,\n obj: Record<string, number | string>\n): string {\n // | Date | Counts |\n // | ----------- | ------ |\n // | Header | 201464 |\n // | Paragraph | 18 |\n let mw1 = title1.length\n let mw2 = title2.length\n for (const [key, value] of Object.entries(obj)) {\n mw1 = Math.max(mw1, key.length)\n mw2 = Math.max(mw2, String(value ?? '').length)\n }\n\n const lines = []\n lines.push(`| ${title1.padEnd(mw1, ' ')} | ${title2.padEnd(mw2)} |`)\n lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)\n for (const [key, value] of Object.entries(obj)) {\n lines.push(\n `| ${key.padEnd(mw1, ' ')} | ${String(value ?? '').padStart(mw2, ' ')} |`\n )\n }\n lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)\n\n return lines.join('\\n')\n}\n\nexport function mdTable<T extends Array<Record<string, string>>>(\n logs: T,\n // This is saying \"an array of strings and the strings are a valid key of elements of T\"\n // In turn, T is defined above as the audit log event type from our OpenAPI docs.\n cols: Array<string & keyof T[number]>\n): string {\n // Max col width required to fit all data in that column\n const cws = cols.map(col => col.length)\n\n for (const log of logs) {\n for (let i = 0; i < cols.length; ++i) {\n // @ts-ignore\n const val: unknown = log[cols[i] ?? ''] ?? ''\n cws[i] = Math.max(cws[i] ?? 0, String(val).length)\n }\n }\n\n let div = '|'\n for (const cw of cws) div += ' ' + '-'.repeat(cw) + ' |'\n\n let header = '|'\n for (let i = 0; i < cols.length; ++i)\n header += ' ' + String(cols[i]).padEnd(cws[i] ?? 0, ' ') + ' |'\n\n let body = ''\n for (const log of logs) {\n body += '|'\n for (let i = 0; i < cols.length; ++i) {\n // @ts-ignore\n const val: unknown = log[cols[i] ?? ''] ?? ''\n body += ' ' + String(val).padEnd(cws[i] ?? 0, ' ') + ' |'\n }\n body += '\\n'\n }\n\n return [div, header, div, body.trim(), div].filter(s => !!s.trim()).join('\\n')\n}\n","import fs from 'node:fs/promises'\n\n// @ts-ignore\nimport ScreenWidget from 'blessed/lib/widgets/screen'\nimport contrib from 'blessed-contrib'\nimport { stripIndents } from 'common-tags'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { fetchOrgAnalyticsData } from './fetch-org-analytics'\nimport { fetchRepoAnalyticsData } from './fetch-repo-analytics'\nimport constants from '../../constants'\nimport { AuthError } from '../../utils/errors'\nimport { mdTableStringNumber } from '../../utils/markdown'\nimport { getDefaultToken } from '../../utils/sdk'\n\nimport type { SocketSdkReturnType } from '@socketsecurity/sdk'\nimport type { Widgets } from 'blessed' // Note: Widgets does not seem to actually work as code :'(\n\ninterface 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\nconst METRICS = [\n 'total_critical_alerts',\n 'total_high_alerts',\n 'total_medium_alerts',\n 'total_low_alerts',\n 'total_critical_added',\n 'total_medium_added',\n 'total_low_added',\n 'total_high_added',\n 'total_critical_prevented',\n 'total_high_prevented',\n 'total_medium_prevented',\n 'total_low_prevented'\n] as const\n\n// Note: This maps `new Date(date).getMonth()` to English three letters\nconst Months = [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec'\n] as const\n\nexport async function displayAnalytics({\n filePath,\n outputKind,\n repo,\n scope,\n time\n}: {\n scope: string\n time: number\n repo: string\n outputKind: 'json' | 'markdown' | 'print'\n filePath: string\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API token.'\n )\n }\n\n await outputAnalyticsWithToken({\n apiToken,\n filePath,\n outputKind,\n repo,\n scope,\n time\n })\n}\n\nasync function outputAnalyticsWithToken({\n apiToken,\n filePath,\n outputKind,\n repo,\n scope,\n time\n}: {\n apiToken: string\n scope: string\n time: number\n repo: string\n outputKind: 'json' | 'markdown' | 'print'\n filePath: string\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Fetching analytics data')\n\n let data:\n | undefined\n | SocketSdkReturnType<'getOrgAnalytics'>['data']\n | SocketSdkReturnType<'getRepoAnalytics'>['data']\n if (scope === 'org') {\n data = await fetchOrgAnalyticsData(time, spinner, apiToken)\n } else if (repo) {\n data = await fetchRepoAnalyticsData(repo, time, spinner, apiToken)\n }\n\n // A message should already have been printed if we have no data here\n if (!data) return\n\n if (outputKind === 'json') {\n const serialized = renderJson(data)\n if (!serialized) return\n\n if (filePath && filePath !== '-') {\n try {\n await fs.writeFile(filePath, serialized, 'utf8')\n logger.log(`Data successfully written to ${filePath}`)\n } catch (e) {\n process.exitCode = 1\n logger.fail('There was an error trying to write the json to disk')\n logger.error(e)\n }\n } else {\n logger.log(serialized)\n }\n } else {\n const fdata = scope === 'org' ? formatDataOrg(data) : formatDataRepo(data)\n\n if (outputKind === 'markdown') {\n const serialized = renderMarkdown(fdata, time, repo)\n\n if (filePath && filePath !== '-') {\n try {\n await fs.writeFile(filePath, serialized, 'utf8')\n logger.log(`Data successfully written to ${filePath}`)\n } catch (e) {\n logger.error(e)\n }\n } else {\n logger.log(serialized)\n }\n } else {\n displayAnalyticsScreen(fdata)\n }\n }\n}\n\nfunction renderJson(data: unknown): string | undefined {\n try {\n return JSON.stringify(data, null, 2)\n } catch (e) {\n process.exitCode = 1\n // This could be caused by circular references, which is an \"us\" problem\n logger.fail(\n 'There was a problem converting the data set to JSON. Please try without --json or with --markdown'\n )\n return\n }\n}\n\nfunction renderMarkdown(\n data: FormattedData,\n days: number,\n repoSlug: string\n): string {\n return stripIndents`\n# Socket Alert Analytics\n\nThese are the Socket.dev stats are 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(\n ([title, table]) =>\n stripIndents`\n## ${title}\n\n${table}\n`\n )\n .join('\\n\\n')}\n\n## Top 5 alert types\n\n${mdTableStringNumber('Name', 'Counts', data['top_five_alert_types'])}\n`\n}\n\nfunction displayAnalyticsScreen(data: FormattedData): void {\n const screen: Widgets.Screen = new ScreenWidget({})\n const grid = new contrib.grid({ 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 bar = grid.set(4, 0, 1, 2, contrib.bar, {\n label: 'Top 5 alert types',\n barWidth: 10,\n barSpacing: 17,\n xOffset: 0,\n maxHeight: 9,\n barBgColor: 'magenta'\n })\n\n screen.append(bar) //must append before setting data\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\n screen.key(['escape', 'q', 'C-c'], () => process.exit(0))\n}\n\nfunction formatDataRepo(\n data: SocketSdkReturnType<'getRepoAnalytics'>['data']\n): FormattedData {\n const sortedTopFiveAlerts: Record<string, number> = {}\n const totalTopAlerts: Record<string, number> = {}\n\n const formattedData = {} as Omit<FormattedData, 'top_five_alert_types'>\n for (const metric of METRICS) {\n formattedData[metric] = {}\n }\n\n for (const entry of data) {\n const topFiveAlertTypes = entry['top_five_alert_types']\n for (const type of Object.keys(topFiveAlertTypes)) {\n const count = topFiveAlertTypes[type] ?? 0\n if (!totalTopAlerts[type]) {\n totalTopAlerts[type] = count\n } else if (count > (totalTopAlerts[type] ?? 0)) {\n totalTopAlerts[type] = count\n }\n }\n }\n for (const entry of data) {\n for (const metric of METRICS) {\n formattedData[metric]![formatDate(entry['created_at'])] = entry[metric]\n }\n }\n\n const topFiveAlertEntries = Object.entries(totalTopAlerts)\n .sort(([_keya, a], [_keyb, b]) => b - a)\n .slice(0, 5)\n for (const [key, value] of topFiveAlertEntries) {\n sortedTopFiveAlerts[key] = value\n }\n\n return {\n ...formattedData,\n top_five_alert_types: sortedTopFiveAlerts\n }\n}\n\nfunction formatDataOrg(\n data: SocketSdkReturnType<'getOrgAnalytics'>['data']\n): FormattedData {\n const sortedTopFiveAlerts: Record<string, number> = {}\n const totalTopAlerts: Record<string, number> = {}\n\n const formattedData = {} as Omit<FormattedData, 'top_five_alert_types'>\n for (const metric of METRICS) {\n formattedData[metric] = {}\n }\n\n for (const entry of data) {\n const topFiveAlertTypes = entry['top_five_alert_types']\n for (const type of Object.keys(topFiveAlertTypes)) {\n const count = topFiveAlertTypes[type] ?? 0\n if (!totalTopAlerts[type]) {\n totalTopAlerts[type] = count\n } else {\n totalTopAlerts[type] += count\n }\n }\n }\n\n for (const metric of METRICS) {\n const formatted = formattedData[metric]\n for (const entry of data) {\n const date = formatDate(entry['created_at'])\n if (!formatted[date]) {\n formatted[date] = entry[metric]!\n } else {\n formatted[date] += entry[metric]!\n }\n }\n }\n\n const topFiveAlertEntries = Object.entries(totalTopAlerts)\n .sort(([_keya, a], [_keyb, b]) => b - a)\n .slice(0, 5)\n for (const [key, value] of topFiveAlertEntries) {\n sortedTopFiveAlerts[key] = value\n }\n\n return {\n ...formattedData,\n top_five_alert_types: sortedTopFiveAlerts\n }\n}\n\nfunction formatDate(date: string): string {\n return `${Months[new Date(date).getMonth()]} ${new Date(date).getDate()}`\n}\n\nfunction renderLineCharts(\n grid: contrib.grid,\n screen: Widgets.Screen,\n title: string,\n coords: number[],\n data: Record<string, number>\n): void {\n const line = grid.set(...coords, contrib.line, {\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 { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { displayAnalytics } from './display-analytics'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'analytics',\n description: `Look up analytics data`,\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n file: {\n type: 'string',\n shortFlag: 'f',\n default: '-',\n description:\n 'Path to a local file to save the output. Only valid with --json/--markdown. Defaults to stdout.'\n },\n repo: {\n type: 'string',\n shortFlag: 'r',\n default: '',\n description: 'Name of the repository. Only valid when scope=repo'\n },\n scope: {\n type: 'string',\n shortFlag: 's',\n default: 'org',\n description:\n \"Scope of the analytics data - either 'org' or 'repo', default: org\"\n },\n time: {\n type: 'number',\n shortFlag: 't',\n default: 7,\n description: 'Time filter - either 7, 30 or 90, default: 7'\n }\n },\n help: (command, { flags }) => `\n Usage\n $ ${command} --scope=<scope> --time=<time filter>\n\n Default parameters are set to show the organization-level analytics over the\n last 7 days.\n\n Options\n ${getFlagListOutput(flags, 6)}\n\n Examples\n $ ${command} --scope=org --time=7\n $ ${command} --scope=org --time=30\n $ ${command} --scope=repo --repo=test-repo --time=30\n `\n}\n\nexport const cmdAnalytics = {\n description: config.description,\n hidden: config.hidden,\n run: run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const { file, json, markdown, repo, scope, time } = cli.flags\n\n const badScope = scope !== 'org' && scope !== 'repo'\n const badTime = time !== 7 && time !== 30 && time !== 90\n const badRepo = scope === 'repo' && !repo\n const badFile = file !== '-' && !json && !markdown\n const badFlags = json && markdown\n\n if (badScope || badTime || badRepo || badFile || badFlags) {\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 stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Scope must be \"repo\" or \"org\" ${badScope ? colors.red('(bad!)') : colors.green('(ok)')}\n\n - The time filter must either be 7, 30 or 90 ${badTime ? colors.red('(bad!)') : colors.green('(ok)')}\n\n ${scope === 'repo' ? `- Repository name using --repo when scope is \"repo\" ${badRepo ? colors.red('(bad!)') : colors.green('(ok)')}` : ''}\n\n ${badFlags ? `- The \\`--json\\` and \\`--markdown\\` flags can not be used at the same time ${badFlags ? colors.red('(bad!)') : colors.green('(ok)')}` : ''}\n\n ${badFile ? `- The \\`--file\\` flag is only valid when using \\`--json\\` or \\`--markdown\\` ${badFile ? colors.red('(bad!)') : colors.green('(ok)')}` : ''}\n `\n .split('\\n')\n .filter(s => !!s.trim())\n .join('\\n')\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n return await displayAnalytics({\n scope,\n time,\n repo: String(repo || ''),\n outputKind: json ? 'json' : markdown ? 'markdown' : 'print',\n filePath: String(file || '')\n })\n}\n","import { stripIndents } from 'common-tags'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { Separator, select } from '@socketsecurity/registry/lib/prompts'\nimport { SocketSdkReturnType } from '@socketsecurity/sdk'\n\nimport constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { mdTable } from '../../utils/markdown'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nimport type { Choice } from '@socketsecurity/registry/lib/prompts'\n\ntype AuditChoice = Choice<string>\n\ntype AuditChoices = Array<Separator | AuditChoice>\n\nexport async function getAuditLog({\n logType,\n orgSlug,\n outputKind,\n page,\n perPage\n}: {\n outputKind: 'json' | 'markdown' | 'print'\n orgSlug: string\n page: number\n perPage: number\n logType: string\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n const auditLogs = await getAuditLogWithToken({\n apiToken,\n orgSlug,\n outputKind,\n page,\n perPage,\n logType\n })\n if (!auditLogs) return\n\n if (outputKind === 'json')\n await outputAsJson(auditLogs.results, orgSlug, logType, page, perPage)\n else if (outputKind === 'markdown')\n await outputAsMarkdown(auditLogs.results, orgSlug, logType, page, perPage)\n else await outputAsPrint(auditLogs.results, orgSlug, logType)\n}\n\nasync function outputAsJson(\n auditLogs: SocketSdkReturnType<'getAuditLogEvents'>['data']['results'],\n orgSlug: string,\n logType: string,\n page: number,\n perPage: number\n): Promise<void> {\n let json\n try {\n json = JSON.stringify(\n {\n desc: 'Audit logs for given query',\n generated: new Date().toISOString(),\n org: orgSlug,\n logType,\n page,\n perPage,\n logs: auditLogs.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 null,\n 2\n )\n } catch (e) {\n process.exitCode = 1\n logger.fail(\n 'There was a problem converting the logs to JSON, please try without the `--json` flag'\n )\n return\n }\n\n logger.log(json)\n}\n\nasync function outputAsMarkdown(\n auditLogs: SocketSdkReturnType<'getAuditLogEvents'>['data']['results'],\n orgSlug: string,\n logType: string,\n page: number,\n perPage: number\n): Promise<void> {\n try {\n const table = mdTable<any>(auditLogs, [\n 'event_id',\n 'created_at',\n 'type',\n 'user_email',\n 'ip_address',\n 'user_agent'\n ])\n\n logger.log(\n stripIndents`\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- per page: ${perPage}\n- generated: ${new Date().toISOString()}\n\n${table}\n`\n )\n } catch (e) {\n process.exitCode = 1\n logger.fail(\n 'There was a problem converting the logs to JSON, please try without the `--json` flag'\n )\n logger.error(e)\n return\n }\n}\n\nasync function outputAsPrint(\n auditLogs: SocketSdkReturnType<'getAuditLogEvents'>['data']['results'],\n orgSlug: string,\n logType: string\n): Promise<void> {\n const data: AuditChoices = []\n const logDetails: { [key: string]: string } = {}\n\n for (const d of auditLogs) {\n const { created_at } = d\n if (created_at) {\n const name = `${new Date(created_at).toLocaleDateString('en-us', { year: 'numeric', month: 'numeric', day: 'numeric' })} - ${d.user_email} - ${d.type} - ${d.ip_address} - ${d.user_agent}`\n data.push({ name } as AuditChoice, new Separator())\n logDetails[name] = JSON.stringify(d.payload)\n }\n }\n\n logger.log(\n logDetails[\n (await select({\n message: logType\n ? `\\n Audit log for: ${orgSlug} with type: ${logType}\\n`\n : `\\n Audit log for: ${orgSlug}\\n`,\n choices: data,\n pageSize: 30\n })) as any\n ]\n )\n}\n\nasync function getAuditLogWithToken({\n apiToken,\n logType,\n orgSlug,\n outputKind,\n page,\n perPage\n}: {\n apiToken: string\n outputKind: 'json' | 'markdown' | 'print'\n orgSlug: string\n page: number\n perPage: number\n logType: string\n}): Promise<SocketSdkReturnType<'getAuditLogEvents'>['data'] | void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start(`Looking up audit log for ${orgSlug}`)\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getAuditLogEvents(orgSlug, {\n outputJson: outputKind === 'json', // I'm not sure this is used at all\n outputMarkdown: outputKind === 'markdown', // I'm not sure this is used at all\n orgSlug,\n type: logType,\n page,\n per_page: perPage\n }),\n `Looking up audit log for ${orgSlug}\\n`\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('getAuditLogEvents', result)\n return\n }\n\n spinner.stop()\n\n return result.data\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getAuditLog } from './get-audit-log'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'audit-log',\n description: 'Look up the audit log for an organization',\n hidden: false,\n flags: {\n type: {\n type: 'string',\n shortFlag: 't',\n default: '',\n description: 'Type of log event'\n },\n perPage: {\n type: 'number',\n shortFlag: 'pp',\n default: 30,\n description: 'Results per page - default is 30'\n },\n page: {\n type: 'number',\n shortFlag: 'p',\n default: 1,\n description: 'Page number - default is 1'\n },\n ...commonFlags,\n ...outputFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <org slug>\n\n This feature requires an Enterprise Plan. To learn more about getting access\n to this feature and many more, please visit https://socket.dev/pricing\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg\n `\n}\n\nexport const cmdAuditLog = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const { json, markdown, page, perPage, type } = cli.flags\n\n const logType = String(type || '')\n const [orgSlug = ''] = cli.input\n\n if (!orgSlug) {\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 stripIndents`\n ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n `\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await getAuditLog({\n orgSlug,\n outputKind: json ? 'json' : markdown ? 'markdown' : 'print',\n page: Number(page || 0),\n perPage: Number(perPage || 0),\n logType: logType.charAt(0).toUpperCase() + logType.slice(1)\n })\n}\n","import { existsSync, promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\n\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport shadowBin from '../../shadow/npm/bin'\n\nconst { NPM, NPX, PACKAGE_LOCK_JSON, PNPM, YARN, YARN_LOCK } = constants\n\nconst nodejsPlatformTypes = new Set([\n 'javascript',\n 'js',\n 'nodejs',\n NPM,\n PNPM,\n 'ts',\n 'tsx',\n 'typescript'\n])\n\nexport async function runCycloneDX(yargvWithYes: any) {\n let cleanupPackageLock = false\n const { yes, ...yargv } = { __proto__: null, ...yargvWithYes } as any\n const yesArgs = yes ? ['--yes'] : []\n if (\n yargv.type !== YARN &&\n nodejsPlatformTypes.has(yargv.type) &&\n existsSync(`./${YARN_LOCK}`)\n ) {\n if (existsSync(`./${PACKAGE_LOCK_JSON}`)) {\n yargv.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 await shadowBin(NPX, [\n ...yesArgs,\n // The '@rollup/plugin-replace' will replace \"process.env['INLINED_SYNP_VERSION']\".\n `synp@${process.env['INLINED_SYNP_VERSION']}`,\n '--source-file',\n `./${YARN_LOCK}`\n ])\n yargv.type = NPM\n cleanupPackageLock = true\n } catch {}\n }\n }\n await shadowBin(NPX, [\n ...yesArgs,\n // The '@rollup/plugin-replace' will replace \"process.env['INLINED_CYCLONEDX_CDXGEN_VERSION']\".\n `@cyclonedx/cdxgen@${process.env['INLINED_CYCLONEDX_CDXGEN_VERSION']}`,\n ...argvToArray(yargv)\n ])\n if (cleanupPackageLock) {\n try {\n await fs.rm(`./${PACKAGE_LOCK_JSON}`)\n } catch {}\n }\n const fullOutputPath = path.join(process.cwd(), yargv.output)\n if (existsSync(fullOutputPath)) {\n logger.log(colors.cyanBright(`${yargv.output} created!`))\n }\n}\n\nfunction argvToArray(argv: {\n [key: string]: boolean | null | number | string | Array<string | number>\n}): string[] {\n if (argv['help']) {\n return ['--help']\n }\n const result = []\n for (const { 0: key, 1: value } of Object.entries(argv)) {\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 if (argv['--']) {\n result.push('--', ...(argv as any)['--'])\n }\n return result\n}\n","const helpFlags = new Set(['--help', '-h'])\n\nexport function cmdFlagsToString(args: string[]) {\n const result = []\n for (let i = 0, { length } = args; i < length; i += 1) {\n if (args[i]!.startsWith('--')) {\n // Check if the next item exists and is NOT another flag.\n if (i + 1 < length && !args[i + 1]!.startsWith('--')) {\n result.push(`${args[i]}=${args[i + 1]}`)\n i += 1\n } else {\n result.push(args[i])\n }\n }\n }\n return result.join(' ')\n}\n\nexport function cmdPrefixMessage(cmdName: string, text: string): string {\n const cmdPrefix = cmdName ? `${cmdName}: ` : ''\n return `${cmdPrefix}${text}`\n}\n\nexport function isHelpFlag(cmdArg: string) {\n return helpFlags.has(cmdArg)\n}\n","// import { meowOrExit } from '../../utils/meow-with-subcommands'\nimport process from 'node:process'\n\nimport yargsParse from 'yargs-parser'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { runCycloneDX } from './run-cyclonedx'\nimport constants from '../../constants'\nimport { isHelpFlag } from '../../utils/cmd'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\n// TODO: convert yargs to meow. Or convert all the other things to yargs.\nconst toLower = (arg: string) => arg.toLowerCase()\nconst arrayToLower = (arg: string[]) => arg.map(toLower)\n\nconst yargsConfig = {\n configuration: {\n 'camel-case-expansion': false,\n 'strip-aliased': true,\n 'parse-numbers': false,\n 'populate--': true,\n 'unknown-options-as-args': true\n },\n coerce: {\n author: arrayToLower,\n filter: arrayToLower,\n only: arrayToLower,\n profile: toLower,\n standard: arrayToLower,\n type: toLower\n },\n default: {\n //author: ['OWASP Foundation'],\n //'auto-compositions': true,\n //babel: true,\n //evidence: false,\n //'include-crypto': false,\n //'include-formulation': false,\n\n // Default 'install-deps' to `false` and 'lifecycle' to 'pre-build' to\n // sidestep arbitrary code execution during a cdxgen scan.\n // https://github.com/CycloneDX/cdxgen/issues/1328\n 'install-deps': false,\n lifecycle: 'pre-build',\n\n //output: 'bom.json',\n //profile: 'generic',\n //'project-version': '',\n //recurse: true,\n //'server-host': '127.0.0.1',\n //'server-port': '9090',\n //'spec-version': '1.5',\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 yes: ['y']\n },\n array: [\n { key: 'author', type: 'string' },\n { key: 'exclude', type: 'string' },\n { key: 'filter', type: 'string' },\n { key: 'only', type: 'string' },\n { key: 'standard', type: 'string' }\n ],\n boolean: [\n 'auto-compositions',\n 'babel',\n 'deep',\n 'evidence',\n 'fail-on-error',\n 'generate-key-and-sign',\n 'help',\n 'include-formulation',\n 'include-crypto',\n 'install-deps',\n 'print',\n 'required-only',\n 'server',\n 'validate',\n 'version',\n // The --yes flag and -y alias map to the corresponding flag and alias of npx.\n // https://docs.npmjs.com/cli/v7/commands/npx#compatibility-with-older-npx-versions\n 'yes'\n ],\n string: [\n 'api-key',\n 'lifecycle',\n 'output',\n 'parent-project-id',\n 'profile',\n 'project-group',\n 'project-name',\n 'project-version',\n 'project-id',\n 'server-host',\n 'server-port',\n 'server-url',\n 'spec-version'\n ]\n}\n\nconst config: CliCommandConfig = {\n commandName: 'cdxgen',\n description: 'Create an SBOM with CycloneDX generator (cdxgen)',\n hidden: false,\n flags: {\n // TODO: convert from yargsConfig\n },\n help: (command, config) => `\n Usage\n $ ${command} [options]\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n `\n}\n\nexport const cmdCdxgen = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n allowUnknownFlags: true,\n // Don't let meow take over --help.\n argv: argv.filter(a => !isHelpFlag(a)),\n config,\n importMeta,\n parentName\n })\n // if (cli.input.length)\n // logger.fail(\n // stripIndents`\n // ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n //\n // - Unexpected arguments\n // `)\n // config.help(parentName, config)\n // return\n // }\n\n // TODO: Convert to meow.\n const yargv = {\n ...yargsParse(argv as string[], yargsConfig)\n } as any\n\n const unknown: string[] = yargv._\n const { length: unknownLength } = unknown\n if (unknownLength) {\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', unknownLength)}: ${yargv._.join(', ')}`\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n if (yargv.output === undefined) {\n yargv.output = 'socket-cdx.json'\n }\n\n await runCycloneDX(yargv)\n}\n","// @ts-ignore\nimport chalkTable from 'chalk-table'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function findDependencies({\n limit,\n offset,\n outputJson\n}: {\n outputJson: boolean\n limit: number\n offset: number\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Searching dependencies...')\n\n const socketSdk = await setupSdk(apiToken)\n\n const result = await handleApiCall(\n socketSdk.searchDependencies({ limit, offset }),\n 'Searching dependencies'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('searchDependencies', result)\n return\n }\n\n spinner.stop('Organization dependencies:')\n\n if (outputJson) {\n logger.log(result.data)\n return\n }\n\n logger.log(\n 'Request details: Offset:',\n offset,\n ', limit:',\n limit,\n ', is there more data after this?',\n result.data.end ? 'no' : 'yes'\n )\n\n const options = {\n columns: [\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: 'type', name: colors.cyan('Type') },\n { field: 'direct', name: colors.cyan('Direct') }\n ]\n }\n\n logger.log(chalkTable(options, result.data.rows))\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { findDependencies } from './find-dependencies'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'dependencies',\n description:\n 'Search for any dependency that is being used in your organization',\n hidden: false,\n flags: {\n ...commonFlags,\n limit: {\n type: 'number',\n shortFlag: 'l',\n default: 50,\n description: 'Maximum number of dependencies returned'\n },\n offset: {\n type: 'number',\n shortFlag: 'o',\n default: 0,\n description: 'Page number'\n },\n ...outputFlags\n },\n help: (command, config) => `\n Usage\n ${command}\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n ${command} --limit 20 --offset 10\n `\n}\n\nexport const cmdScanCreate = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n // TODO: markdown flag is ignored\n await findDependencies({\n limit: Number(cli.flags['limit'] || 0) || 0,\n offset: Number(cli.flags['offset'] || 0) || 0,\n outputJson: Boolean(cli.flags['json'])\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'\nimport { SocketSdkReturnType } from '@socketsecurity/sdk'\n\nimport constants from '../../constants'\nimport { handleAPIError, handleApiCall, queryAPI } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken } from '../../utils/sdk'\n\nexport async function getDiffScan({\n after,\n before,\n depth,\n file,\n orgSlug,\n outputJson\n}: {\n after: string\n before: string\n depth: number\n file: string\n orgSlug: string\n outputJson: boolean\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await getDiffScanWithToken({\n after,\n before,\n depth,\n file,\n orgSlug,\n outputJson,\n apiToken\n })\n}\nexport async function getDiffScanWithToken({\n after,\n apiToken,\n before,\n depth,\n file,\n orgSlug,\n outputJson\n}: {\n after: string\n apiToken: string\n depth: number\n before: string\n file: string\n orgSlug: string\n outputJson: boolean\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Getting diff scan...')\n\n const response = await queryAPI(\n `orgs/${orgSlug}/full-scans/diff?before=${encodeURIComponent(before)}&after=${encodeURIComponent(after)}`,\n apiToken\n )\n\n if (!response.ok) {\n const err = await handleAPIError(response.status)\n spinner.errorAndStop(\n `${colors.bgRed(colors.white(response.statusText))}: ${err}`\n )\n return\n }\n\n const result = await handleApiCall(\n (await response.json()) as Promise<\n SocketSdkReturnType<'GetOrgDiffScan'>['data']\n >,\n 'Deserializing json'\n )\n\n spinner.stop()\n\n const dashboardUrl = (result as any)?.['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 (outputJson || file) {\n let json\n try {\n json = JSON.stringify(result, null, 2)\n } catch (e) {\n process.exitCode = 1\n // Most likely caused by a circular reference (or OOM)\n logger.fail('There was a problem converting the data to JSON')\n logger.error(e)\n return\n }\n\n if (file && file !== '-') {\n logger.log(`Writing json to \\`${file}\\``)\n fs.writeFile(file, JSON.stringify(result, null, 2), err => {\n if (err) {\n logger.fail(`Writing to \\`${file}\\` failed...`)\n logger.error(err)\n } else {\n logger.log(`Data successfully written to \\`${file}\\``)\n }\n logger.error(dashboardMessage)\n })\n } else {\n // TODO: expose different method for writing to stderr when simply dodging stdout\n logger.error(`\\n Diff scan result: \\n`)\n logger.log(json)\n logger.error(dashboardMessage)\n }\n\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, {\n showHidden: false,\n depth: depth > 0 ? depth : null,\n colors: true,\n maxArrayLength: null\n })\n )\n logger.log(\n `\\n 📝 To display the detailed report in the terminal, use the --json flag \\n`\n )\n logger.log(dashboardMessage)\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getDiffScan } from './get-diff-scan'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'get',\n description: 'Get a diff scan for an organization',\n hidden: false,\n flags: {\n ...commonFlags,\n after: {\n type: 'string',\n shortFlag: 'a',\n default: '',\n description: 'The full scan ID of the head scan'\n },\n before: {\n type: 'string',\n shortFlag: 'b',\n default: '',\n description: 'The full scan ID of the base scan'\n },\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 json: {\n type: 'boolean',\n shortFlag: 'j',\n default: false,\n description:\n 'Output result as json. This can be big. Use --file to store it to disk without truncation.'\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 },\n help: (command, config) => `\n Usage\n $ ${command} <org slug> --before=<before> --after=<after>\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 to be further analyzed by other tools.\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeCorp --before=aaa0aa0a-aaaa-0000-0a0a-0000000a00a0 --after=aaa1aa1a-aaaa-1111-1a1a-1111111a11a1\n `\n}\n\nexport const cmdDiffScanGet = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const before = String(cli.flags['before'] || '')\n const after = String(cli.flags['after'] || '')\n const [orgSlug = ''] = cli.input\n\n if (!before || !after || cli.input.length < 1) {\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(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\\n\n - Specify a before and after full scan ID ${!before && !after ? colors.red('(missing before and after!)') : !before ? colors.red('(missing before!)') : !after ? colors.red('(missing after!)') : colors.green('(ok)')}\\n\n - To get full scans IDs, you can run the command \"socket scan list <your org slug>\".\n The args are expecting a full \\`aaa0aa0a-aaaa-0000-0a0a-0000000a00a0\\` ID.\\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\\n`)\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await getDiffScan({\n outputJson: Boolean(cli.flags['json']),\n before,\n after,\n depth: Number(cli.flags['depth']),\n orgSlug,\n file: String(cli.flags['file'] || '')\n })\n}\n","import { cmdDiffScanGet } from './cmd-diff-scan-get'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands'\n\nconst description = 'Diff scans related commands'\n\nexport const cmdDiffScan: 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: true,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n get: cmdDiffScanGet\n },\n {\n argv,\n description,\n importMeta,\n name: parentName + ' diff-scan'\n }\n )\n }\n}\n","import { getManifestData } from '@socketsecurity/registry'\nimport { runScript } from '@socketsecurity/registry/lib/npm'\nimport {\n fetchPackagePackument,\n readPackageJson\n} from '@socketsecurity/registry/lib/packages'\n\nimport constants from '../../constants'\nimport {\n Arborist,\n SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES,\n SafeArborist\n} from '../../shadow/npm/arborist/lib/arborist'\nimport {\n findPackageNodes,\n getAlertsMapFromArborist,\n updateNode\n} from '../../utils/lockfile/package-lock-json'\nimport { getCveInfoByAlertsMap } from '../../utils/socket-package-alert'\n\nimport type { SafeNode } from '../../shadow/npm/arborist/lib/node'\nimport type { EnvDetails } from '../../utils/package-environment'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nconst { NPM } = constants\n\nfunction isTopLevel(tree: SafeNode, node: SafeNode): boolean {\n return tree.children.get(node.name) === node\n}\n\ntype NpmFixOptions = {\n spinner?: Spinner | undefined\n}\n\nexport async function npmFix(\n _pkgEnvDetails: EnvDetails,\n cwd: string,\n options?: NpmFixOptions | undefined\n) {\n const { spinner } = { __proto__: null, ...options } as NpmFixOptions\n\n spinner?.start()\n\n const arb = new SafeArborist({\n path: cwd,\n ...SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES\n })\n\n await arb.reify()\n\n const alertsMap = await getAlertsMapFromArborist(arb, {\n consolidate: true,\n include: {\n existing: true,\n unfixable: false,\n upgrade: false\n }\n })\n\n const infoByPkg = getCveInfoByAlertsMap(alertsMap)\n if (!infoByPkg) {\n spinner?.stop()\n return\n }\n\n await arb.buildIdealTree()\n\n const editablePkgJson = await readPackageJson(cwd, { editable: true })\n\n for (const { 0: name, 1: infos } of infoByPkg) {\n const revertToIdealTree = arb.idealTree!\n arb.idealTree = null\n // eslint-disable-next-line no-await-in-loop\n await arb.buildIdealTree()\n\n const tree = arb.idealTree!\n\n const hasUpgrade = !!getManifestData(NPM, name)\n if (hasUpgrade) {\n spinner?.info(`Skipping ${name}. Socket Optimize package exists.`)\n continue\n }\n\n const nodes = findPackageNodes(tree, name)\n\n const packument =\n nodes.length && infos.length\n ? // eslint-disable-next-line no-await-in-loop\n await fetchPackagePackument(name)\n : null\n if (!packument) {\n continue\n }\n\n for (let i = 0, { length: nodesLength } = nodes; i < nodesLength; i += 1) {\n const node = nodes[i]!\n for (\n let j = 0, { length: infosLength } = infos;\n j < infosLength;\n j += 1\n ) {\n const { firstPatchedVersionIdentifier, vulnerableVersionRange } =\n infos[j]!\n const { version: oldVersion } = node\n if (\n updateNode(\n node,\n packument,\n vulnerableVersionRange,\n firstPatchedVersionIdentifier\n )\n ) {\n try {\n // eslint-disable-next-line no-await-in-loop\n await runScript('test', [], { spinner, stdio: 'ignore' })\n\n spinner?.info(`Patched ${name} ${oldVersion} -> ${node.version}`)\n\n if (isTopLevel(tree, node)) {\n for (const depField of [\n 'dependencies',\n 'optionalDependencies',\n 'peerDependencies'\n ]) {\n const { content: pkgJson } = editablePkgJson\n const oldVersion = (pkgJson[depField] as any)?.[name]\n if (oldVersion) {\n const decorator = /^[~^]/.exec(oldVersion)?.[0] ?? ''\n ;(pkgJson as any)[depField][name] =\n `${decorator}${node.version}`\n }\n }\n }\n // eslint-disable-next-line no-await-in-loop\n await editablePkgJson.save()\n } catch {\n spinner?.error(`Reverting ${name} to ${oldVersion}`)\n arb.idealTree = revertToIdealTree\n }\n } else {\n spinner?.error(`Could not patch ${name} ${oldVersion}`)\n }\n }\n }\n }\n\n const arb2 = new Arborist({ path: cwd })\n arb2.idealTree = arb.idealTree\n await arb2.reify()\n\n spinner?.stop()\n}\n","import { detectDepTypes } from '@pnpm/lockfile.detect-dep-types'\n\nimport { batchScan } from '../alert/artifact'\nimport { AlertsByPkgId, addArtifactToAlertsMap } from '../socket-package-alert'\n\nimport type { Lockfile } from '@pnpm/lockfile-file'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\ntype AlertIncludeFilter = {\n critical?: boolean | undefined\n cve?: boolean | undefined\n existing?: boolean | undefined\n unfixable?: boolean | undefined\n upgrade?: boolean | undefined\n}\n\ntype GetAlertsMapFromPnpmLockfileOptions = {\n consolidate?: boolean | undefined\n include?: AlertIncludeFilter | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function getAlertsMapFromPnpmLockfile(\n lockfile: Lockfile,\n options?: GetAlertsMapFromPnpmLockfileOptions | undefined\n): Promise<AlertsByPkgId> {\n const { include: _include, spinner } = {\n __proto__: null,\n ...options\n } as GetAlertsMapFromPnpmLockfileOptions\n\n const depTypes = detectDepTypes(lockfile)\n const pkgIds = Object.keys(depTypes)\n let { length: remaining } = pkgIds\n const alertsByPkgId: AlertsByPkgId = new Map()\n if (!remaining) {\n return alertsByPkgId\n }\n const getText = () => `Looking up data for ${remaining} packages`\n\n spinner?.start(getText())\n\n const toAlertsMapOptions = {\n overrides: lockfile.overrides,\n ...options\n }\n for await (const artifact of batchScan(pkgIds)) {\n await addArtifactToAlertsMap(artifact, alertsByPkgId, toAlertsMapOptions)\n remaining -= 1\n if (spinner && remaining > 0) {\n spinner.start()\n spinner.setText(getText())\n }\n }\n\n spinner?.stop()\n\n return alertsByPkgId\n}\n","import process from 'node:process'\n\nimport { isDebug } from '@socketsecurity/registry/lib/debug'\nimport {\n isAuditFlag,\n isFundFlag,\n isLoglevelFlag,\n isProgressFlag\n} from '@socketsecurity/registry/lib/npm'\nimport { isObject } from '@socketsecurity/registry/lib/objects'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../constants'\nimport { getNpmBinPath } from '../shadow/npm/paths'\n\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nconst { SOCKET_IPC_HANDSHAKE } = constants\n\ntype SpawnOption = Exclude<Parameters<typeof spawn>[2], undefined>\n\ntype SafeNpmInstallOptions = SpawnOption & {\n agentExecPath?: string | undefined\n args?: string[] | readonly string[] | undefined\n ipc?: object | undefined\n spinner?: Spinner | undefined\n}\n\nexport function safeNpmInstall(options?: SafeNpmInstallOptions) {\n const {\n agentExecPath = getNpmBinPath(),\n args = [],\n ipc,\n spinner,\n ...spawnOptions\n } = { __proto__: null, ...options } as SafeNpmInstallOptions\n const useIpc = isObject(ipc)\n const useDebug = isDebug()\n const terminatorPos = args.indexOf('--')\n const binArgs = (\n terminatorPos === -1 ? args : args.slice(0, terminatorPos)\n ).filter(a => !isAuditFlag(a) && !isFundFlag(a) && !isProgressFlag(a))\n const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos)\n const isSilent = !useDebug && !binArgs.some(isLoglevelFlag)\n const logLevelArgs = isSilent ? ['--loglevel', 'silent'] : []\n const spawnPromise = spawn(\n // Lazily access constants.execPath.\n constants.execPath,\n [\n // Lazily access constants.nodeHardenFlags.\n ...constants.nodeHardenFlags,\n // Lazily access constants.nodeNoWarningsFlags.\n ...constants.nodeNoWarningsFlags,\n // Lazily access process.env['INLINED_SOCKET_CLI_SENTRY_BUILD'].\n ...(process.env['INLINED_SOCKET_CLI_SENTRY_BUILD']\n ? [\n '--require',\n // Lazily access constants.distInstrumentWithSentryPath.\n constants.distInstrumentWithSentryPath\n ]\n : []),\n '--require',\n // Lazily access constants.distShadowNpmInjectPath.\n constants.distShadowNpmInjectPath,\n agentExecPath,\n 'install',\n // Avoid code paths for 'audit' and 'fund'.\n '--no-audit',\n '--no-fund',\n // Add '--no-progress' to fix input being swallowed by the npm spinner.\n '--no-progress',\n // Add '--loglevel=silent' if a loglevel flag is not provided and the\n // SOCKET_CLI_DEBUG environment variable is not truthy.\n ...logLevelArgs,\n ...binArgs,\n ...otherArgs\n ],\n {\n spinner,\n // Set stdio to include 'ipc'.\n // See https://github.com/nodejs/node/blob/v23.6.0/lib/child_process.js#L161-L166\n // and https://github.com/nodejs/node/blob/v23.6.0/lib/internal/child_process.js#L238.\n stdio: useIpc ? [0, 1, 2, 'ipc'] : 'inherit',\n ...spawnOptions,\n env: {\n ...process.env,\n ...spawnOptions.env\n }\n }\n )\n if (useIpc) {\n spawnPromise.process.send({ [SOCKET_IPC_HANDSHAKE]: ipc })\n }\n return spawnPromise\n}\n","import { isDebug } from '@socketsecurity/registry/lib/debug'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\nimport { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nimport constants from '../../constants'\nimport { cmdFlagsToString } from '../../utils/cmd'\nimport { safeNpmInstall } from '../../utils/npm'\n\nimport type { EnvDetails } from '../../utils/package-environment'\n\nconst { NPM } = constants\n\ntype SpawnOption = Exclude<Parameters<typeof spawn>[2], undefined>\ntype SpawnResult = ReturnType<typeof spawn>\n\nexport type AgentInstallOptions = SpawnOption & {\n args?: string[] | readonly string[] | undefined\n spinner?: Spinner | undefined\n}\n\nexport function runAgentInstall(\n pkgEnvDetails: EnvDetails,\n options?: AgentInstallOptions | undefined\n): SpawnResult {\n const { agent, agentExecPath } = pkgEnvDetails\n // All package managers support the \"install\" command.\n if (agent === NPM) {\n return safeNpmInstall({\n agentExecPath,\n ...options\n })\n }\n const {\n args = [],\n spinner,\n ...spawnOptions\n } = { __proto__: null, ...options } as AgentInstallOptions\n return spawn(agentExecPath, ['install', ...args], {\n spinner,\n stdio: isDebug() ? 'inherit' : 'ignore',\n ...spawnOptions,\n env: {\n ...process.env,\n NODE_OPTIONS: cmdFlagsToString([\n // Lazily access constants.nodeHardenFlags.\n ...constants.nodeHardenFlags,\n // Lazily access constants.nodeNoWarningsFlags.\n ...constants.nodeNoWarningsFlags\n ]),\n ...spawnOptions.env\n }\n })\n}\n","import { readWantedLockfile } from '@pnpm/lockfile-file'\n\nimport { getManifestData } from '@socketsecurity/registry'\nimport {\n fetchPackagePackument,\n readPackageJson\n} from '@socketsecurity/registry/lib/packages'\n\nimport constants from '../../constants'\nimport {\n SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES,\n SafeArborist\n} from '../../shadow/npm/arborist/lib/arborist'\nimport {\n findBestPatchVersion,\n findPackageNodes\n} from '../../utils/lockfile/package-lock-json'\nimport { getAlertsMapFromPnpmLockfile } from '../../utils/lockfile/pnpm-lock-yaml'\nimport { getCveInfoByAlertsMap } from '../../utils/socket-package-alert'\nimport { runAgentInstall } from '../optimize/run-agent'\n\nimport type { EnvDetails } from '../../utils/package-environment'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nconst { NPM, OVERRIDES, PNPM } = constants\n\ntype PnpmFixOptions = {\n spinner?: Spinner | undefined\n}\n\nexport async function pnpmFix(\n pkgEnvDetails: EnvDetails,\n cwd: string,\n options?: PnpmFixOptions | undefined\n) {\n const { spinner } = { __proto__: null, ...options } as PnpmFixOptions\n\n spinner?.start()\n\n const lockfile = await readWantedLockfile(cwd, { ignoreIncompatible: false })\n if (!lockfile) {\n spinner?.stop()\n return\n }\n\n const alertsMap = await getAlertsMapFromPnpmLockfile(lockfile, {\n consolidate: true,\n include: {\n existing: true,\n unfixable: false,\n upgrade: false\n }\n })\n\n const infoByPkg = getCveInfoByAlertsMap(alertsMap)\n if (!infoByPkg) {\n spinner?.stop()\n return\n }\n\n const arb = new SafeArborist({\n path: cwd,\n ...SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES\n })\n\n await arb.loadActual()\n\n const editablePkgJson = await readPackageJson(cwd, { editable: true })\n const { content: pkgJson } = editablePkgJson\n\n for (const { 0: name, 1: infos } of infoByPkg) {\n const tree = arb.actualTree!\n\n const hasUpgrade = !!getManifestData(NPM, name)\n if (hasUpgrade) {\n spinner?.info(`Skipping ${name}. Socket Optimize package exists.`)\n continue\n }\n\n const nodes = findPackageNodes(tree, name)\n\n const packument =\n nodes.length && infos.length\n ? // eslint-disable-next-line no-await-in-loop\n await fetchPackagePackument(name)\n : null\n if (!packument) {\n continue\n }\n\n for (let i = 0, { length: nodesLength } = nodes; i < nodesLength; i += 1) {\n const node = nodes[i]!\n for (\n let j = 0, { length: infosLength } = infos;\n j < infosLength;\n j += 1\n ) {\n const { firstPatchedVersionIdentifier, vulnerableVersionRange } =\n infos[j]!\n const { version: oldVersion } = node\n const availableVersions = Object.keys(packument.versions)\n // Find the highest non-vulnerable version within the same major range\n const targetVersion = findBestPatchVersion(\n node,\n availableVersions,\n vulnerableVersionRange,\n firstPatchedVersionIdentifier\n )\n const targetPackument = targetVersion\n ? packument.versions[targetVersion]\n : undefined\n if (targetPackument) {\n const oldPnpm = (pkgJson as any)[PNPM]\n const oldOverrides = oldPnpm?.[OVERRIDES] as\n | { [key: string]: string }\n | undefined\n try {\n editablePkgJson.update({\n [PNPM]: {\n ...oldPnpm,\n [OVERRIDES]: {\n [`${node.name}@${vulnerableVersionRange}`]: `^${targetVersion}`,\n ...oldOverrides\n }\n }\n })\n\n spinner?.info(`Patched ${name} ${oldVersion} -> ${node.version}`)\n\n // eslint-disable-next-line no-await-in-loop\n await editablePkgJson.save()\n // eslint-disable-next-line no-await-in-loop\n await runAgentInstall(pkgEnvDetails, { spinner })\n } catch {\n spinner?.error(`Reverting ${name} to ${oldVersion}`)\n }\n } else {\n spinner?.error(`Could not patch ${name} ${oldVersion}`)\n }\n }\n }\n }\n\n spinner?.stop()\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\n\nimport browserslist from 'browserslist'\nimport semver from 'semver'\nimport which from 'which'\n\nimport { parse as parseBunLockb } from '@socketregistry/hyrious__bun.lockb/index.cjs'\nimport { Logger } from '@socketsecurity/registry/lib/logger'\nimport { isObjectObject } from '@socketsecurity/registry/lib/objects'\nimport { readPackageJson } from '@socketsecurity/registry/lib/packages'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { cmdPrefixMessage } from './cmd'\nimport { findUp, readFileBinary, readFileUtf8 } from './fs'\nimport constants from '../constants'\n\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\nimport type { EditablePackageJson } from '@socketsecurity/registry/lib/packages'\nimport type { SemVer } from 'semver'\n\nconst {\n BINARY_LOCK_EXT,\n BUN,\n HIDDEN_PACKAGE_LOCK_JSON,\n LOCK_EXT,\n NPM,\n NPM_BUGGY_OVERRIDES_PATCHED_VERSION,\n PACKAGE_JSON,\n PNPM,\n VLT,\n YARN,\n YARN_BERRY,\n YARN_CLASSIC\n} = constants\n\nexport const AGENTS = [BUN, NPM, PNPM, YARN_BERRY, YARN_CLASSIC, VLT] as const\nexport type Agent = (typeof AGENTS)[number]\nexport type StringKeyValueObject = { [key: string]: string }\n\nconst binByAgent = {\n __proto__: null,\n [BUN]: BUN,\n [NPM]: NPM,\n [PNPM]: PNPM,\n [YARN_BERRY]: YARN,\n [YARN_CLASSIC]: YARN,\n [VLT]: VLT\n}\n\nasync function getAgentExecPath(agent: Agent): Promise<string> {\n const binName = binByAgent[agent]\n return (await which(binName, { nothrow: true })) ?? binName\n}\n\nasync function getAgentVersion(\n agentExecPath: string,\n cwd: string\n): Promise<SemVer | undefined> {\n let result\n try {\n result =\n semver.coerce(\n // All package managers support the \"--version\" flag.\n (await spawn(agentExecPath, ['--version'], { cwd })).stdout\n ) ?? undefined\n } catch {}\n return result\n}\n\n// The order of LOCKS properties IS significant as it affects iteration order.\nconst LOCKS: Record<string, Agent> = {\n [`bun${LOCK_EXT}`]: BUN,\n [`bun${BINARY_LOCK_EXT}`]: BUN,\n // If both package-lock.json and npm-shrinkwrap.json are present in the root\n // of a project, npm-shrinkwrap.json will take precedence and package-lock.json\n // will be ignored.\n // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#package-lockjson-vs-npm-shrinkwrapjson\n 'npm-shrinkwrap.json': NPM,\n 'package-lock.json': NPM,\n 'pnpm-lock.yaml': PNPM,\n 'pnpm-lock.yml': PNPM,\n [`yarn${LOCK_EXT}`]: YARN_CLASSIC,\n 'vlt-lock.json': VLT,\n // Lastly, look for a hidden lock file which is present if .npmrc has package-lock=false:\n // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#hidden-lockfiles\n //\n // Unlike the other LOCKS keys this key contains a directory AND filename so\n // it has to be handled differently.\n 'node_modules/.package-lock.json': NPM\n}\n\ntype ReadLockFile =\n | ((lockPath: string) => Promise<string | undefined>)\n | ((lockPath: string, agentExecPath: string) => Promise<string | undefined>)\n\nconst readLockFileByAgent: Record<Agent, ReadLockFile> = (() => {\n function wrapReader<T extends (...args: any[]) => Promise<any>>(\n reader: T\n ): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>> | undefined> {\n return async (...args: any[]): Promise<any> => {\n try {\n return await reader(...args)\n } catch {}\n return undefined\n }\n }\n\n const binaryReader = wrapReader(readFileBinary)\n\n const defaultReader = wrapReader(\n async (lockPath: string) => await readFileUtf8(lockPath)\n )\n\n return {\n [BUN]: wrapReader(async (lockPath: string, agentExecPath: string) => {\n const ext = path.extname(lockPath)\n if (ext === LOCK_EXT) {\n return await defaultReader(lockPath)\n }\n if (ext === BINARY_LOCK_EXT) {\n const lockBuffer = await binaryReader(lockPath)\n if (lockBuffer) {\n try {\n return parseBunLockb(lockBuffer)\n } catch {}\n }\n // To print a Yarn lockfile to your console without writing it to disk\n // use `bun bun.lockb`.\n // https://bun.sh/guides/install/yarnlock\n return (await spawn(agentExecPath, [lockPath])).stdout.trim()\n }\n return undefined\n }),\n [NPM]: defaultReader,\n [PNPM]: defaultReader,\n [VLT]: defaultReader,\n [YARN_BERRY]: defaultReader,\n [YARN_CLASSIC]: defaultReader\n }\n})()\n\nexport type DetectOptions = {\n cwd?: string | undefined\n onUnknown?: (pkgManager: string | undefined) => void\n}\n\ntype EnvBase = {\n agent: Agent\n agentExecPath: string\n features: {\n // Fixed by https://github.com/npm/cli/pull/8089.\n // Landed in npm v11.2.0.\n npmBuggyOverrides: boolean\n }\n minimumNodeVersion: string\n npmExecPath: string\n pkgSupported: boolean\n targets: {\n browser: boolean\n node: boolean\n }\n}\n\nexport type EnvDetails = Readonly<\n Remap<\n EnvBase & {\n agentVersion: SemVer\n lockName: string\n lockPath: string\n lockSrc: string\n pkgJson: EditablePackageJson\n pkgPath: string\n }\n >\n>\n\nexport type PartialEnvDetails = Readonly<\n Remap<\n EnvBase & {\n agentVersion: SemVer | undefined\n lockName: string | undefined\n lockPath: string | undefined\n lockSrc: string | undefined\n pkgJson: EditablePackageJson | undefined\n pkgPath: string | undefined\n }\n >\n>\n\nexport async function detectPackageEnvironment({\n cwd = process.cwd(),\n onUnknown\n}: DetectOptions = {}): Promise<EnvDetails | PartialEnvDetails> {\n let lockPath = await findUp(Object.keys(LOCKS), { cwd })\n let lockName = lockPath ? path.basename(lockPath) : undefined\n const isHiddenLockFile = lockName === HIDDEN_PACKAGE_LOCK_JSON\n const pkgJsonPath = lockPath\n ? path.resolve(\n lockPath,\n `${isHiddenLockFile ? '../' : ''}../${PACKAGE_JSON}`\n )\n : await findUp(PACKAGE_JSON, { cwd })\n const pkgPath =\n pkgJsonPath && existsSync(pkgJsonPath)\n ? path.dirname(pkgJsonPath)\n : undefined\n const editablePkgJson = pkgPath\n ? await readPackageJson(pkgPath, { editable: true })\n : undefined\n const pkgJson = editablePkgJson?.content\n // Read Corepack `packageManager` field in package.json:\n // https://nodejs.org/api/packages.html#packagemanager\n const pkgManager = isNonEmptyString(pkgJson?.packageManager)\n ? pkgJson.packageManager\n : undefined\n\n let agent: Agent | undefined\n let agentVersion: SemVer | undefined\n if (pkgManager) {\n const atSignIndex = pkgManager.lastIndexOf('@')\n if (atSignIndex !== -1) {\n const name = pkgManager.slice(0, atSignIndex) as Agent\n const version = pkgManager.slice(atSignIndex + 1)\n if (version && AGENTS.includes(name)) {\n agent = name\n agentVersion = semver.coerce(version) ?? undefined\n }\n }\n }\n if (\n agent === undefined &&\n !isHiddenLockFile &&\n typeof pkgJsonPath === 'string' &&\n typeof lockName === 'string'\n ) {\n agent = LOCKS[lockName] as Agent\n }\n if (agent === undefined) {\n agent = NPM\n onUnknown?.(pkgManager)\n }\n const agentExecPath = await getAgentExecPath(agent)\n\n const npmExecPath =\n agent === NPM ? agentExecPath : await getAgentExecPath(NPM)\n if (agentVersion === undefined) {\n agentVersion = await getAgentVersion(agentExecPath, cwd)\n }\n if (agent === YARN_CLASSIC && (agentVersion?.major ?? 0) > 1) {\n agent = YARN_BERRY\n }\n const targets = {\n browser: false,\n node: true\n }\n let lockSrc: string | undefined\n // Lazily access constants.maintainedNodeVersions.\n let minimumNodeVersion = constants.maintainedNodeVersions.last\n if (pkgJson) {\n const browserField = pkgJson.browser\n if (isNonEmptyString(browserField) || isObjectObject(browserField)) {\n targets.browser = true\n }\n const nodeRange = pkgJson.engines?.['node']\n if (isNonEmptyString(nodeRange)) {\n const coerced = semver.coerce(nodeRange)\n if (coerced && semver.lt(coerced, minimumNodeVersion)) {\n minimumNodeVersion = coerced.version\n }\n }\n const browserslistQuery = pkgJson['browserslist'] as string[] | undefined\n if (Array.isArray(browserslistQuery)) {\n const browserslistTargets = browserslist(browserslistQuery)\n .map(s => s.toLowerCase())\n .sort(naturalCompare)\n const browserslistNodeTargets = browserslistTargets\n .filter(v => v.startsWith('node '))\n .map(v => v.slice(5 /*'node '.length*/))\n if (!targets.browser && browserslistTargets.length) {\n targets.browser =\n browserslistTargets.length !== browserslistNodeTargets.length\n }\n if (browserslistNodeTargets.length) {\n const coerced = semver.coerce(browserslistNodeTargets[0])\n if (coerced && semver.lt(coerced, minimumNodeVersion)) {\n minimumNodeVersion = coerced.version\n }\n }\n }\n // Lazily access constants.maintainedNodeVersions.\n targets.node = constants.maintainedNodeVersions.some(v =>\n semver.satisfies(v, `>=${minimumNodeVersion}`)\n )\n lockSrc =\n typeof lockPath === 'string'\n ? await readLockFileByAgent[agent](lockPath, agentExecPath)\n : undefined\n } else {\n lockName = undefined\n lockPath = undefined\n }\n const pkgSupported = targets.browser || targets.node\n const npmBuggyOverrides =\n agent === NPM &&\n !!agentVersion &&\n semver.lt(agentVersion, NPM_BUGGY_OVERRIDES_PATCHED_VERSION)\n return {\n agent,\n agentExecPath,\n agentVersion,\n lockName,\n lockPath,\n lockSrc,\n minimumNodeVersion,\n npmExecPath,\n pkgJson: editablePkgJson,\n pkgPath,\n pkgSupported,\n features: {\n npmBuggyOverrides\n },\n targets\n }\n}\n\nexport type DetectAndValidateOptions = {\n cmdName?: string | undefined\n logger?: Logger | undefined\n prod?: boolean | undefined\n}\nexport async function detectAndValidatePackageEnvironment(\n cwd: string,\n options?: DetectAndValidateOptions | undefined\n): Promise<void | EnvDetails> {\n const {\n cmdName = '',\n logger,\n prod\n } = {\n __proto__: null,\n ...options\n } as DetectAndValidateOptions\n const details = await detectPackageEnvironment({\n cwd,\n onUnknown(pkgManager: string | undefined) {\n logger?.warn(\n cmdPrefixMessage(\n cmdName,\n `Unknown package manager${pkgManager ? ` ${pkgManager}` : ''}, defaulting to npm`\n )\n )\n }\n })\n if (!details.pkgSupported) {\n logger?.fail(\n cmdPrefixMessage(cmdName, 'No supported Node or browser range detected')\n )\n return\n }\n if (details.agent === VLT) {\n logger?.fail(\n cmdPrefixMessage(\n cmdName,\n `${details.agent} does not support overrides. Soon, though ⚡`\n )\n )\n return\n }\n const lockName = details.lockName ?? 'lock file'\n if (details.lockName === undefined || details.lockSrc === undefined) {\n logger?.fail(cmdPrefixMessage(cmdName, `No ${lockName} found`))\n return\n }\n if (details.lockSrc.trim() === '') {\n logger?.fail(cmdPrefixMessage(cmdName, `${lockName} is empty`))\n return\n }\n if (details.pkgPath === undefined) {\n logger?.fail(cmdPrefixMessage(cmdName, `No ${PACKAGE_JSON} found`))\n return\n }\n if (prod && (details.agent === BUN || details.agent === YARN_BERRY)) {\n logger?.fail(\n cmdPrefixMessage(\n cmdName,\n `--prod not supported for ${details.agent}${details.agentVersion ? `@${details.agentVersion.version}` : ''}`\n )\n )\n return\n }\n if (\n details.lockPath &&\n path.relative(cwd, details.lockPath).startsWith('.')\n ) {\n logger?.warn(\n cmdPrefixMessage(\n cmdName,\n `Package ${lockName} found at ${details.lockPath}`\n )\n )\n }\n return details as EnvDetails\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { npmFix } from './npm-fix'\nimport { pnpmFix } from './pnpm-fix'\nimport constants from '../../constants'\nimport { detectAndValidatePackageEnvironment } from '../../utils/package-environment'\n\nconst { NPM, PNPM } = constants\n\nconst CMD_NAME = 'socket fix'\n\nexport async function runFix() {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start()\n\n const cwd = process.cwd()\n\n const pkgEnvDetails = await detectAndValidatePackageEnvironment(cwd, {\n cmdName: CMD_NAME,\n logger\n })\n if (!pkgEnvDetails) {\n spinner.stop()\n return\n }\n\n switch (pkgEnvDetails.agent) {\n case NPM: {\n await npmFix(pkgEnvDetails, cwd)\n break\n }\n case PNPM: {\n await pnpmFix(pkgEnvDetails, cwd)\n break\n }\n }\n spinner.successAndStop('Socket.dev fix successful')\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { runFix } from './run-fix'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'fix',\n description: 'Fix \"fixable\" Socket alerts',\n hidden: true,\n flags: {\n ...commonFlags\n },\n help: (command, config) => `\n Usage\n $ ${command}\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n `\n}\n\nexport const cmdFix = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await runFix()\n}\n","import { PackageData } from './get-package-info'\nimport { getSeverityCount } from '../../utils/alert/severity'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { getPublicToken, setupSdk } from '../../utils/sdk'\n\nexport async function fetchPackageInfo(\n pkgName: string,\n pkgVersion: string,\n includeAllIssues: boolean\n): Promise<void | PackageData> {\n const socketSdk = await setupSdk(getPublicToken())\n const result = await handleApiCall(\n socketSdk.getIssuesByNPMPackage(pkgName, pkgVersion),\n 'looking up package'\n )\n const scoreResult = await handleApiCall(\n socketSdk.getScoreByNPMPackage(pkgName, pkgVersion),\n 'looking up package score'\n )\n\n if (result.success === false) {\n return handleUnsuccessfulApiResponse('getIssuesByNPMPackage', result)\n }\n\n if (scoreResult.success === false) {\n return handleUnsuccessfulApiResponse('getScoreByNPMPackage', scoreResult)\n }\n\n const severityCount = getSeverityCount(\n result.data,\n includeAllIssues ? undefined : 'high'\n )\n\n return {\n data: result.data,\n severityCount,\n score: scoreResult.data\n }\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport constants from '@socketsecurity/registry/lib/constants'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { hasKeys } from '@socketsecurity/registry/lib/objects'\n\nimport { PackageData } from './get-package-info'\nimport { SEVERITY, formatSeverityCount } from '../../utils/alert/severity'\nimport { ColorOrMarkdown } from '../../utils/color-or-markdown'\nimport {\n getSocketDevAlertUrl,\n getSocketDevPackageOverviewUrl\n} from '../../utils/socket-url'\n\nimport type { SocketSdkReturnType } from '@socketsecurity/sdk'\n\nconst { NPM } = constants\n\nfunction formatScore(score: number): string {\n if (score > 80) {\n return colors.green(`${score}`)\n } else if (score < 80 && score > 60) {\n return colors.yellow(`${score}`)\n }\n return colors.red(`${score}`)\n}\n\nfunction logPackageIssuesDetails(\n packageData: SocketSdkReturnType<'getIssuesByNPMPackage'>['data'],\n outputMarkdown: boolean\n) {\n const issueDetails = packageData.filter(\n d =>\n d.value?.severity === SEVERITY.critical ||\n d.value?.severity === SEVERITY.high\n )\n const uniqueIssueDetails = issueDetails.reduce((acc, issue) => {\n const { type } = issue\n if (type) {\n const details = acc.get(type)\n if (details) {\n details.count += 1\n } else {\n acc.set(type, {\n label: issue.value?.label ?? '',\n count: 1\n })\n }\n }\n return acc\n }, new Map<string, { count: number; label: string }>())\n const format = new ColorOrMarkdown(outputMarkdown)\n for (const [type, details] of uniqueIssueDetails.entries()) {\n const issueWithLink = format.hyperlink(\n details.label,\n getSocketDevAlertUrl(type),\n { fallbackToUrl: true }\n )\n if (details.count === 1) {\n logger.log(`- ${issueWithLink}`)\n } else {\n logger.log(`- ${issueWithLink}: ${details.count}`)\n }\n }\n}\n\nexport function logPackageInfo(\n { data, score, severityCount }: PackageData,\n {\n name,\n outputKind,\n pkgName,\n pkgVersion\n }: {\n includeAllIssues: boolean\n name: string\n outputKind: 'json' | 'markdown' | 'print'\n pkgName: string\n pkgVersion: string\n }\n): void {\n if (outputKind === 'json') {\n logger.log(JSON.stringify(data, undefined, 2))\n return\n }\n if (outputKind === 'markdown') {\n logger.log(stripIndents`\n # Package report for ${pkgName}\n\n Package report card:\n `)\n } else {\n logger.log(`Package report card for ${pkgName}:`)\n }\n const scoreResult = {\n 'Supply Chain Risk': Math.floor(score.supplyChainRisk.score * 100),\n Maintenance: Math.floor(score.maintenance.score * 100),\n Quality: Math.floor(score.quality.score * 100),\n Vulnerabilities: Math.floor(score.vulnerability.score * 100),\n License: Math.floor(score.license.score * 100)\n }\n logger.log('\\n')\n Object.entries(scoreResult).map(score =>\n logger.log(`- ${score[0]}: ${formatScore(score[1])}`)\n )\n logger.log('\\n')\n if (hasKeys(severityCount)) {\n if (outputKind === 'markdown') {\n logger.log('# Issues\\n')\n }\n logger.log(\n `Package has these issues: ${formatSeverityCount(severityCount)}\\n`\n )\n logPackageIssuesDetails(data, outputKind === 'markdown')\n } else {\n logger.log('Package has no issues')\n }\n\n const format = new ColorOrMarkdown(outputKind === 'markdown')\n const url = getSocketDevPackageOverviewUrl(NPM, pkgName, pkgVersion)\n\n logger.log('\\n')\n if (pkgVersion === 'latest') {\n logger.log(\n `Detailed info on socket.dev: ${format.hyperlink(`${pkgName}`, url, { fallbackToUrl: true })}`\n )\n } else {\n logger.log(\n `Detailed info on socket.dev: ${format.hyperlink(`${pkgName} v${pkgVersion}`, url, { fallbackToUrl: true })}`\n )\n }\n if (outputKind !== 'markdown') {\n logger.log(\n colors.dim(\n `\\nOr rerun ${colors.italic(name)} using the ${colors.italic('--json')} flag to get full JSON output`\n )\n )\n } else {\n logger.log('')\n }\n}\n","import process from 'node:process'\n\nimport { hasKeys } from '@socketsecurity/registry/lib/objects'\n\nimport { fetchPackageInfo } from './fetch-package-info'\nimport { logPackageInfo } from './log-package-info'\nimport constants from '../../constants'\n\nimport type { SocketSdkAlert } from '../../utils/alert/severity'\nimport type { SocketSdkReturnType } from '@socketsecurity/sdk'\n\nexport interface PackageData {\n data: SocketSdkReturnType<'getIssuesByNPMPackage'>['data']\n severityCount: Record<SocketSdkAlert['severity'], number>\n score: SocketSdkReturnType<'getScoreByNPMPackage'>['data']\n}\n\nexport async function getPackageInfo({\n commandName,\n includeAllIssues,\n outputKind,\n pkgName,\n pkgVersion,\n strict\n}: {\n commandName: string\n includeAllIssues: boolean\n outputKind: 'json' | 'markdown' | 'print'\n pkgName: string\n pkgVersion: string\n strict: boolean\n}) {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start(\n pkgVersion === 'latest'\n ? `Looking up data for the latest version of ${pkgName}`\n : `Looking up data for version ${pkgVersion} of ${pkgName}`\n )\n\n const packageData = await fetchPackageInfo(\n pkgName,\n pkgVersion,\n includeAllIssues\n )\n\n spinner.successAndStop('Data fetched')\n\n if (packageData) {\n logPackageInfo(packageData, {\n name: commandName,\n includeAllIssues,\n outputKind,\n pkgName,\n pkgVersion\n })\n\n if (strict && hasKeys(packageData.severityCount)) {\n // Let NodeJS exit gracefully but with exit(1)\n process.exitCode = 1\n }\n }\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getPackageInfo } from './get-package-info'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags, validationFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'info',\n description: 'Look up info regarding a package',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n ...validationFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <name>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} webtorrent\n $ ${command} webtorrent@1.9.1\n `\n}\n\nexport const cmdInfo = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const { all, json, markdown, strict } = cli.flags\n const [rawPkgName = ''] = cli.input\n\n if (!rawPkgName || cli.input.length > 1) {\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(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\\n\n - Expecting a package name ${!rawPkgName ? colors.red('(missing!)') : colors.green('(ok)')}\\n\n - Can only accept one package at a time ${cli.input.length > 1 ? colors.red('(got ' + cli.input.length + '!)') : colors.green('(ok)')}\\n`)\n return\n }\n\n const versionSeparator = rawPkgName.lastIndexOf('@')\n const pkgName =\n versionSeparator < 1 ? rawPkgName : rawPkgName.slice(0, versionSeparator)\n const pkgVersion =\n versionSeparator < 1 ? 'latest' : rawPkgName.slice(versionSeparator + 1)\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await getPackageInfo({\n commandName: `${parentName} ${config.commandName}`,\n includeAllIssues: Boolean(all),\n outputKind: json ? 'json' : markdown ? 'markdown' : 'print',\n pkgName,\n pkgVersion,\n strict: Boolean(strict)\n })\n}\n","import { updateSetting } from '../../utils/settings'\n\nexport function applyLogin(\n apiToken: string,\n enforcedOrgs: string[],\n apiBaseUrl: string | undefined,\n apiProxy: string | undefined\n) {\n updateSetting('enforcedOrgs', enforcedOrgs)\n updateSetting('apiToken', apiToken)\n updateSetting('apiBaseUrl', apiBaseUrl)\n updateSetting('apiProxy', apiProxy)\n}\n","import terminalLink from 'terminal-link'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { confirm, password, select } from '@socketsecurity/registry/lib/prompts'\n\nimport { applyLogin } from './apply-login'\nimport constants from '../../constants'\nimport { AuthError } from '../../utils/errors'\nimport { setupSdk } from '../../utils/sdk'\nimport { getSetting } from '../../utils/settings'\n\nimport type { Choice, Separator } from '@socketsecurity/registry/lib/prompts'\nimport type { SocketSdkReturnType } from '@socketsecurity/sdk'\n\ntype OrgChoice = Choice<string>\ntype OrgChoices = Array<Separator | OrgChoice>\nconst { SOCKET_PUBLIC_API_TOKEN } = constants\n\nexport async function attemptLogin(\n apiBaseUrl: string | undefined,\n apiProxy: string | undefined\n) {\n apiBaseUrl ??= getSetting('apiBaseUrl') ?? undefined\n apiProxy ??= getSetting('apiProxy') ?? undefined\n const apiToken =\n (await password({\n message: `Enter your ${terminalLink(\n 'Socket.dev API key',\n 'https://docs.socket.dev/docs/api-keys'\n )} (leave blank for a public key)`\n })) || SOCKET_PUBLIC_API_TOKEN\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Verifying API key...')\n\n let orgs: SocketSdkReturnType<'getOrganizations'>['data']\n try {\n const sdk = await setupSdk(apiToken, apiBaseUrl, apiProxy)\n const result = await sdk.getOrganizations()\n if (!result.success) {\n throw new AuthError()\n }\n orgs = result.data\n spinner.success('API key verified')\n } catch {\n spinner.errorAndStop('Invalid API key')\n return\n }\n\n const enforcedChoices: OrgChoices = Object.values(orgs.organizations)\n .filter(org => org?.plan === 'enterprise')\n .map(org => ({\n name: org.name,\n value: org.id\n }))\n\n let enforcedOrgs: string[] = []\n if (enforcedChoices.length > 1) {\n const id = (await select(\n {\n message:\n \"Which organization's policies should Socket enforce system-wide?\",\n choices: enforcedChoices.concat({\n name: 'None',\n value: '',\n description: 'Pick \"None\" if this is a personal device'\n })\n },\n {\n spinner\n }\n )) as string | null\n if (id) {\n enforcedOrgs = [id]\n }\n } else if (enforcedChoices.length) {\n const confirmOrg = await confirm(\n {\n message: `Should Socket enforce ${(enforcedChoices[0] as OrgChoice)?.name}'s security policies system-wide?`,\n default: true\n },\n {\n spinner\n }\n )\n if (confirmOrg) {\n const existing = enforcedChoices[0] as OrgChoice\n if (existing) {\n enforcedOrgs = [existing.value]\n }\n }\n }\n\n spinner.stop()\n\n const oldToken = getSetting('apiToken')\n try {\n applyLogin(apiToken, enforcedOrgs, apiBaseUrl, apiProxy)\n logger.success(`API credentials ${oldToken ? 'updated' : 'set'}`)\n } catch {\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'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { InputError } from '../../utils/errors'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'login',\n description: 'Socket API login',\n hidden: false,\n flags: {\n ...commonFlags,\n apiBaseUrl: {\n type: 'string',\n description: 'API server to connect to for login'\n },\n apiProxy: {\n type: 'string',\n description: 'Proxy to use when making connection to API server'\n }\n },\n help: (command, config) => `\n Usage\n $ ${command}\n\n Logs into the Socket API by prompting for an API key\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command}\n $ ${command} --api-proxy=http://localhost:1234\n `\n}\n\nexport const cmdLogin = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const apiBaseUrl = cli.flags['apiBaseUrl'] as string | undefined\n const apiProxy = cli.flags['apiProxy'] as string | undefined\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n if (!isInteractive()) {\n throw new InputError(\n 'Cannot prompt for credentials in a non-interactive shell'\n )\n }\n\n await attemptLogin(apiBaseUrl, apiProxy)\n}\n","import { updateSetting } from '../../utils/settings'\n\nexport function applyLogout() {\n updateSetting('apiToken', null)\n updateSetting('apiBaseUrl', null)\n updateSetting('apiProxy', null)\n updateSetting('enforcedOrgs', null)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { applyLogout } from './apply-logout'\n\nexport function attemptLogout() {\n try {\n applyLogout()\n logger.success('Successfully logged out')\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'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\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}\n\n Logs out of the Socket API and clears all Socket credentials from disk\n `\n}\n\nexport const cmdLogout = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n attemptLogout()\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants'\n\nexport async function convertGradleToMaven(\n target: string,\n bin: string,\n _out: string,\n verbose: boolean,\n gradleOpts: string[]\n) {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n const rbin = path.resolve(bin)\n const rtarget = path.resolve(target)\n\n if (verbose) {\n logger.group('gradle2maven:')\n logger.log(`[VERBOSE] - Absolute bin path: \\`${rbin}\\``)\n logger.log(`[VERBOSE] - Absolute target path: \\`${rtarget}\\``)\n logger.groupEnd()\n } else {\n logger.group('gradle2maven:')\n logger.log(`- executing: \\`${bin}\\``)\n logger.log(`- src dir: \\`${target}\\``)\n logger.groupEnd()\n }\n\n try {\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\n // Note: init.gradle will be exported by .config/rollup.dist.config.mjs\n const initLocation = path.join(constants.rootDistPath, 'init.gradle')\n const commandArgs = ['--init-script', initLocation, ...gradleOpts, 'pom']\n\n if (verbose) {\n logger.log('[VERBOSE] Executing:', bin, commandArgs)\n }\n\n spinner.start(\n `Converting gradle to maven from \\`${bin}\\` on \\`${target}\\`...`\n )\n\n const output = await spawn(bin, commandArgs, {\n cwd: target || '.'\n })\n\n spinner.stop()\n\n if (verbose) {\n logger.group('[VERBOSE] gradle 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 gradle')\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 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\n // const loc = output.stdout?.match(/Wrote (.*?.pom)\\n/)?.[1]?.trim()\n // if (!loc) {\n // logger.fail(\n // 'There were no errors from sbt but could not find the location of resulting .pom file either'\n // )\n // process.exit(1)\n // }\n //\n // // Move the pom file to ...? initial cwd? loc will be an absolute path, or dump to stdout\n // if (out === '-') {\n // spinner.start('Result:\\n```')\n // spinner.log(await safeReadFile(loc))\n // spinner.log('```')\n // spinner.successAndStop(`OK`)\n // } else {\n // spinner.start()\n // if (verbose) {\n // spinner.log(\n // `Moving manifest file from \\`${loc.replace(/^\\/home\\/[^/]*?\\//, '~/')}\\` to \\`${out}\\``\n // )\n // } else {\n // spinner.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 // spinner.successAndStop(`OK. File should be available in \\`${out}\\``)\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 path from 'node:path'\n\nimport { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { convertGradleToMaven } from './convert_gradle_to_maven'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\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 cwd: {\n type: 'string',\n description: 'Set the cwd, defaults to process.cwd()'\n },\n gradleOpts: {\n type: 'string',\n default: '',\n description:\n 'Additional options to pass on to ./gradlew, see `./gradlew --help`'\n },\n out: {\n type: 'string',\n default: './socket.pom.xml',\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 task: {\n type: 'string',\n default: 'all',\n description: 'Task to target. By default targets all.'\n },\n verbose: {\n type: 'boolean',\n description: 'Print debug messages'\n }\n },\n help: (command, config) => `\n Usage\n $ ${command} [--gradle=path/to/gradle/binary] [--out=path/to/result] DIR\n\n Options\n ${getFlagListOutput(config.flags, 6)}\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.\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} --gradlew=../gradlew .\n `\n}\n\nexport const cmdManifestGradle = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const verbose = Boolean(cli.flags['verbose'])\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 const target = cli.input[0]\n\n // TODO: I'm 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 if (!target || target === '-' || cli.input.length > 1) {\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(stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - The DIR arg is required ${!target ? colors.red('(missing!)') : target === '-' ? colors.red('(stdin is not supported)') : colors.green('(ok)')}\n\n - Can only accept one DIR (make sure to escape spaces!) ${cli.input.length > 1 ? colors.red(`(received ${cli.input.length}!)`) : colors.green('(ok)')}`)\n return\n }\n\n let bin: string\n if (cli.flags['bin']) {\n bin = cli.flags['bin'] as string\n } else {\n bin = path.join(target, 'gradlew')\n }\n\n let out: string = './socket.pom.xml'\n if (cli.flags['out']) {\n out = cli.flags['out'] as string\n }\n if (cli.flags['stdout']) {\n out = '-'\n }\n\n if (verbose) {\n logger.group()\n logger.log('- target:', target)\n logger.log('- gradle bin:', bin)\n logger.log('- out:', out)\n logger.groupEnd()\n }\n\n let gradleOpts: string[] = []\n if (cli.flags['gradleOpts']) {\n gradleOpts = (cli.flags['gradleOpts'] as string)\n .split(' ')\n .map(s => s.trim())\n .filter(Boolean)\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await convertGradleToMaven(target, bin, out, verbose, gradleOpts)\n}\n","import path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants'\nimport { safeReadFile } from '../../utils/fs'\n\nexport async function convertSbtToMaven(\n target: string,\n bin: string,\n out: string,\n verbose: boolean,\n sbtOpts: string[]\n) {\n // Lazily access constants.spinner.\n const { spinner } = constants\n const rbin = path.resolve(bin)\n const rtarget = path.resolve(target)\n\n if (verbose) {\n logger.group('sbt2maven:')\n logger.log(`[VERBOSE] - Absolute bin path: \\`${rbin}\\``)\n logger.log(`[VERBOSE] - Absolute target path: \\`${rtarget}\\``)\n // logger.log(`[VERBOSE] - Absolute out path: \\`${rout}\\``)\n logger.groupEnd()\n } else {\n logger.group('sbt2maven:')\n logger.log(`- executing: \\`${bin}\\``)\n logger.log(`- src dir: \\`${target}\\``)\n // logger.log(`- dst dir: \\`${out}\\``)\n logger.groupEnd()\n }\n\n try {\n spinner.start(`Converting sbt to maven from \\`${bin}\\` on \\`${target}\\`...`)\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'].concat(sbtOpts), {\n cwd: target || '.'\n })\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 to 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.fail(\n 'Requested out target was stdout but there are multiple generated files'\n )\n poms.forEach(fn => logger.error('-', fn))\n logger.error('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 { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { convertSbtToMaven } from './convert_sbt_to_maven'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\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 default: 'sbt',\n description: 'Location of sbt binary to use'\n },\n cwd: {\n type: 'string',\n description: 'Set the cwd, defaults to process.cwd()'\n },\n out: {\n type: 'string',\n default: './socket.pom.xml',\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 default: '',\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} [--sbt=path/to/sbt/binary] [--out=path/to/result] FILE|DIR\n\n Options\n ${getFlagListOutput(config.flags, 6)}\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 optionally configure the path to the \\`sbt\\` bin 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} ./build.sbt\n $ ${command} --bin=/usr/bin/sbt ./build.sbt\n `\n}\n\nexport const cmdManifestScala = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const verbose = Boolean(cli.flags['verbose'])\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 const target = cli.input[0]\n\n // TODO: I'm 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 if (!target || target === '-' || cli.input.length > 1) {\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 stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - The DIR or FILE arg is required ${!target ? colors.red('(missing!)') : target === '-' ? colors.red('(stdin is not supported)') : colors.green('(ok)')}\n\n - Can only accept one DIR or FILE (make sure to escape spaces!) ${cli.input.length > 1 ? colors.red(`(received ${cli.input.length}!)`) : colors.green('(ok)')}`\n )\n return\n }\n\n let bin: string = 'sbt'\n if (cli.flags['bin']) {\n bin = cli.flags['bin'] as string\n }\n\n let out: string = './socket.pom.xml'\n if (cli.flags['out']) {\n out = cli.flags['out'] as string\n }\n if (cli.flags['stdout']) {\n out = '-'\n }\n\n if (verbose) {\n logger.group()\n logger.log('- target:', target)\n logger.log('- gradle bin:', bin)\n logger.log('- out:', out)\n logger.groupEnd()\n }\n\n let sbtOpts: string[] = []\n if (cli.flags['sbtOpts']) {\n sbtOpts = (cli.flags['sbtOpts'] as string)\n .split(' ')\n .map(s => s.trim())\n .filter(Boolean)\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await convertSbtToMaven(target, bin, out, verbose, sbtOpts)\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport meow from 'meow'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { cmdManifestGradle } from './cmd-manifest-gradle'\nimport { cmdManifestScala } from './cmd-manifest-scala'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\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 cwd: {\n type: 'string',\n description: 'Set the cwd, defaults to process.cwd()'\n },\n verbose: {\n type: 'boolean',\n default: false,\n description: 'Enable debug output, may help when running into errors'\n }\n // TODO: support output flags\n },\n help: (command, config) => `\n Usage\n $ ${command}\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Tries to figure out what language your current 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}\n\nexport const cmdManifestAuto = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n const verbose = !!cli.flags['verbose']\n const cwd = (cli.flags['cwd'] as string) ?? process.cwd()\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 subArgs = []\n if (verbose) {\n subArgs.push('--verbose')\n }\n\n const dir = cwd\n\n if (existsSync(path.join(dir, 'build.sbt'))) {\n logger.log('Detected a Scala sbt build, running default Scala generator...')\n if (cwd) {\n subArgs.push('--cwd', cwd)\n }\n subArgs.push(dir)\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n await cmdManifestScala.run(subArgs, importMeta, { parentName })\n return\n }\n\n if (existsSync(path.join(dir, 'gradlew'))) {\n logger.log('Detected a gradle build, running default gradle generator...')\n if (cwd) {\n // This command takes the cwd as first arg.\n subArgs.push(cwd)\n }\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n await cmdManifestGradle.run(subArgs, importMeta, { parentName })\n return\n }\n\n // Show new help screen and exit.\n meow(\n `\n $ ${parentName} ${config.commandName}\n\n Unfortunately this script did not discover a supported language in the\n current folder.\n\n - Make sure this script would work with your target build\n - Make sure to run it from the correct folder\n - Make sure the necessary build tools are available (\\`PATH\\`)\n\n If that doesn't work, see \\`${parentName} <lang> --help\\` for config details for\n your target language.\n `,\n {\n argv: [],\n description: config.description,\n importMeta\n }\n ).showHelp()\n}\n","import path from 'node:path'\n\nimport { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { convertGradleToMaven } from './convert_gradle_to_maven'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\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 cwd: {\n type: 'string',\n description: 'Set the cwd, defaults to process.cwd()'\n },\n gradleOpts: {\n type: 'string',\n default: '',\n description:\n 'Additional options to pass on to ./gradlew, see `./gradlew --help`'\n },\n out: {\n type: 'string',\n default: './socket.pom.xml',\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 task: {\n type: 'string',\n default: 'all',\n description: 'Task to target. By default targets all.'\n },\n verbose: {\n type: 'boolean',\n description: 'Print debug messages'\n }\n },\n help: (command, config) => `\n Usage\n $ ${command} [--gradle=path/to/gradle/binary] [--out=path/to/result] DIR\n\n Options\n ${getFlagListOutput(config.flags, 6)}\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} --gradlew=../gradlew .\n `\n}\n\nexport const cmdManifestKotlin = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const verbose = Boolean(cli.flags['verbose'])\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 const target = cli.input[0]\n\n // TODO: I'm 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 if (!target || target === '-' || cli.input.length > 1) {\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 stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - The DIR arg is required ${!target ? colors.red('(missing!)') : target === '-' ? colors.red('(stdin is not supported)') : colors.green('(ok)')}\n\n - Can only accept one DIR (make sure to escape spaces!) ${cli.input.length > 1 ? colors.red(`(received ${cli.input.length}!)`) : colors.green('(ok)')}`\n )\n return\n }\n\n let bin: string\n if (cli.flags['bin']) {\n bin = cli.flags['bin'] as string\n } else {\n bin = path.join(target, 'gradlew')\n }\n\n let out: string = './socket.pom.xml'\n if (cli.flags['out']) {\n out = cli.flags['out'] as string\n }\n if (cli.flags['stdout']) {\n out = '-'\n }\n\n if (verbose) {\n logger.group()\n logger.log('- target:', target)\n logger.log('- gradle bin:', bin)\n logger.log('- out:', out)\n logger.groupEnd()\n }\n\n let gradleOpts: string[] = []\n if (cli.flags['gradleOpts']) {\n gradleOpts = (cli.flags['gradleOpts'] as string)\n .split(' ')\n .map(s => s.trim())\n .filter(Boolean)\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await convertGradleToMaven(target, bin, out, verbose, gradleOpts)\n}\n","import { cmdManifestAuto } from './cmd-manifest-auto'\nimport { cmdManifestGradle } from './cmd-manifest-gradle'\nimport { cmdManifestKotlin } from './cmd-manifest-kotlin'\nimport { cmdManifestScala } from './cmd-manifest-scala'\nimport { commonFlags } from '../../flags'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst config: CliCommandConfig = {\n commandName: 'manifest',\n description: 'Generate a dependency manifest for given file or dir',\n hidden: false,\n flags: {\n ...commonFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <language> <target>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\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} yolo\n `\n}\n\nexport const cmdManifest = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n await meowWithSubcommands(\n {\n auto: cmdManifestAuto,\n scala: cmdManifestScala,\n gradle: cmdManifestGradle,\n kotlin: cmdManifestKotlin\n },\n {\n argv,\n aliases: {\n yolo: {\n description: config.description,\n hidden: true,\n argv: ['auto']\n }\n },\n description: config.description,\n importMeta,\n flags: config.flags,\n name: `${parentName} ${config.commandName}`\n }\n )\n}\n","import constants from '../../constants'\n\nconst { NPM } = constants\n\nexport async function wrapNpm(argv: readonly string[]) {\n // Lazily access constants.distShadowNpmBinPath.\n const shadowBin = require(constants.distShadowNpmBinPath)\n await shadowBin(NPM, argv)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { wrapNpm } from './wrap-npm'\nimport constants from '../../constants'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT, NPM } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'npm',\n description: `${NPM} wrapper functionality`,\n hidden: false,\n flags: {},\n help: (command, _config) => `\n Usage\n $ ${command}\n `\n}\n\nexport const cmdNpm = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n allowUnknownFlags: true,\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await wrapNpm(argv)\n}\n","import constants from '../../constants'\n\nconst { NPX } = constants\n\nexport async function wrapNpx(argv: readonly string[]) {\n // Lazily access constants.distShadowNpmBinPath.\n const shadowBin = require(constants.distShadowNpmBinPath)\n await shadowBin(NPX, argv)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { wrapNpx } from './wrap-npx'\nimport constants from '../../constants'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT, NPX } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'npx',\n description: `${NPX} wrapper functionality`,\n hidden: false,\n flags: {},\n help: (command, _config) => `\n Usage\n $ ${command}\n `\n}\n\nexport const cmdNpx = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n allowUnknownFlags: true,\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await wrapNpx(argv)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'oops',\n description: 'Trigger an intentional error (for development)',\n hidden: true,\n flags: {\n ...commonFlags\n },\n help: (parentName, config) => `\n Usage\n $ ${parentName} ${config.commandName}\n\n Don't run me.\n `\n}\n\nexport const cmdOops = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n throw new Error('This error was intentionally left blank')\n}\n","import constants from '../../constants'\n\nimport type { Agent } from '../../utils/package-environment'\n\ntype AgentDepsIncludesFn = (stdout: string, name: string) => boolean\n\nconst { BUN, NPM, PNPM, VLT, YARN_BERRY, YARN_CLASSIC } = constants\n\nfunction matchLsCmdViewHumanStdout(stdout: string, name: string) {\n return stdout.includes(` ${name}@`)\n}\n\nfunction matchQueryCmdStdout(stdout: string, name: string) {\n return stdout.includes(`\"${name}\"`)\n}\n\nexport const depsIncludesByAgent = new Map<Agent, AgentDepsIncludesFn>([\n [BUN, matchLsCmdViewHumanStdout],\n [NPM, matchQueryCmdStdout],\n [PNPM, matchQueryCmdStdout],\n [VLT, matchQueryCmdStdout],\n [YARN_BERRY, matchLsCmdViewHumanStdout],\n [YARN_CLASSIC, matchLsCmdViewHumanStdout]\n])\n","import { readPackageJson } from '@socketsecurity/registry/lib/packages'\n\ntype PackageJson = Awaited<ReturnType<typeof readPackageJson>>\n\nexport function getDependencyEntries(pkgJson: PackageJson) {\n const {\n dependencies,\n devDependencies,\n optionalDependencies,\n peerDependencies\n } = pkgJson\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 { readPackageJson } from '@socketsecurity/registry/lib/packages'\n\nimport constants from '../../constants'\n\nimport type {\n Agent,\n StringKeyValueObject\n} from '../../utils/package-environment'\n\ntype PackageJson = Awaited<ReturnType<typeof readPackageJson>>\ntype NpmOverrides = { [key: string]: string | StringKeyValueObject }\ntype PnpmOrYarnOverrides = { [key: string]: string }\ntype Overrides = NpmOverrides | PnpmOrYarnOverrides\ntype GetOverrides = (pkgJson: PackageJson) => GetOverridesResult\ntype GetOverridesResult = { type: Agent; overrides: Overrides }\n\nconst {\n BUN,\n NPM,\n OVERRIDES,\n PNPM,\n RESOLUTIONS,\n VLT,\n YARN_BERRY,\n YARN_CLASSIC\n} = constants\n\nfunction getOverridesDataBun(pkgJson: PackageJson) {\n const overrides = (pkgJson as any)?.[RESOLUTIONS] ?? {}\n return { type: YARN_BERRY, overrides }\n}\n\n// npm overrides documentation:\n// https://docs.npmjs.com/cli/v10/configuring-npm/package-json#overrides\nfunction getOverridesDataNpm(pkgJson: PackageJson) {\n const overrides = (pkgJson as any)?.[OVERRIDES] ?? {}\n return { type: NPM, overrides }\n}\n\n// pnpm overrides documentation:\n// https://pnpm.io/package_json#pnpmoverrides\nfunction getOverridesDataPnpm(pkgJson: PackageJson) {\n const overrides = (pkgJson as any)?.pnpm?.[OVERRIDES] ?? {}\n return { type: PNPM, overrides }\n}\n\nfunction getOverridesDataVlt(pkgJson: PackageJson) {\n const overrides = (pkgJson as any)?.[OVERRIDES] ?? {}\n return { type: VLT, overrides }\n}\n\n// Yarn resolutions documentation:\n// https://yarnpkg.com/configuration/manifest#resolutions\nfunction getOverridesDataYarn(pkgJson: PackageJson) {\n const overrides = (pkgJson as any)?.[RESOLUTIONS] ?? {}\n return { type: YARN_BERRY, overrides }\n}\n\n// Yarn resolutions documentation:\n// https://classic.yarnpkg.com/en/docs/selective-version-resolutions\nfunction getOverridesDataClassic(pkgJson: PackageJson) {\n const overrides = (pkgJson as any)?.[RESOLUTIONS] ?? {}\n return { type: YARN_CLASSIC, overrides }\n}\n\nexport const overridesDataByAgent = new Map<Agent, GetOverrides>([\n [BUN, getOverridesDataBun],\n [NPM, getOverridesDataNpm],\n [PNPM, getOverridesDataPnpm],\n [VLT, getOverridesDataVlt],\n [YARN_BERRY, getOverridesDataYarn],\n [YARN_CLASSIC, getOverridesDataClassic]\n])\n","import path from 'node:path'\n\nimport { parse as yamlParse } from 'yaml'\n\nimport { readPackageJson } from '@socketsecurity/registry/lib/packages'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport constants from '../../constants'\nimport { safeReadFile } from '../../utils/fs'\n\nimport type { Agent } from '../../utils/package-environment'\n\ntype PackageJson = Awaited<ReturnType<typeof readPackageJson>>\n\nconst { PNPM } = constants\n\nconst PNPM_WORKSPACE = `${PNPM}-workspace`\n\nexport async function getWorkspaceGlobs(\n agent: Agent,\n pkgPath: string,\n pkgJson: PackageJson\n): Promise<string[] | undefined> {\n let workspacePatterns\n if (agent === PNPM) {\n for (const workspacePath of [\n path.join(pkgPath, `${PNPM_WORKSPACE}.yaml`),\n path.join(pkgPath, `${PNPM_WORKSPACE}.yml`)\n ]) {\n // eslint-disable-next-line no-await-in-loop\n const yml = await safeReadFile(workspacePath)\n if (yml) {\n try {\n workspacePatterns = yamlParse(yml)?.packages\n } catch {}\n if (workspacePatterns) {\n break\n }\n }\n }\n } else {\n workspacePatterns = pkgJson['workspaces']\n }\n return Array.isArray(workspacePatterns)\n ? workspacePatterns\n .filter(isNonEmptyString)\n .map(workspacePatternToGlobPattern)\n : undefined\n}\n\nfunction workspacePatternToGlobPattern(workspace: string): string {\n const { length } = workspace\n if (!length) {\n return ''\n }\n // If the workspace ends with \"/\"\n if (workspace.charCodeAt(length - 1) === 47 /*'/'*/) {\n return `${workspace}/*/package.json`\n }\n // If the workspace ends with \"/**\"\n if (\n workspace.charCodeAt(length - 1) === 42 /*'*'*/ &&\n workspace.charCodeAt(length - 2) === 42 /*'*'*/ &&\n workspace.charCodeAt(length - 3) === 47 /*'/'*/\n ) {\n return `${workspace}/*/**/package.json`\n }\n // Things like \"packages/a\" or \"packages/*\"\n return `${workspace}/package.json`\n}\n","import { escapeRegExp } from '@socketsecurity/registry/lib/regexps'\n\nimport constants from '../../constants'\n\nimport type { Agent } from '../../utils/package-environment'\n\nexport type AgentLockIncludesFn = (\n lockSrc: string,\n name: string,\n ext?: string | undefined\n) => boolean\n\nconst { BUN, LOCK_EXT, NPM, PNPM, VLT, YARN_BERRY, YARN_CLASSIC } = constants\n\nfunction includesNpm(lockSrc: string, name: string) {\n // Detects the package name in the following cases:\n // \"name\":\n return lockSrc.includes(`\"${name}\":`)\n}\n\nfunction includesBun(lockSrc: string, name: string, lockName?: string) {\n // This is a bit counterintuitive. When lockName ends with a .lockb\n // we treat it as a yarn.lock. When lockName ends with a .lock we\n // treat it as a package-lock.json. The bun.lock format is not identical\n // package-lock.json, however it close enough for npmLockIncludes to work.\n const lockfileScanner = lockName?.endsWith(LOCK_EXT)\n ? includesNpm\n : includesYarn\n return lockfileScanner(lockSrc, name)\n}\n\nfunction includesPnpm(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*)(?:(['/])${escapedName}\\\\1|${escapedName}(?=[:@]))`,\n 'm'\n ).test(lockSrc)\n}\n\nfunction includesVlt(lockSrc: string, name: string) {\n // Detects the package name in the following cases:\n // \"name\"\n return lockSrc.includes(`\"${name}\"`)\n}\n\nfunction includesYarn(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 const lockfileIncludesByAgent = new Map<Agent, AgentLockIncludesFn>([\n [BUN, includesBun],\n [NPM, includesNpm],\n [PNPM, includesPnpm],\n [VLT, includesVlt],\n [YARN_BERRY, includesYarn],\n [YARN_CLASSIC, includesYarn]\n])\n","import { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants from '../../constants'\n\nimport type { Agent } from '../../utils/package-environment'\n\ntype AgentListDepsOptions = { npmExecPath?: string }\n\ntype AgentListDepsFn = (\n agentExecPath: string,\n cwd: string,\n options?: AgentListDepsOptions | undefined\n) => Promise<string>\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)) {\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([...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([...names], null, 2)\n}\n\nasync function npmQuery(npmExecPath: string, cwd: string): Promise<string> {\n let stdout = ''\n try {\n stdout = (await spawn(npmExecPath, ['query', ':not(.dev)'], { cwd })).stdout\n } catch {}\n return cleanupQueryStdout(stdout)\n}\n\nasync function lsBun(agentExecPath: string, cwd: string): Promise<string> {\n try {\n // Bun does not support filtering by production packages yet.\n // https://github.com/oven-sh/bun/issues/8283\n return (await spawn(agentExecPath!, ['pm', 'ls', '--all'], { cwd })).stdout\n } catch {}\n return ''\n}\n\nasync function lsNpm(agentExecPath: string, cwd: string): Promise<string> {\n return await npmQuery(agentExecPath, cwd)\n}\n\nasync function lsPnpm(\n agentExecPath: string,\n cwd: string,\n options?: AgentListDepsOptions | undefined\n): Promise<string> {\n const npmExecPath = options?.npmExecPath\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 agentExecPath,\n // Pnpm uses the alternative spelling of parsable.\n // https://en.wiktionary.org/wiki/parsable\n ['ls', '--parseable', '--prod', '--depth', 'Infinity'],\n { cwd }\n )\n ).stdout\n } catch {}\n return parsableToQueryStdout(stdout)\n}\n\nasync function lsVlt(agentExecPath: string, cwd: string): Promise<string> {\n let stdout = ''\n try {\n // See https://docs.vlt.sh/cli/commands/list#options.\n stdout = (\n await spawn(agentExecPath, ['ls', '--view', 'human', ':not(.dev)'], {\n cwd\n })\n ).stdout\n } catch {}\n return cleanupQueryStdout(stdout)\n}\n\nasync function lsYarnBerry(\n agentExecPath: string,\n cwd: string\n): Promise<string> {\n try {\n return (\n // Yarn Berry does not support filtering by production packages yet.\n // https://github.com/yarnpkg/berry/issues/5117\n (\n await spawn(agentExecPath, ['info', '--recursive', '--name-only'], {\n cwd\n })\n ).stdout.trim()\n )\n } catch {}\n return ''\n}\n\nasync function lsYarnClassic(\n agentExecPath: string,\n cwd: string\n): Promise<string> {\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(agentExecPath, ['list', '--prod'], { cwd })\n ).stdout.trim()\n } catch {}\n return ''\n}\n\nexport const lsByAgent = new Map<Agent, AgentListDepsFn>([\n [BUN, lsBun],\n [NPM, lsNpm],\n [PNPM, lsPnpm],\n [VLT, lsVlt],\n [YARN_BERRY, lsYarnBerry],\n [YARN_CLASSIC, lsYarnClassic]\n])\n","import { hasKeys, isObject } from '@socketsecurity/registry/lib/objects'\n\nimport constants from '../../constants'\n\nimport type {\n Agent,\n StringKeyValueObject\n} from '../../utils/package-environment'\nimport type { EditablePackageJson } from '@socketsecurity/registry/lib/packages'\n\ntype NpmOverrides = { [key: string]: string | StringKeyValueObject }\ntype PnpmOrYarnOverrides = { [key: string]: string }\ntype Overrides = NpmOverrides | PnpmOrYarnOverrides\ntype AgentModifyManifestFn = (\n pkgJson: EditablePackageJson,\n overrides: Overrides\n) => void\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 { content: pkgJson } = editablePkgJson\n const oldValue = pkgJson[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 omitted 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 pkgJson\n )\n }\n } else if (field === OVERRIDES || field === RESOLUTIONS) {\n // Properties with undefined values are omitted when saved as JSON.\n editablePkgJson.update({\n [field]: hasKeys(value) ? value : undefined\n } as typeof pkgJson)\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(pkgJson)\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\nfunction updateOverridesField(\n editablePkgJson: EditablePackageJson,\n overrides: Overrides\n) {\n updatePkgJsonField(editablePkgJson, OVERRIDES, overrides)\n}\n\nfunction updateResolutionsField(\n editablePkgJson: EditablePackageJson,\n overrides: Overrides\n) {\n updatePkgJsonField(editablePkgJson, RESOLUTIONS, overrides)\n}\n\nfunction updatePnpmField(\n editablePkgJson: EditablePackageJson,\n overrides: Overrides\n) {\n updatePkgJsonField(editablePkgJson, PNPM, overrides)\n}\n\nexport const updateManifestByAgent = new Map<Agent, AgentModifyManifestFn>([\n [BUN, updateResolutionsField],\n [NPM, updateOverridesField],\n [PNPM, updatePnpmField],\n [VLT, updateOverridesField],\n [YARN_BERRY, updateResolutionsField],\n [YARN_CLASSIC, updateResolutionsField]\n])\n","import path from 'node:path'\n\nimport npa from 'npm-package-arg'\nimport semver from 'semver'\nimport { glob as tinyGlob } from 'tinyglobby'\n\nimport { getManifestData } from '@socketsecurity/registry'\nimport { hasOwn, toSortedObject } from '@socketsecurity/registry/lib/objects'\nimport {\n fetchPackageManifest,\n readPackageJson\n} from '@socketsecurity/registry/lib/packages'\nimport { pEach } from '@socketsecurity/registry/lib/promises'\nimport { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nimport { depsIncludesByAgent } from './deps-includes-by-agent'\nimport { getDependencyEntries } from './get-dependency-entries'\nimport { overridesDataByAgent } from './get-overrides-by-agent'\nimport { getWorkspaceGlobs } from './get-workspace-globs'\nimport { lockfileIncludesByAgent } from './lockfile-includes-by-agent'\nimport { lsByAgent } from './ls-by-agent'\nimport { updateManifestByAgent } from './update-manifest-by-agent'\nimport constants from '../../constants'\nimport { cmdPrefixMessage } from '../../utils/cmd'\n\nimport type { AgentLockIncludesFn } from './lockfile-includes-by-agent'\nimport type {\n Agent,\n EnvDetails,\n StringKeyValueObject\n} from '../../utils/package-environment'\nimport type { Logger } from '@socketsecurity/registry/lib/logger'\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}\ntype GetOverridesResult = { type: Agent; overrides: Overrides }\ntype NpmOverrides = { [key: string]: string | StringKeyValueObject }\ntype PackageJson = Awaited<ReturnType<typeof readPackageJson>>\ntype PnpmOrYarnOverrides = { [key: string]: string }\ntype Overrides = NpmOverrides | PnpmOrYarnOverrides\n\nconst { NPM, PNPM, YARN_CLASSIC } = constants\n\nconst CMD_NAME = 'socket optimize'\n\nconst manifestNpmOverrides = getManifestData(NPM)\n\nexport async function addOverrides(\n pkgPath: string,\n pkgEnvDetails: EnvDetails,\n options?: AddOverridesOptions | undefined\n): Promise<AddOverridesState> {\n const {\n agent,\n agentExecPath,\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 let { pkgJson: editablePkgJson } = pkgEnvDetails\n if (editablePkgJson === undefined) {\n editablePkgJson = await readPackageJson(pkgPath, { editable: true })\n }\n const { content: pkgJson } = editablePkgJson\n\n const workspaceName = path.relative(rootPath, pkgPath)\n const workspaceGlobs = await getWorkspaceGlobs(agent, pkgPath, pkgJson)\n const isRoot = pkgPath === rootPath\n const isLockScanned = isRoot && !prod\n const isWorkspace = !!workspaceGlobs\n if (\n isWorkspace &&\n agent === PNPM &&\n // npmExecPath will === the agent name IF it CANNOT be resolved.\n npmExecPath === NPM &&\n !state.warnedPnpmWorkspaceRequiresNpm\n ) {\n state.warnedPnpmWorkspaceRequiresNpm = true\n logger?.warn(\n cmdPrefixMessage(\n CMD_NAME,\n `${agent} workspace support requires \\`npm ls\\`, falling back to \\`${agent} list\\``\n )\n )\n }\n\n const overridesDataObjects = [] as GetOverridesResult[]\n if (pkgJson['private'] || isWorkspace) {\n overridesDataObjects.push(overridesDataByAgent.get(agent)!(pkgJson))\n } else {\n overridesDataObjects.push(\n overridesDataByAgent.get(NPM)!(pkgJson),\n overridesDataByAgent.get(YARN_CLASSIC)!(pkgJson)\n )\n }\n\n spinner?.setText(\n `Adding overrides${workspaceName ? ` to ${workspaceName}` : ''}...`\n )\n\n const depAliasMap = new Map<string, string>()\n const depEntries = getDependencyEntries(pkgJson)\n\n const nodeRange = `>=${pkgEnvDetails.minimumNodeVersion}`\n const manifestEntries = manifestNpmOverrides.filter(({ 1: data }) =>\n semver.satisfies(semver.coerce(data.engines.node)!, nodeRange)\n )\n\n // Chunk package names to process them in parallel 3 at a time.\n await pEach(manifestEntries, 3, async ({ 1: data }) => {\n const { name: sockRegPkgName, package: origPkgName, version } = data\n const major = semver.major(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 errors.\n // https://docs.npmjs.com/cli/v8/using-npm/package-spec#aliases\n if (\n !(\n thisSpec.startsWith(sockOverridePrefix) &&\n semver.coerce(npa(thisSpec).rawSpec)?.version\n )\n ) {\n thisSpec = sockOverrideSpec\n depObj[origPkgName] = thisSpec\n state.added.add(sockRegPkgName)\n if (workspaceName) {\n state.addedInWorkspaces.add(workspaceName)\n }\n }\n depAliasMap.set(origPkgName, thisSpec)\n }\n }\n if (isRoot) {\n // The AgentDepsIncludesFn and AgentLockIncludesFn types overlap in their\n // first two parameters. AgentLockIncludesFn accepts an optional third\n // parameter which AgentDepsIncludesFn will ignore so we cast thingScanner\n // as an AgentLockIncludesFn type.\n const thingScanner = (\n isLockScanned\n ? lockfileIncludesByAgent.get(agent)\n : depsIncludesByAgent.get(agent)\n ) as AgentLockIncludesFn\n const thingToScan = isLockScanned\n ? lockSrc\n : await lsByAgent.get(agent)!(agentExecPath, pkgPath, { npmExecPath })\n // Chunk package names to process them in parallel 3 at a time.\n await pEach(overridesDataObjects, 3, async ({ overrides, type }) => {\n const overrideExists = hasOwn(overrides, origPkgName)\n if (\n overrideExists ||\n thingScanner(thingToScan, origPkgName, lockName)\n ) {\n const oldSpec = overrideExists ? overrides[origPkgName]! : 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 semver.major(\n semver.coerce(npa(thisSpec).rawSpec)?.version ?? version\n ) !== major\n ) {\n const otherVersion = (await fetchPackageManifest(thisSpec))\n ?.version\n if (otherVersion && otherVersion !== version) {\n newSpec = `${sockOverridePrefix}${pin ? otherVersion : `^${semver.major(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 }\n }\n })\n }\n })\n\n if (workspaceGlobs) {\n const workspacePkgJsonPaths = await tinyGlob(workspaceGlobs, {\n absolute: true,\n cwd: pkgPath!,\n ignore: ['**/node_modules/**', '**/bower_components/**']\n })\n // Chunk package names to process them in parallel 3 at a time.\n await pEach(workspacePkgJsonPaths, 3, async workspacePkgJsonPath => {\n const otherState = await addOverrides(\n path.dirname(workspacePkgJsonPath),\n pkgEnvDetails,\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. This allows\n // us to do the SetA.add(setB.get) pump type-safe without casts.\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 }\n\n if (state.added.size > 0 || state.updated.size > 0) {\n editablePkgJson.update(Object.fromEntries(depEntries) as PackageJson)\n for (const { overrides, type } of overridesDataObjects) {\n updateManifestByAgent.get(type)!(\n editablePkgJson,\n toSortedObject(overrides)\n )\n }\n await editablePkgJson.save()\n }\n\n return state\n}\n","import { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nimport { runAgentInstall } from './run-agent'\nimport constants from '../../constants'\nimport { cmdPrefixMessage } from '../../utils/cmd'\n\nimport type { EnvDetails } from '../../utils/package-environment'\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}\nexport async function updateLockfile(\n pkgEnvDetails: EnvDetails,\n options: UpdateLockfileOptions\n) {\n const {\n cmdName = '',\n logger,\n spinner\n } = {\n __proto__: null,\n ...options\n } as UpdateLockfileOptions\n const isSpinning = !!spinner?.isSpinning\n if (!isSpinning) {\n spinner?.start()\n }\n spinner?.setText(`Updating ${pkgEnvDetails.lockName}...`)\n try {\n await runAgentInstall(pkgEnvDetails, { spinner })\n if (pkgEnvDetails.features.npmBuggyOverrides) {\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 logger?.fail(\n cmdPrefixMessage(\n cmdName,\n `${pkgEnvDetails.agent} install failed to update ${pkgEnvDetails.lockName}`\n )\n )\n logger?.error(e)\n }\n if (isSpinning) {\n spinner?.start()\n } else {\n spinner?.stop()\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { addOverrides } from './add-overrides'\nimport { updateLockfile } from './update-lockfile'\nimport constants from '../../constants'\nimport { detectAndValidatePackageEnvironment } from '../../utils/package-environment'\n\nconst CMD_NAME = 'socket optimize'\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\nexport async function applyOptimization(\n cwd: string,\n pin: boolean,\n prod: boolean\n) {\n const pkgEnvDetails = await detectAndValidatePackageEnvironment(cwd, {\n cmdName: CMD_NAME,\n logger,\n prod\n })\n if (!pkgEnvDetails) {\n return\n }\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Socket optimizing...')\n\n const state = await addOverrides(pkgEnvDetails.pkgPath, pkgEnvDetails, {\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 await updateLockfile(pkgEnvDetails, { cmdName: CMD_NAME, logger, spinner })\n }\n\n spinner.stop()\n\n if (pkgJsonChanged) {\n if (updatedCount > 0) {\n logger?.log(\n `${createActionMessage('Updated', updatedCount, state.updatedInWorkspaces.size)}${addedCount ? '.' : '🚀'}`\n )\n }\n if (addedCount > 0) {\n logger?.log(\n `${createActionMessage('Added', addedCount, state.addedInWorkspaces.size)} 🚀`\n )\n }\n } else {\n logger?.log('Congratulations! Already Socket.dev optimized 🎉')\n }\n}\n","import process from 'node:process'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { applyOptimization } from './apply-optimization'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'optimize',\n description: 'Optimize dependencies with @socketregistry overrides',\n hidden: false,\n flags: {\n ...commonFlags,\n pin: {\n type: 'boolean',\n default: false,\n description: 'Pin overrides to their latest version'\n },\n prod: {\n type: 'boolean',\n default: false,\n description: 'Only add overrides for production dependencies'\n }\n },\n help: (command, config) => `\n Usage\n $ ${command}\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command}\n $ ${command} --pin\n `\n}\n\nexport const cmdOptimize = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const cwd = process.cwd()\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await applyOptimization(\n cwd,\n Boolean(cli.flags['pin']),\n Boolean(cli.flags['prod'])\n )\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport {\n getLastFiveOfApiToken,\n handleApiCall,\n handleUnsuccessfulApiResponse\n} from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function getOrganization(\n format: 'text' | 'json' | 'markdown' = 'text'\n): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n await printOrganizationsFromToken(apiToken, format)\n}\n\nasync function printOrganizationsFromToken(\n apiToken: string,\n format: 'text' | 'json' | 'markdown' = 'text'\n) {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Fetching organizations...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getOrganizations(),\n 'looking up organizations'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('getOrganizations', result)\n return\n }\n\n spinner.stop()\n\n const organizations = Object.values(result.data.organizations)\n const lastFiveOfApiToken = getLastFiveOfApiToken(apiToken)\n\n switch (format) {\n case 'json': {\n logger.log(\n JSON.stringify(\n organizations.map(o => ({\n name: o.name,\n id: o.id,\n plan: o.plan\n })),\n null,\n 2\n )\n )\n return\n }\n case 'markdown': {\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)\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 key, ending with: ${colors.italic(lastFiveOfApiToken)}\\n`\n )\n logger.log(\n `| Name${' '.repeat(mw1 - 4)} | ID${' '.repeat(mw2 - 2)} | Plan${' '.repeat(mw3 - 4)} |`\n )\n logger.log(\n `| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} | ${'-'.repeat(mw3)} |`\n )\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(\n `| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} | ${'-'.repeat(mw3)} |`\n )\n return\n }\n default: {\n logger.log(\n `List of organizations associated with your API key, ending with: ${colors.italic(lastFiveOfApiToken)}\\n`\n )\n // Just dump\n for (const o of organizations) {\n logger.log(\n `- Name: ${colors.bold(o.name)}, ID: ${colors.bold(o.id)}, Plan: ${colors.bold(o.plan)}`\n )\n }\n }\n }\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getOrganization } from './get-organization'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'organizations',\n description: 'List organizations associated with the API key used',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags\n },\n help: (command, _config) => `\n Usage\n $ ${command}\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n `\n}\n\nexport const cmdOrganization = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const json = Boolean(cli.flags['json'])\n const markdown = Boolean(cli.flags['markdown'])\n if (json && markdown) {\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(stripIndents`\n${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - The json and markdown flags cannot be both set, pick one\n `)\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await getOrganization(json ? 'json' : markdown ? 'markdown' : 'text')\n}\n","import process from 'node:process'\n\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport { getNpmBinPath } from '../../shadow/npm/paths'\n\nexport async function runRawNpm(\n argv: string[] | readonly string[]\n): Promise<void> {\n const spawnPromise = spawn(getNpmBinPath(), argv as string[], {\n stdio: 'inherit'\n })\n // See https://nodejs.org/api/all.html#all_child_process_event-exit.\n spawnPromise.process.on('exit', (code, signalName) => {\n if (signalName) {\n process.kill(process.pid, signalName)\n } else if (code !== null) {\n process.exit(code)\n }\n })\n await spawnPromise\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { runRawNpm } from './run-raw-npm'\nimport constants from '../../constants'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT, NPM } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'raw-npm',\n description: `Temporarily disable the Socket ${NPM} wrapper`,\n hidden: false,\n flags: {},\n help: command => `\n Usage\n $ ${command} <command>\n\n Examples\n $ ${command} install\n `\n}\n\nexport const cmdRawNpm = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n allowUnknownFlags: true,\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await runRawNpm(argv)\n}\n","import process from 'node:process'\n\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport { getNpxBinPath } from '../../shadow/npm/paths'\n\nexport async function runRawNpx(\n argv: string[] | readonly string[]\n): Promise<void> {\n const spawnPromise = spawn(getNpxBinPath(), argv as string[], {\n stdio: 'inherit'\n })\n // See https://nodejs.org/api/all.html#all_child_process_event-exit.\n spawnPromise.process.on('exit', (code, signalName) => {\n if (signalName) {\n process.kill(process.pid, signalName)\n } else if (code !== null) {\n process.exit(code)\n }\n })\n await spawnPromise\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { runRawNpx } from './run-raw-npx'\nimport constants from '../../constants'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT, NPX } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'raw-npx',\n description: `Temporarily disable the Socket ${NPX} wrapper`,\n hidden: false,\n flags: {},\n help: command => `\n Usage\n $ ${command} <command>\n\n Examples\n $ ${command} install\n `\n}\n\nexport const cmdRawNpx = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n allowUnknownFlags: true,\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await runRawNpx(argv)\n}\n","import { debugLog, isDebug } from '@socketsecurity/registry/lib/debug'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { getPackageFilesFullScans } from '../../utils/path-resolve'\nimport { setupSdk } from '../../utils/sdk'\n\nimport type { SocketYml } from '@socketsecurity/config'\nimport type {\n SocketSdkResultType,\n SocketSdkReturnType\n} from '@socketsecurity/sdk'\n\nconst { DRY_RUN_LABEL } = constants\n\nexport async function createReport(\n socketConfig: SocketYml | undefined,\n inputPaths: string[],\n {\n cwd,\n dryRun\n }: {\n cwd: string\n dryRun: boolean\n }\n): Promise<undefined | SocketSdkResultType<'createReport'>> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n const socketSdk = await setupSdk()\n const supportedFiles = await socketSdk\n .getReportSupportedFiles()\n .then(res => {\n if (!res.success)\n handleUnsuccessfulApiResponse('getReportSupportedFiles', res)\n return (res as SocketSdkReturnType<'getReportSupportedFiles'>).data\n })\n .catch((cause: Error) => {\n throw new Error('Failed getting supported files for report', {\n cause\n })\n })\n const packagePaths = await getPackageFilesFullScans(\n cwd,\n inputPaths,\n supportedFiles,\n socketConfig\n )\n const packagePathsCount = packagePaths.length\n if (packagePathsCount && isDebug()) {\n for (const pkgPath of packagePaths) {\n debugLog(`Uploading: ${pkgPath}`)\n }\n }\n if (dryRun) {\n debugLog(`${DRY_RUN_LABEL}: Skipped actual upload`)\n return undefined\n }\n spinner.start(\n `Creating report with ${packagePathsCount} package ${pluralize('file', packagePathsCount)}`\n )\n const apiCall = socketSdk.createReportFromFilePaths(\n packagePaths,\n cwd,\n socketConfig?.issueRules\n )\n const result = await handleApiCall(apiCall, 'creating report')\n if (!result.success) {\n handleUnsuccessfulApiResponse('createReport', result)\n return undefined\n }\n spinner.successAndStop()\n return result\n}\n","import { betterAjvErrors } from '@apideck/better-ajv-errors'\n\nimport { SocketValidationError, readSocketConfig } from '@socketsecurity/config'\n\nimport { InputError } from '../../utils/errors'\n\nexport async function getSocketConfig(absoluteConfigPath: string) {\n const socketConfig = await readSocketConfig(absoluteConfigPath).catch(\n (cause: unknown) => {\n if (\n cause &&\n typeof cause === 'object' &&\n cause instanceof SocketValidationError\n ) {\n // Inspired by workbox-build:\n // https://github.com/GoogleChrome/workbox/blob/95f97a207fd51efb3f8a653f6e3e58224183a778/packages/workbox-build/src/lib/validate-options.ts#L68-L71\n const betterErrors = betterAjvErrors({\n basePath: 'config',\n data: cause.data,\n errors: cause.validationErrors,\n schema: cause.schema as Parameters<\n typeof betterAjvErrors\n >[0]['schema']\n })\n throw new InputError(\n 'The socket.yml config is not valid',\n betterErrors\n .map(\n err =>\n `[${err.path}] ${err.message}.${err.suggestion ? err.suggestion : ''}`\n )\n .join('\\n')\n )\n } else {\n throw new Error('Failed to read socket.yml config', { cause })\n }\n }\n )\n\n return socketConfig\n}\n","import constants from '../../constants'\nimport {\n formatSeverityCount,\n getSeverityCount\n} from '../../utils/alert/severity'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { setupSdk } from '../../utils/sdk'\n\nimport type {\n SocketSdkResultType,\n SocketSdkReturnType\n} from '@socketsecurity/sdk'\n\nexport type ReportData = SocketSdkReturnType<'getReport'>['data']\n\nconst MAX_TIMEOUT_RETRY = 5\nconst HTTP_CODE_TIMEOUT = 524\n\nexport async function fetchReportData(\n reportId: string,\n includeAllIssues: boolean,\n strict: boolean\n): Promise<void | ReportData> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start(`Fetching report with ID ${reportId} (this could take a while)`)\n\n const socketSdk = await setupSdk()\n let result: SocketSdkResultType<'getReport'> | undefined\n for (let retry = 1; !result; ++retry) {\n try {\n // eslint-disable-next-line no-await-in-loop\n result = await handleApiCall(\n socketSdk.getReport(reportId),\n 'fetching report'\n )\n } catch (err) {\n if (\n retry >= MAX_TIMEOUT_RETRY ||\n !(err instanceof Error) ||\n (err.cause as any)?.cause?.response?.statusCode !== HTTP_CODE_TIMEOUT\n ) {\n spinner.stop()\n throw err\n }\n }\n }\n\n if (!result.success) {\n return handleUnsuccessfulApiResponse('getReport', result)\n }\n\n // Conclude the status of the API call.\n if (strict) {\n if (result.data.healthy) {\n spinner.success('Report result is healthy and great!')\n } else {\n spinner.error('Report result deemed unhealthy for project')\n }\n } else if (!result.data.healthy) {\n const severityCount = getSeverityCount(\n result.data.issues,\n includeAllIssues ? undefined : 'high'\n )\n const issueSummary = formatSeverityCount(severityCount)\n spinner.success(`Report has these issues: ${issueSummary}`)\n } else {\n spinner.success('Report has no issues')\n }\n spinner.stop()\n\n return result.data\n}\n","import process from 'node:process'\n\nimport { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { ColorOrMarkdown } from '../../utils/color-or-markdown'\n\nimport type { ReportData } from './fetch-report-data'\n\nexport function formatReportDataOutput(\n reportId: string,\n data: ReportData,\n commandName: string,\n outputJson: boolean,\n outputMarkdown: boolean,\n strict: boolean\n): void {\n if (outputJson) {\n logger.log(JSON.stringify(data, undefined, 2))\n } else {\n const format = new ColorOrMarkdown(outputMarkdown)\n logger.log(stripIndents`\n Detailed info on socket.dev: ${format.hyperlink(reportId, data.url, {\n fallbackToUrl: true\n })}`)\n if (!outputMarkdown) {\n logger.log(\n colors.dim(\n `Or rerun ${colors.italic(commandName)} using the ${colors.italic('--json')} flag to get full JSON output`\n )\n )\n }\n }\n\n if (strict && !data.healthy) {\n process.exit(1)\n }\n}\n","import { fetchReportData } from './fetch-report-data'\nimport { formatReportDataOutput } from './format-report-data'\n\nexport async function viewReport(\n reportId: string,\n {\n all,\n commandName,\n json,\n markdown,\n strict\n }: {\n commandName: string\n all: boolean\n json: boolean\n markdown: boolean\n strict: boolean\n }\n) {\n const result = await fetchReportData(reportId, all, strict)\n if (result) {\n formatReportDataOutput(\n reportId,\n result,\n commandName,\n json,\n markdown,\n strict\n )\n }\n}\n","import path from 'node:path'\nimport process from 'node:process'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { createReport } from './create-report'\nimport { getSocketConfig } from './get-socket-config'\nimport { viewReport } from './view-report'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags, validationFlags } from '../../flags'\nimport { ColorOrMarkdown } from '../../utils/color-or-markdown'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'create',\n description: '[Deprecated] Create a project report',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n ...validationFlags,\n dryRun: {\n type: 'boolean',\n default: false,\n description: 'Only output what will be done without actually doing it'\n },\n view: {\n type: 'boolean',\n shortFlag: 'v',\n default: false,\n description: 'Will wait for and return the created report'\n }\n },\n help: () => `\n This command is deprecated in favor of \\`socket scan create\\`.\n It will be removed in the next major release of the CLI.\n `\n}\n\nexport const cmdReportCreate = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n // TODO: Allow setting a custom cwd and/or configFile path?\n const cwd = process.cwd()\n const absoluteConfigPath = path.join(cwd, 'socket.yml')\n\n const dryRun = Boolean(cli.flags['dryRun'])\n const json = Boolean(cli.flags['json'])\n const markdown = Boolean(cli.flags['markdown'])\n const strict = Boolean(cli.flags['strict'])\n const includeAllIssues = Boolean(cli.flags['all'])\n const view = Boolean(cli.flags['view'])\n\n // Note exiting earlier to skirt a hidden auth requirement\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n const socketConfig = await getSocketConfig(absoluteConfigPath)\n\n const result = await createReport(socketConfig, cli.input, { cwd, dryRun })\n\n const commandName = `${parentName} ${config.commandName}`\n\n if (result?.success) {\n if (view) {\n const reportId = result.data.id\n await viewReport(reportId, {\n all: includeAllIssues,\n commandName,\n json,\n markdown,\n strict\n })\n } else if (json) {\n logger.log(JSON.stringify(result.data, undefined, 2))\n } else {\n const format = new ColorOrMarkdown(markdown)\n logger.log(\n `New report: ${format.hyperlink(result.data.id, result.data.url, { fallbackToUrl: true })}`\n )\n }\n }\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { viewReport } from './view-report'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags, validationFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'view',\n description: '[Deprecated] View a project report',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n ...validationFlags\n },\n help: () => `\n This command is deprecated in favor of \\`socket scan view\\`.\n It will be removed in the next major release of the CLI.\n `\n}\n\nexport const cmdReportView = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const [reportId, ...extraInput] = cli.input\n\n // Validate the input.\n if (extraInput.length || !reportId) {\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(stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Need at least one report ID ${!reportId ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Can only handle a single report ID ${extraInput.length < 2 ? colors.red(`(received ${extraInput.length}!)`) : colors.green('(ok)')}`)\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await viewReport(reportId, {\n all: Boolean(cli.flags['all']),\n commandName: `${parentName} ${config.commandName}`,\n json: Boolean(cli.flags['json']),\n markdown: Boolean(cli.flags['markdown']),\n strict: Boolean(cli.flags['strict'])\n })\n}\n","import { cmdReportCreate } from './cmd-report-create'\nimport { cmdReportView } from './cmd-report-view'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands'\n\nconst description = '[Deprecated] Project report related commands'\n\nexport const cmdReport: CliSubcommand = {\n description,\n hidden: true, // Deprecated in favor of `scan`\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n create: cmdReportCreate,\n view: cmdReportView\n },\n {\n argv,\n description,\n importMeta,\n name: parentName + ' report'\n }\n )\n }\n}\n","import constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function createRepo({\n default_branch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility\n}: {\n orgSlug: string\n repoName: string\n description: string\n homepage: string\n default_branch: string\n visibility: string\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await createRepoWithToken({\n apiToken,\n default_branch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility\n })\n}\n\nasync function createRepoWithToken({\n apiToken,\n default_branch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility\n}: {\n apiToken: string\n orgSlug: string\n repoName: string\n description: string\n homepage: string\n default_branch: string\n visibility: string\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Creating repository...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.createOrgRepo(orgSlug, {\n name: repoName,\n description,\n homepage,\n default_branch,\n visibility\n }),\n 'creating repository'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('createOrgRepo', result)\n return\n }\n\n spinner.successAndStop('Repository created successfully')\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { createRepo } from './create-repo'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'create',\n description: 'Create a repository in an organization',\n hidden: false,\n flags: {\n ...commonFlags,\n repoName: {\n type: 'string',\n shortFlag: 'n',\n default: '',\n description: 'Repository name'\n },\n repoDescription: {\n type: 'string',\n shortFlag: 'd',\n default: '',\n description: 'Repository description'\n },\n homepage: {\n type: 'string',\n shortFlag: 'h',\n default: '',\n description: 'Repository url'\n },\n defaultBranch: {\n type: 'string',\n shortFlag: 'b',\n default: 'main',\n description: 'Repository default branch'\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} <org slug>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg --repoName=test-repo\n `\n}\n\nexport const cmdReposCreate = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const repoName = cli.flags['repoName']\n const [orgSlug = ''] = cli.input\n\n if (!repoName || typeof repoName !== 'string' || !orgSlug) {\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(stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Repository name using --repoName ${!repoName ? colors.red('(missing!)') : typeof repoName !== 'string' ? colors.red('(invalid!)') : colors.green('(ok)')}`)\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await createRepo({\n orgSlug,\n repoName,\n description: String(cli.flags['repoDescription'] || ''),\n homepage: String(cli.flags['homepage'] || ''),\n default_branch: String(cli.flags['defaultBranch'] || ''),\n visibility: String(cli.flags['visibility'] || 'private')\n })\n}\n","import constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function deleteRepo(\n orgSlug: string,\n repoName: string\n): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await deleteRepoWithToken(orgSlug, repoName, apiToken)\n}\n\nasync function deleteRepoWithToken(\n orgSlug: string,\n repoName: string,\n apiToken: string\n): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Deleting repository...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.deleteOrgRepo(orgSlug, repoName),\n 'deleting repository'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('deleteOrgRepo', result)\n return\n }\n\n spinner.successAndStop('Repository deleted successfully')\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { deleteRepo } from './delete-repo'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'del',\n description: 'Delete a repository in an organization',\n hidden: false,\n flags: {\n ...commonFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <org slug> <repo slug>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg test-repo\n `\n}\n\nexport const cmdReposDel = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const [orgSlug = '', repoName = ''] = cli.input\n\n if (!orgSlug || !repoName) {\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(stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Repository name as the second argument ${!repoName ? colors.red('(missing!)') : typeof repoName !== 'string' ? colors.red('(invalid!)') : colors.green('(ok)')}\n\n - At least one TARGET (e.g. \\`.\\` or \\`./package.json\\``)\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await deleteRepo(orgSlug, repoName)\n}\n","// @ts-ignore\nimport chalkTable from 'chalk-table'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function listRepos({\n direction,\n orgSlug,\n outputKind,\n page,\n per_page,\n sort\n}: {\n direction: string\n orgSlug: string\n outputKind: 'json' | 'markdown' | 'print'\n page: number\n per_page: number\n sort: string\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await listReposWithToken({\n apiToken,\n direction,\n orgSlug,\n outputKind,\n page,\n per_page,\n sort\n })\n}\n\nasync function listReposWithToken({\n apiToken,\n direction,\n orgSlug,\n outputKind,\n page,\n per_page,\n sort\n}: {\n apiToken: string\n direction: string\n orgSlug: string\n outputKind: 'json' | 'markdown' | 'print'\n page: number\n per_page: number\n sort: string\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Fetching list of repositories...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getOrgRepoList(orgSlug, {\n sort,\n direction,\n per_page,\n page\n }),\n 'listing repositories'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('getOrgRepoList', result)\n return\n }\n\n spinner.stop('Fetch complete.')\n\n if (outputKind === 'json') {\n const data = result.data.results.map(o => ({\n id: o.id,\n name: o.name,\n visibility: o.visibility,\n defaultBranch: o.default_branch,\n archived: o.archived\n }))\n logger.log(JSON.stringify(data, null, 2))\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: 'archived', name: colors.magenta('Archived') }\n ]\n }\n\n logger.log(chalkTable(options, result.data.results))\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { listRepos } from './list-repos'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'list',\n description: 'List repositories in an organization',\n hidden: false,\n flags: {\n ...commonFlags,\n sort: {\n type: 'string',\n shortFlag: 's',\n default: 'created_at',\n description: 'Sorting option'\n },\n direction: {\n type: 'string',\n default: 'desc',\n description: 'Direction option'\n },\n perPage: {\n type: 'number',\n shortFlag: 'pp',\n default: 30,\n description: 'Number of results per page'\n },\n page: {\n type: 'number',\n shortFlag: 'p',\n default: 1,\n description: 'Page number'\n },\n ...outputFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <org slug>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg\n `\n}\n\nexport const cmdReposList = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const [orgSlug = ''] = cli.input\n\n if (!orgSlug) {\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(stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - At least one TARGET (e.g. \\`.\\` or \\`./package.json\\``)\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await listRepos({\n direction: cli.flags['direction'] === 'asc' ? 'asc' : 'desc',\n orgSlug,\n outputKind: cli.flags['json']\n ? 'json'\n : cli.flags['markdown']\n ? 'markdown'\n : 'print',\n page: Number(cli.flags['page']) || 1,\n per_page: Number(cli.flags['perPage']) || 30,\n sort: String(cli.flags['sort'] || 'created_at')\n })\n}\n","import constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function updateRepo({\n default_branch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility\n}: {\n orgSlug: string\n repoName: string\n description: string\n homepage: string\n default_branch: string\n visibility: string\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await updateRepoWithToken({\n apiToken,\n default_branch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility\n })\n}\n\nasync function updateRepoWithToken({\n apiToken,\n default_branch,\n description,\n homepage,\n orgSlug,\n repoName,\n visibility\n}: {\n apiToken: string\n orgSlug: string\n repoName: string\n description: string\n homepage: string\n default_branch: string\n visibility: string\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Updating repository...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.updateOrgRepo(orgSlug, repoName, {\n orgSlug,\n name: repoName,\n description,\n homepage,\n default_branch,\n visibility\n }),\n 'updating repository'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('updateOrgRepo', result)\n return\n }\n\n spinner.successAndStop('Repository updated successfully')\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { updateRepo } from './update-repo'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'update',\n description: 'Update a repository in an organization',\n hidden: false,\n flags: {\n ...commonFlags,\n repoName: {\n type: 'string',\n shortFlag: 'n',\n default: '',\n description: 'Repository name'\n },\n repoDescription: {\n type: 'string',\n shortFlag: 'd',\n default: '',\n description: 'Repository description'\n },\n homepage: {\n type: 'string',\n shortFlag: 'h',\n default: '',\n description: 'Repository url'\n },\n defaultBranch: {\n type: 'string',\n shortFlag: 'b',\n default: 'main',\n description: 'Repository default branch'\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} <org slug>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg\n `\n}\n\nexport const cmdReposUpdate = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const repoName = cli.flags['repoName']\n const [orgSlug = ''] = cli.input\n\n if (!repoName || typeof repoName !== 'string' || !orgSlug) {\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(stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Repository name using --repoName ${!repoName ? colors.red('(missing!)') : typeof repoName !== 'string' ? colors.red('(invalid!)') : colors.green('(ok)')}\n\n - At least one TARGET (e.g. \\`.\\` or \\`./package.json\\``)\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await updateRepo({\n orgSlug,\n repoName,\n description: String(cli.flags['repoDescription'] || ''),\n homepage: String(cli.flags['homepage'] || ''),\n default_branch: String(cli.flags['defaultBranch'] || ''),\n visibility: String(cli.flags['visibility'] || 'private')\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 constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function viewRepo(\n orgSlug: string,\n repoName: string,\n outputKind: 'json' | 'markdown' | 'print'\n): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n await viewRepoWithToken(orgSlug, repoName, apiToken, outputKind)\n}\n\nasync function viewRepoWithToken(\n orgSlug: string,\n repoName: string,\n apiToken: string,\n outputKind: 'json' | 'markdown' | 'print'\n): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Fetching repository data...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getOrgRepo(orgSlug, repoName),\n 'fetching repository'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('getOrgRepo', result)\n return\n }\n\n spinner.stop('Fetched repository data.')\n\n if (outputKind === 'json') {\n const {\n archived,\n created_at,\n default_branch,\n homepage,\n id,\n name,\n visibility\n } = result.data\n logger.log(\n JSON.stringify(\n {\n id,\n name,\n visibility,\n default_branch,\n homepage,\n archived,\n created_at\n },\n null,\n 2\n )\n )\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 { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { viewRepo } from './view-repo'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'view',\n description: 'View repositories in an organization',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n repoName: {\n description: 'The repository to check',\n default: '',\n type: 'string'\n }\n },\n help: (command, config) => `\n Usage\n $ ${command} <org slug>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg\n `\n}\n\nexport const cmdReposView = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const repoName = cli.flags['repoName']\n const [orgSlug = ''] = cli.input\n\n if (!repoName || typeof repoName !== 'string' || !orgSlug) {\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 stripIndents`\n ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${\n !orgSlug ? colors.red('(missing!)') : colors.green('(ok)')\n }\n\n - Repository name using --repoName ${\n !repoName\n ? colors.red('(missing!)')\n : typeof repoName !== 'string'\n ? colors.red('(invalid!)')\n : colors.green('(ok)')\n }\n `\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await viewRepo(\n orgSlug,\n repoName,\n cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print'\n )\n}\n","import { cmdReposCreate } from './cmd-repos-create'\nimport { cmdReposDel } from './cmd-repos-del'\nimport { cmdReposList } from './cmd-repos-list'\nimport { cmdReposUpdate } from './cmd-repos-update'\nimport { cmdReposView } from './cmd-repos-view'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands'\n\nconst description = 'Repositories related commands'\n\nexport const cmdRepos: CliSubcommand = {\n description,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n create: cmdReposCreate,\n view: cmdReposView,\n list: cmdReposList,\n del: cmdReposDel,\n update: cmdReposUpdate\n },\n {\n argv,\n description,\n importMeta,\n name: `${parentName} repos`\n }\n )\n }\n}\n","import { select } from '@socketsecurity/registry/lib/prompts'\nimport { SocketSdk } from '@socketsecurity/sdk'\n\nimport { handleApiCall } from '../../utils/api'\n\nexport async function suggestOrgSlug(\n socketSdk: SocketSdk\n): Promise<string | void> {\n const result = await handleApiCall(\n socketSdk.getOrganizations(),\n 'looking up organizations'\n )\n // Ignore a failed request here. It was not the primary goal of\n // running this command and reporting it only leads to end-user confusion.\n if (result.success) {\n const proceed = await select<string>({\n message:\n 'Missing org name; do you want to use any of these orgs for this scan?',\n choices: Array.from(Object.values(result.data.organizations))\n .map(({ name: slug }) => ({\n name: 'Yes [' + slug + ']',\n value: slug,\n description: `Use \"${slug}\" as the organization`\n }))\n .concat({\n name: 'No',\n value: '',\n description:\n 'Do not use any of these organizations (will end in a no-op)'\n })\n })\n if (proceed) {\n return proceed\n }\n } else {\n // TODO: in verbose mode, report this error to stderr\n }\n}\n","import path from 'node:path'\nimport process from 'node:process'\n\nimport { select } from '@socketsecurity/registry/lib/prompts'\nimport { SocketSdk } from '@socketsecurity/sdk'\n\nimport { handleApiCall } from '../../utils/api'\n\nexport async function suggestRepoSlug(\n socketSdk: SocketSdk,\n orgSlug: string\n): Promise<{\n slug: string\n defaultBranch: string\n} | void> {\n // Same as above, but if there's a repo with the same name as cwd then\n // default the selection to that name.\n const result = await handleApiCall(\n socketSdk.getOrgRepoList(orgSlug, {\n orgSlug,\n sort: 'name',\n direction: 'asc',\n // There's no guarantee that the cwd is part of this page. If it's not\n // then do an additional request and specific search for it instead.\n // This way we can offer the tip of \"do you want to create [cwd]?\".\n perPage: 10,\n page: 0\n }),\n 'looking up known repos'\n )\n // Ignore a failed request here. It was not the primary goal of\n // running this command and reporting it only leads to end-user confusion.\n if (result.success) {\n const currentDirName = dirNameToSlug(path.basename(process.cwd()))\n\n let cwdIsKnown =\n !!currentDirName &&\n result.data.results.some(obj => obj.slug === currentDirName)\n if (!cwdIsKnown && currentDirName) {\n // Do an explicit request so we can assert that the cwd exists or not\n const result = await handleApiCall(\n socketSdk.getOrgRepo(orgSlug, currentDirName),\n 'checking if current cwd is a known repo'\n )\n if (result.success) {\n cwdIsKnown = true\n }\n }\n\n const proceed = await select<string>({\n message:\n 'Missing repo name; do you want to use any of these known repo names for this scan?',\n choices:\n // Put the CWD suggestion at the top, whether it exists or not\n (currentDirName\n ? [\n {\n name: `Yes, current dir [${cwdIsKnown ? currentDirName : `create repo for ${currentDirName}`}]`,\n value: currentDirName,\n description: cwdIsKnown\n ? 'Register a new repo name under the given org and use it'\n : 'Use current dir as repo'\n }\n ]\n : []\n ).concat(\n result.data.results\n .filter(({ slug }) => !!slug && slug !== currentDirName)\n .map(({ slug }) => ({\n name: 'Yes [' + slug + ']',\n value: slug || '', // Filtered above but TS is like nah.\n description: `Use \"${slug}\" as the repo name`\n })),\n {\n name: 'No',\n value: '',\n description: 'Do not use any of these repos (will end in a no-op)'\n }\n )\n })\n\n if (proceed) {\n const repoName = proceed\n let repoDefaultBranch = ''\n // Store the default branch to help with the branch name question next\n result.data.results.some(obj => {\n if (obj.slug === proceed && obj.default_branch) {\n repoDefaultBranch = obj.default_branch\n return\n }\n })\n return { slug: repoName, defaultBranch: repoDefaultBranch }\n }\n } else {\n // TODO: in verbose mode, report this error to stderr\n }\n}\n\nfunction dirNameToSlug(name: string): string {\n // Uses slug specs asserted by our servers\n // Note: this can lead to collisions; eg. slug for `x--y` and `x---y` is `x-y`\n return name\n .toLowerCase()\n .replace(/[^[a-zA-Z0-9_.-]/g, '_')\n .replace(/--+/g, '-')\n .replace(/__+/g, '_')\n .replace(/\\.\\.+/g, '.')\n .replace(/[._-]+$/, '')\n}\n","import { select } from '@socketsecurity/registry/lib/prompts'\nimport { spawnSync } from '@socketsecurity/registry/lib/spawn'\n\nexport async function suggestBranchSlug(\n repoDefaultBranch: string | undefined\n): Promise<string | void> {\n const spawnResult = spawnSync('git', ['branch', '--show-current'])\n const currentBranch = spawnResult.stdout.toString('utf8').trim()\n if (currentBranch && spawnResult.status === 0) {\n const proceed = await select<string>({\n message: 'Use the current git branch as target branch name?',\n choices: [\n {\n name: `Yes [${currentBranch}]`,\n value: currentBranch,\n description: 'Use the current git branch for branch name'\n },\n ...(repoDefaultBranch && repoDefaultBranch !== currentBranch\n ? [\n {\n name: `No, use the default branch [${repoDefaultBranch}]`,\n value: repoDefaultBranch,\n description:\n 'Use the default branch for target repo as the target branch name'\n }\n ]\n : []),\n {\n name: 'No',\n value: '',\n description:\n 'Do not use the current git branch as name (will end in a no-op)'\n }\n ].filter(Boolean)\n })\n if (proceed) {\n return proceed\n }\n }\n}\n","import { select } from '@socketsecurity/registry/lib/prompts'\n\nexport async function suggestTarget(): Promise<string[] | void> {\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 if (proceed) {\n return ['.']\n }\n}\n","import assert from 'node:assert'\nimport process from 'node:process'\nimport readline from 'node:readline/promises'\n\nimport { stripIndents } from 'common-tags'\nimport open from 'open'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { suggestOrgSlug } from './suggest-org-slug'\nimport { suggestRepoSlug } from './suggest-repo-slug'\nimport { suggestBranchSlug } from './suggest_branch_slug'\nimport { suggestTarget } from './suggest_target'\nimport constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getPackageFilesFullScans } from '../../utils/path-resolve'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function createFullScan({\n branchName,\n commitHash: _commitHash,\n commitMessage,\n committers: _committers,\n cwd,\n defaultBranch,\n orgSlug,\n pendingHead,\n pullRequest: _pullRequest,\n readOnly,\n repoName,\n targets,\n tmp\n}: {\n branchName: string\n commitHash: string\n commitMessage: string\n committers: string\n cwd: string\n defaultBranch: boolean\n orgSlug: string\n pendingHead: boolean\n pullRequest: number | undefined\n readOnly: boolean\n repoName: string\n targets: string[]\n tmp: boolean\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n const socketSdk = await setupSdk()\n const supportedFiles = await socketSdk\n .getReportSupportedFiles()\n .then(res => {\n if (!res.success) {\n handleUnsuccessfulApiResponse('getReportSupportedFiles', res)\n assert(\n false,\n 'handleUnsuccessfulApiResponse should unconditionally throw'\n )\n }\n\n return res.data\n })\n .catch((cause: Error) => {\n throw new Error('Failed getting supported files for report', { cause })\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 if (!targets.length) {\n const received = await suggestTarget()\n targets = received ?? []\n updatedInput = true\n }\n\n // // TODO: we'll probably use socket.json or something else soon...\n // const absoluteConfigPath = path.join(cwd, 'socket.yml')\n // const socketConfig = await getSocketConfig(absoluteConfigPath)\n\n const packagePaths = await getPackageFilesFullScans(\n cwd,\n targets,\n supportedFiles\n // socketConfig\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 apiToken = getDefaultToken()\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 let repoDefaultBranch = ''\n\n if (apiToken) {\n if (!orgSlug) {\n const suggestion = await suggestOrgSlug(socketSdk)\n if (suggestion) orgSlug = suggestion\n updatedInput = true\n }\n\n // (Don't bother asking for the rest if we didn't get an org slug above)\n if (orgSlug && !repoName) {\n const suggestion = await suggestRepoSlug(socketSdk, orgSlug)\n if (suggestion) {\n repoDefaultBranch = suggestion.defaultBranch\n repoName = suggestion.slug\n }\n updatedInput = true\n }\n\n // (Don't bother asking for the rest if we didn't get an org/repo above)\n if (orgSlug && repoName && !branchName) {\n const suggestion = await suggestBranchSlug(repoDefaultBranch)\n if (suggestion) branchName = suggestion\n updatedInput = true\n }\n }\n\n if (!orgSlug || !repoName || !branchName || !packagePaths.length) {\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 stripIndents`\n ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Repository name using --repo ${!repoName ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Branch name using --branch ${!branchName ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - At least one TARGET (e.g. \\`.\\` or \\`./package.json\\`) ${\n !packagePaths.length\n ? colors.red(\n targets.length > 0\n ? '(TARGET' +\n (targets.length ? 's' : '') +\n ' contained no matching/supported files!)'\n : '(missing)'\n )\n : colors.green('(ok)')\n }\n\n ${!apiToken ? 'Note: was unable to make suggestions because no API Token was found; this would make command fail regardless' : ''}\n `\n )\n return\n }\n\n if (updatedInput) {\n logger.log(\n 'Note: You can invoke this command next time to skip the interactive questions:'\n )\n logger.log('```')\n logger.log(\n ` socket scan create [other flags...] --repo ${repoName} --branch ${branchName} ${orgSlug} ${targets.join(' ')}`\n )\n logger.log('```')\n }\n\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n if (readOnly) {\n logger.log('[ReadOnly] Bailing now')\n return\n }\n\n spinner.start(`Creating a scan with ${packagePaths.length} packages...`)\n\n const result = await handleApiCall(\n socketSdk.createOrgFullScan(\n orgSlug,\n {\n repo: repoName,\n branch: branchName,\n commit_message: commitMessage,\n make_default_branch: defaultBranch,\n set_as_pending_head: pendingHead,\n tmp\n },\n packagePaths,\n cwd\n ),\n 'Creating scan'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('CreateOrgFullScan', result)\n return\n }\n\n spinner.successAndStop('Scan created successfully')\n\n const link = colors.underline(colors.cyan(`${result.data.html_report_url}`))\n logger.log(`Available at: ${link}`)\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout\n })\n\n const answer = await rl.question(\n 'Would you like to open it in your browser? (y/n)'\n )\n\n if (answer.toLowerCase() === 'y') {\n await open(`${result.data.html_report_url}`)\n }\n rl.close()\n}\n","import process from 'node:process'\n\nimport { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { createFullScan } from './create-full-scan'\nimport constants from '../../constants'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\nimport { getDefaultToken } from '../../utils/sdk'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'create',\n description: 'Create a scan',\n hidden: false,\n flags: {\n repo: {\n type: 'string',\n shortFlag: 'r',\n default: '',\n description: 'Repository name'\n },\n branch: {\n type: 'string',\n shortFlag: 'b',\n default: '',\n description: 'Branch name'\n },\n commitMessage: {\n type: 'string',\n shortFlag: 'm',\n default: '',\n description: 'Commit message'\n },\n commitHash: {\n type: 'string',\n shortFlag: 'ch',\n default: '',\n description: 'Commit hash'\n },\n cwd: {\n type: 'string',\n description: 'working directory, defaults to process.cwd()'\n },\n dryRun: {\n type: 'boolean',\n description:\n 'run input validation part of command without any concrete side effects'\n },\n pullRequest: {\n type: 'number',\n shortFlag: 'pr',\n description: 'Commit hash'\n },\n committers: {\n type: 'string',\n shortFlag: 'c',\n default: '',\n description: 'Committers'\n },\n defaultBranch: {\n type: 'boolean',\n shortFlag: 'db',\n default: false,\n description: 'Make default branch'\n },\n pendingHead: {\n type: 'boolean',\n shortFlag: 'ph',\n default: false,\n description: 'Set as pending head'\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 tmp: {\n type: 'boolean',\n shortFlag: 't',\n default: false,\n description:\n 'Set the visibility (true/false) of the scan in your dashboard'\n },\n view: {\n type: 'boolean',\n shortFlag: 'v',\n default: true,\n description:\n 'Will wait for and return the created report. Use --no-view to disable.'\n }\n },\n // TODO: your project's \"socket.yml\" file's \"projectIgnorePaths\"\n help: (command, config) => `\n Usage\n $ ${command} [...options] <org> <TARGET> [TARGET...]\n\n Uploads the specified \"package.json\" and lock files for JavaScript, Python,\n Go, Scala, Gradle, and Kotlin dependency manifests.\n If any folder is specified, the ones found in there recursively are uploaded.\n\n Supports globbing such as \"**/package.json\", \"**/requirements.txt\", etc.\n\n Ignores any file specified in your project's \".gitignore\" and also has a\n sensible set of default ignores from the \"ignore-by-default\" module.\n\n TARGET should be a FILE or DIR that _must_ be inside the CWD.\n\n When a FILE is given only that FILE is targeted. Otherwise any eligible\n files in the given DIR will be considered.\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} --repo=test-repo --branch=main FakeOrg ./package.json\n `\n}\n\nexport const cmdScanCreate = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const [orgSlug = '', ...targets] = cli.input\n\n const cwd =\n cli.flags['cwd'] && cli.flags['cwd'] !== 'process.cwd()'\n ? String(cli.flags['cwd'])\n : process.cwd()\n\n const { branch: branchName, repo: repoName } = cli.flags\n\n const apiToken = getDefaultToken() // This checks if we _can_ suggest anything\n\n if (!apiToken && (!orgSlug || !repoName || !branchName || !targets.length)) {\n // Without api token we cannot recover because we can't request more info\n // from the server, to match and help with the current cwd/git status.\n //\n // Use exit status of 2 to indicate incorrect usage, generally invalid\n // options or missing arguments.\n // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html\n process.exitCode = 2\n logger.fail(\n stripIndents`\n ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Repository name using --repo ${!repoName ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Branch name using --branch ${!branchName ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - At least one TARGET (e.g. \\`.\\` or \\`./package.json\\`) ${!targets.length ? '(missing)' : colors.green('(ok)')}\n\n (Additionally, no API Token was set so we cannot auto-discover these details)\n `\n )\n return\n }\n\n // Note exiting earlier to skirt a hidden auth requirement\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await createFullScan({\n branchName: branchName as string,\n commitHash: (cli.flags['commitHash'] as string) ?? '',\n commitMessage: (cli.flags['commitMessage'] as string) ?? '',\n committers: (cli.flags['committers'] as string) ?? '',\n cwd,\n defaultBranch: Boolean(cli.flags['defaultBranch']),\n orgSlug,\n pendingHead: Boolean(cli.flags['pendingHead']),\n pullRequest: (cli.flags['pullRequest'] as number) ?? undefined,\n readOnly: Boolean(cli.flags['readOnly']),\n repoName: repoName as string,\n targets,\n tmp: Boolean(cli.flags['tmp'])\n })\n}\n","import constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function deleteOrgFullScan(\n orgSlug: string,\n fullScanId: string\n): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await deleteOrgFullScanWithToken(orgSlug, fullScanId, apiToken)\n}\nexport async function deleteOrgFullScanWithToken(\n orgSlug: string,\n fullScanId: string,\n apiToken: string\n): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Deleting scan...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.deleteOrgFullScan(orgSlug, fullScanId),\n 'Deleting scan'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('deleteOrgFullScan', result)\n return\n }\n\n spinner.successAndStop('Scan deleted successfully')\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { deleteOrgFullScan } from './delete-full-scan'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'del',\n description: 'Delete a scan',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <org slug> <scan ID>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0\n `\n}\n\nexport const cmdScanDel = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const [orgSlug = '', fullScanId = ''] = cli.input\n\n if (!orgSlug || !fullScanId) {\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 stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Full Scan ID to delete as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}`\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await deleteOrgFullScan(orgSlug, fullScanId)\n}\n","// @ts-ignore\nimport chalkTable from 'chalk-table'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function listFullScans({\n direction,\n from_time,\n orgSlug,\n outputKind,\n page,\n per_page,\n sort\n}: {\n direction: string\n from_time: string\n orgSlug: string\n outputKind: 'json' | 'markdown' | 'print'\n page: number\n per_page: number\n sort: string\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await listFullScansWithToken({\n apiToken,\n direction,\n from_time,\n orgSlug,\n outputKind,\n page,\n per_page,\n sort\n })\n}\n\nasync function listFullScansWithToken({\n apiToken,\n direction,\n from_time,\n orgSlug,\n outputKind,\n page,\n per_page,\n sort\n}: {\n apiToken: string\n direction: string\n from_time: string // seconds\n orgSlug: string\n outputKind: 'json' | 'markdown' | 'print'\n page: number\n per_page: number\n sort: string\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Fetching list of scans...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getOrgFullScanList(orgSlug, {\n sort,\n direction,\n per_page,\n page,\n from: from_time\n }),\n 'Listing scans'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('getOrgFullScanList', result)\n return\n }\n\n spinner.stop(`Fetch complete`)\n\n if (outputKind === 'json') {\n logger.log(result.data)\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: '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 branch: d.branch\n }\n })\n\n logger.log(chalkTable(options, formattedResults))\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { listFullScans } from './list-full-scans'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type {\n CliCommandConfig,\n CliSubcommand\n} from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'list',\n description: 'List the full scans for an organization',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\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 direction: {\n type: 'string',\n shortFlag: 'd',\n default: 'desc',\n description: 'Direction option (`desc` or `asc`) - Default is `desc`'\n },\n perPage: {\n type: 'number',\n shortFlag: 'pp',\n default: 30,\n description: 'Results per page - Default is 30'\n },\n page: {\n type: 'number',\n shortFlag: 'p',\n default: 1,\n description: 'Page number - Default is 1'\n },\n fromTime: {\n type: 'string',\n shortFlag: 'f',\n default: '',\n description: 'From time - as a unix timestamp'\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} <org slug>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg\n `\n}\n\nexport const cmdScanList: CliSubcommand = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n) {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const orgSlug = cli.input[0]\n\n if (!orgSlug) {\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 stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}`\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await listFullScans({\n direction: String(cli.flags['direction'] || ''),\n from_time: String(cli.flags['fromTime'] || ''),\n orgSlug,\n outputKind: cli.flags['json']\n ? 'json'\n : cli.flags['markdown']\n ? 'markdown'\n : 'print',\n page: Number(cli.flags['page'] || 1),\n per_page: Number(cli.flags['perPage'] || 30),\n sort: String(cli.flags['sort'] || '')\n })\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nexport async function getOrgScanMetadata(\n orgSlug: string,\n scanId: string,\n outputKind: 'json' | 'markdown' | 'print'\n): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await getOrgScanMetadataWithToken(orgSlug, scanId, apiToken, outputKind)\n}\nexport async function getOrgScanMetadataWithToken(\n orgSlug: string,\n scanId: string,\n apiToken: string,\n outputKind: 'json' | 'markdown' | 'print'\n): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n spinner.start('Fetching meta data for a full scan...')\n\n const socketSdk = await setupSdk(apiToken)\n const result = await handleApiCall(\n socketSdk.getOrgFullScanMetadata(orgSlug, scanId),\n 'Listing scans'\n )\n\n if (!result.success) {\n handleUnsuccessfulApiResponse('getOrgFullScanMetadata', result)\n return\n }\n\n spinner?.successAndStop('Fetched the meta data\\n')\n\n if (outputKind === 'json') {\n logger.log(result.data)\n } else {\n // Markdown = print\n if (outputKind === 'markdown') {\n logger.log('# Scan meta data\\n')\n }\n logger.log(`Scan ID: ${scanId}\\n`)\n for (const [key, value] of Object.entries(result.data)) {\n if (\n [\n 'id',\n 'updated_at',\n 'organization_id',\n 'repository_id',\n 'commit_hash',\n 'html_report_url'\n ].includes(key)\n )\n continue\n 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}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getOrgScanMetadata } from './get-full-scan-metadata'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type {\n CliCommandConfig,\n CliSubcommand\n} from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'metadata',\n description: \"Get a full scan's metadata\",\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <org slug> <scan id>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0\n `\n}\n\nexport const cmdScanMetadata: CliSubcommand = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const [orgSlug = '', fullScanId = ''] = cli.input\n\n if (!orgSlug || !fullScanId) {\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 stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Full Scan ID to inspect as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}`\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await getOrgScanMetadata(\n orgSlug,\n fullScanId,\n cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print'\n )\n}\n","import constants from '../../constants'\nimport { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken, setupSdk } from '../../utils/sdk'\n\nimport type { SocketSdkResultType } from '@socketsecurity/sdk'\n\nexport async function streamFullScan(\n orgSlug: string,\n fullScanId: string,\n file: string | undefined\n): Promise<SocketSdkResultType<'getOrgFullScan'> | undefined> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n spinner.start('Fetching scan...')\n\n const socketSdk = await setupSdk(apiToken)\n const data = await handleApiCall(\n socketSdk.getOrgFullScan(\n orgSlug,\n fullScanId,\n file === '-' ? undefined : file\n ),\n 'Fetching a scan'\n )\n\n if (!data?.success) {\n handleUnsuccessfulApiResponse('getOrgFullScan', data)\n return\n }\n\n spinner?.successAndStop(\n file ? `Full scan details written to ${file}` : 'stdout'\n )\n\n return data\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { components } from '@socketsecurity/sdk/types/api'\n\nimport constants from '../../constants'\nimport { handleAPIError, queryAPI } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken } from '../../utils/sdk'\n\nexport async function getFullScan(\n orgSlug: string,\n fullScanId: string\n): Promise<Array<components['schemas']['SocketArtifact']> | undefined> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n spinner.start('Fetching full-scan...')\n\n const response = await queryAPI(\n `orgs/${orgSlug}/full-scans/${encodeURIComponent(fullScanId)}`,\n apiToken\n )\n\n spinner.stop('Fetch complete.')\n\n if (!response.ok) {\n const err = await handleAPIError(response.status)\n logger.fail(\n `${colors.bgRed(colors.white(response.statusText))}: Fetch error: ${err}`\n )\n return\n }\n\n // This is nd-json; each line is a json object\n const jsons = await response.text()\n const lines = jsons.split('\\n').filter(Boolean)\n const data = lines.map(line => {\n try {\n return JSON.parse(line)\n } catch {\n console.error(\n 'At least one line item was returned that could not be parsed as JSON...'\n )\n return {}\n }\n }) as unknown as Array<components['schemas']['SocketArtifact']>\n\n return data\n}\n","import fs from 'node:fs/promises'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { components } from '@socketsecurity/sdk/types/api'\n\nimport { getFullScan } from './get-full-scan'\nimport { mdTable } from '../../utils/markdown'\n\nexport async function viewFullScan(\n orgSlug: string,\n fullScanId: string,\n filePath: string\n): Promise<void> {\n const artifacts: Array<components['schemas']['SocketArtifact']> | undefined =\n await getFullScan(orgSlug, fullScanId)\n if (!artifacts) return\n\n const display = artifacts.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\nSscan ID: ${fullScanId}\n\n${md}\n\nView this report at: https://socket.dev/dashboard/org/${orgSlug}/sbom/${fullScanId}\n `.trim() + '\\n'\n\n if (filePath && filePath !== '-') {\n try {\n await fs.writeFile(filePath, report, 'utf8')\n logger.log(`Data successfully written to ${filePath}`)\n } catch (e) {\n process.exitCode = 1\n logger.fail('There was an error trying to write the json to disk')\n logger.error(e)\n }\n } else {\n logger.log(report)\n }\n}\n","import { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { streamFullScan } from './stream-full-scan'\nimport { viewFullScan } from './view-full-scan'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type {\n CliCommandConfig,\n CliSubcommand\n} from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'view',\n description: 'View the raw results of a scan',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags\n },\n help: (command, config) => `\n Usage\n $ ${command} <org slug> <scan ID> [path to output file]\n\n When no output path is given the contents is sent to stdout.\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0 ./stream.txt\n `\n}\n\nexport const cmdScanView: CliSubcommand = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const [orgSlug = '', fullScanId = '', file = '-'] = cli.input\n\n if (!orgSlug || !fullScanId) {\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 stripIndents`\n ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n\n - Full Scan ID to fetch as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}\n `\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n if (cli.flags['json']) {\n await streamFullScan(orgSlug, fullScanId, file)\n } else {\n await viewFullScan(orgSlug, fullScanId, file)\n }\n}\n","import { cmdScanCreate } from './cmd-scan-create'\nimport { cmdScanDel } from './cmd-scan-del'\nimport { cmdScanList } from './cmd-scan-list'\nimport { cmdScanMetadata } from './cmd-scan-metadata'\nimport { cmdScanView } from './cmd-scan-view'\nimport { meowWithSubcommands } from '../../utils/meow-with-subcommands'\n\nimport type { CliSubcommand } from '../../utils/meow-with-subcommands'\n\nconst description = 'Full Scan related commands'\n\nexport const cmdScan: CliSubcommand = {\n description,\n async run(argv, importMeta, { parentName }) {\n await meowWithSubcommands(\n {\n create: cmdScanCreate,\n list: cmdScanList,\n del: cmdScanDel,\n metadata: cmdScanMetadata,\n view: cmdScanView\n },\n {\n aliases: {\n // Backwards compat. TODO: Drop next major bump\n stream: {\n description: cmdScanView.description,\n hidden: true,\n argv: ['view'] // Original args will be appended (!)\n }\n },\n argv,\n description,\n importMeta,\n name: parentName + ' scan'\n }\n )\n }\n}\n","import process from 'node:process'\n\n// @ts-ignore\nimport BoxWidget from 'blessed/lib/widgets/box'\n// @ts-ignore\nimport ScreenWidget from 'blessed/lib/widgets/screen'\n// @ts-ignore\nimport TableWidget from 'blessed-contrib/lib/widget/table'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../../constants'\nimport { queryAPI } from '../../utils/api'\nimport { AuthError } from '../../utils/errors'\nimport { getDefaultToken } from '../../utils/sdk'\n\nimport type { Widgets } from 'blessed' // Note: Widgets does not seem to actually work as code :'(\n\ntype ThreatResult = {\n createdAt: string\n description: string\n id: number\n locationHtmlUrl: string\n packageHtmlUrl: string\n purl: string\n removedAt: string\n threatType: string\n}\n\nexport async function getThreatFeed({\n direction,\n ecosystem,\n filter,\n outputKind,\n page,\n perPage\n}: {\n direction: string\n ecosystem: string\n filter: string\n outputKind: 'json' | 'markdown' | 'print'\n page: string\n perPage: number\n}): Promise<void> {\n const apiToken = getDefaultToken()\n if (!apiToken) {\n throw new AuthError(\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'\n )\n }\n\n await getThreatFeedWithToken({\n apiToken,\n direction,\n ecosystem,\n filter,\n outputKind,\n page,\n perPage\n })\n}\n\nasync function getThreatFeedWithToken({\n apiToken,\n direction,\n ecosystem,\n filter,\n outputKind,\n page,\n perPage\n}: {\n apiToken: string\n direction: string\n ecosystem: string\n filter: string\n outputKind: 'json' | 'markdown' | 'print'\n page: string\n perPage: number\n}): Promise<void> {\n // Lazily access constants.spinner.\n const { spinner } = constants\n\n const queryParams = new URLSearchParams([\n ['direction', direction],\n ['ecosystem', ecosystem],\n ['filter', filter],\n ['page', page],\n ['per_page', String(perPage)]\n ])\n\n spinner.start('Fetching Threat Feed data...')\n\n const response = await queryAPI(`threat-feed?${queryParams}`, apiToken)\n const data = (await response.json()) as {\n results: ThreatResult[]\n nextPage: string\n }\n\n spinner.stop('Threat feed data fetched')\n\n if (outputKind === 'json') {\n logger.log(data)\n return\n }\n\n const screen: Widgets.Screen = new ScreenWidget()\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 height: '70%', // Changed from 100% to 70%\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 // Create details box at the bottom\n const detailsBox: Widgets.BoxElement = new BoxWidget({\n bottom: 0,\n height: '30%',\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 // allow control the table with the keyboard\n table.focus()\n\n screen.append(table)\n screen.append(detailsBox)\n\n const formattedOutput = formatResults(data.results)\n const descriptions = data.results.map(d => d.description)\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 // 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 `Ecosystem: ${selectedRow[0]}\\n` +\n `Name: ${selectedRow[1]}\\n` +\n `Version:${selectedRow[2]}\\n` +\n `Threat type:${selectedRow[3]}\\n` +\n `Detected at:${selectedRow[4]}\\n` +\n `Details: ${selectedRow[5]}\\n` +\n `Description: ${descriptions[selectedIndex]}`\n )\n screen.render()\n }\n }\n })\n\n screen.render()\n\n screen.key(['escape', 'q', 'C-c'], () => process.exit(0))\n screen.key(['return'], () => {\n const selectedIndex = table.rows.selected\n screen.destroy()\n const selectedRow = formattedOutput[selectedIndex]\n console.log(selectedRow)\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\nfunction msAtHome(isoTimeStamp: string): string {\n const timeStart = Date.parse(isoTimeStamp)\n const timeEnd = Date.now()\n\n const rtf = new Intl.RelativeTimeFormat('en', {\n numeric: 'always',\n style: 'short'\n })\n\n const delta = timeEnd - timeStart\n if (delta < 60 * 60 * 1000) {\n return rtf.format(-Math.round(delta / (60 * 1000)), 'minute')\n // return Math.round(delta / (60 * 1000)) + ' min ago'\n } else if (delta < 24 * 60 * 60 * 1000) {\n return rtf.format(-(delta / (60 * 60 * 1000)).toFixed(1), 'hour')\n // return (delta / (60 * 60 * 1000)).toFixed(1) + ' hr ago'\n } else if (delta < 7 * 24 * 60 * 60 * 1000) {\n return rtf.format(-(delta / (24 * 60 * 60 * 1000)).toFixed(1), 'day')\n // return (delta / (24 * 60 * 60 * 1000)).toFixed(1) + ' day ago'\n } else {\n return isoTimeStamp.slice(0, 10)\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { getThreatFeed } from './get-threat-feed'\nimport constants from '../../constants'\nimport { commonFlags, outputFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\n\nconst config: CliCommandConfig = {\n commandName: 'threat-feed',\n description: '[beta] View the threat feed',\n hidden: false,\n flags: {\n ...commonFlags,\n ...outputFlags,\n perPage: {\n type: 'number',\n shortFlag: 'pp',\n default: 30,\n description: 'Number of items per page'\n },\n page: {\n type: 'string',\n shortFlag: 'p',\n default: '1',\n description: 'Page token'\n },\n direction: {\n type: 'string',\n shortFlag: 'd',\n default: 'desc',\n description: 'Order asc or desc by the createdAt attribute.'\n },\n eco: {\n type: 'string',\n shortFlag: 'e',\n default: '',\n description: 'Only show threats for a particular ecosystem'\n },\n filter: {\n type: 'string',\n shortFlag: 'f',\n default: 'mal',\n description: 'Filter what type of threats to return'\n }\n },\n help: (command, config) => `\n Usage\n $ ${command}\n\n This feature requires a Threat Feed license. Please contact\n sales@socket.dev if you are interested in purchasing this access.\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Valid 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 Valid ecosystems:\n\n - gem\n - golang\n - maven\n - npm\n - nuget\n - pypi\n\n Examples\n $ ${command}\n $ ${command} --perPage=5 --page=2 --direction=asc --filter=joke\n `\n}\n\nexport const cmdThreatFeed = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n await getThreatFeed({\n direction: String(cli.flags['direction'] || 'desc'),\n ecosystem: String(cli.flags['eco'] || ''),\n filter: String(cli.flags['filter'] || 'mal'),\n outputKind: cli.flags['json']\n ? 'json'\n : cli.flags['markdown']\n ? 'markdown'\n : 'print',\n page: String(cli.flags['page'] || '1'),\n perPage: Number(cli.flags['perPage']) || 30\n })\n}\n","import fs from 'node:fs'\n\nimport { stripIndents } from 'common-tags'\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 // TODO: pretty sure you need to source the file or restart\n // any terminal session before changes are reflected.\n logger.log(\n stripIndents`\nThe alias was added to ${file}. Running 'npm install' will now be wrapped in Socket's \"safe npm\" 🎉\nIf you want to disable it at any time, run \\`socket wrapper --disable\\`\n`\n )\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 return true\n }\n return false\n}\n","import { existsSync } from 'node:fs'\nimport process from 'node:process'\nimport readline from 'node:readline'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { addSocketWrapper } from './add-socket-wrapper'\nimport { checkSocketWrapperSetup } from './check-socket-wrapper-setup'\nimport constants from '../../constants'\n\nexport function postinstallWrapper() {\n // Lazily access constants.bashRcPath and constants.zshRcPath.\n const { bashRcPath, zshRcPath } = constants\n const socketWrapperEnabled =\n (existsSync(bashRcPath) && checkSocketWrapperSetup(bashRcPath)) ||\n (existsSync(zshRcPath) && checkSocketWrapperSetup(zshRcPath))\n\n if (!socketWrapperEnabled) {\n installSafeNpm(`The Socket CLI is now successfully installed! 🎉\n\n To better protect yourself against supply-chain attacks, our \"safe npm\" wrapper can warn you about malicious packages whenever you run 'npm install'.\n\n Do you want to install \"safe npm\" (this will create an alias to the socket-npm command)? (y/n)`)\n }\n}\n\nfunction installSafeNpm(query: string): void {\n logger.log(`\n _____ _ _\n| __|___ ___| |_ ___| |_\n|__ | . | _| '_| -_| _|\n|_____|___|___|_,_|___|_|\n\n`)\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout\n })\n return askQuestion(rl, query)\n}\n\nfunction askQuestion(rl: readline.Interface, query: string): void {\n rl.question(query, (ans: string) => {\n if (ans.toLowerCase() === 'y') {\n // Lazily access constants.bashRcPath and constants.zshRcPath.\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(`There was an issue setting up the alias: ${e}`)\n }\n rl.close()\n } else if (ans.toLowerCase() !== 'n') {\n askQuestion(\n rl,\n 'Incorrect input: please enter either y (yes) or n (no): '\n )\n } else {\n rl.close()\n }\n })\n}\n","import fs from 'node:fs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nexport function removeSocketWrapper(file: string): void {\n return fs.readFile(file, 'utf8', function (err, data) {\n if (err) {\n logger.fail('There was an error removing the alias:')\n logger.error(err)\n return\n }\n const linesWithoutSocketAlias = data\n .split('\\n')\n .filter(\n l => l !== 'alias npm=\"socket npm\"' && l !== 'alias npx=\"socket npx\"'\n )\n\n const updatedFileContent = linesWithoutSocketAlias.join('\\n')\n\n fs.writeFile(file, updatedFileContent, function (err) {\n if (err) {\n logger.error(err)\n return\n }\n // TODO: pretty sure you need to source the file or restart\n // any terminal session before changes are reflected.\n logger.log(\n `The alias was removed from ${file}. Running 'npm install' will now run the standard npm command.`\n )\n })\n })\n}\n","import { existsSync } from 'node:fs'\n\nimport { stripIndents } from 'common-tags'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { addSocketWrapper } from './add-socket-wrapper'\nimport { checkSocketWrapperSetup } from './check-socket-wrapper-setup'\nimport { postinstallWrapper } from './postinstall-wrapper'\nimport { removeSocketWrapper } from './remove-socket-wrapper'\nimport constants from '../../constants'\nimport { commonFlags } from '../../flags'\nimport { meowOrExit } from '../../utils/meow-with-subcommands'\nimport { getFlagListOutput } from '../../utils/output-formatting'\n\nimport type { CliCommandConfig } from '../../utils/meow-with-subcommands'\n\nconst { DRY_RUN_BAIL_TEXT } = constants\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 enable: {\n type: 'boolean',\n default: false,\n description: 'Enables the Socket npm/npx wrapper'\n },\n disable: {\n type: 'boolean',\n default: false,\n description: 'Disables the Socket npm/npx wrapper'\n }\n },\n help: (command, config) => `\n Usage\n $ ${command} <flag>\n\n Options\n ${getFlagListOutput(config.flags, 6)}\n\n Examples\n $ ${command} --enable\n $ ${command} --disable\n `\n}\n\nexport const cmdWrapper = {\n description: config.description,\n hidden: config.hidden,\n run\n}\n\nasync function run(\n argv: readonly string[],\n importMeta: ImportMeta,\n { parentName }: { parentName: string }\n): Promise<void> {\n // I don't think meow would mess with this but ...\n if (argv[0] === '--postinstall') {\n postinstallWrapper()\n return\n }\n\n const cli = meowOrExit({\n argv,\n config,\n importMeta,\n parentName\n })\n\n const { enable } = cli.flags\n if (!enable && !cli.flags['disable']) {\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 stripIndents`\n ${colors.bgRed(colors.white('Input error'))}: Please provide the required flags:\n\n - Must use --enabled or --disabled\n `\n )\n return\n }\n\n if (cli.flags['dryRun']) {\n logger.log(DRY_RUN_BAIL_TEXT)\n return\n }\n\n // Lazily access constants.bashRcPath and constants.zshRcPath.\n const { bashRcPath, zshRcPath } = constants\n if (enable) {\n if (existsSync(bashRcPath) && !checkSocketWrapperSetup(bashRcPath)) {\n addSocketWrapper(bashRcPath)\n }\n if (existsSync(zshRcPath) && !checkSocketWrapperSetup(zshRcPath)) {\n addSocketWrapper(zshRcPath)\n }\n } else {\n if (existsSync(bashRcPath)) {\n removeSocketWrapper(bashRcPath)\n }\n if (existsSync(zshRcPath)) {\n removeSocketWrapper(zshRcPath)\n }\n }\n if (!existsSync(bashRcPath) && !existsSync(zshRcPath)) {\n logger.fail('There was an issue setting up the alias in your bash profile')\n }\n}\n","#!/usr/bin/env node\n\nimport process from 'node:process'\nimport { pathToFileURL } from 'node:url'\n\nimport { messageWithCauses, stackWithCauses } from 'pony-cause'\nimport updateNotifier from 'tiny-updater'\nimport colors from 'yoctocolors-cjs'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { cmdAction } from './commands/action/cmd-action'\nimport { cmdAnalytics } from './commands/analytics/cmd-analytics'\nimport { cmdAuditLog } from './commands/audit-log/cmd-audit-log'\nimport { cmdCdxgen } from './commands/cdxgen/cmd-cdxgen'\nimport { cmdScanCreate } from './commands/dependencies/cmd-dependencies'\nimport { cmdDiffScan } from './commands/diff-scan/cmd-diff-scan'\nimport { cmdFix } from './commands/fix/cmd-fix'\nimport { cmdInfo } from './commands/info/cmd-info'\nimport { cmdLogin } from './commands/login/cmd-login'\nimport { cmdLogout } from './commands/logout/cmd-logout'\nimport { cmdManifest } from './commands/manifest/cmd-manifest'\nimport { cmdNpm } from './commands/npm/cmd-npm'\nimport { cmdNpx } from './commands/npx/cmd-npx'\nimport { cmdOops } from './commands/oops/cmd-oops'\nimport { cmdOptimize } from './commands/optimize/cmd-optimize'\nimport { cmdOrganization } from './commands/organization/cmd-organization'\nimport { cmdRawNpm } from './commands/raw-npm/cmd-raw-npm'\nimport { cmdRawNpx } from './commands/raw-npx/cmd-raw-npx'\nimport { cmdReport } from './commands/report/cmd-report'\nimport { cmdRepos } from './commands/repos/cmd-repos'\nimport { cmdScan } from './commands/scan/cmd-scan'\nimport { cmdThreatFeed } from './commands/threat-feed/cmd-threat-feed'\nimport { cmdWrapper } from './commands/wrapper/cmd-wrapper'\nimport constants from './constants'\nimport { AuthError, InputError, captureException } from './utils/errors'\nimport { meowWithSubcommands } from './utils/meow-with-subcommands'\n\nconst { SOCKET_CLI_BIN_NAME } = constants\n\n// TODO: Add autocompletion using https://socket.dev/npm/package/omelette\nvoid (async () => {\n await updateNotifier({\n name: SOCKET_CLI_BIN_NAME,\n // The '@rollup/plugin-replace' will replace \"process.env['INLINED_SOCKET_CLI_VERSION']\".\n version: process.env['INLINED_SOCKET_CLI_VERSION']!,\n ttl: 86_400_000 /* 24 hours in milliseconds */\n })\n\n try {\n await meowWithSubcommands(\n {\n action: cmdAction,\n cdxgen: cmdCdxgen,\n fix: cmdFix,\n info: cmdInfo,\n login: cmdLogin,\n logout: cmdLogout,\n npm: cmdNpm,\n npx: cmdNpx,\n oops: cmdOops,\n optimize: cmdOptimize,\n organization: cmdOrganization,\n 'raw-npm': cmdRawNpm,\n 'raw-npx': cmdRawNpx,\n report: cmdReport,\n wrapper: cmdWrapper,\n scan: cmdScan,\n 'audit-log': cmdAuditLog,\n repos: cmdRepos,\n dependencies: cmdScanCreate,\n analytics: cmdAnalytics,\n 'diff-scan': cmdDiffScan,\n 'threat-feed': cmdThreatFeed,\n manifest: cmdManifest\n },\n {\n aliases: {\n ci: {\n description: 'Alias for \"report create --view --strict\"',\n argv: ['report', 'create', '--view', '--strict']\n }\n },\n argv: process.argv.slice(2),\n name: SOCKET_CLI_BIN_NAME,\n importMeta: { url: `${pathToFileURL(__filename)}` } as ImportMeta\n }\n )\n } catch (e) {\n process.exitCode = 1\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 logger.fail(\n `${colors.bgRed(colors.white(`${errorTitle}:`))} ${errorMessage}`\n )\n if (errorBody) {\n logger.error(`\\n${errorBody}`)\n }\n await captureException(e)\n }\n})()\n"],"names":["id","body","body_list","newPackages","removedPackages","newAlerts","sbom","reportUrl","diffUrl","created_at","updated_at","organizationId","repositoryId","branch","commit_message","commit_hash","pull_request","sbom_artifacts","constructor","pkg_type","pkg_name","pkg_version","category","type","severity","pkg_id","key","error","warn","ignore","monitor","description","title","emoji","next_step_title","suggestion","introduced_by","manifests","url","purl","arr","name","version","release","direct","manifestFiles","author","size","alerts","topLevelAncestors","transitives","license","license_text","supplyChain","quality","overall","vulnerability","critical","high","middle","low","ecosystem","capabilities","author_url","generateAuthorData","socket","fullScanId","data","params","fullScan","getSourceData","pkg","file","createPurl","packages","nextStepTitle","issueAlert","compareIssueAlerts","newScanAlerts","consolidatedAlerts","checkAlertCapabilities","envVars","networkAccess","filesystemAccess","shellAccess","newAlert","compareCapabilities","headPackage","packageId","newScan","diff","consolidated","headScanAlerts","headPackages","getLicenseDetails","package","logger","topLevelCount","headFullScanId","headFullScan","headScan","diffReport","comments","security","overview","comment","socketComments","ignoreAll","result","ignoredPackages","line","start","command","ignoreCommands","octokit","checkEventType","commentId","comment_id","content","securityComment","rows","md","removedLine","sources","nextSteps","mdTable","changedFiles","newSecurityComment","newOverviewComment","API_V0_URL","process","cause","method","headers","keyPrefix","padName","help","default","shortFlag","dryRun","json","markdown","all","strict","REDACTED","__proto__","parentName","hidden","allowUnknownFlags","DRY_RUN_BAIL_TEXT","commandName","flags","githubEventBefore","githubEventAfter","run","handleUnsuccessfulApiResponse","mw2","lines","cols","time","spinner","screen","label","barWidth","barSpacing","xOffset","maxHeight","barBgColor","formattedData","totalTopAlerts","sortedTopFiveAlerts","top_five_alert_types","formatted","style","text","baseline","xLabelPadding","xPadding","wholeNumbersOnly","legend","width","x","y","repo","scope","filePath","perPage","logType","desc","org","logs","user_email","year","month","day","choices","pageSize","per_page","page","YARN_LOCK","cleanupPackageLock","length","i","configuration","coerce","filter","only","profile","standard","lifecycle","alias","array","boolean","argv","outputJson","offset","columns","field","limit","apiToken","fs","showHidden","depth","colors","maxArrayLength","after","before","get","NPM","path","consolidate","include","existing","unfixable","upgrade","editable","vulnerableVersionRange","stdio","pkgJson","arb2","remaining","SOCKET_IPC_HANDSHAKE","args","constants","env","spawnPromise","agentExecPath","PNPM","ignoreIncompatible","YARN_CLASSIC","nothrow","cwd","onUnknown","agent","agentVersion","browser","node","lockSrc","lockName","lockPath","features","npmBuggyOverrides","targets","cmdName","prod","acc","count","fallbackToUrl","severityCount","pkgVersion","Maintenance","Quality","Vulnerabilities","Object","logPackageIssuesDetails","includeAllIssues","updateSetting","SOCKET_PUBLIC_API_TOKEN","apiBaseUrl","apiProxy","message","value","enforcedOrgs","applyLogout","attemptLogout","bin","gradleOpts","out","stdout","task","verbose","poms","sbtOpts","subArgs","meow","importMeta","auto","scala","gradle","kotlin","aliases","yolo","NPX","peerDependencies","overrides","workspacePatterns","pkgs","pkgid","names","editablePkgJson","isPlacingHigher","insertIndex","entries","updatePkgJsonField","pkgPath","state","added","addedInWorkspaces","updated","updatedInWorkspaces","warnedPnpmWorkspaceRequiresNpm","npmExecPath","overridesDataObjects","depAliasMap","thisSpec","depObj","newSpec","absolute","updateManifestByAgent","NPM_BUGGY_OVERRIDES_PATCHED_VERSION","pin","mw1","mw3","DRY_RUN_LABEL","debugLog","basePath","formatReportDataOutput","view","create","visibility","repoName","repoDescription","homepage","defaultBranch","sort","direction","outputKind","list","del","update","cwdIsKnown","slug","commitHash","committers","pullRequest","tmp","assert","updatedInput","make_default_branch","set_as_pending_head","commitMessage","pendingHead","readOnly","branchName","from","fromTime","untilTime","console","score","metadata","stream","keys","fg","selectedFg","selectedBg","interactive","height","border","columnWidth","columnSpacing","truncate","bottom","table","numeric","eco","zshRcPath","installSafeNpm","rl","askQuestion","enable","disable","postinstallWrapper","SOCKET_CLI_BIN_NAME","action","cdxgen","fix","info","login","logout","npm","npx","oops","optimize","organization","report","wrapper","scan","repos","dependencies","analytics","manifest","ci","errorTitle","errorMessage","errorBody"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;AAqBO;AACLA;AACAC;AACAC;;;;;AAMA;AACF;AAEO;AACLC;;AAEAC;AACAC;AACAL;AACAM;;AAEAC;AACAC;AACF;AAEO;AACLR;AACAS;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;;AAGAC;;;;;;;;;;;;AAYA;AACF;AAEO;AACLC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;;AAEAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;;;;;;;;;;;;;;;;;;;;AA0CI;AACE;AACAC;AACF;;AAEF;AACF;AACF;AAEO;AACLjB;AACAkB;AACAC;AACAC;AACA3C;AACA4C;AACAC;AACAC;AACAC;;AAGAC;;AAEAC;AACAX;AACAY;AACAC;AACAC;AACAb;;;;;;;;;;;;;AA6BE;;AAGIc;AACAC;AACAH;AACAI;AACAC;AACF;;AAGAC;AACAC;AACAC;AACAC;;AAEF;AACF;AACF;AAEO;AACL5D;AACAyC;AACAC;AACAmB;AACAjB;AACAE;AACAC;AACAG;AACAd;AACA0B;AACA;AACAC;AACAzB;AACAC;;;;;;;;;;;;;AA2BE;AACF;AAEQyB;;AAEN;AACE;;AAEF;AACA;AACF;AACF;AAEO;AACLX;AACAC;AACAH;AACAI;AACAC;;;;;;;AAQA;AACF;;AC1RA;AAcO;;AAULtC;;;AAGE+C;AACiD;;;;;AAKnD;AAEA;AACEC;AAGF;AACE;AAKA;AACE;AACF;;AAEQC;AAAgC;;;AAOxC;AAEA;AACF;AAEA;AACEC;AAGF;;AAGI;AACA;AACA;;;AAAiD;AAInD;;AAEA;;AAEQpE;;;;AAGNqE;AAAmDH;AAAW;AAChE;AACA;AACF;AAEAI;;AAEEC;AAIF;;;;AAI+CC;AAAK;;AAGlD;AACE;AACE;;AAGE;AACF;AAEA;;AAEUA;AAAK;;AAIjB;AACF;AAEA;AACF;AAEAC;;AAEEC;AAIF;AACE;AACA;;AAA+CA;AAAS;AACxD;;;;;;AAMEtC;;;;;;AAMF;;;AACemC;;AACjB;AAEA;;;AAGEA;AAKF;;AAKE;AACE;;;;;;AAQExC;AACAC;AACAG;AACAwC;AACF;AAEA;;AAA+CD;AAAS;AAExD;;;;;;;;;;;;AAYExC;AACAE;;;AAGAT;AACAE;AACAD;AACAE;AACF;AAEA;;;AAGI8C;AACF;AACF;AAEA;AACE;;AAEA;;AAEA;AACF;AACF;AAEA;AACF;AAEAC;;;AAGEC;AAKF;AACE;AAEA;AACE;AACE;AAEA;AACE;AAEA;AACE;AACE9B;AACA+B;AACF;AACF;AACF;AACF;AACE;AACA;AAEA;AACE;AACA;AAIE;AACE/B;AACA+B;AACF;AACF;AACF;AACF;AACF;AAEA;AACF;AAEAC;;;;AAIET;AAMF;AACE;AACEU;AACAC;AACAC;AACAC;;AAGF;;AAEE;AACEC;AACF;AACA;AACE;AACA;AACEvB;AACF;;AAEIA;AACF;AACF;AACF;AACF;AAEA;AACF;AAEAwB;;AAEEnF;AAIF;;AAGE;AACE;;AAGE;AACA;;AAEI2D;;;;AAIEyB;AACF;AACF;AACF;AACF;AACEzB;;;AAGE0B;AACF;AACF;AACF;AAEA;AACF;;;AAKE;AACE;;AAGE;;AAEErF;AACF;AACF;AACEA;AACF;AACF;;AAGA;AACF;AAEA;;AAEEsF;AAIF;AACE;AACA;AACA;;;AAIA;AAEA;;;AACelD;AAAK;;AAEhBmC;AACF;AACA;AAEA;AAKEgB;AACAC;AACF;AACA;AACAb;;AAEE9B;AACA0B;AACF;AACF;AAEA;;;AACenC;AAAK;;AAEhBmC;AACF;;AAGEgB;AACF;AACA;AACAE;;AAEE5C;AACA0B;AACF;AACF;AAEAgB;;;;AAIA;AACAA;;AAEEG;AACF;AACAH;AAEA;AACF;;;;;;;;;;;;;;;;AAqBM;AAEN;AAEAI;AAAoBC;AAAmC;;AAOrD;;;AAGA;AACA;AACF;;;;AAQE;AACE;;;;;;;;;;;;;;AAcA;AAEA;AACEC;AACF;AACEzB;AAA+BwB;AAAa;AAC5CrB;;AAGE;AACEuB;AACF;AACEA;AACF;AACF;AACF;AACF;;AAGE;AACE;AACA;;AAEA;AACF;AACF;AAEA;AACF;AAEA;AACE7B;AAMF;;;;AAKI;;AAKE8B;;AAEEC;AAAwCjC;AAA2B;AACrE;AACF;;AAEA8B;AACF;AAEA;AAAgD5B;AAAO;;AAGvD;;AAEEgC;AACF;AACAC;;AAGAA;AACAA;;AAEEA;AACF;AACEA;AACF;AAEA;AACF;AACF;;AC/iBA;AAWO;AACLC;AAGF;AACE;AAKEC;AACAC;AACA3E;;AAGF;AACE;;;;;AAMA;AACE;AACA;AACA;AACA;AACA4E;AAKAC;AACF;AACF;AAEA;AACF;;AAEA;AACA;AACA;AACO;AACL;AAAiBhC;AAA0BiC;;AAC3C;;;AAGE;AACF;;AAEE;AACE;AACAC;AACF;AACA;AACF;AACA;AACF;;AAEA;AACO;AACL/E;AACA0E;AAC2C;;;;;AAM3C;AACE;;AAIEI;AACA;AACF;AACAE;AACF;;AAEA;AACA;AACA;AACEC;AAEA;AACEC;AACAH;;AAIA;AACA;AAMA;AACA;;AAGA;;;;AAMA;;AAEA;AACE/E;AACF;;AAEI;AAIEA;AACA;AACF;AACF;AACF;AAEA;AACE;AACF;AACA+E;;AAEAG;AACAH;AACF;AACEA;AACF;AACF;AAEA;AACF;AAEO;AAA4BN;AAAuC;;;AAIxE;AACE;;;;;AAKIU;;AAEEL;AACF;AACEK;;AAEA;AACAC;AACF;;AAEAjB;AACAA;AACF;AACF;AACF;;;AACoBiB;;AACtB;AAEO;;AAEL5G;AAIF;;AAGE;AACE;AACF;;;AAEmB4G;;AACjBX;AACF;AAEA;AACE;AACE;AACF;;;AAGE;AACA;AACEN;AACF;AACEA;AAGAhD;AACF;AACF;AACF;AAEA;AACF;;ACtNA;AACA;AAQO;AACLkE;AAKAhG;;AAIE;;;;;AAOF;AAEAiG;AACE;AACE;AACE;AACF;AAAqB;AACnB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AACF;;AAEE;AACF;AACAnB;;AAEF;AACA;AACE;AACF;;AAIF;AACF;;;AAGU7B;;;;;AAKN;;AAEF;;;AAGIlE;;AAEF;AACF;;AAC4CqG;AAAS;AACvD;AAEA;AACEc;AAGF;;AACUjD;;;;AAGNkD;AACF;;AAEF;AAEA;AAAqBD;AAAiC;AACpD;;;AAGEC;AACAC;AACF;AACF;AAEA;AACEhB;AAGF;AACE;AACE;;;;;AAMwD;AACxD;AACF;AACF;AAEA;;AAA4BtG;AAAiC;AAC3D;;;AAGEqH;AACApH;AACF;AACF;AAEA;AACEqG;AAGF;AACE;;AAEE;;;AAGA;;AACkCA;AAAS;;;AACMrG;AAAc;AACjE;AACF;AAEA;AAAoBA;AAAuB;AACzC;;;;AAIEA;AACF;AACF;AAEA;;;;;AAKEsH;AAOF;;AAEIf;AACAD;AACF;AACA;AACEP;;AAEEA;;AAEE/F;;AAEF;AACF;AACE+F;;AACyB/F;AAAsB;AACjD;AACF;AACA;AACE+F;;AAEEA;;AAEE/F;;AAEF;AACF;AACE+F;;AACyB/F;AAAsB;AACjD;AACF;AACF;AACF;;ACjMA;;AAyBO;;AAEL;AACF;AAEO;AACL;;AASA;AACE;;;AAUAuH;AACF;;;AAIAC;AACA;;AAEA;AAEA;AACF;AAEO;AACL;AACA;AACE;AACAC;AACF;AACA;AACF;AAEO;;AAELD;AACAA;AACAA;AAEAA;AACA;AACEA;AACF;AACA;AACF;AAEO;;;;AAIH;AACA;AACA;AACEE;AACF;AACA;AACEtF;AACF;AACF;AACA;AACA;AACA;AACA;AACA;AACF;AAEO;AAKL;;;;AAWA;AACE;AACEuF;AACF;AACA;AACA;AACEX;AACF;;;;;AAKA;;AAEA;AACA;AACA;AACEO;AACF;AACF;;;AAIAC;AACA;;AAEA;;;;AAEoCI;;AACtC;AAEO;;AAEL;AACE;AACAJ;;AAEA;;AAEA;AACAA;AACF;AACA;AACF;AAEO;;AAELA;AACAA;AACAA;AAEAA;AACA;AACF;AAEO;;AAELA;AACAA;AACAA;AAEAA;AACA;AACF;AAEO;;AAELA;AACAA;AACAA;AAEAA;AACA;;AAEA;AACAA;AACAA;AACA;AACF;AAEO;;AAELA;AACAA;AACAA;AAEAA;AAEAA;;;;AACiCG;AAAU;AAC3CH;AACAA;AACAA;;;AAGAA;AACA;AACF;;AC3NA;;;AAcA;AACO;AAIL;;AAGA;AACA;;AAQaK;AAAa;AAC1B;;AAMA;AAEA;AAEA;AACE9B;AACA;;AACgCM;AAAS;;AAEzCN;AACA;;;;AAAiE/B;AAAO;;AAExE;AACAyB;;;AAGA;AACA;AACA;;;AAGA;AACA;AACA;;AAEIqC;AACA/B;AACF;AACEA;AACF;AACF;AACA;;AAEIgC;AACAhC;AAGF;AACEA;AACF;AACF;;;;;;AAMEgC;AACF;AACF;AACF;;ACzEA;AAAQC;AAAW;AAEZ;AAIL;AACA;;;AAME;AACF;AACAjC;AAGAkC;AACF;AAEO;AAIL;;;;AAIE;AAA2CC;AAAM;AACnD;AACA;AACF;AAEO;;AAEH;AACF;AACE;AACF;AACF;AAEO;AACL;;AAEF;AAEO;;AAEHC;AACAC;;AAEA;AACF;AACF;;AC7DO;AAGHC;AAAkBC;AAAQ;AAE5B;;;;AAKeA;AAAQ;AAEzB;AAEO;AAGHD;AAAgBC;AAAa;;;AAI/B;AACE;AACA;;AASF;AACA;AACF;;ACvCA;;AAMA;;AAGO;AACLC;AACEjH;AACAkH;AACAC;AACA3G;;AAEF4G;AACEpH;AACAkH;AACA1G;AACF;AACF;AAEO;AACL6G;AACErH;AACAmH;AACAD;AACA1G;;AAEF8G;AACEtH;AACAmH;AACAD;AACA1G;AACF;AACF;AAEO;AACL+G;AACEvH;AACAkH;AACA1G;;AAEFgH;AACExH;AACAkH;AACA1G;AACF;AACF;;AClCA;;AAAuBiH;AAAS;AA+CzB;;;;;;;AAUL;AAAMC;;;AACN;AACA;AACA;AACE;AACA;;AAIA;;AAEA;AACA;;AAEIC;AACF;AACF;AACF;AACA;AACE;AACA;;AAEF;;;;AAMF;AACA;;AAEA;AACA;AAEU;AAGS;AAAc;AAIvB;AAEsC;AAAS;;AACjCC;AAAO;;;AAGf;AACF;;;AAOd;AACA;;AAEA;AACA;AACA;;;AAIM;;;AAGF;AAEF;;AAEEnD;AACF;;AAEA;AACF;;AAEA;AACA;AACA;AACO;;AACc;;;;AAInBkD;AAOF;;;;AAME;;;;;;;AAOEE;;AAEF;AACA;;AAEA;AACA;AACF;AAEO;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACApD;AACF;AAEA;AAIE;AAGIkC;;AAEJ;AACA;AAKA;AAaA;AACF;AACA;AACA;AACA;;AAEA;;AC1OA;;AAWA;AAAQmB;AAAkB;AAE1B;AACEC;AACAvH;AAAsC;AACtCoH;AACAI;AACE;AACA;AACA;AACA;AACA;AACA;AACAC;AACEjI;AACAkH;AACA1G;;AAEF0H;AACElI;AACAkH;AACA1G;AACF;;;AAEgBwH;AAAM;AAC1B;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGLG;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;AAEA;AACA;AAEA;AACElD;AACA;AACF;AAEA;AACF;;AC/DO;AAKL;AACA;AAKA;AACE2D;AACA;AACF;;AAIA;AACE3D;AACA;AACF;;AAGF;;ACxBO;AAML;AACA;AAKA;AACE2D;AACA;AACF;;AAIA;AACE3D;AACA;AACF;;AAGF;;ACjCO;AAKL;AACA;AACA;AACA;AACA;AACA;AACA;;AAEE4D;AACF;;;AAIAC;AACA;;AAIA;AACAA;AAEA;AACF;AAEO;AAEL;AACA;AACAC;AAEA;;AAGA;AACE;AACE;AACA;;AAEF;AACF;;AAGA;;AAGA;;AAIA;AACE7J;AACA;AACE;AACA;;AAEF;AACAA;AACF;AAEA;AACF;;AChDuC;;AAkBvC;;AAeA;AACA;AAeO;;;;;AAKL8J;AAOF;AACE;;AAEE;AAGF;AAEA;;;;;;AAMEA;AACF;AACF;AAEA;;;;;;AAMEA;AAQF;AACE;;AACQC;AAAQ;AAEhBA;AAEA;;;;;AAQA;;AAEA;;;AAIE;;AAGA;;;AAGIhE;;;AAGAA;AACAA;AACF;AACF;AACEA;AACF;AACF;AACE;;;AAKE;;;AAGIA;;AAEAA;AACF;AACF;AACEA;AACF;AACF;;AAEA;AACF;AACF;AAEA;;;;;AAKI;AACAA;AAGA;AACF;AACF;AAEA;AAKE;AACF;;AAEA;;AAEA;AAqCA;;AAEA;AACA;;AAIA;;AAEA;AACA;AACA;AAEA;AACE;AACA;AAAgCwB;AAASsC;AAASG;A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AACF;;AC7DO;;;;;;AAMLD;AAQF;AACE;;AAEE;AAGF;AAEA;;;;;;;AAOEK;AACF;AACF;AACO;;;;;;;AAOLL;AASF;AACE;;AACQtD;AAAQ;AAEhBA;;AAOA;;;AAKE;AACF;AAEA;;AASA;AACA;;AAIA;AACA;AACA;;AAEE;;;;;AAKE;AACAhE;AACAA;AACA;AACF;AAEA;AACEA;AACA4H;AACE;AACE5H;AACAA;AACF;AACEA;AACF;AACAA;AACF;AACF;AACE;AACAA;AACAA;AACAA;AACF;AAEA;AACF;;AAEA;AACA;;AAEAA;;AAGI6H;AACAC;AACAC;AACAC;AACF;AAEFhI;AAGAA;AACF;;ACtIA;AAAQqD;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;AACE;AACA0E;AACE1M;AACAmH;AACAD;AACA1G;;AAEFmM;AACE3M;AACAmH;AACAD;AACA1G;;AAEF+L;AACEvM;AACAkH;AACA1G;;AAGF6G;AACErH;AACAmH;AACAD;AACA1G;;AAGFyC;AACEjD;AACAmH;AACAD;AACA1G;AAEF;;AAEFyG;AACF;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;AAEA;AACA;;AAGA;AACE;AACA;AACA;;AAEAlD;AACJ;AACA;AACA;AACA;AACI;AACF;AAEA;AACEA;AACA;AACF;AAEA;;;;;;;AAOA;AACF;;AChHA;AAEO;;AAEL;AACA;AACA;AACA;AACAmD;AACA;AAA8BD;AAAW;AACvC;AAEIiF;AACF;;;;;AAMA;AAEJ;AACF;;ACHA;AAAQC;AAAI;AAEZ;;AAEA;AAMO;;AAKGpE;AAAQ;AAAMf;;;;AAItB;AACEoF;;AAEF;AAEA;AAEA;AACEC;AACAC;AACEC;AACAC;AACAC;AACF;AACF;AAEA;;;AAGE;AACF;AAEA;AAEA;AAAqDC;AAAe;AAEpE;AAAa;AAAS;;AACpB;;AAEA;AACA;AAEA;;AAGA;AACE3E;AACA;AACF;AAEA;;AAIM;AACA;;AAGJ;AACF;AAEA;AAAkByC;;AAChB;AACA;AACeA;;;;AAI0BmC;AAAuB;;AAEtDlM;AAAoB;;;AAUxB;AACA;;AAAuCmM;AAAgB;AAEvD7E;AAEA;;;AAMY1C;AAAiB;;AAEzB;AACE;AACEwH;AAEJ;AACF;AACF;AACA;AACA;AACF;;;AAGA;AACF;;AAEA;AACF;AACF;AACF;AAEA;AAA4BT;AAAU;AACtCU;AACA;;AAGF;;ACjIO;;AAIGR;AAA2B;AACjCtF;;;AAIF;AACA;;AACMwD;AAAkB;AACxB;;AAEE;AACF;AACA;AAEAzC;AAEA;;;;AAIA;AACE;AACAgF;AACA;;AAEEhF;AACF;AACF;;AAIA;AACF;;ACzCA;AAAQiF;AAAqB;AAWtB;;;AAGHC;;;;AAIF;AAAMjG;;;AACN;AACA;AACA;AACA;AAGA;;;;AAIE;;AAGE;;AAEA;;AAEA;;AAIM;AACAkG;AAIN;AACAA;AAGA;AACA;AAEA;;AAEA;AACA;;;AAOA;AACA;AACA;AACAN;AACA;AACAO;;AAEE;AACF;AACF;AAEF;AACEC;AAA4B;AAA4B;AAC1D;AACA;AACF;;ACpFA;AAAQjB;AAAI;AAUL;;;AAIUkB;AAAc;AAC7B;;AAEE;;;AAGA;AACF;;AAEEJ;;;AAGF;AAAMjG;;;;;AAGJ4F;AACA;AACAO;;;AAGI;;AAEA;AACA;AAEF;AACF;AACF;AACF;;AC5BA;;;AAAwBG;AAAK;AAMtB;;AAKGvF;AAAQ;AAAMf;;;;AAItB;AAAiDuG;AAA0B;;;AAGzE;AACF;AAEA;AACElB;AACAC;AACEC;AACAC;AACAC;AACF;AACF;AAEA;;;AAGE;AACF;AAEA;AACEL;;AAEF;AAEA;AAEA;AAAqDM;AAAe;;AAC5DrH;AAAiB;AAEzB;AAAa;AAAS;;AACpB;;AAGA;AACE0C;AACA;AACF;AAEA;;AAIM;AACA;;AAGJ;AACF;AAEA;AAAkByC;;AAChB;AACA;AACeA;;;;AAI0BmC;AAAuB;;AAEtDlM;AAAoB;;AAE5B;;;AAUA;AACE;AACA;;;AAKI;AACE;AACA;;;AAGA;AACF;AACF;AAEAsH;;AAEA;AACA;AACA;;AACuCA;AAAQ;AACjD;;AAEA;AACF;;AAEA;AACF;AACF;AACF;;AAGF;;ACxHA;;;;;;;;;;;;AAYEyF;AACF;AAEO;AAIP;AACExG;;;;;;AAMA;AACF;AAEA;AACE;AACA;AAA+ByG;;AACjC;AAEA;AAIE;;;AAIM;;AAC6CC;AAAI;;AAGvD;AACF;;AAEA;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAMA;;;;AAMQ;;AAEF;;AAEJ;AAEA;AAEA;;;AAMI;;AAEE;AACF;;AAEE;AACA;;;;AAIA;AACA;AACA;AACA;AACA;AACF;AACA;AACF;;;;;AAKA;;AAEJ;AAkDO;AACLA;AACAC;AACa;;AACqCD;AAAI;;AAEtD;;AAMiCA;AAAI;AACrC;;AAKqChB;;AAErC;AACA;AACA;AACA;AAIA;AACA;AACA;AACE;AACA;;;;AAIIkB;;AAEF;AACF;AACF;AACA;AAMEA;AACF;;AAEEA;;AAEF;AACA;AAEA;;AAGEC;AACF;AACA;AACED;AACF;AACA;AACEE;AACAC;;AAEF;AACA;AACA;AACA;AACE;;;AAGA;AACA;AACA;AACE;;;AAGA;AACF;AACA;AACA;;AAIE;;;AAMA;;;;;AAKE;AACF;AACF;AACA;;AAIAC;AAIF;AACEC;AACAC;AACF;;AAEA;;;;;;;;;;AAaErB;;;AAGAsB;AACEC;;AAEFC;;AAEJ;AAOO;;AAKHC;;AAEAC;AACF;AACEvH;;;AAGF;;;AAGIjD;AAMF;AACF;AACA;;AAIE;AACF;AACA;AACEA;AAMA;AACF;AACA;;;AAGE;AACF;;;AAGE;AACF;AACA;;AAEE;AACF;AACA;;AAOE;AACF;AACA;AAIEA;AAMF;AACA;AACF;;AC/YA;;AAAauJ;AAAK;AAElB;AAEO;AACL;;AACQvF;AAAQ;;AAIhB;AAEA;AACEuG;AACAvK;AACF;;;AAGE;AACF;;AAGE;AAAU;AACR;AACA;AACF;AACA;AAAW;AACT;AACA;AACF;AACF;AACAgE;AACF;;AC7BA;AAAQX;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;;;AAGAf;AACF;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;AAEA;AACElD;AACA;AACF;;AAGF;;AC/CO;;AAML;AAIA;AAKA;AACE;AACF;AAEA;AACE;AACF;AAEA;;;;;;AAUF;;ACrBA;AAAQoI;AAAI;AAEZ;;AAEI;;AAEA;AACF;AACA;AACF;AAEA;;;;AAUY7M;AAAK;AACb;AACE;AACA;;AAEA;AACEkP;AACEvG;AACAwG;AACF;AACF;AACF;AACA;AACF;AACA;AACA;AACE;AAGIC;AAAoB;AAExB;AACE3K;AACF;;AAEA;AACF;AACF;AAEO;;;AACU4K;AAA2B;;;;AAKxCC;AAOF;;AAGE7K;AACA;AACF;;;AAGF;;AAEA;AACA;AACE;AACEA;AACF;AACA;AACE;AACA8K;AACAC;AACAC;;;AAGFhL;AACAiL;AAGAjL;AACA;;AAEIA;AACF;;AAIAkL;AACF;AACElL;AACF;;;AAKAA;;AAEEA;AACwE2K;;AAE1E;AACE3K;AACuF2K;;AAEzF;;;AAOA;AACE3K;AACF;AACF;;AC5HO;;;;;;AAML+C;AAQF;AACE;;AACQiB;AAAQ;AAEhBA;;AAYAA;AAEA;;AAEIvH;;;AAIAoO;AACF;;AAGE;;AAEF;AACF;AACF;;ACnDA;AAAQxH;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;AACE;AACA;;;AAGFf;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;;;;;AAE6BH;;;;AAI3B;AACA;AACA;;AAEA/C;AACJ;AACA;AACI;AACF;AAEA;AACA;AAEA;AAGA;AACEA;AACA;AACF;AAEA;AACEsD;AACA6H;;;;;AAKF;AACF;;ACrFO;AAMLC;AACAA;AACAA;AACAA;AACF;;ACIA;AAAQC;AAAwB;AAEzB;AAILC;AACAC;AACA;AAEIC;;AAKJ;;AACQxH;AAAQ;AAEhBA;AAEA;;;AAGE;AACA;;AAEA;;AAEAA;AACF;AACEA;AACA;AACF;;;;AAOE;;AAGF;AACE;AAEIwH;AAEArF;AACE1J;AACAgP;AACA1P;;AAEJ;AAEEiI;AACF;AAEF;;AAEA;AACF;AACE;;AAGIvB;AACF;AAEEuB;AACF;AAEF;AACE;AACA;AACE0H;AACF;AACF;AACF;;AAIA;;;;AAIA;AACE1L;AACF;AACF;;AC3FA;AAAQqD;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;AACE;AACA+H;AACE/P;AACAQ;;AAEFwP;AACEhQ;AACAQ;AACF;;AAEFyG;AACF;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;AAEA;AACA;AAEA;AACElD;AACA;AACF;AAEA;AACE;AAGF;AAEA;AACF;;AC3EO;AACLoL;AACAA;AACAA;AACAA;AACF;;ACHO;;AAEHO;AACA3L;AACF;AACEA;AACF;AACF;;ACFA;AAAQqD;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;;;AAGAf;AACF;AACA;;AAEA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;AAEA;AACElD;AACA;AACF;AAEA4L;AACF;;AC3CO;AAOL;;AACQ5H;AAAQ;AAEhB;AACA;AAEA;AACEhE;AACAA;AACAA;;AAEF;AACEA;AACAA;AACAA;;AAEF;;AAGE;AACA;AACA;AACA;AACA;;AAEA;;;AAIA;;AAEA;;;;AAQA;;AAIA;AACEA;AACAA;;AAEF;;;AAGEA;AACA;;AAEEA;AACAA;;AAEF;AACA;AACF;AACAA;AACAA;;AAIIA;AACA;AACF;;AAGF;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;;;;;AAQA;AACEA;AACAA;;AAEF;AACF;AACF;;AC5GA;AAAQqD;AAAkB;AAE1B;AACEC;AACAvH;AAEAoH;AACAI;AACE;AACAsI;AACEtQ;AACAQ;;AAEF4N;AACEpO;AAC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AEA;;AAKA;AAEA;AAIE;AACF;AAEA;;AAKUnI;AAAiB;AACzB;AACA;AACE;;AAEE;AACA;;AAEI;AACE;AACA0L;;;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;AACA;;;;;AAKE;AACEM;;AAEF;AACF;AACEA;AACAC;AACF;;AAEE;AACED;AACAC;AACF;AACF;AACA;;AAEA;AACA;AACED;AACAC;AACF;AACA;;;AAGEA;AACF;AACAC;AAEqB;;;AAKvB;AAEA;AAIEC;AACF;AAEA;AAIEA;AACF;AAEA;AAIEA;AACF;AAEO;;AC3HP;;;AAAmBhE;AAAa;AAEhC;AAEA;AAEO;;;;;;;AAWHiE;AACF;;;;;;AAMEC;AACEC;AACAC;AACAC;AACAC;AACAC;AACF;AACF;AAAM/K;;;;AACA6F;AAAyB;;AAE7BuE;AAAmD1E;AAAe;AACpE;;AACQrH;AAAiB;;;AAIzB;AACA;AACA;AACA;AAGE;AACA2M;;AAIAjO;AAMF;;AAGA;AACEkO;AACF;;AAKA;AAEAlK;AAIA;AACA;AAEA;AACA;AAAuD;AAAQ;;AAI/D;AACA;AAAyC;AAAQ;;AACvCvH;AAAsBsD;AAAsBrD;AAAQ;AAC5D;AACA;AACA;AACA;AAAa;;AACX;AAGA;AACEyR;AACF;AACA;AAGA;;AAEE;AACA;;AAOEC;AACAC;AACAV;AACA;AACEA;AACF;AACF;AACAQ;AACF;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AAKA;AAE0DF;AAAY;AACtE;AACA;;AAAyD1S;AAAK;AAC5D;;;AAME;AACA;AACA;;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA+S;AACF;AACE;AAGA;;;AASI;AACEA;AACF;AACF;AACF;AACEA;AACF;AACF;;AAEEtB;AACA;AACAW;AACF;AACF;AACF;AACF;AACF;AAEA;AACE;AACEY;AACA5E;AACA9N;AACF;AACA;;AAEE;;;;AAO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AAEE;AAGF;AAEAA;AAEA;AAKAA;AAEA;;;AAKE;AACF;;AAEA;AACA;AACA;AACA;;AAEI;AACF;AACE+M;AAGA;AACF;AACF;AAEA;AACF;;AChDO;;;AASL;AACE;;;;;;AAQEC;;AAEJ;AAEA;AAQA;AAEF;;AAEA;;AAEA;;AAEA;;AAEA;AACA;AAEE;;;AAGIhR;;;AAGAA;AACAA;AACF;AACF;AACEA;AACF;AACF;;AC9CA;AAAQqD;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;AACE;;;AAGFf;AACF;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;AAEA;AAEA;AACE;AACA;AACA;;;AAIJ;;AAEA;;AAEA;AACA;AAEI;AACF;AAEA;AACElD;AACA;AACF;AAEA;AACE;AACF;AACE;AACF;AACF;;AC/EA;AAEO;;AAEL;AAA8BkD;AAAW;AACvC;AAEIgM;AACAS;AACAC;AACAqB;AACAhC;AACF;AAEErC;AACE;AACAsE;;AAEE/N;AACAkE;AACF;;;;;;AAMJ;AAEJ;AACF;;ACtBuC;;AAahC;;;;;;AAML3B;AAQF;AACE;;AAEE;AAGF;AAEA;;;;;;;AAOEA;AACF;AACF;AAEA;;;;;;;AAOEA;AASF;AACE;;AACQ1B;AAAQ;AAEhB;AAQAA;;AAGA;AAKAA;;AAGEhE;AACA;AACF;AAEA;AAEA;AACEmR;AACAC;AACAC;AACAC;AACAC;AACArN;AACAkB;AACAoM;AAAe;AACfC;AACElW;AACA6V;;AAEFM;AACA;AACA;AACAC;AACAC;AACF;;AAEA;AACA;AACEC;AACAL;AACApM;AACAqM;AACElW;AACA6V;;AAEFlN;AACA5C;AAEAuD;AACEuM;AACF;AACF;;AAEA;;AAGAnN;AACAA;AAEA;AACA;;AAGE5B;AAQAlE;AACF;;AAEA;AACA2T;AACE;AACA;AACE;AACA;AACE;;;AAWF;AACF;AACF;;AAIA7N;AACAA;AACE;;AAEA;AACA8M;AACF;AACF;AAEA;AACE;;;AAGE;AAEA;;AAEA;;AASF;AACF;AAEA;AACE;AACA;;AAGEgB;AACAlN;AACF;AAEA;AACA;AACE;AACA;;;AAGA;AACF;;AAEE;AACF;AACE;AACF;AACF;;ACrOA;AAAQxB;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;AACE;AACA;AACAmC;AACEnK;AACAmH;AACAD;AACA1G;;AAEFuK;AACE/K;AACAmH;AACAD;AACA1G;;AAEF0T;AACElU;AACAmH;AACAD;AACA1G;;AAEFiW;AACEzW;AACAmH;AACAD;AACA1G;;AAEF8K;AACEtL;AACAmH;AACAD;AACA1G;AACF;;AAEFyG;AACF;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;;;;;AAMnCA;AACF;AAEA;AACElD;AACA;AACF;AAEA;;;;AAIE0P;;;AAOF;AACF;;ACtHO;;AAKD;AACE;AACF;AACA;AACA;;AAGN;AACA;AACA;AAEI;AAEJ;;ACpBO;;;;AASH1P;AAGA;AACF;AACA;AACF;;ACTO;AACL;;;AACoBiS;AAAU;;;AAM5BC;;AAEJ;;AAEA;AACE;AACF;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AAEE;;;AAGA;AACA;AACF;AAEA;AACEC;AACE;AACE;;;AACoBF;AAAU;;AAE5B;;AAEA;AACA;;AAEA;;AAEA;AACF;;;AAGAG;AAIF;;AAEA;AACF;AACF;;AC/DO;AACL;AACE;AACEpS;AACAA;AACA;AACF;;AAOA;;AAGE;AACEA;AACA;AACF;AACA;AACA;AACAA;AAGF;AACF;AACF;;ACbA;AAAQqD;AAAkB;AAE1B;AACEC;AACAvH;AACAoH;AACAI;AACE;AACA8O;AACE9W;AACAkH;AACA1G;;AAEFuW;AACE/W;AACAkH;AACA1G;AACF;;AAEFyG;AACF;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEO;;;AAGLkB;AACF;AAEA;AAGIR;AAAmC;AAErC;AACA;AACEqP;AACA;AACF;;;;;AAMErP;AACF;;AAEQmP;;;AAEN;AACA;AACA;;;AAIJ;;AAEA;AACA;AAEI;AACF;AAEA;AACErS;AACA;AACF;;AAEA;;;AACoBiS;AAAU;AAC9B;;;AAGE;;;AAGA;AACF;AACE;;AAEA;AACA;;AAEA;AACF;;AAEEjS;AACF;AACF;;AC7EA;AAAQwS;AAAoB;;AAE5B;AACA;AACE;AACE/V;AACA;AACAC;;AAEF;;AAGE;AAEI+V;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACA;AACA;AACAC;AACAC;AACAC;AACA;AACAC;AACAC;AACAC;AACA;AACA;AACAC;AACF;AAEE9G;AACE+G;AACE5X;;AAEF;;;AAGFU;AACA8P;AAAcjQ;AAAoC;AACpD;;;AAIF;AACA;;;AAGEsX;;AAEF;AACEA;;;AAGF;AACEA;AACAC;AACAC;AACF;AACEF;AACF;;AAIA;AACE5T;AACF;;AAEF;AACF","debugId":"9c105835-2edc-4093-b8ea-33da621e35d6"}
|