socket 1.1.122 → 1.1.124

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
+ ## [1.1.124](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.124) - 2026-06-19
8
+
9
+ ### Added
10
+ - `socket scan create --reach` accepts a new `--reach-retain-facts-file` flag. By default the CLI deletes the `.socket.facts.json` reachability report from the scan directory after a successful scan; pass this flag to keep it (e.g. for inspection or debugging). **Important:** you must delete the retained `.socket.facts.json` before running a fresh tier 1 reachability scan — a stale file left in place is picked up as a pre-generated input and silently overrides fresh analysis, so the new scan results will not be reliable.
11
+
12
+ ### Changed
13
+ - Updated the Coana CLI to v `15.5.4`.
14
+
15
+ ## [1.1.123](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.123) - 2026-06-18
16
+
17
+ ### Added
18
+ - `socket scan create --reach` and `socket scan reach` now accept unit suffixes on `--reach-analysis-timeout` (`s`, `m`, `h` — e.g. `90s`, `10m`, `1h`) and `--reach-analysis-memory-limit` (`MB`, `GB` — e.g. `512MB`, `8GB`). Plain numbers keep working as before.
19
+
20
+ ### Changed
21
+ - Updated the Coana CLI to v `15.5.0`.
22
+
7
23
  ## [1.1.122](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.122) - 2026-06-17
8
24
 
9
25
  ### Changed
