Skip to content
Merged
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
22 changes: 15 additions & 7 deletions src/commands/fix/cmd-fix.mts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { handleFix } from './handle-fix.mts'
import constants from '../../constants.mts'
import { commonFlags } from '../../flags.mts'
import { checkCommandInput } from '../../utils/check-input.mts'
import { cmdFlagValueToArray } from '../../utils/cmd.mts'
import { getOutputKind } from '../../utils/get-output-kind.mts'
import { meowOrExit } from '../../utils/meow-with-subcommands.mts'
import { getFlagListOutput } from '../../utils/output-formatting.mts'
Expand Down Expand Up @@ -38,6 +39,15 @@ const config: CliCommandConfig = {
default: false,
description: `Shorthand for --autoMerge --test`,
},
ghsa: {
type: 'string',
default: [],
description: `Provide a list of ${terminalLink(
'GHSA IDs',
'https://docs.github.com/en/code-security/security-advisories/working-with-global-security-advisories-from-the-github-advisory-database/about-the-github-advisory-database#about-ghsa-ids',
)} to compute fixes for, as either a comma separated value or as multiple flags`,
isMultiple: true,
},
limit: {
type: 'number',
default: Infinity,
Expand All @@ -47,9 +57,9 @@ const config: CliCommandConfig = {
type: 'string',
default: [],
description: `Provide a list of ${terminalLink(
'package URLs',
'PURLs',
'https://github.com/package-url/purl-spec?tab=readme-ov-file#purl',
)} (PURLs) to fix, as either a comma separated value or as multiple flags,\n instead of querying the Socket API`,
)} to compute fixes for, as either a comma separated value or as multiple flags,\n instead of querying the Socket API`,
isMultiple: true,
shortFlag: 'p',
},
Expand Down Expand Up @@ -150,20 +160,18 @@ async function run(
test = true
}

const ghsas = cmdFlagValueToArray(cli.flags['ghsa'])
const limit =
(cli.flags['limit']
? parseInt(String(cli.flags['limit'] || ''), 10)
: Infinity) || Infinity

const purls: string[] = Array.isArray(cli.flags['purl'])
? cli.flags['purl'].flatMap(p => p.split(/, */))
: []

const purls = cmdFlagValueToArray(cli.flags['purl'])
const testScript = String(cli.flags['testScript'] || 'test')

await handleFix({
autoMerge,
cwd,
ghsas,
limit,
outputKind,
purls,
Expand Down
113 changes: 87 additions & 26 deletions src/commands/fix/handle-fix.mts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { outputFixResult } from './output-fix-result.mts'
import { pnpmFix } from './pnpm-fix.mts'
import { CMD_NAME } from './shared.mts'
import constants from '../../constants.mts'
import { cmdFlagValueToArray } from '../../utils/cmd.mts'
import { spawnCoana } from '../../utils/coana.mts'
import { detectAndValidatePackageEnvironment } from '../../utils/package-environment.mts'

import type { OutputKind } from '../../types.mts'
Expand All @@ -15,6 +17,7 @@ const { NPM, PNPM } = constants
export async function handleFix({
autoMerge,
cwd,
ghsas,
limit,
outputKind,
purls,
Expand All @@ -24,28 +27,81 @@ export async function handleFix({
}: {
autoMerge: boolean
cwd: string
ghsas: string[]
limit: number
outputKind: OutputKind
purls: string[]
rangeStyle: RangeStyle
test: boolean
testScript: string
}) {
const pkgEnvResult = await detectAndValidatePackageEnvironment(cwd, {
let { length: ghsasCount } = ghsas
if (ghsasCount) {
// Lazily access constants.spinner.
const { spinner } = constants

spinner.start()

if (ghsasCount === 1 && ghsas[0] === 'auto') {
const autoCResult = await spawnCoana(
['compute-fixes-and-upgrade-purls', cwd],
{ cwd, spinner },
)
if (autoCResult.ok) {
console.log(autoCResult.data)
ghsas = cmdFlagValueToArray(
/(?<=Vulnerabilities found: )[^\n]+/.exec(
autoCResult.data as string,
)?.[0],
)
ghsasCount = ghsas.length
} else {
ghsas = []
ghsasCount = 0
}
}

spinner.stop()

if (ghsasCount) {
spinner.start()
await outputFixResult(
await spawnCoana(
[
'compute-fixes-and-upgrade-purls',
cwd,
'--apply-fixes-to',
...ghsas,
],
{ cwd, spinner },
),
outputKind,
)
spinner.stop()
return
}
}

const pkgEnvCResult = await detectAndValidatePackageEnvironment(cwd, {
cmdName: CMD_NAME,
logger,
})
if (!pkgEnvResult.ok) {
return pkgEnvResult
if (!pkgEnvCResult.ok) {
await outputFixResult(pkgEnvCResult, outputKind)
return
}

const pkgEnvDetails = pkgEnvResult.data
const { data: pkgEnvDetails } = pkgEnvCResult
if (!pkgEnvDetails) {
return {
ok: false,
message: 'No package found',
cause: `No valid package environment was found in given cwd (${cwd})`,
}
await outputFixResult(
{
ok: false,
message: 'No package found',
cause: `No valid package environment was found in given cwd (${cwd})`,
},
outputKind,
)
return
}

logger.info(
Expand All @@ -54,27 +110,32 @@ export async function handleFix({

const { agent } = pkgEnvDetails
if (agent !== NPM && agent !== PNPM) {
return {
ok: false,
message: 'Not supported',
cause: `${agent} is not supported by this command at the moment.`,
}
await outputFixResult(
{
ok: false,
message: 'Not supported',
cause: `${agent} is not supported by this command at the moment.`,
},
outputKind,
)
return
}

// Lazily access spinner.
const { spinner } = constants
const fixer = agent === NPM ? npmFix : pnpmFix

const result = await fixer(pkgEnvDetails, {
autoMerge,
cwd,
limit,
purls,
rangeStyle,
spinner,
test,
testScript,
})

await outputFixResult(result, outputKind)
await outputFixResult(
await fixer(pkgEnvDetails, {
autoMerge,
cwd,
limit,
purls,
rangeStyle,
spinner,
test,
testScript,
}),
outputKind,
)
}
47 changes: 14 additions & 33 deletions src/commands/scan/scan-reachability.mts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { spawn } from '@socketsecurity/registry/lib/spawn'

import constants from '../../constants.mts'
import { getDefaultToken } from '../../utils/sdk.mts'
import { spawnCoana } from '../../utils/coana.mts'

import type { CResult } from '../../types.mts'

Expand All @@ -11,34 +9,17 @@ export async function scanReachability(
argv: string[] | readonly string[],
cwd: string,
): Promise<CResult<unknown>> {
try {
const result = await spawn(
constants.execPath,
[
// Lazily access constants.nodeNoWarningsFlags.
...constants.nodeNoWarningsFlags,
// Lazily access constants.coanaBinPath.
constants.coanaBinPath,
'run',
cwd,
'--output-dir',
cwd,
'--socket-mode',
DOT_SOCKET_DOT_FACTS_JSON,
'--disable-report-submission',
...argv,
],
{
cwd,
env: {
...process.env,
SOCKET_CLI_API_TOKEN: getDefaultToken(),
},
},
)
return { ok: true, data: result.stdout.trim() }
} catch (e) {
const message = (e as any)?.stdout ?? (e as Error)?.message
return { ok: false, data: e, message }
}
return await spawnCoana(
[
'run',
cwd,
'--output-dir',
cwd,
'--socket-mode',
DOT_SOCKET_DOT_FACTS_JSON,
'--disable-report-submission',
...argv,
],
{ cwd },
)
}
10 changes: 10 additions & 0 deletions src/utils/cmd.mts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ export function cmdFlagsToString(args: string[]) {
return result.join(' ')
}

export function cmdFlagValueToArray(flagValue: any): string[] {
if (typeof flagValue === 'string') {
return flagValue.trim().split(/, */)
}
if (Array.isArray(flagValue)) {
return flagValue.flatMap(v => v.split(/, */))
}
return []
}
Comment on lines +19 to +27
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider replacing the any type for the flagValue parameter with a more specific type or unknown for better type safety. The current implementation suggests the parameter could be a string, an array, or potentially undefined, which could be more precisely typed as string | string[] | undefined. This would maintain the same functionality while providing better type checking and documentation.

Suggested change
export function cmdFlagValueToArray(flagValue: any): string[] {
if (typeof flagValue === 'string') {
return flagValue.trim().split(/, */)
}
if (Array.isArray(flagValue)) {
return flagValue.flatMap(v => v.split(/, */))
}
return []
}
export function cmdFlagValueToArray(flagValue: string | string[] | undefined): string[] {
if (typeof flagValue === 'string') {
return flagValue.trim().split(/, */)
}
if (Array.isArray(flagValue)) {
return flagValue.flatMap(v => v.split(/, */))
}
return []
}

Spotted by Diamond (based on custom rules)

Is this helpful? React 👍 or 👎 to let us know.


export function cmdPrefixMessage(cmdName: string, text: string): string {
const cmdPrefix = cmdName ? `${cmdName}: ` : ''
return `${cmdPrefix}${text}`
Expand Down
45 changes: 45 additions & 0 deletions src/utils/coana.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { spawn } from '@socketsecurity/registry/lib/spawn'

import constants from '../constants.mts'
import { getDefaultToken } from './sdk.mts'

import type { CResult } from '../types.mts'
import type {
SpawnExtra,
SpawnOptions,
} from '@socketsecurity/registry/lib/spawn'

export async function spawnCoana(
args: string[] | readonly string[],
options?: SpawnOptions | undefined,
extra?: SpawnExtra | undefined,
): Promise<CResult<unknown>> {
const { env: optionsEnv } = { __proto__: null, ...options } as SpawnOptions
try {
const output = await spawn(
constants.execPath,
[
// Lazily access constants.nodeNoWarningsFlags.
...constants.nodeNoWarningsFlags,
// Lazily access constants.coanaBinPath.
constants.coanaBinPath,
...args,
],
{
...options,
env: {
...process.env,
...optionsEnv,
SOCKET_CLI_API_BASE_URL:
constants.ENV.SOCKET_CLI_API_BASE_URL || undefined,
SOCKET_CLI_API_TOKEN: getDefaultToken(),
},
},
extra,
)
return { ok: true, data: output.stdout.trim() }
} catch (e) {
const message = (e as any)?.stdout ?? (e as Error)?.message
return { ok: false, data: e, message }
}
}