Skip to content

Commit 1fd87c4

Browse files
committed
ensure the target directory is handled correctly by the reachability analysis
1 parent 2d7e5a1 commit 1fd87c4

File tree

5 files changed

+108
-1
lines changed

5 files changed

+108
-1
lines changed

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

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import path from 'node:path'
22

3+
import { existsSync, promises as fs } from 'node:fs'
4+
35
import { joinAnd } from '@socketsecurity/lib/arrays'
46
import { logger } from '@socketsecurity/lib/logger'
57

@@ -444,6 +446,32 @@ async function run(
444446
hasReachExcludePaths ||
445447
reachSkipCache
446448

449+
// Validate target constraints when --reach is enabled.
450+
let reachTargetValid = true
451+
let reachTargetIsDirectory = false
452+
let reachTargetExists = false
453+
let reachTargetInsideCwd = false
454+
455+
if (reach) {
456+
// Resolve target path to absolute for validation.
457+
const targetPath = path.isAbsolute(targets[0]!)
458+
? targets[0]!
459+
: path.resolve(cwd, targets[0]!)
460+
461+
// Check if target is inside cwd.
462+
const relativePath = path.relative(cwd, targetPath)
463+
reachTargetInsideCwd =
464+
!relativePath.startsWith('..') && !path.isAbsolute(relativePath)
465+
466+
reachTargetExists = existsSync(targetPath)
467+
if (reachTargetExists) {
468+
const targetStat = await fs.stat(targetPath)
469+
reachTargetIsDirectory = targetStat.isDirectory()
470+
}
471+
472+
reachTargetValid = targets.length === 1
473+
}
474+
447475
const wasValidInput = checkCommandInput(
448476
outputKind,
449477
{
@@ -487,6 +515,33 @@ async function run(
487515
message: 'Reachability analysis flags require --reach to be enabled',
488516
fail: 'add --reach flag to use --reach-* options',
489517
},
518+
{
519+
nook: true,
520+
test: !reach || reachTargetValid,
521+
message:
522+
'Reachability analysis requires exactly one target directory when --reach is enabled',
523+
fail: 'provide exactly one directory path',
524+
},
525+
{
526+
nook: true,
527+
test: !reach || reachTargetIsDirectory,
528+
message:
529+
'Reachability analysis target must be a directory when --reach is enabled',
530+
fail: 'provide a directory path, not a file',
531+
},
532+
{
533+
nook: true,
534+
test: !reach || reachTargetExists,
535+
message: 'Target directory must exist when --reach is enabled',
536+
fail: 'provide an existing directory path',
537+
},
538+
{
539+
nook: true,
540+
test: !reach || reachTargetInsideCwd,
541+
message:
542+
'Target directory must be inside the current working directory when --reach is enabled',
543+
fail: 'provide a path inside the working directory',
544+
},
490545
)
491546
if (!wasValidInput) {
492547
return

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import path from 'node:path'
22

3+
import { existsSync, promises as fs } from 'node:fs'
4+
35
import { joinAnd } from '@socketsecurity/lib/arrays'
46
import { logger } from '@socketsecurity/lib/logger'
57

@@ -175,6 +177,22 @@ async function run(
175177

176178
const outputKind = getOutputKind(json, markdown)
177179

180+
// Resolve target path to absolute for validation.
181+
const targetPath = path.isAbsolute(targets[0]!)
182+
? targets[0]!
183+
: path.resolve(cwd, targets[0]!)
184+
185+
// Check if target is inside cwd.
186+
const relativePath = path.relative(cwd, targetPath)
187+
const isInsideCwd =
188+
!relativePath.startsWith('..') && !path.isAbsolute(relativePath)
189+
190+
let isDirectory = false
191+
if (existsSync(targetPath)) {
192+
const targetStat = await fs.stat(targetPath)
193+
isDirectory = targetStat.isDirectory()
194+
}
195+
178196
const wasValidInput = checkCommandInput(
179197
outputKind,
180198
{
@@ -201,6 +219,30 @@ async function run(
201219
message: 'The --output path must end with .json',
202220
fail: 'use a path ending with .json',
203221
},
222+
{
223+
nook: true,
224+
test: targets.length === 1,
225+
message: 'Reachability analysis requires exactly one target directory',
226+
fail: 'provide exactly one directory path',
227+
},
228+
{
229+
nook: true,
230+
test: isDirectory,
231+
message: 'Reachability analysis target must be a directory',
232+
fail: 'provide a directory path, not a file',
233+
},
234+
{
235+
nook: true,
236+
test: existsSync(targetPath),
237+
message: 'Target directory must exist',
238+
fail: 'provide an existing directory path',
239+
},
240+
{
241+
nook: true,
242+
test: isInsideCwd,
243+
message: 'Target directory must be inside the current working directory',
244+
fail: 'provide a path inside the working directory',
245+
},
204246
)
205247
if (!wasValidInput) {
206248
return

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ export async function handleCreateNewScan({
165165
reachabilityOptions: reach,
166166
repoName,
167167
spinner,
168+
target: targets[0]!,
168169
})
169170

170171
spinner.stop()

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export async function handleScanReach({
8080
packagePaths,
8181
reachabilityOptions,
8282
spinner,
83+
target: targets[0]!,
8384
uploadManifests: true,
8485
})
8586

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type ReachabilityAnalysisOptions = {
3535
reachabilityOptions: ReachabilityOptions
3636
repoName?: string | undefined
3737
spinner?: Spinner | undefined
38+
target: string
3839
uploadManifests?: boolean | undefined
3940
}
4041

@@ -55,9 +56,16 @@ export async function performReachabilityAnalysis(
5556
reachabilityOptions,
5657
repoName,
5758
spinner,
59+
target,
5860
uploadManifests = true,
5961
} = { __proto__: null, ...options } as ReachabilityAnalysisOptions
6062

63+
// Determine the analysis target - make it relative to cwd if absolute.
64+
let analysisTarget = target
65+
if (path.isAbsolute(analysisTarget)) {
66+
analysisTarget = path.relative(cwd, analysisTarget) || '.'
67+
}
68+
6169
// Check if user has enterprise plan for reachability analysis.
6270
const orgsCResult = await fetchOrganization()
6371
if (!orgsCResult.ok) {
@@ -140,7 +148,7 @@ export async function performReachabilityAnalysis(
140148
// Build Coana arguments.
141149
const coanaArgs = [
142150
'run',
143-
cwd,
151+
analysisTarget,
144152
'--output-dir',
145153
path.dirname(outputFilePath),
146154
'--socket-mode',

0 commit comments

Comments
 (0)