package/bin/cli.js CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  void (async () => {
5
5
  const Module = require('node:module')
6
+ const os = require('node:os')
6
7
  const path = require('node:path')
7
8
  const rootPath = path.join(__dirname, '..')
8
9
  Module.enableCompileCache?.(path.join(rootPath, '.cache'))
@@ -38,10 +39,41 @@ void (async () => {
38
39
  },
39
40
  )
40
41
 
42
+ // The child shares our process group and handles the signal itself; wait briefly for it
43
+ // to exit (so its final output isn't printed after the prompt returns) and mirror its
44
+ // exit below. SIGKILL and leave if it outlasts the grace, or on a second signal.
45
+ const SHUTDOWN_GRACE_MS = 3_000
46
+ const hardAbort = signalName => {
47
+ const child = spawnPromise.process
48
+ if (child.exitCode === null && child.signalCode === null) {
49
+ child.kill('SIGKILL')
50
+ }
51
+ // eslint-disable-next-line n/no-process-exit
52
+ process.exit(signalName === 'SIGTERM' ? 143 : 130)
53
+ }
54
+ let sawSignal = false
55
+ const onSignal = signalName => {
56
+ if (sawSignal) {
57
+ hardAbort(signalName)
58
+ return
59
+ }
60
+ sawSignal = true
61
+ setTimeout(() => hardAbort(signalName), SHUTDOWN_GRACE_MS).unref?.()
62
+ }
63
+ const onSigint = () => onSignal('SIGINT')
64
+ const onSigterm = () => onSignal('SIGTERM')
65
+ process.on('SIGINT', onSigint)
66
+ process.on('SIGTERM', onSigterm)
67
+
41
68
  // See https://nodejs.org/api/child_process.html#event-exit.
42
69
  spawnPromise.process.on('exit', (code, signalName) => {
43
70
  if (signalName) {
44
- process.kill(process.pid, signalName)
71
+ // Mirror a signal death as the conventional 128 + signum exit code. Exit explicitly
72
+ // rather than re-raising the signal: with our handlers installed the re-raise would
73
+ // race `await spawnPromise` resolving and could leave the default exitCode of 1.
74
+ const signum = os.constants.signals[signalName] ?? 0
75
+ // eslint-disable-next-line n/no-process-exit
76
+ process.exit(128 + signum)
45
77
  } else if (typeof code === 'number') {
46
78
  // eslint-disable-next-line n/no-process-exit
47
79
  process.exit(code)
package/dist/cli.js CHANGED
@@ -1707,11 +1707,45 @@ async function outputCreateNewScan(result, options) {
1707
1707
  }
1708
1708
  }
1709
1709
 
1710
+ // Helpers for the reachability unit values. Coana (@coana-tech/cli) is the sole
1711
+ // validator/parser of these values; the Socket CLI forwards the raw string
1712
+ // through verbatim. These helpers do NOT validate grammar (that would duplicate
1713
+ // Coana's and drift): they only handle the meow-default sentinel and detect
1714
+ // whether a value differs from the default, neither of which Coana models.
1715
+
1716
+ // A zero-magnitude or empty value (e.g. "", "0", "0s", "0gb") means "use the
1717
+ // default": the flag is omitted when forwarding and Coana applies its own
1718
+ // default. This preserves the historical sentinel where a numeric 0 dropped the
1719
+ // flag, and avoids Coana's undefined zero (0ms / 0MB) path.
1720
+ function isOmittedReachValue(value) {
1721
+ const match = /^\d+/.exec(value);
1722
+ return !match || Number(match[0]) === 0;
1723
+ }
1724
+
1725
+ // Resolve a memory-limit value to its magnitude in MB (the unit Coana uses), or
1726
+ // null when the value is omitted/zero (Coana then applies its own default).
1727
+ // Used only to compare a value against the default regardless of how the unit
1728
+ // is written: 8192, 8192MB and 8GB all resolve to 8192. This is default
1729
+ // detection, not validation, so an unrecognized value resolves to null and is
1730
+ // simply treated as "not a non-default value".
1731
+ function reachMemoryLimitToMb(value) {
1732
+ if (isOmittedReachValue(value)) {
1733
+ return null;
1734
+ }
1735
+ const match = /^(\d+)(mb|gb)?$/i.exec(value);
1736
+ if (!match) {
1737
+ return null;
1738
+ }
1739
+ const amount = Number(match[1]);
1740
+ return match[2]?.toLowerCase() === 'gb' ? amount * 1024 : amount;
1741
+ }
1742
+
1710
1743
  async function performReachabilityAnalysis(options) {
1711
1744
  const {
1712
1745
  branchName,
1713
1746
  cwd = process.cwd(),
1714
1747
  orgSlug,
1748
+ outputKind = 'text',
1715
1749
  outputPath,
1716
1750
  packagePaths,
1717
1751
  reachabilityOptions,
@@ -1817,7 +1851,7 @@ async function performReachabilityAnalysis(options) {
1817
1851
  }
1818
1852
 
1819
1853
  // Build Coana arguments.
1820
- const coanaArgs = ['run', analysisTarget, '--output-dir', path.dirname(outputFilePath), '--socket-mode', outputFilePath, '--disable-report-submission', ...(reachabilityOptions.reachAnalysisTimeout ? ['--analysis-timeout', `${reachabilityOptions.reachAnalysisTimeout}`] : []), ...(reachabilityOptions.reachAnalysisMemoryLimit ? ['--memory-limit', `${reachabilityOptions.reachAnalysisMemoryLimit}`] : []), ...(reachabilityOptions.reachConcurrency ? ['--concurrency', `${reachabilityOptions.reachConcurrency}`] : []), ...(reachabilityOptions.reachContinueOnAnalysisErrors ? ['--reach-continue-on-analysis-errors'] : []), ...(reachabilityOptions.reachContinueOnInstallErrors ? ['--reach-continue-on-install-errors'] : []), ...(reachabilityOptions.reachContinueOnMissingLockFiles ? ['--reach-continue-on-missing-lock-files'] : []), ...(reachabilityOptions.reachContinueOnNoSourceFiles ? ['--reach-continue-on-no-source-files'] : []), ...(reachabilityOptions.reachDebug ? ['--debug'] : []), ...(reachabilityOptions.reachDetailedAnalysisLogFile ? ['--print-analysis-log-file'] : []), ...(reachabilityOptions.reachDisableAnalytics ? ['--disable-analytics-sharing'] : []), ...(reachabilityOptions.reachDisableExternalToolChecks ? ['--disable-external-tool-checks'] : []), ...(reachabilityOptions.reachEnableAnalysisSplitting ? [] : ['--disable-analysis-splitting']), ...(tarHash ? ['--run-without-docker', '--manifests-tar-hash', tarHash] : []),
1854
+ const coanaArgs = ['run', analysisTarget, '--output-dir', path.dirname(outputFilePath), '--socket-mode', outputFilePath, '--disable-report-submission', ...(isOmittedReachValue(reachabilityOptions.reachAnalysisTimeout) ? [] : ['--analysis-timeout', reachabilityOptions.reachAnalysisTimeout]), ...(isOmittedReachValue(reachabilityOptions.reachAnalysisMemoryLimit) ? [] : ['--memory-limit', reachabilityOptions.reachAnalysisMemoryLimit]), ...(reachabilityOptions.reachConcurrency ? ['--concurrency', `${reachabilityOptions.reachConcurrency}`] : []), ...(reachabilityOptions.reachContinueOnAnalysisErrors ? ['--reach-continue-on-analysis-errors'] : []), ...(reachabilityOptions.reachContinueOnInstallErrors ? ['--reach-continue-on-install-errors'] : []), ...(reachabilityOptions.reachContinueOnMissingLockFiles ? ['--reach-continue-on-missing-lock-files'] : []), ...(reachabilityOptions.reachContinueOnNoSourceFiles ? ['--reach-continue-on-no-source-files'] : []), ...(reachabilityOptions.reachDebug ? ['--debug'] : []), ...(reachabilityOptions.reachDetailedAnalysisLogFile ? ['--print-analysis-log-file'] : []), ...(reachabilityOptions.reachDisableAnalytics ? ['--disable-analytics-sharing'] : []), ...(reachabilityOptions.reachDisableExternalToolChecks ? ['--disable-external-tool-checks'] : []), ...(reachabilityOptions.reachEnableAnalysisSplitting ? [] : ['--disable-analysis-splitting']), ...(tarHash ? ['--run-without-docker', '--manifests-tar-hash', tarHash] : []),
1821
1855
  // Empty reachEcosystems implies scanning all ecosystems.
1822
1856
  ...(reachabilityOptions.reachEcosystems.length ? ['--purl-types', ...reachabilityOptions.reachEcosystems] : []), ...(reachabilityOptions.reachExcludePaths.length ? ['--exclude-dirs', ...reachabilityOptions.reachExcludePaths] : []), ...(reachabilityOptions.reachLazyMode ? ['--lazy-mode'] : []), ...(reachabilityOptions.reachSkipCache ? ['--skip-cache-usage'] : []), ...(reachabilityOptions.reachUseOnlyPregeneratedSboms ? ['--use-only-pregenerated-sboms'] : []),
1823
1857
  // Hand the per-ecosystem build-tool config (mapped from socket.json) to
@@ -1834,6 +1868,13 @@ async function performReachabilityAnalysis(options) {
1834
1868
  if (branchName && branchName !== constants.default.SOCKET_DEFAULT_BRANCH) {
1835
1869
  coanaEnv['SOCKET_BRANCH_NAME'] = branchName;
1836
1870
  }
1871
+
1872
+ // In machine-readable modes (--json/--markdown) the final payload is written
1873
+ // to stdout by the output layer. Coana streams progress/logs over stdout
1874
+ // under `inherit`, which would corrupt that payload, so redirect the child's
1875
+ // stdout to our stderr (fd 2). Progress stays visible for humans and
1876
+ // `2>/dev/null` isolates the JSON/markdown. stdin and stderr stay inherited.
1877
+ const coanaStdio = outputKind === 'text' ? 'inherit' : ['inherit', 2, 'inherit'];
1837
1878
  try {
1838
1879
  // Run Coana with the manifests tar hash.
1839
1880
  const coanaResult = await utils.spawnCoanaDlx(coanaArgs, orgSlug, {
@@ -1841,7 +1882,7 @@ async function performReachabilityAnalysis(options) {
1841
1882
  cwd,
1842
1883
  env: coanaEnv,
1843
1884
  spinner,
1844
- stdio: 'inherit'
1885
+ stdio: coanaStdio
1845
1886
  });
1846
1887
  if (wasSpinning) {
1847
1888
  spinner.start();
@@ -5091,6 +5132,7 @@ async function handleCreateNewScan({
5091
5132
  branchName,
5092
5133
  cwd,
5093
5134
  orgSlug,
5135
+ outputKind,
5094
5136
  packagePaths,
5095
5137
  reachabilityOptions: mergedReachabilityOptions,
5096
5138
  repoName,
@@ -5163,8 +5205,11 @@ async function handleCreateNewScan({
5163
5205
  // (e.g. from `socket manifest gradle --facts`) are NOT touched here —
5164
5206
  // those are user-owned input that the user can clean up themselves; in
5165
5207
  // the --reach path coana overwrites that file with its enriched output
5166
- // anyway, so it's the same path that gets removed.
5167
- if (fullScanCResult.ok && scanId && reachabilityReport) {
5208
+ // anyway, so it's the same path that gets removed. `--reach-retain-facts-file`
5209
+ // opts out of this cleanup so the report can be inspected; the user is then
5210
+ // responsible for deleting it before the next tier 1 scan (a stale file is
5211
+ // picked up as pre-generated input and would make those results unreliable).
5212
+ if (fullScanCResult.ok && scanId && reachabilityReport && !reach.reachRetainFactsFile) {
5168
5213
  try {
5169
5214
  await fs.unlink(path.resolve(cwd, reachabilityReport));
5170
5215
  require$$9.debugFn('notice', `[socket-facts] removed coana output after successful scan: ${reachabilityReport}`);
@@ -5247,8 +5292,8 @@ async function handleCi(autoManifest) {
5247
5292
  pullRequest: 0,
5248
5293
  reach: {
5249
5294
  excludePaths: [],
5250
- reachAnalysisMemoryLimit: 0,
5251
- reachAnalysisTimeout: 0,
5295
+ reachAnalysisMemoryLimit: '',
5296
+ reachAnalysisTimeout: '',
5252
5297
  reachConcurrency: 1,
5253
5298
  reachContinueOnAnalysisErrors: false,
5254
5299
  reachContinueOnInstallErrors: false,
@@ -5262,6 +5307,7 @@ async function handleCi(autoManifest) {
5262
5307
  reachEnableAnalysisSplitting: false,
5263
5308
  reachExcludePaths: [],
5264
5309
  reachLazyMode: false,
5310
+ reachRetainFactsFile: false,
5265
5311
  reachSkipCache: false,
5266
5312
  reachUseOnlyPregeneratedSboms: false,
5267
5313
  reachVersion: undefined,
@@ -15605,14 +15651,14 @@ const reachabilityFlags = {
15605
15651
  description: `Override the version of @coana-tech/cli used for reachability analysis. Default: ${constants.default.ENV.INLINED_SOCKET_CLI_COANA_TECH_CLI_VERSION}.`
15606
15652
  },
15607
15653
  reachAnalysisMemoryLimit: {
15608
- type: 'number',
15609
- default: 8192,
15610
- description: 'The maximum memory in MB to use for the reachability analysis. The default is 8192MB.'
15654
+ type: 'string',
15655
+ default: '8192',
15656
+ description: 'The maximum memory for the reachability analysis as a whole number optionally followed by MB or GB (e.g. 512MB, 8GB). The default is 8GB.'
15611
15657
  },
15612
15658
  reachAnalysisTimeout: {
15613
- type: 'number',
15614
- default: 0,
15615
- description: 'Set timeout for the reachability analysis. Split analysis runs may cause the total scan time to exceed this timeout significantly.'
15659
+ type: 'string',
15660
+ default: '',
15661
+ description: 'Set the timeout for the reachability analysis as a whole number optionally followed by s, m or h (e.g. 90s, 10m, 1h). Defaults to 10m. Split analysis runs may cause the total scan time to exceed this timeout significantly.'
15616
15662
  },
15617
15663
  reachConcurrency: {
15618
15664
  type: 'number',
@@ -15687,6 +15733,11 @@ const reachabilityFlags = {
15687
15733
  description: 'Enable lazy mode for reachability analysis.',
15688
15734
  hidden: true
15689
15735
  },
15736
+ reachRetainFactsFile: {
15737
+ type: 'boolean',
15738
+ default: false,
15739
+ description: 'Keep the `.socket.facts.json` reachability report that the analysis writes to the scan directory instead of deleting it after a successful scan. IMPORTANT: you must delete this file before running a fresh tier 1 reachability scan. A stale `.socket.facts.json` left in place is picked up as a pre-generated input and silently overrides fresh analysis, so the new scan results will not be reliable.'
15740
+ },
15690
15741
  reachSkipCache: {
15691
15742
  type: 'boolean',
15692
15743
  default: false,
@@ -15962,6 +16013,7 @@ async function run$d(argv, importMeta, {
15962
16013
  reachDisableExternalToolChecks,
15963
16014
  reachEnableAnalysisSplitting,
15964
16015
  reachLazyMode,
16016
+ reachRetainFactsFile,
15965
16017
  reachSkipCache,
15966
16018
  reachUseOnlyPregeneratedSboms,
15967
16019
  reachVersion,
@@ -16110,8 +16162,14 @@ async function run$d(argv, importMeta, {
16110
16162
  // Validation helpers for better readability.
16111
16163
  const hasReachEcosystems = reachEcosystems.length > 0;
16112
16164
  const hasReachExcludePaths = reachExcludePaths.length > 0;
16113
- const isUsingNonDefaultMemoryLimit = reachAnalysisMemoryLimit !== reachabilityFlags['reachAnalysisMemoryLimit']?.default;
16114
- const isUsingNonDefaultTimeout = reachAnalysisTimeout !== reachabilityFlags['reachAnalysisTimeout']?.default;
16165
+
16166
+ // Compare by resolved magnitude, not string identity: 8192, 8192MB and 8GB
16167
+ // all mean the default, and an omitted/zero timeout means "use the default".
16168
+ // A naive string compare would flag those equivalents as non-default and
16169
+ // wrongly require --reach.
16170
+ const memoryLimitMb = reachMemoryLimitToMb(reachAnalysisMemoryLimit);
16171
+ const isUsingNonDefaultMemoryLimit = memoryLimitMb !== null && memoryLimitMb !== reachMemoryLimitToMb(String(reachabilityFlags['reachAnalysisMemoryLimit']?.default ?? ''));
16172
+ const isUsingNonDefaultTimeout = !isOmittedReachValue(reachAnalysisTimeout);
16115
16173
  const isUsingNonDefaultConcurrency = reachConcurrency !== reachabilityFlags['reachConcurrency']?.default;
16116
16174
  const isUsingNonDefaultAnalytics = reachDisableAnalytics !== reachabilityFlags['reachDisableAnalytics']?.default;
16117
16175
  const isUsingNonDefaultVersion = reachVersion !== reachabilityFlags['reachVersion']?.default;
@@ -16208,8 +16266,8 @@ async function run$d(argv, importMeta, {
16208
16266
  autoManifest: Boolean(autoManifest)
16209
16267
  }) : undefined,
16210
16268
  excludePaths,
16211
- reachAnalysisMemoryLimit: Number(reachAnalysisMemoryLimit),
16212
- reachAnalysisTimeout: Number(reachAnalysisTimeout),
16269
+ reachAnalysisMemoryLimit,
16270
+ reachAnalysisTimeout,
16213
16271
  reachConcurrency: Number(reachConcurrency),
16214
16272
  reachContinueOnAnalysisErrors: Boolean(reachContinueOnAnalysisErrors),
16215
16273
  reachContinueOnInstallErrors: Boolean(reachContinueOnInstallErrors),
@@ -16223,6 +16281,7 @@ async function run$d(argv, importMeta, {
16223
16281
  reachEnableAnalysisSplitting: Boolean(reachEnableAnalysisSplitting),
16224
16282
  reachExcludePaths,
16225
16283
  reachLazyMode: Boolean(reachLazyMode),
16284
+ reachRetainFactsFile: Boolean(reachRetainFactsFile),
16226
16285
  reachSkipCache: Boolean(reachSkipCache),
16227
16286
  reachUseOnlyPregeneratedSboms: Boolean(reachUseOnlyPregeneratedSboms),
16228
16287
  reachVersion,
@@ -16867,8 +16926,8 @@ async function scanOneRepo(repoSlug, {
16867
16926
  pullRequest: 0,
16868
16927
  reach: {
16869
16928
  excludePaths: [],
16870
- reachAnalysisMemoryLimit: 0,
16871
- reachAnalysisTimeout: 0,
16929
+ reachAnalysisMemoryLimit: '',
16930
+ reachAnalysisTimeout: '',
16872
16931
  reachConcurrency: 1,
16873
16932
  reachContinueOnAnalysisErrors: false,
16874
16933
  reachContinueOnInstallErrors: false,
@@ -16882,6 +16941,7 @@ async function scanOneRepo(repoSlug, {
16882
16941
  reachEnableAnalysisSplitting: false,
16883
16942
  reachExcludePaths: [],
16884
16943
  reachLazyMode: false,
16944
+ reachRetainFactsFile: false,
16885
16945
  reachSkipCache: false,
16886
16946
  reachUseOnlyPregeneratedSboms: false,
16887
16947
  reachVersion: undefined,
@@ -18100,6 +18160,7 @@ async function handleScanReach({
18100
18160
  const result = await performReachabilityAnalysis({
18101
18161
  cwd,
18102
18162
  orgSlug,
18163
+ outputKind,
18103
18164
  outputPath,
18104
18165
  packagePaths,
18105
18166
  reachabilityOptions: mergedReachabilityOptions,
@@ -18228,6 +18289,7 @@ async function run$7(argv, importMeta, {
18228
18289
  reachDisableExternalToolChecks,
18229
18290
  reachEnableAnalysisSplitting,
18230
18291
  reachLazyMode,
18292
+ reachRetainFactsFile,
18231
18293
  reachSkipCache,
18232
18294
  reachUseOnlyPregeneratedSboms,
18233
18295
  reachVersion
@@ -18323,8 +18385,8 @@ async function run$7(argv, importMeta, {
18323
18385
  outputPath: outputPath || '',
18324
18386
  reachabilityOptions: {
18325
18387
  excludePaths,
18326
- reachAnalysisMemoryLimit: Number(reachAnalysisMemoryLimit),
18327
- reachAnalysisTimeout: Number(reachAnalysisTimeout),
18388
+ reachAnalysisMemoryLimit,
18389
+ reachAnalysisTimeout,
18328
18390
  reachConcurrency: Number(reachConcurrency),
18329
18391
  reachContinueOnAnalysisErrors: Boolean(reachContinueOnAnalysisErrors),
18330
18392
  reachContinueOnInstallErrors: Boolean(reachContinueOnInstallErrors),
@@ -18338,6 +18400,7 @@ async function run$7(argv, importMeta, {
18338
18400
  reachEnableAnalysisSplitting: Boolean(reachEnableAnalysisSplitting),
18339
18401
  reachExcludePaths,
18340
18402
  reachLazyMode: Boolean(reachLazyMode),
18403
+ reachRetainFactsFile: Boolean(reachRetainFactsFile),
18341
18404
  reachSkipCache: Boolean(reachSkipCache),
18342
18405
  reachUseOnlyPregeneratedSboms: Boolean(reachUseOnlyPregeneratedSboms),
18343
18406
  reachVersion
@@ -20266,5 +20329,5 @@ process.on('unhandledRejection', async (reason, promise) => {
20266
20329
  // eslint-disable-next-line n/no-process-exit
20267
20330
  process.exit(1);
20268
20331
  });
20269
- //# debugId=85257911-3f47-4452-8e33-51ee5c9c6b59
20332
+ //# debugId=3456501d-db35-49f6-b25b-d2bd0fbae11f
20270
20333
  //# sourceMappingURL=cli.js.map