Skip to content

Commit 4b88093

Browse files
authored
Add coana fix to socket fix (#673)
1 parent b18555a commit 4b88093

File tree

5 files changed

+171
-66
lines changed

5 files changed

+171
-66
lines changed

src/commands/fix/cmd-fix.mts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { handleFix } from './handle-fix.mts'
99
import constants from '../../constants.mts'
1010
import { commonFlags } from '../../flags.mts'
1111
import { checkCommandInput } from '../../utils/check-input.mts'
12+
import { cmdFlagValueToArray } from '../../utils/cmd.mts'
1213
import { getOutputKind } from '../../utils/get-output-kind.mts'
1314
import { meowOrExit } from '../../utils/meow-with-subcommands.mts'
1415
import { getFlagListOutput } from '../../utils/output-formatting.mts'
@@ -38,6 +39,15 @@ const config: CliCommandConfig = {
3839
default: false,
3940
description: `Shorthand for --autoMerge --test`,
4041
},
42+
ghsa: {
43+
type: 'string',
44+
default: [],
45+
description: `Provide a list of ${terminalLink(
46+
'GHSA IDs',
47+
'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',
48+
)} to compute fixes for, as either a comma separated value or as multiple flags`,
49+
isMultiple: true,
50+
},
4151
limit: {
4252
type: 'number',
4353
default: Infinity,
@@ -47,9 +57,9 @@ const config: CliCommandConfig = {
4757
type: 'string',
4858
default: [],
4959
description: `Provide a list of ${terminalLink(
50-
'package URLs',
60+
'PURLs',
5161
'https://github.com/package-url/purl-spec?tab=readme-ov-file#purl',
52-
)} (PURLs) to fix, as either a comma separated value or as multiple flags,\n instead of querying the Socket API`,
62+
)} to compute fixes for, as either a comma separated value or as multiple flags,\n instead of querying the Socket API`,
5363
isMultiple: true,
5464
shortFlag: 'p',
5565
},
@@ -150,20 +160,18 @@ async function run(
150160
test = true
151161
}
152162

163+
const ghsas = cmdFlagValueToArray(cli.flags['ghsa'])
153164
const limit =
154165
(cli.flags['limit']
155166
? parseInt(String(cli.flags['limit'] || ''), 10)
156167
: Infinity) || Infinity
157-
158-
const purls: string[] = Array.isArray(cli.flags['purl'])
159-
? cli.flags['purl'].flatMap(p => p.split(/, */))
160-
: []
161-
168+
const purls = cmdFlagValueToArray(cli.flags['purl'])
162169
const testScript = String(cli.flags['testScript'] || 'test')
163170

164171
await handleFix({
165172
autoMerge,
166173
cwd,
174+
ghsas,
167175
limit,
168176
outputKind,
169177
purls,

src/commands/fix/handle-fix.mts

Lines changed: 87 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { outputFixResult } from './output-fix-result.mts'
55
import { pnpmFix } from './pnpm-fix.mts'
66
import { CMD_NAME } from './shared.mts'
77
import constants from '../../constants.mts'
8+
import { cmdFlagValueToArray } from '../../utils/cmd.mts'
9+
import { spawnCoana } from '../../utils/coana.mts'
810
import { detectAndValidatePackageEnvironment } from '../../utils/package-environment.mts'
911

1012
import type { OutputKind } from '../../types.mts'
@@ -15,6 +17,7 @@ const { NPM, PNPM } = constants
1517
export async function handleFix({
1618
autoMerge,
1719
cwd,
20+
ghsas,
1821
limit,
1922
outputKind,
2023
purls,
@@ -24,28 +27,81 @@ export async function handleFix({
2427
}: {
2528
autoMerge: boolean
2629
cwd: string
30+
ghsas: string[]
2731
limit: number
2832
outputKind: OutputKind
2933
purls: string[]
3034
rangeStyle: RangeStyle
3135
test: boolean
3236
testScript: string
3337
}) {
34-
const pkgEnvResult = await detectAndValidatePackageEnvironment(cwd, {
38+
let { length: ghsasCount } = ghsas
39+
if (ghsasCount) {
40+
// Lazily access constants.spinner.
41+
const { spinner } = constants
42+
43+
spinner.start()
44+
45+
if (ghsasCount === 1 && ghsas[0] === 'auto') {
46+
const autoCResult = await spawnCoana(
47+
['compute-fixes-and-upgrade-purls', cwd],
48+
{ cwd, spinner },
49+
)
50+
if (autoCResult.ok) {
51+
console.log(autoCResult.data)
52+
ghsas = cmdFlagValueToArray(
53+
/(?<=Vulnerabilities found: )[^\n]+/.exec(
54+
autoCResult.data as string,
55+
)?.[0],
56+
)
57+
ghsasCount = ghsas.length
58+
} else {
59+
ghsas = []
60+
ghsasCount = 0
61+
}
62+
}
63+
64+
spinner.stop()
65+
66+
if (ghsasCount) {
67+
spinner.start()
68+
await outputFixResult(
69+
await spawnCoana(
70+
[
71+
'compute-fixes-and-upgrade-purls',
72+
cwd,
73+
'--apply-fixes-to',
74+
...ghsas,
75+
],
76+
{ cwd, spinner },
77+
),
78+
outputKind,
79+
)
80+
spinner.stop()
81+
return
82+
}
83+
}
84+
85+
const pkgEnvCResult = await detectAndValidatePackageEnvironment(cwd, {
3586
cmdName: CMD_NAME,
3687
logger,
3788
})
38-
if (!pkgEnvResult.ok) {
39-
return pkgEnvResult
89+
if (!pkgEnvCResult.ok) {
90+
await outputFixResult(pkgEnvCResult, outputKind)
91+
return
4092
}
4193

42-
const pkgEnvDetails = pkgEnvResult.data
94+
const { data: pkgEnvDetails } = pkgEnvCResult
4395
if (!pkgEnvDetails) {
44-
return {
45-
ok: false,
46-
message: 'No package found',
47-
cause: `No valid package environment was found in given cwd (${cwd})`,
48-
}
96+
await outputFixResult(
97+
{
98+
ok: false,
99+
message: 'No package found',
100+
cause: `No valid package environment was found in given cwd (${cwd})`,
101+
},
102+
outputKind,
103+
)
104+
return
49105
}
50106

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

55111
const { agent } = pkgEnvDetails
56112
if (agent !== NPM && agent !== PNPM) {
57-
return {
58-
ok: false,
59-
message: 'Not supported',
60-
cause: `${agent} is not supported by this command at the moment.`,
61-
}
113+
await outputFixResult(
114+
{
115+
ok: false,
116+
message: 'Not supported',
117+
cause: `${agent} is not supported by this command at the moment.`,
118+
},
119+
outputKind,
120+
)
121+
return
62122
}
63123

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

68-
const result = await fixer(pkgEnvDetails, {
69-
autoMerge,
70-
cwd,
71-
limit,
72-
purls,
73-
rangeStyle,
74-
spinner,
75-
test,
76-
testScript,
77-
})
78-
79-
await outputFixResult(result, outputKind)
128+
await outputFixResult(
129+
await fixer(pkgEnvDetails, {
130+
autoMerge,
131+
cwd,
132+
limit,
133+
purls,
134+
rangeStyle,
135+
spinner,
136+
test,
137+
testScript,
138+
}),
139+
outputKind,
140+
)
80141
}
Lines changed: 14 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import { spawn } from '@socketsecurity/registry/lib/spawn'
2-
31
import constants from '../../constants.mts'
4-
import { getDefaultToken } from '../../utils/sdk.mts'
2+
import { spawnCoana } from '../../utils/coana.mts'
53

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

@@ -11,34 +9,17 @@ export async function scanReachability(
119
argv: string[] | readonly string[],
1210
cwd: string,
1311
): Promise<CResult<unknown>> {
14-
try {
15-
const result = await spawn(
16-
constants.execPath,
17-
[
18-
// Lazily access constants.nodeNoWarningsFlags.
19-
...constants.nodeNoWarningsFlags,
20-
// Lazily access constants.coanaBinPath.
21-
constants.coanaBinPath,
22-
'run',
23-
cwd,
24-
'--output-dir',
25-
cwd,
26-
'--socket-mode',
27-
DOT_SOCKET_DOT_FACTS_JSON,
28-
'--disable-report-submission',
29-
...argv,
30-
],
31-
{
32-
cwd,
33-
env: {
34-
...process.env,
35-
SOCKET_CLI_API_TOKEN: getDefaultToken(),
36-
},
37-
},
38-
)
39-
return { ok: true, data: result.stdout.trim() }
40-
} catch (e) {
41-
const message = (e as any)?.stdout ?? (e as Error)?.message
42-
return { ok: false, data: e, message }
43-
}
12+
return await spawnCoana(
13+
[
14+
'run',
15+
cwd,
16+
'--output-dir',
17+
cwd,
18+
'--socket-mode',
19+
DOT_SOCKET_DOT_FACTS_JSON,
20+
'--disable-report-submission',
21+
...argv,
22+
],
23+
{ cwd },
24+
)
4425
}

src/utils/cmd.mts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ export function cmdFlagsToString(args: string[]) {
1616
return result.join(' ')
1717
}
1818

19+
export function cmdFlagValueToArray(flagValue: any): string[] {
20+
if (typeof flagValue === 'string') {
21+
return flagValue.trim().split(/, */)
22+
}
23+
if (Array.isArray(flagValue)) {
24+
return flagValue.flatMap(v => v.split(/, */))
25+
}
26+
return []
27+
}
28+
1929
export function cmdPrefixMessage(cmdName: string, text: string): string {
2030
const cmdPrefix = cmdName ? `${cmdName}: ` : ''
2131
return `${cmdPrefix}${text}`

src/utils/coana.mts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { spawn } from '@socketsecurity/registry/lib/spawn'
2+
3+
import constants from '../constants.mts'
4+
import { getDefaultToken } from './sdk.mts'
5+
6+
import type { CResult } from '../types.mts'
7+
import type {
8+
SpawnExtra,
9+
SpawnOptions,
10+
} from '@socketsecurity/registry/lib/spawn'
11+
12+
export async function spawnCoana(
13+
args: string[] | readonly string[],
14+
options?: SpawnOptions | undefined,
15+
extra?: SpawnExtra | undefined,
16+
): Promise<CResult<unknown>> {
17+
const { env: optionsEnv } = { __proto__: null, ...options } as SpawnOptions
18+
try {
19+
const output = await spawn(
20+
constants.execPath,
21+
[
22+
// Lazily access constants.nodeNoWarningsFlags.
23+
...constants.nodeNoWarningsFlags,
24+
// Lazily access constants.coanaBinPath.
25+
constants.coanaBinPath,
26+
...args,
27+
],
28+
{
29+
...options,
30+
env: {
31+
...process.env,
32+
...optionsEnv,
33+
SOCKET_CLI_API_BASE_URL:
34+
constants.ENV.SOCKET_CLI_API_BASE_URL || undefined,
35+
SOCKET_CLI_API_TOKEN: getDefaultToken(),
36+
},
37+
},
38+
extra,
39+
)
40+
return { ok: true, data: output.stdout.trim() }
41+
} catch (e) {
42+
const message = (e as any)?.stdout ?? (e as Error)?.message
43+
return { ok: false, data: e, message }
44+
}
45+
}

0 commit comments

Comments
 (0)