Skip to content

Commit 5675a51

Browse files
authored
feat(scan): unit suffixes for reachability timeout/memory limits (1.1.123, Coana 15.5.0) (#1369)
* feat(scan): unit suffixes for reachability timeout/memory limits (1.1.123, Coana 15.5.0) --reach-analysis-timeout and --reach-analysis-memory-limit now accept unit suffixes (s/m/h for duration, MB/GB for memory, case-insensitive). Coana owns the canonical parsing, so the CLI forwards the raw string verbatim instead of coercing to a number. A thin local validator gives fast errors before the Coana binary is spawned. Empty or zero-magnitude values are omitted when forwarding so Coana applies its own defaults, preserving the prior numeric-0 sentinel. Bare numbers keep working but are no longer documented. Bumps the bundled Coana CLI to 15.5.0, whose parser handles these units. * fix(scan): treat default-equivalent reach unit values as default in --reach guard The "reachability flags require --reach" guard compared the raw flag strings to the default string, so unit-equivalent inputs were wrongly flagged as non-default and rejected without --reach: 8GB / 8192MB (= the 8192MB default) and the zero/omit timeout sentinel 0 / 0s. The latter was a regression from the number→string change (numeric 0 used to equal the numeric default). Compare by resolved magnitude instead: reachMemoryLimitToMb normalizes 8192/8192MB/8GB to 8192, and the timeout uses the omit sentinel so any zero counts as default. * refactor(scan): drop local reach unit validation, defer to Coana Coana (@coana-tech/cli) is now the sole validator/parser of the --reach-analysis-timeout and --reach-analysis-memory-limit values, matching the Python CLI. Removes the local grammar mirror (isValid* regex fast-fail) that had already drifted from Coana twice (unit case-sensitivity, and a whitespace gap where Coana trims but the mirror did not). An invalid unit now surfaces as Coana's error instead of a fast local one. Kept the non-validation helpers, which Coana does not model: isOmittedReachValue (empty/zero -> omit the flag so Coana applies its default) and reachMemoryLimitToMb (unit-agnostic default-equivalence for the "requires --reach" guard). The raw string is still forwarded to Coana verbatim.
1 parent 8b5ffcf commit 5675a51

17 files changed

Lines changed: 279 additions & 77 deletions

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

7+
## [1.1.123](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.123) - 2026-06-18
8+
9+
### Added
10+
- `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.
11+
12+
### Changed
13+
- Updated the Coana CLI to v `15.5.0`.
14+
715
## [1.1.122](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.122) - 2026-06-17
816

917
### Changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "socket",
3-
"version": "1.1.122",
3+
"version": "1.1.123",
44
"description": "CLI for Socket.dev",
55
"homepage": "https://github.com/SocketDev/socket-cli",
66
"license": "MIT",
@@ -96,7 +96,7 @@
9696
"@babel/preset-typescript": "7.27.1",
9797
"@babel/runtime": "7.28.4",
9898
"@biomejs/biome": "2.2.4",
99-
"@coana-tech/cli": "15.4.6",
99+
"@coana-tech/cli": "15.5.0",
100100
"@cyclonedx/cdxgen": "12.1.2",
101101
"@dotenvx/dotenvx": "1.49.0",
102102
"@eslint/compat": "1.3.2",

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/commands/ci/handle-ci.mts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ export async function handleCi(autoManifest: boolean): Promise<void> {
5252
pullRequest: 0,
5353
reach: {
5454
excludePaths: [],
55-
reachAnalysisMemoryLimit: 0,
56-
reachAnalysisTimeout: 0,
55+
reachAnalysisMemoryLimit: '',
56+
reachAnalysisTimeout: '',
5757
reachConcurrency: 1,
5858
reachContinueOnAnalysisErrors: false,
5959
reachContinueOnInstallErrors: false,

src/commands/scan/cmd-scan-create.mts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import { assertValidExcludePaths } from './exclude-paths.mts'
88
import { handleCreateNewScan } from './handle-create-new-scan.mts'
99
import { outputCreateNewScan } from './output-create-new-scan.mts'
1010
import { excludePathsFlag, reachabilityFlags } from './reachability-flags.mts'
11+
import {
12+
isOmittedReachValue,
13+
reachMemoryLimitToMb,
14+
} from './reachability-units.mts'
1115
import { suggestOrgSlug } from './suggest-org-slug.mts'
1216
import { suggestTarget } from './suggest_target.mts'
1317
import { validateReachabilityTarget } from './validate-reachability-target.mts'
@@ -284,8 +288,8 @@ async function run(
284288
tmp: boolean
285289
// Reachability flags.
286290
reach: boolean
287-
reachAnalysisMemoryLimit: number
288-
reachAnalysisTimeout: number
291+
reachAnalysisMemoryLimit: string
292+
reachAnalysisTimeout: string
289293
reachConcurrency: number
290294
reachContinueOnAnalysisErrors: boolean
291295
reachContinueOnInstallErrors: boolean
@@ -485,12 +489,19 @@ async function run(
485489

486490
const hasReachExcludePaths = reachExcludePaths.length > 0
487491

492+
// Compare by resolved magnitude, not string identity: 8192, 8192MB and 8GB
493+
// all mean the default, and an omitted/zero timeout means "use the default".
494+
// A naive string compare would flag those equivalents as non-default and
495+
// wrongly require --reach.
496+
const memoryLimitMb = reachMemoryLimitToMb(reachAnalysisMemoryLimit)
488497
const isUsingNonDefaultMemoryLimit =
489-
reachAnalysisMemoryLimit !==
490-
reachabilityFlags['reachAnalysisMemoryLimit']?.default
498+
memoryLimitMb !== null &&
499+
memoryLimitMb !==
500+
reachMemoryLimitToMb(
501+
String(reachabilityFlags['reachAnalysisMemoryLimit']?.default ?? ''),
502+
)
491503

492-
const isUsingNonDefaultTimeout =
493-
reachAnalysisTimeout !== reachabilityFlags['reachAnalysisTimeout']?.default
504+
const isUsingNonDefaultTimeout = !isOmittedReachValue(reachAnalysisTimeout)
494505

495506
const isUsingNonDefaultConcurrency =
496507
reachConcurrency !== reachabilityFlags['reachConcurrency']?.default
@@ -633,8 +644,8 @@ async function run(
633644
})
634645
: undefined,
635646
excludePaths,
636-
reachAnalysisMemoryLimit: Number(reachAnalysisMemoryLimit),
637-
reachAnalysisTimeout: Number(reachAnalysisTimeout),
647+
reachAnalysisMemoryLimit,
648+
reachAnalysisTimeout,
638649
reachConcurrency: Number(reachConcurrency),
639650
reachContinueOnAnalysisErrors: Boolean(reachContinueOnAnalysisErrors),
640651
reachContinueOnInstallErrors: Boolean(reachContinueOnInstallErrors),

src/commands/scan/cmd-scan-create.test.mts

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ describe('socket scan create', async () => {
5656
--workspace The workspace in the Socket Organization that the repository is in to associate with the full scan.
5757
5858
Reachability Options (when --reach is used)
59-
--reach-analysis-memory-limit The maximum memory in MB to use for the reachability analysis. The default is 8192MB.
60-
--reach-analysis-timeout Set timeout for the reachability analysis. Split analysis runs may cause the total scan time to exceed this timeout significantly.
59+
--reach-analysis-memory-limit 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.
60+
--reach-analysis-timeout 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.
6161
--reach-concurrency Set the maximum number of concurrent reachability analysis runs. It is recommended to choose a concurrency level that ensures each analysis run has at least the --reach-analysis-memory-limit amount of memory available.
6262
--reach-continue-on-analysis-errors Continue reachability analysis when errors occur (timeouts, OOM, parse errors, etc.), falling back to precomputed (Tier 2) results. By default, the CLI halts on analysis errors.
6363
--reach-continue-on-install-errors Continue reachability analysis when package installation fails, falling back to precomputed (Tier 2) results. By default, the CLI halts on installation errors.
@@ -266,6 +266,56 @@ describe('socket scan create', async () => {
266266
},
267267
)
268268

269+
cmdit(
270+
[
271+
'scan',
272+
'create',
273+
FLAG_ORG,
274+
'fakeOrg',
275+
'target',
276+
FLAG_DRY_RUN,
277+
'--repo',
278+
'xyz',
279+
'--branch',
280+
'abc',
281+
'--reach-analysis-memory-limit',
282+
'8GB',
283+
FLAG_CONFIG,
284+
'{"apiToken":"fakeToken"}',
285+
],
286+
'should succeed when --reach-analysis-memory-limit equals the default in a different unit (8GB) without --reach',
287+
async cmd => {
288+
const { code, stdout } = await spawnSocketCli(binCliPath, cmd)
289+
expect(stdout).toMatchInlineSnapshot(`"[DryRun]: Bailing now"`)
290+
expect(code, 'should treat 8GB as the default 8192MB').toBe(0)
291+
},
292+
)
293+
294+
cmdit(
295+
[
296+
'scan',
297+
'create',
298+
FLAG_ORG,
299+
'fakeOrg',
300+
'target',
301+
FLAG_DRY_RUN,
302+
'--repo',
303+
'xyz',
304+
'--branch',
305+
'abc',
306+
'--reach-analysis-timeout',
307+
'0',
308+
FLAG_CONFIG,
309+
'{"apiToken":"fakeToken"}',
310+
],
311+
'should succeed when --reach-analysis-timeout is the zero/omit sentinel without --reach',
312+
async cmd => {
313+
const { code, stdout } = await spawnSocketCli(binCliPath, cmd)
314+
expect(stdout).toMatchInlineSnapshot(`"[DryRun]: Bailing now"`)
315+
expect(code, 'should treat 0 as the default (omit) timeout').toBe(0)
316+
},
317+
)
318+
269319
cmdit(
270320
[
271321
'scan',

src/commands/scan/cmd-scan-reach.mts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ async function run(
147147
markdown: boolean
148148
org: string
149149
output: string
150-
reachAnalysisMemoryLimit: number
151-
reachAnalysisTimeout: number
150+
reachAnalysisMemoryLimit: string
151+
reachAnalysisTimeout: string
152152
reachConcurrency: number
153153
reachContinueOnAnalysisErrors: boolean
154154
reachContinueOnInstallErrors: boolean
@@ -277,8 +277,8 @@ async function run(
277277
outputPath: outputPath || '',
278278
reachabilityOptions: {
279279
excludePaths,
280-
reachAnalysisMemoryLimit: Number(reachAnalysisMemoryLimit),
281-
reachAnalysisTimeout: Number(reachAnalysisTimeout),
280+
reachAnalysisMemoryLimit,
281+
reachAnalysisTimeout,
282282
reachConcurrency: Number(reachConcurrency),
283283
reachContinueOnAnalysisErrors: Boolean(reachContinueOnAnalysisErrors),
284284
reachContinueOnInstallErrors: Boolean(reachContinueOnInstallErrors),

src/commands/scan/cmd-scan-reach.test.mts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ describe('socket scan reach', async () => {
3838
3939
Reachability Options
4040
--exclude-paths List of glob patterns to exclude from the scan, including SCA/SBOM manifest discovery and (when --reach is enabled) Tier 1 reachability analysis. Patterns are anchored micromatch globs matched relative to the Socket scan root, which is the command working directory (\`--cwd\` if set), not the reachability target: \`tests\` matches only \`<cwd>/tests\`; use \`**/tests\` to match at any depth. Negation patterns (\`!path\`) are not supported. Accepts a comma-separated value or multiple flags.
41-
--reach-analysis-memory-limit The maximum memory in MB to use for the reachability analysis. The default is 8192MB.
42-
--reach-analysis-timeout Set timeout for the reachability analysis. Split analysis runs may cause the total scan time to exceed this timeout significantly.
41+
--reach-analysis-memory-limit 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.
42+
--reach-analysis-timeout 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.
4343
--reach-concurrency Set the maximum number of concurrent reachability analysis runs. It is recommended to choose a concurrency level that ensures each analysis run has at least the --reach-analysis-memory-limit amount of memory available.
4444
--reach-continue-on-analysis-errors Continue reachability analysis when errors occur (timeouts, OOM, parse errors, etc.), falling back to precomputed (Tier 2) results. By default, the CLI halts on analysis errors.
4545
--reach-continue-on-install-errors Continue reachability analysis when package installation fails, falling back to precomputed (Tier 2) results. By default, the CLI halts on installation errors.
@@ -1050,7 +1050,7 @@ describe('socket scan reach', async () => {
10501050
FLAG_CONFIG,
10511051
'{"apiToken":"fake-token"}',
10521052
],
1053-
'should show clear error for invalid memory limit',
1053+
'should forward an unrecognized memory value to Coana without locally rejecting it',
10541054
async cmd => {
10551055
const { code, stderr, stdout } = await spawnSocketCli(binCliPath, cmd)
10561056
const output = stdout + stderr
@@ -1065,13 +1065,13 @@ describe('socket scan reach', async () => {
10651065
'reach',
10661066
FLAG_DRY_RUN,
10671067
'--reach-analysis-memory-limit',
1068-
'-1',
1068+
'512kb',
10691069
'--org',
10701070
'fakeOrg',
10711071
FLAG_CONFIG,
10721072
'{"apiToken":"fake-token"}',
10731073
],
1074-
'should show clear error for negative memory limit',
1074+
'should forward an unsupported memory unit to Coana without locally rejecting it',
10751075
async cmd => {
10761076
const { code, stderr, stdout } = await spawnSocketCli(binCliPath, cmd)
10771077
const output = stdout + stderr
@@ -1092,7 +1092,7 @@ describe('socket scan reach', async () => {
10921092
FLAG_CONFIG,
10931093
'{"apiToken":"fake-token"}',
10941094
],
1095-
'should show clear error for invalid timeout value',
1095+
'should forward an unrecognized timeout value to Coana without locally rejecting it',
10961096
async cmd => {
10971097
const { code, stderr, stdout } = await spawnSocketCli(binCliPath, cmd)
10981098
const output = stdout + stderr
@@ -1107,13 +1107,13 @@ describe('socket scan reach', async () => {
11071107
'reach',
11081108
FLAG_DRY_RUN,
11091109
'--reach-analysis-timeout',
1110-
'0',
1110+
'10m',
11111111
'--org',
11121112
'fakeOrg',
11131113
FLAG_CONFIG,
11141114
'{"apiToken":"fake-token"}',
11151115
],
1116-
'should show clear error for zero timeout',
1116+
'should accept a timeout value with a unit suffix',
11171117
async cmd => {
11181118
const { code, stderr, stdout } = await spawnSocketCli(binCliPath, cmd)
11191119
const output = stdout + stderr

src/commands/scan/create-scan-from-github.mts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,8 @@ async function scanOneRepo(
251251
pullRequest: 0,
252252
reach: {
253253
excludePaths: [],
254-
reachAnalysisMemoryLimit: 0,
255-
reachAnalysisTimeout: 0,
254+
reachAnalysisMemoryLimit: '',
255+
reachAnalysisTimeout: '',
256256
reachConcurrency: 1,
257257
reachContinueOnAnalysisErrors: false,
258258
reachContinueOnInstallErrors: false,

src/commands/scan/exclude-paths.test.mts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ function makeReachOptions(
1515
): ReachabilityOptions {
1616
return {
1717
excludePaths: [],
18-
reachAnalysisMemoryLimit: 8192,
19-
reachAnalysisTimeout: 0,
18+
reachAnalysisMemoryLimit: '8192',
19+
reachAnalysisTimeout: '',
2020
reachConcurrency: 1,
2121
reachContinueOnAnalysisErrors: false,
2222
reachContinueOnInstallErrors: false,

0 commit comments

Comments
 (0)