Skip to content

Commit 4f686c7

Browse files
committed
refactor(fix): use find-vulnerabilities for GHSA discovery
Ported from v1.x commit cdd5971 (#958) - Replace compute-fixes-and-upgrade-purls discovery with simpler find-vulnerabilities command - Parse GHSA IDs from JSON output on stdout instead of temp file - Remove temp file creation/cleanup for discovery - Add error handling for JSON parsing - Simplify discovery logic with direct stdout parsing The find-vulnerabilities command is more efficient and cleaner than using compute-fixes-and-upgrade-purls with --output-file for discovery. Based on PR #958
1 parent 678554b commit 4f686c7

File tree

2 files changed

+37
-60
lines changed

2 files changed

+37
-60
lines changed

packages/cli/src/commands/fix/coana-fix.mts

Lines changed: 20 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ export async function coanaFix(
148148
}
149149
}
150150

151-
const shouldDiscoverGhsaIds = all || !ghsas.length
151+
const shouldDiscoverGhsaIds = all || !ghsas.length || (ghsas.length === 1 && ghsas[0] === 'all')
152152

153153
const shouldOpenPrs = fixEnv.isCi && fixEnv.repoInfo
154154

@@ -180,9 +180,9 @@ export async function coanaFix(
180180
}
181181
}
182182

