Skip to content

Commit b61aa9a

Browse files
committed
fix: warn about per-vulnerability reachability errors in scan output
1 parent 040ee14 commit b61aa9a

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

src/commands/scan/output-scan-reach.mts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { logger } from '@socketsecurity/registry/lib/logger'
2+
import { pluralize } from '@socketsecurity/registry/lib/words'
23

34
import constants from '../../constants.mts'
5+
import { extractReachabilityErrors } from '../../utils/coana.mts'
46
import { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'
57
import { serializeResultJson } from '../../utils/serialize-result-json.mts'
68

@@ -29,4 +31,20 @@ export async function outputScanReach(
2931
logger.log('')
3032
logger.success('Reachability analysis completed successfully!')
3133
logger.info(`Reachability report has been written to: ${actualOutputPath}`)
34+
35+
// Warn about individual vulnerabilities where reachability analysis errored.
36+
const errors = extractReachabilityErrors(
37+
result.data.reachabilityReport,
38+
)
39+
if (errors.length) {
40+
logger.log('')
41+
logger.warn(
42+
`Reachability analysis returned ${errors.length} ${pluralize('error', errors.length)} for individual ${pluralize('vulnerability', errors.length)}:`,
43+
)
44+
for (const err of errors) {
45+
logger.warn(
46+
` - ${err.ghsaId} in ${err.componentName}@${err.componentVersion} (${err.subprojectPath})`,
47+
)
48+
}
49+
}
3250
}

src/utils/coana.mts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,59 @@
1313

1414
import { readJsonSync } from '@socketsecurity/registry/lib/fs'
1515

16+
export type ReachabilityError = {
17+
componentName: string
18+
componentVersion: string
19+
ghsaId: string
20+
subprojectPath: string
21+
}
22+
23+
export function extractReachabilityErrors(
24+
socketFactsFile: string,
25+
): ReachabilityError[] {
26+
const json = readJsonSync(socketFactsFile, { throws: false }) as
27+
| {
28+
components?: Array<{
29+
name?: string
30+
reachability?: Array<{
31+
ghsa_id?: string
32+
reachability?: Array<{
33+
subprojectPath?: string
34+
type?: string
35+
}>
36+
}>
37+
version?: string
38+
}>
39+
}
40+
| null
41+
| undefined
42+
if (!json || !Array.isArray(json.components)) {
43+
return []
44+
}
45+
const errors: ReachabilityError[] = []
46+
for (const component of json.components) {
47+
if (!Array.isArray(component.reachability)) {
48+
continue
49+
}
50+
for (const ghsaEntry of component.reachability) {
51+
if (!Array.isArray(ghsaEntry.reachability)) {
52+
continue
53+
}
54+
for (const entry of ghsaEntry.reachability) {
55+
if (entry.type === 'error') {
56+
errors.push({
57+
componentName: String(component.name ?? ''),
58+
componentVersion: String(component.version ?? ''),
59+
ghsaId: String(ghsaEntry.ghsa_id ?? ''),
60+
subprojectPath: String(entry.subprojectPath ?? ''),
61+
})
62+
}
63+
}
64+
}
65+
}
66+
return errors
67+
}
68+
1669
export function extractTier1ReachabilityScanId(
1770
socketFactsFile: string,
1871
): string | undefined {

0 commit comments

Comments
 (0)