Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions packages/cli/src/commands/scan/cmd-scan-create.mts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import path from 'node:path'

import { existsSync, promises as fs } from 'node:fs'

import { joinAnd } from '@socketsecurity/lib/arrays'
import { logger } from '@socketsecurity/lib/logger'

Expand Down Expand Up @@ -444,6 +446,32 @@ async function run(
hasReachExcludePaths ||
reachSkipCache

// Validate target constraints when --reach is enabled.
let reachTargetValid = true
let reachTargetIsDirectory = false
let reachTargetExists = false
let reachTargetInsideCwd = false

if (reach) {
// Resolve target path to absolute for validation.
const targetPath = path.isAbsolute(targets[0]!)
? targets[0]!
: path.resolve(cwd, targets[0]!)

// Check if target is inside cwd.
const relativePath = path.relative(cwd, targetPath)
reachTargetInsideCwd =
!relativePath.startsWith('..') && !path.isAbsolute(relativePath)

reachTargetExists = existsSync(targetPath)
if (reachTargetExists) {
const targetStat = await fs.stat(targetPath)
reachTargetIsDirectory = targetStat.isDirectory()
}

reachTargetValid = targets.length === 1
}

const wasValidInput = checkCommandInput(
outputKind,
{
Expand Down Expand Up @@ -487,6 +515,33 @@ async function run(
message: 'Reachability analysis flags require --reach to be enabled',
fail: 'add --reach flag to use --reach-* options',
},
{
nook: true,
test: !reach || reachTargetValid,
message:
'Reachability analysis requires exactly one target directory when --reach is enabled',
fail: 'provide exactly one directory path',
},
{
nook: true,
test: !reach || reachTargetIsDirectory,
message:
'Reachability analysis target must be a directory when --reach is enabled',
fail: 'provide a directory path, not a file',
},
{
nook: true,
test: !reach || reachTargetExists,
message: 'Target directory must exist when --reach is enabled',
fail: 'provide an existing directory path',
},
{
nook: true,
test: !reach || reachTargetInsideCwd,
message:
'Target directory must be inside the current working directory when --reach is enabled',
fail: 'provide a path inside the working directory',
},
)
if (!wasValidInput) {
return
Expand Down
42 changes: 42 additions & 0 deletions packages/cli/src/commands/scan/cmd-scan-reach.mts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import path from 'node:path'

import { existsSync, promises as fs } from 'node:fs'

import { joinAnd } from '@socketsecurity/lib/arrays'
import { logger } from '@socketsecurity/lib/logger'

Expand Down Expand Up @@ -175,6 +177,22 @@ async function run(

const outputKind = getOutputKind(json, markdown)

// Resolve target path to absolute for validation.
const targetPath = path.isAbsolute(targets[0]!)
? targets[0]!
: path.resolve(cwd, targets[0]!)

// Check if target is inside cwd.
const relativePath = path.relative(cwd, targetPath)
const isInsideCwd =
!relativePath.startsWith('..') && !path.isAbsolute(relativePath)

let isDirectory = false
if (existsSync(targetPath)) {
const targetStat = await fs.stat(targetPath)
isDirectory = targetStat.isDirectory()
}

const wasValidInput = checkCommandInput(
outputKind,
{
Expand All @@ -201,6 +219,30 @@ async function run(
message: 'The --output path must end with .json',
fail: 'use a path ending with .json',
},
{
nook: true,
test: targets.length === 1,
message: 'Reachability analysis requires exactly one target directory',
fail: 'provide exactly one directory path',
},
{
nook: true,
test: isDirectory,
message: 'Reachability analysis target must be a directory',
fail: 'provide a directory path, not a file',
},
{
nook: true,
test: existsSync(targetPath),
message: 'Target directory must exist',
fail: 'provide an existing directory path',
},
{
nook: true,
test: isInsideCwd,
message: 'Target directory must be inside the current working directory',
fail: 'provide a path inside the working directory',
},
)
if (!wasValidInput) {
return
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/scan/handle-create-new-scan.mts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export async function handleCreateNewScan({
reachabilityOptions: reach,
repoName,
spinner: spinner ?? undefined,
target: targets[0]!,
})

spinner?.stop()
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/scan/handle-scan-reach.mts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export async function handleScanReach({
packagePaths,
reachabilityOptions,
spinner: spinner ?? undefined,
target: targets[0]!,
uploadManifests: true,
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export type ReachabilityAnalysisOptions = {
reachabilityOptions: ReachabilityOptions
repoName?: string | undefined
spinner?: Spinner | undefined
target: string
uploadManifests?: boolean | undefined
}

Expand All @@ -55,9 +56,16 @@ export async function performReachabilityAnalysis(
reachabilityOptions,
repoName,
spinner,
target,
uploadManifests = true,
} = { __proto__: null, ...options } as ReachabilityAnalysisOptions

// Determine the analysis target - make it relative to cwd if absolute.
let analysisTarget = target
if (path.isAbsolute(analysisTarget)) {
analysisTarget = path.relative(cwd, analysisTarget) || '.'
}

// Check if user has enterprise plan for reachability analysis.
const orgsCResult = await fetchOrganization()
if (!orgsCResult.ok) {
Expand Down Expand Up @@ -140,7 +148,7 @@ export async function performReachabilityAnalysis(
// Build Coana arguments.
const coanaArgs = [
'run',
cwd,
analysisTarget,
'--output-dir',
path.dirname(outputFilePath),
'--socket-mode',
Expand Down