Skip to content

Commit 51e278c

Browse files
authored
Various fixes for handling of target paths. (#933)
* Fix handling of target in reachability mode. Fix recursive inclusion of manifest files when target is a directory * update Socket CLI to v1.1.34
1 parent 07ada33 commit 51e278c

13 files changed

+189
-20
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ 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+
8+
## [1.1.34](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.33) - 2025-11-21
9+
10+
### Fixed
11+
- The target path is now properly considered when conducting reachability analysis: `socket scan reach <target>` and `socket scan create --reach <target>`.
12+
- Fixed a bug where manifest files `<target>` were not included in a scan when the target was pointing to a directory.
13+
714
## [1.1.33](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.33) - 2025-11-20
815

916
### Changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "socket",
3-
"version": "1.1.33",
3+
"version": "1.1.34",
44
"description": "CLI for Socket.dev",
55
"homepage": "https://github.com/SocketDev/socket-cli",
66
"license": "MIT AND OFL-1.1",

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { outputCreateNewScan } from './output-create-new-scan.mts'
88
import { reachabilityFlags } from './reachability-flags.mts'
99
import { suggestOrgSlug } from './suggest-org-slug.mts'
1010
import { suggestTarget } from './suggest_target.mts'
11+
import { validateReachabilityTarget } from './validate-reachability-target.mts'
1112
import constants, { REQUIREMENTS_TXT, SOCKET_JSON } from '../../constants.mts'
1213
import { commonFlags, outputFlags } from '../../flags.mts'
1314
import { checkCommandInput } from '../../utils/check-input.mts'
@@ -453,6 +454,16 @@ async function run(
453454
reachSkipCache ||
454455
reachDisableAnalysisSplitting
455456

457+
// Validate target constraints when --reach is enabled.
458+
const reachTargetValidation = reach
459+
? await validateReachabilityTarget(targets, cwd)
460+
: {
461+
isDirectory: false,
462+
isInsideCwd: false,
463+
isValid: true,
464+
targetExists: false,
465+
}
466+
456467
const wasValidInput = checkCommandInput(
457468
outputKind,
458469
{
@@ -496,6 +507,33 @@ async function run(
496507
message: 'Reachability analysis flags require --reach to be enabled',
497508
fail: 'add --reach flag to use --reach-* options',
498509
},
510+
{
511+
nook: true,
512+
test: !reach || reachTargetValidation.isValid,
513+
message:
514+
'Reachability analysis requires exactly one target directory when --reach is enabled',
515+
fail: 'provide exactly one directory path',
516+
},
517+
{
518+
nook: true,
519+
test: !reach || reachTargetValidation.isDirectory,
520+
message:
521+
'Reachability analysis target must be a directory when --reach is enabled',
522+
fail: 'provide a directory path, not a file',
523+
},
524+
{
525+
nook: true,
526+
test: !reach || reachTargetValidation.targetExists,
527+
message: 'Target directory must exist when --reach is enabled',
528+
fail: 'provide an existing directory path',
529+
},
530+
{
531+
nook: true,
532+
test: !reach || reachTargetValidation.isInsideCwd,
533+
message:
534+
'Target directory must be inside the current working directory when --reach is enabled',
535+
fail: 'provide a path inside the working directory',
536+
},
499537
)
500538
if (!wasValidInput) {
501539
return

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

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ describe('socket scan create', async () => {
305305
'create',
306306
FLAG_ORG,
307307
'fakeOrg',
308-
'target',
308+
'test/fixtures/commands/scan/reach',
309309
FLAG_DRY_RUN,
310310
'--repo',
311311
'xyz',
@@ -370,7 +370,7 @@ describe('socket scan create', async () => {
370370
'create',
371371
FLAG_ORG,
372372
'fakeOrg',
373-
'target',
373+
'test/fixtures/commands/scan/reach',
374374
FLAG_DRY_RUN,
375375
'--repo',
376376
'xyz',
@@ -405,7 +405,7 @@ describe('socket scan create', async () => {
405405
'create',
406406
FLAG_ORG,
407407
'fakeOrg',
408-
'target',
408+
'test/fixtures/commands/scan/reach',
409409
FLAG_DRY_RUN,
410410
'--repo',
411411
'xyz',
@@ -434,7 +434,7 @@ describe('socket scan create', async () => {
434434
'create',
435435
FLAG_ORG,
436436
'fakeOrg',
437-
'target',
437+
'test/fixtures/commands/scan/reach',
438438
FLAG_DRY_RUN,
439439
'--repo',
440440
'xyz',
@@ -527,7 +527,7 @@ describe('socket scan create', async () => {
527527
'create',
528528
FLAG_ORG,
529529
'fakeOrg',
530-
'target',
530+
'test/fixtures/commands/scan/reach',
531531
FLAG_DRY_RUN,
532532
'--repo',
533533
'xyz',
@@ -595,7 +595,7 @@ describe('socket scan create', async () => {
595595
'create',
596596
FLAG_ORG,
597597
'fakeOrg',
598-
'target',
598+
'test/fixtures/commands/scan/reach',
599599
FLAG_DRY_RUN,
600600
'--repo',
601601
'xyz',
@@ -621,7 +621,7 @@ describe('socket scan create', async () => {
621621
'create',
622622
FLAG_ORG,
623623
'fakeOrg',
624-
'target',
624+
'test/fixtures/commands/scan/reach',
625625
FLAG_DRY_RUN,
626626
'--repo',
627627
'xyz',
@@ -647,7 +647,7 @@ describe('socket scan create', async () => {
647647
'create',
648648
FLAG_ORG,
649649
'fakeOrg',
650-
'target',
650+
'test/fixtures/commands/scan/reach',
651651
FLAG_DRY_RUN,
652652
'--repo',
653653
'xyz',
@@ -677,7 +677,7 @@ describe('socket scan create', async () => {
677677
'create',
678678
FLAG_ORG,
679679
'fakeOrg',
680-
'target',
680+
'test/fixtures/commands/scan/reach',
681681
FLAG_DRY_RUN,
682682
'--repo',
683683
'xyz',
@@ -710,7 +710,7 @@ describe('socket scan create', async () => {
710710
'create',
711711
FLAG_ORG,
712712
'fakeOrg',
713-
'target',
713+
'test/fixtures/commands/scan/reach',
714714
FLAG_DRY_RUN,
715715
'--repo',
716716
'xyz',
@@ -735,7 +735,7 @@ describe('socket scan create', async () => {
735735
'create',
736736
FLAG_ORG,
737737
'fakeOrg',
738-
'target',
738+
'test/fixtures/commands/scan/reach',
739739
FLAG_DRY_RUN,
740740
'--repo',
741741
'xyz',
@@ -760,7 +760,7 @@ describe('socket scan create', async () => {
760760
'create',
761761
FLAG_ORG,
762762
'fakeOrg',
763-
'target',
763+
'test/fixtures/commands/scan/reach',
764764
FLAG_DRY_RUN,
765765
'--repo',
766766
'xyz',
@@ -790,7 +790,7 @@ describe('socket scan create', async () => {
790790
'create',
791791
FLAG_ORG,
792792
'fakeOrg',
793-
'target',
793+
'test/fixtures/commands/scan/reach',
794794
FLAG_DRY_RUN,
795795
'--repo',
796796
'xyz',

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { logger } from '@socketsecurity/registry/lib/logger'
66
import { handleScanReach } from './handle-scan-reach.mts'
77
import { reachabilityFlags } from './reachability-flags.mts'
88
import { suggestTarget } from './suggest_target.mts'
9+
import { validateReachabilityTarget } from './validate-reachability-target.mts'
910
import constants from '../../constants.mts'
1011
import { commonFlags, outputFlags } from '../../flags.mts'
1112
import { checkCommandInput } from '../../utils/check-input.mts'
@@ -156,7 +157,7 @@ async function run(
156157
: processCwd
157158

158159
// Accept zero or more paths. Default to cwd() if none given.
159-
let targets = cli.input || [cwd]
160+
let targets = cli.input.length ? cli.input : [cwd]
160161

161162
// Use suggestTarget if no targets specified and in interactive mode
162163
if (!targets.length && !dryRun && interactive) {
@@ -169,6 +170,9 @@ async function run(
169170

170171
const outputKind = getOutputKind(json, markdown)
171172

173+
// Validate target constraints for reachability analysis.
174+
const targetValidation = await validateReachabilityTarget(targets, cwd)
175+
172176
const wasValidInput = checkCommandInput(
173177
outputKind,
174178
{
@@ -189,6 +193,30 @@ async function run(
189193
message: 'The json and markdown flags cannot be both set, pick one',
190194
fail: 'omit one',
191195
},
196+
{
197+
nook: true,
198+
test: targetValidation.isValid,
199+
message: 'Reachability analysis requires exactly one target directory',
200+
fail: 'provide exactly one directory path',
201+
},
202+
{
203+
nook: true,
204+
test: targetValidation.isDirectory,
205+
message: 'Reachability analysis target must be a directory',
206+
fail: 'provide a directory path, not a file',
207+
},
208+
{
209+
nook: true,
210+
test: targetValidation.targetExists,
211+
message: 'Target directory must exist',
212+
fail: 'provide an existing directory path',
213+
},
214+
{
215+
nook: true,
216+
test: targetValidation.isInsideCwd,
217+
message: 'Target directory must be inside the current working directory',
218+
fail: 'provide a path inside the working directory',
219+
},
192220
)
193221
if (!wasValidInput) {
194222
return

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,7 @@ describe('socket scan reach', async () => {
779779
const { code, stderr, stdout } = await spawnSocketCli(binCliPath, cmd)
780780
const output = stdout + stderr
781781
expect(output).toMatch(
782-
/no eligible files|file.*dir.*must contain|not.*found/i,
782+
/Target directory must exist|no eligible files|file.*dir.*must contain|not.*found/i,
783783
)
784784
expect(code).toBeGreaterThan(0)
785785
},

src/commands/scan/handle-create-new-scan.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ export async function handleCreateNewScan({
173173
reachabilityOptions: reach,
174174
repoName,
175175
spinner,
176+
target: targets[0]!,
176177
})
177178

178179
spinner.stop()

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export async function handleScanReach({
7373
packagePaths,
7474
reachabilityOptions,
7575
spinner,
76+
target: targets[0]!,
7677
uploadManifests: true,
7778
})
7879

src/commands/scan/perform-reachability-analysis.mts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export type ReachabilityAnalysisOptions = {
3333
reachabilityOptions: ReachabilityOptions
3434
repoName?: string | undefined
3535
spinner?: Spinner | undefined
36+
target: string
3637
uploadManifests?: boolean | undefined
3738
}
3839

@@ -52,9 +53,16 @@ export async function performReachabilityAnalysis(
5253
reachabilityOptions,
5354
repoName,
5455
spinner,
56+
target,
5557
uploadManifests = true,
5658
} = { __proto__: null, ...options } as ReachabilityAnalysisOptions
5759

60+
// Determine the analysis target - make it relative to cwd if absolute.
61+
let analysisTarget = target
62+
if (path.isAbsolute(analysisTarget)) {
63+
analysisTarget = path.relative(cwd, analysisTarget) || '.'
64+
}
65+
5866
// Check if user has enterprise plan for reachability analysis.
5967
const orgsCResult = await fetchOrganization()
6068
if (!orgsCResult.ok) {
@@ -137,7 +145,7 @@ export async function performReachabilityAnalysis(
137145
// Build Coana arguments.
138146
const coanaArgs = [
139147
'run',
140-
cwd,
148+
analysisTarget,
141149
'--output-dir',
142150
cwd,
143151
'--socket-mode',
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { existsSync, promises as fs } from 'node:fs'
2+
import path from 'node:path'
3+
4+
export type ReachabilityTargetValidation = {
5+
isDirectory: boolean
6+
isInsideCwd: boolean
7+
isValid: boolean
8+
targetExists: boolean
9+
}
10+
11+
/**
12+
* Validates that a target directory meets the requirements for reachability analysis.
13+
*
14+
* @param targets - Array of target paths to validate.
15+
* @param cwd - Current working directory.
16+
* @returns Validation result object with boolean flags.
17+
*/
18+
export async function validateReachabilityTarget(
19+
targets: string[],
20+
cwd: string,
21+
): Promise<ReachabilityTargetValidation> {
22+
const result: ReachabilityTargetValidation = {
23+
isDirectory: false,
24+
isInsideCwd: false,
25+
isValid: targets.length === 1,
26+
targetExists: false,
27+
}
28+
29+
if (!result.isValid || !targets[0]) {
30+
return result
31+
}
32+
33+
// Resolve cwd to absolute path to handle relative cwd values.
34+
const absoluteCwd = path.resolve(cwd)
35+
36+
// Resolve target path to absolute for validation.
37+
const targetPath = path.isAbsolute(targets[0])
38+
? targets[0]
39+
: path.resolve(absoluteCwd, targets[0])
40+
41+
// Check if target is inside cwd.
42+
const relativePath = path.relative(absoluteCwd, targetPath)
43+
result.isInsideCwd =
44+
!relativePath.startsWith('..') && !path.isAbsolute(relativePath)
45+
46+
result.targetExists = existsSync(targetPath)
47+
if (result.targetExists) {
48+
const targetStat = await fs.stat(targetPath)
49+
result.isDirectory = targetStat.isDirectory()
50+
}
51+
52+
return result
53+
}

0 commit comments

Comments
 (0)