Skip to content

Commit 0a2c825

Browse files
committed
Continue to move alerts together
1 parent e085e37 commit 0a2c825

File tree

5 files changed

+145
-134
lines changed

5 files changed

+145
-134
lines changed

src/commands/info.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
getSocketDevPackageOverviewUrl
2020
} from '../utils/socket-url'
2121

22-
import type { SocketAlert } from '../utils/alert/severity'
22+
import type { SocketSdkAlert } from '../utils/alert/severity'
2323
import type { CliSubcommand } from '../utils/meow-with-subcommands'
2424
import type { SocketSdkReturnType } from '@socketsecurity/sdk'
2525

@@ -129,7 +129,7 @@ function setupCommand(
129129

130130
interface PackageData {
131131
data: SocketSdkReturnType<'getIssuesByNPMPackage'>['data']
132-
severityCount: Record<SocketAlert['severity'], number>
132+
severityCount: Record<SocketSdkAlert['severity'], number>
133133
score: SocketSdkReturnType<'getScoreByNPMPackage'>['data']
134134
}
135135

src/shadow/arborist/lib/arborist/reify.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,26 @@ import {
1212
import { confirm } from '@socketsecurity/registry/lib/prompts'
1313
import { Spinner } from '@socketsecurity/registry/lib/spinner'
1414

15-
import { batchScan, isAlertFixable, isAlertFixableCve, walk } from './alerts'
1615
import { kCtorArgs, kRiskyReify } from './index'
16+
import { walk } from './walk'
1717
import constants from '../../../../constants'
1818
import { uxLookup } from '../../../../utils/alert/rules'
19+
import {
20+
batchScan,
21+
isAlertFixable,
22+
isAlertFixableCve
23+
} from '../../../../utils/alert/scan'
1924
import { ColorOrMarkdown } from '../../../../utils/color-or-markdown'
2025
import { debugLog } from '../../../../utils/debug'
2126
import { getSocketDevPackageOverviewUrl } from '../../../../utils/socket-url'
2227
import { pacotePath } from '../../../npm-paths'
2328
import { Edge, SafeEdge } from '../edge'
2429

25-
import type { InstallEffect, SocketArtifact } from './alerts'
2630
import type { ArboristClass, AuditAdvisory, SafeArborist } from './index'
31+
import type {
32+
InstallEffect,
33+
SocketScanArtifact
34+
} from '../../../../utils/alert/scan'
2735
import type { SafeNode } from '../node'
2836
import type { Writable } from 'node:stream'
2937

@@ -163,7 +171,7 @@ async function getPackagesAlerts(
163171
p.existing?.startsWith(`${name}@`)
164172
)?.existing
165173
if (existing) {
166-
const oldArtifact: SocketArtifact | undefined =
174+
const oldArtifact: SocketScanArtifact | undefined =
167175
// eslint-disable-next-line no-await-in-loop
168176
(await batchScan([existing]).next()).value
169177
if (oldArtifact?.alerts?.length) {
@@ -364,7 +372,7 @@ export async function reify(
364372
this: SafeArborist,
365373
...args: Parameters<InstanceType<ArboristClass>['reify']>
366374
): Promise<SafeNode> {
367-
const needInfoOn = await walk(this.diff)
375+
const needInfoOn = walk(this.diff)
368376
if (
369377
!needInfoOn.length ||
370378
needInfoOn.findIndex(c => c.repository_url === NPM_REGISTRY_URL) === -1
@@ -421,13 +429,9 @@ export async function reify(
421429
ret = await this[kRiskyReify](...args)
422430
await this.loadActual()
423431
await this.buildIdealTree()
424-
alerts = await getPackagesAlerts(
425-
this,
426-
await walk(this.diff, { fix: true }),
427-
{
428-
fixable: true
429-
}
430-
)
432+
alerts = await getPackagesAlerts(this, walk(this.diff, { fix: true }), {
433+
fixable: true
434+
})
431435
alerts = alerts.filter(a => {
432436
const { key } = a
433437
if (prev.has(key)) {

src/shadow/arborist/lib/arborist/alerts.ts renamed to src/shadow/arborist/lib/arborist/walk.ts

Lines changed: 7 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,9 @@
1-
import events from 'node:events'
2-
import https from 'node:https'
3-
import rl from 'node:readline'
4-
51
import constants from '../../../../constants'
6-
import { getPublicToken } from '../../../../utils/sdk'
72

83
import type { SafeNode } from '../node'
94
import type { Diff } from '@npmcli/arborist'
105

11-
export type InstallEffect = {
12-
pkgid: SafeNode['pkgid']
13-
repository_url: string
14-
existing?: SafeNode['pkgid'] | undefined
15-
}
16-
17-
export type SocketAlert = {
18-
key: string
19-
type: string
20-
severity: string
21-
category: string
22-
action?: string
23-
actionPolicyIndex?: number
24-
file?: string
25-
props?: any
26-
start?: number
27-
end?: number
28-
}
29-
30-
export type SocketArtifact = {
31-
type: string
32-
namespace?: string
33-
name?: string
34-
version?: string
35-
subpath?: string
36-
release?: string
37-
id?: string
38-
author?: string[]
39-
license?: string
40-
licenseDetails?: {
41-
spdxDisj: string
42-
provenance: string
43-
filepath: string
44-
match_strength: number
45-
}[]
46-
licenseAttrib?: {
47-
attribText: string
48-
attribData: {
49-
purl: string
50-
foundInFilepath: string
51-
spdxExpr: string
52-
foundAuthors: string[]
53-
}[]
54-
}[]
55-
score?: {
56-
supplyChain: number
57-
quality: number
58-
maintenance: number
59-
vulnerability: number
60-
license: number
61-
overall: number
62-
}
63-
alerts?: SocketAlert[]
64-
size?: number
65-
batchIndex?: number
66-
}
67-
68-
const {
69-
API_V0_URL,
70-
LOOP_SENTINEL,
71-
SOCKET_CLI_FIX_PACKAGE_LOCK_FILE,
72-
abortSignal
73-
} = constants
74-
75-
export async function* batchScan(
76-
pkgIds: string[]
77-
): AsyncGenerator<SocketArtifact> {
78-
const req = https
79-
.request(`${API_V0_URL}/purl?alerts=true`, {
80-
method: 'POST',
81-
headers: {
82-
Authorization: `Basic ${Buffer.from(`${getPublicToken()}:`).toString('base64url')}`
83-
},
84-
signal: abortSignal
85-
})
86-
.end(
87-
JSON.stringify({
88-
components: pkgIds.map(id => ({ purl: `pkg:npm/${id}` }))
89-
})
90-
)
91-
const { 0: res } = await events.once(req, 'response')
92-
const ok = res.statusCode >= 200 && res.statusCode <= 299
93-
if (!ok) {
94-
throw new Error(`Socket API Error: ${res.statusCode}`)
95-
}
96-
const rli = rl.createInterface(res)
97-
for await (const line of rli) {
98-
yield JSON.parse(line)
99-
}
100-
}
101-
102-
export function isAlertFixable(alert: SocketAlert): boolean {
103-
return alert.type === 'socketUpgradeAvailable' || isAlertFixableCve(alert)
104-
}
105-
106-
export function isAlertFixableCve(alert: SocketAlert): boolean {
107-
const { type } = alert
108-
return (
109-
(type === 'cve' ||
110-
type === 'mediumCVE' ||
111-
type === 'mildCVE' ||
112-
type === 'criticalCVE') &&
113-
!!alert.props?.['firstPatchedVersionIdentifier']
114-
)
115-
}
6+
const { LOOP_SENTINEL, SOCKET_CLI_FIX_PACKAGE_LOCK_FILE } = constants
1167

1178
function toRepoUrl(resolved: string): string {
1189
try {
@@ -121,6 +12,12 @@ function toRepoUrl(resolved: string): string {
12112
return ''
12213
}
12314

15+
export type InstallEffect = {
16+
pkgid: SafeNode['pkgid']
17+
repository_url: string
18+
existing?: SafeNode['pkgid'] | undefined
19+
}
20+
12421
export type WalkOptions = { fix?: boolean }
12522

12623
export function walk(

src/utils/alert/scan.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import events from 'node:events'
2+
import https from 'node:https'
3+
import rl from 'node:readline'
4+
5+
import constants from '../../constants'
6+
import { getPublicToken } from '../sdk'
7+
8+
import type { SafeNode } from '../../shadow/arborist/lib/node'
9+
10+
export type InstallEffect = {
11+
pkgid: SafeNode['pkgid']
12+
repository_url: string
13+
existing?: SafeNode['pkgid'] | undefined
14+
}
15+
16+
export type SocketScanArtifactAlert = {
17+
key: string
18+
type: string
19+
severity: string
20+
category: string
21+
action?: string
22+
actionPolicyIndex?: number
23+
file?: string
24+
props?: any
25+
start?: number
26+
end?: number
27+
}
28+
29+
export type SocketScanArtifact = {
30+
type: string
31+
namespace?: string
32+
name?: string
33+
version?: string
34+
subpath?: string
35+
release?: string
36+
id?: string
37+
author?: string[]
38+
license?: string
39+
licenseDetails?: {
40+
spdxDisj: string
41+
provenance: string
42+
filepath: string
43+
match_strength: number
44+
}[]
45+
licenseAttrib?: {
46+
attribText: string
47+
attribData: {
48+
purl: string
49+
foundInFilepath: string
50+
spdxExpr: string
51+
foundAuthors: string[]
52+
}[]
53+
}[]
54+
score?: {
55+
supplyChain: number
56+
quality: number
57+
maintenance: number
58+
vulnerability: number
59+
license: number
60+
overall: number
61+
}
62+
alerts?: SocketScanArtifactAlert[]
63+
size?: number
64+
batchIndex?: number
65+
}
66+
67+
const { API_V0_URL, abortSignal } = constants
68+
69+
export async function* batchScan(
70+
pkgIds: string[]
71+
): AsyncGenerator<SocketScanArtifact> {
72+
const req = https
73+
.request(`${API_V0_URL}/purl?alerts=true`, {
74+
method: 'POST',
75+
headers: {
76+
Authorization: `Basic ${Buffer.from(`${getPublicToken()}:`).toString('base64url')}`
77+
},
78+
signal: abortSignal
79+
})
80+
.end(
81+
JSON.stringify({
82+
components: pkgIds.map(id => ({ purl: `pkg:npm/${id}` }))
83+
})
84+
)
85+
const { 0: res } = await events.once(req, 'response')
86+
const ok = res.statusCode >= 200 && res.statusCode <= 299
87+
if (!ok) {
88+
throw new Error(`Socket API Error: ${res.statusCode}`)
89+
}
90+
const rli = rl.createInterface(res)
91+
for await (const line of rli) {
92+
yield JSON.parse(line)
93+
}
94+
}
95+
96+
export function isAlertFixable(alert: SocketScanArtifactAlert): boolean {
97+
return alert.type === 'socketUpgradeAvailable' || isAlertFixableCve(alert)
98+
}
99+
100+
export function isAlertFixableCve(alert: SocketScanArtifactAlert): boolean {
101+
const { type } = alert
102+
return (
103+
(type === 'cve' ||
104+
type === 'mediumCVE' ||
105+
type === 'mildCVE' ||
106+
type === 'criticalCVE') &&
107+
!!alert.props?.['firstPatchedVersionIdentifier']
108+
)
109+
}

src/utils/alert/severity.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,27 @@ import { stringJoinWithSeparateFinalSeparator } from '../strings'
33

44
import type { SocketSdkReturnType } from '@socketsecurity/sdk'
55

6-
type SocketAlertList = SocketSdkReturnType<'getIssuesByNPMPackage'>['data']
6+
export type SocketSdkAlertList =
7+
SocketSdkReturnType<'getIssuesByNPMPackage'>['data']
78

8-
export type SocketAlert = SocketAlertList[number]['value'] extends
9+
export type SocketSdkAlert = SocketSdkAlertList[number]['value'] extends
910
| infer U
1011
| undefined
1112
? U
1213
: never
1314

1415
// Ordered from most severe to least.
15-
const SEVERITIES_BY_ORDER: SocketAlert['severity'][] = [
16+
const SEVERITIES_BY_ORDER: SocketSdkAlert['severity'][] = [
1617
'critical',
1718
'high',
1819
'middle',
1920
'low'
2021
]
2122

2223
function getDesiredSeverities(
23-
lowestToInclude: SocketAlert['severity'] | undefined
24-
): SocketAlert['severity'][] {
25-
const result: SocketAlert['severity'][] = []
24+
lowestToInclude: SocketSdkAlert['severity'] | undefined
25+
): SocketSdkAlert['severity'][] {
26+
const result: SocketSdkAlert['severity'][] = []
2627
for (const severity of SEVERITIES_BY_ORDER) {
2728
result.push(severity)
2829
if (severity === lowestToInclude) {
@@ -33,7 +34,7 @@ function getDesiredSeverities(
3334
}
3435

3536
export function formatSeverityCount(
36-
severityCount: Record<SocketAlert['severity'], number>
37+
severityCount: Record<SocketSdkAlert['severity'], number>
3738
): string {
3839
const summary: string[] = []
3940
for (const severity of SEVERITIES_BY_ORDER) {
@@ -45,13 +46,13 @@ export function formatSeverityCount(
4546
}
4647

4748
export function getSeverityCount(
48-
issues: SocketAlertList,
49-
lowestToInclude: SocketAlert['severity'] | undefined
50-
): Record<SocketAlert['severity'], number> {
49+
issues: SocketSdkAlertList,
50+
lowestToInclude: SocketSdkAlert['severity'] | undefined
51+
): Record<SocketSdkAlert['severity'], number> {
5152
const severityCount = pick(
5253
{ low: 0, middle: 0, high: 0, critical: 0 },
5354
getDesiredSeverities(lowestToInclude)
54-
) as Record<SocketAlert['severity'], number>
55+
) as Record<SocketSdkAlert['severity'], number>
5556

5657
for (const issue of issues) {
5758
const { value } = issue

0 commit comments

Comments
 (0)