183-
// In local mode, process all discovered/provided IDs (no limit).
184-
const ids = shouldDiscoverGhsaIds ? ['all'] : ghsas
185-
if (!ids.length) {
183+
// In local mode, apply limit to provided IDs.
184+
const idsToProcess = shouldDiscoverGhsaIds ? ['all'] : ghsas.slice(0, prLimit)
185+
if (!idsToProcess.length) {
186186
spinner?.stop()
187187
return { ok: true, data: { fixed: false } }
188188
}
@@ -199,7 +199,7 @@ export async function coanaFix(
199199
'--manifests-tar-hash',
200200
tarHash,
201201
'--apply-fixes-to',
202-
...ids,
202+
...idsToProcess,
203203
...(fixConfig.rangeStyle
204204
? ['--range-style', fixConfig.rangeStyle]
205205
: []),
@@ -271,52 +271,31 @@ export async function coanaFix(
271271

272272
let ids: string[] | undefined
273273

274-
// When shouldDiscoverGhsaIds is true, discover vulnerabilities by running coana with --output-file.
274+
// When shouldDiscoverGhsaIds is true, discover vulnerabilities using find-vulnerabilities command.
275275
// This gives us the GHSA IDs needed to create individual PRs in CI mode.
276276
if (shouldSpawnCoana && shouldDiscoverGhsaIds) {
277-
const discoverTmpFile = path.join(
278-
os.tmpdir(),
279-
`socket-discover-${Date.now()}.json`,
280-
)
281-
282277
try {
283278
const discoverCResult = await spawnCoanaDlx(
284-
[
285-
'compute-fixes-and-upgrade-purls',
286-
cwd,
287-
'--manifests-tar-hash',
288-
tarHash,
289-
'--show-affected-direct-dependencies',
290-
'--output-file',
291-
discoverTmpFile,
292-
...(fixConfig.rangeStyle
293-
? ['--range-style', fixConfig.rangeStyle]
294-
: []),
295-
...(minimumReleaseAge
296-
? ['--minimum-release-age', minimumReleaseAge]
297-
: []),
298-
...(include.length ? ['--include', ...include] : []),
299-
...(exclude.length ? ['--exclude', ...exclude] : []),
300-
...(disableMajorUpdates ? ['--disable-major-updates'] : []),
301-
...fixConfig.unknownFlags,
302-
],
279+
['find-vulnerabilities', cwd, '--manifests-tar-hash', tarHash],
303280
fixConfig.orgSlug,
304-
{ coanaVersion, cwd, spinner, stdio: coanaStdio },
281+
{ coanaVersion, cwd, spinner },
282+
{ stdio: 'pipe' },
305283
)
306284

307285
if (discoverCResult.ok) {
308-
const discoverResult = readJsonSync(discoverTmpFile, { throws: false })
309-
// Extract GHSA IDs from the discovery result.
310-
// When compute-fixes-and-upgrade-purls is called without --apply-fixes-to,
311-
// it returns { type: 'no-ghsas-fix-requested', ghsas: [...] }
312-
const discoveredIds = Array.isArray((discoverResult as any)?.ghsas)
313-
? (discoverResult as any).ghsas
314-
: []
286+
// Coana prints ghsaIds as json-formatted string on the final line of the output.
287+
const discoveredIds: string[] = []
288+
try {
289+
const ghsaIdsRaw = discoverCResult.data.trim().split('\n').pop()
290+
if (ghsaIdsRaw) {
291+
discoveredIds.push(...JSON.parse(ghsaIdsRaw))
292+
}
293+
} catch (e) {
294+
debug('Failed to parse GHSA IDs from find-vulnerabilities output')
295+
debugDir(e)
296+
}
315297
ids = discoveredIds.slice(0, adjustedLimit)
316298
}
317-
318-
// Clean up discovery temp file.
319-
await cleanupTempFile(discoverTmpFile)
320299
} catch (e) {
321300
debug('Failed to discover vulnerabilities')
322301
debugDir(e)

packages/cli/test/unit/commands/fix/handle-fix-limit.test.mts

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ describe('socket fix --limit behavior verification', () => {
208208
const result = await coanaFix({
209209
...baseConfig,
210210
ghsas,
211-
limit: 3,
211+
prLimit: 3,
212212
})
213213

214214
expect(result.ok).toBe(true)
@@ -242,7 +242,7 @@ describe('socket fix --limit behavior verification', () => {
242242
const result = await coanaFix({
243243
...baseConfig,
244244
ghsas,
245-
limit: 10,
245+
prLimit: 10,
246246
})
247247

248248
expect(result.ok).toBe(true)
@@ -267,7 +267,7 @@ describe('socket fix --limit behavior verification', () => {
267267
const result = await coanaFix({
268268
...baseConfig,
269269
ghsas,
270-
limit: 0,
270+
prLimit: 0,
271271
})
272272

273273
expect(result.ok).toBe(true)
@@ -286,7 +286,7 @@ describe('socket fix --limit behavior verification', () => {
286286
const result = await coanaFix({
287287
...baseConfig,
288288
ghsas: ['all'],
289-
limit: 10,
289+
prLimit: 10,
290290
})
291291

292292
expect(result.ok).toBe(true)
@@ -325,13 +325,10 @@ describe('socket fix --limit behavior verification', () => {
325325
'GHSA-dddd-dddd-dddd',
326326
]
327327

328-
// Mock discovery call result with proper ghsas format.
329-
mockReadJsonSync.mockReturnValueOnce({ ghsas })
330-
331-
// Discovery call writes to output file.
328+
// Mock discovery call result with JSON output on last line.
332329
mockSpawnCoanaDlx.mockResolvedValueOnce({
333330
ok: true,
334-
data: '',
331+
data: `Some discovery output\n${JSON.stringify(ghsas)}`,
335332
})
336333

337334
// Subsequent calls are for individual GHSA fixes.
@@ -345,10 +342,12 @@ describe('socket fix --limit behavior verification', () => {
345342
data: ['package.json'],
346343
})
347344

345+
mockReadJsonSync.mockReturnValue({ fixed: true })
346+
348347
const result = await coanaFix({
349348
...baseConfig,
350349
ghsas: ['all'],
351-
limit: 2,
350+
prLimit: 2,
352351
})
353352

354353
expect(result.ok).toBe(true)
@@ -372,13 +371,10 @@ describe('socket fix --limit behavior verification', () => {
372371
// Second call returns no open PRs for specific GHSAs.
373372
mockGetSocketFixPrs.mockResolvedValue([])
374373

375-
// Mock discovery call result with proper ghsas format.
376-
mockReadJsonSync.mockReturnValueOnce({ ghsas })
377-
378-
// Discovery call writes to output file.
374+
// Mock discovery call result with JSON output on last line.
379375
mockSpawnCoanaDlx.mockResolvedValueOnce({
380376
ok: true,
381-
data: '',
377+
data: `Some discovery output\n${JSON.stringify(ghsas)}`,
382378
})
383379

384380
mockSpawnCoanaDlx.mockResolvedValue({
@@ -391,10 +387,12 @@ describe('socket fix --limit behavior verification', () => {
391387
data: ['package.json'],
392388
})
393389

390+
mockReadJsonSync.mockReturnValue({ fixed: true })
391+
394392
const result = await coanaFix({
395393
...baseConfig,
396394
ghsas: ['all'],
397-
limit: 3,
395+
prLimit: 3,
398396
})
399397

400398
expect(result.ok).toBe(true)
@@ -417,7 +415,7 @@ describe('socket fix --limit behavior verification', () => {
417415
const result = await coanaFix({
418416
...baseConfig,
419417
ghsas: ['all'],
420-
limit: 3,
418+
prLimit: 3,
421419
})
422420

423421
expect(result.ok).toBe(true)
@@ -446,7 +444,7 @@ describe('socket fix --limit behavior verification', () => {
446444
const result = await coanaFix({
447445
...baseConfig,
448446
ghsas,
449-
limit: 2,
447+
prLimit: 2,
450448
})
451449

452450
expect(result.ok).toBe(true)
@@ -474,7 +472,7 @@ describe('socket fix --limit behavior verification', () => {
474472
const result = await coanaFix({
475473
...baseConfig,
476474
ghsas,
477-
limit: 1,
475+
prLimit: 1,
478476
})
479477

480478
expect(result.ok).toBe(true)

0 commit comments

Comments
 (0)