Skip to content

Commit bc1b372

Browse files
committed
Add socket patch stub
1 parent c231525 commit bc1b372

File tree

6 files changed

+217
-5
lines changed

6 files changed

+217
-5
lines changed

src/commands.mts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { cmdOrganizationPolicyLicense } from './commands/organization/cmd-organi
2020
import { cmdOrganizationPolicySecurity } from './commands/organization/cmd-organization-policy-security.mts'
2121
import { cmdOrganization } from './commands/organization/cmd-organization.mts'
2222
import { cmdPackage } from './commands/package/cmd-package.mts'
23+
import { cmdPatch } from './commands/patch/cmd-patch.mts'
2324
import { cmdRawNpm } from './commands/raw-npm/cmd-raw-npm.mts'
2425
import { cmdRawNpx } from './commands/raw-npx/cmd-raw-npx.mts'
2526
import { cmdRepository } from './commands/repository/cmd-repository.mts'
@@ -48,6 +49,7 @@ export const rootCommands = {
4849
optimize: cmdOptimize,
4950
organization: cmdOrganization,
5051
package: cmdPackage,
52+
patch: cmdPatch,
5153
'raw-npm': cmdRawNpm,
5254
'raw-npx': cmdRawNpx,
5355
repository: cmdRepository,

src/commands/fix/pull-request.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ export async function cleanupPrs(
187187
debugFn(
188188
'error',
189189
`pr: failed to close ${prRef} for ${prToVersion}\n`,
190-
(e as Error)?.message || 'unknown error',
190+
(e as Error)?.message || 'Unknown error',
191191
)
192192
}
193193
}

src/commands/patch/cmd-patch.mts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import path from 'node:path'
2+
3+
import { logger } from '@socketsecurity/registry/lib/logger'
4+
5+
import { handlePatch } from './handle-patch.mts'
6+
import constants from '../../constants.mts'
7+
import { commonFlags, outputFlags } from '../../flags.mts'
8+
import { checkCommandInput } from '../../utils/check-input.mts'
9+
import { getOutputKind } from '../../utils/get-output-kind.mts'
10+
import { meowOrExit } from '../../utils/meow-with-subcommands.mts'
11+
import {
12+
getFlagApiRequirementsOutput,
13+
getFlagListOutput,
14+
} from '../../utils/output-formatting.mts'
15+
16+
import type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'
17+
18+
const { DRY_RUN_NOT_SAVING } = constants
19+
20+
export const CMD_NAME = 'patch'
21+
22+
const description = 'Apply security patches to dependencies'
23+
24+
const hidden = false
25+
26+
export const cmdPatch = {
27+
description,
28+
hidden,
29+
run,
30+
}
31+
32+
async function run(
33+
argv: string[] | readonly string[],
34+
importMeta: ImportMeta,
35+
{ parentName }: { parentName: string },
36+
): Promise<void> {
37+
const config: CliCommandConfig = {
38+
commandName: CMD_NAME,
39+
description,
40+
hidden,
41+
flags: {
42+
...commonFlags,
43+
...outputFlags,
44+
package: {
45+
type: 'string',
46+
default: [],
47+
description:
48+
'Specify packages to patch, as either a comma separated value or as multiple flags',
49+
isMultiple: true,
50+
shortFlag: 'p',
51+
},
52+
},
53+
help: (command, config) => `
54+
Usage
55+
$ ${command} [options] [CWD=.]
56+
57+
API Token Requirements
58+
${getFlagApiRequirementsOutput(`${parentName}:${CMD_NAME}`)}
59+
60+
Options
61+
${getFlagListOutput(config.flags)}
62+
63+
Examples
64+
$ ${command}
65+
$ ${command} --package lodash
66+
$ ${command} ./proj/tree --package lodash,react
67+
`,
68+
}
69+
70+
const cli = meowOrExit({
71+
allowUnknownFlags: false,
72+
argv,
73+
config,
74+
importMeta,
75+
parentName,
76+
})
77+
78+
const dryRun = !!cli.flags['dryRun']
79+
const outputKind = getOutputKind(cli.flags['json'], cli.flags['markdown'])
80+
81+
const wasValidInput = checkCommandInput(outputKind, {
82+
nook: true,
83+
test: !cli.flags['json'] || !cli.flags['markdown'],
84+
message: 'The json and markdown flags cannot be both set, pick one',
85+
fail: 'omit one',
86+
})
87+
if (!wasValidInput) {
88+
return
89+
}
90+
91+
if (dryRun) {
92+
logger.log(DRY_RUN_NOT_SAVING)
93+
return
94+
}
95+
96+
let [cwd = '.'] = cli.input
97+
// Note: path.resolve vs .join:
98+
// If given path is absolute then cwd should not affect it.
99+
cwd = path.resolve(process.cwd(), cwd)
100+
101+
// Lazily access constants.spinner.
102+
const { spinner } = constants
103+
104+
const packages = Array.isArray(cli.flags['package'])
105+
? cli.flags['package'].flatMap(p => String(p).split(','))
106+
: String(cli.flags['package'] || '')
107+
.split(',')
108+
.filter(Boolean)
109+
110+
await handlePatch({
111+
cwd,
112+
outputKind,
113+
packages,
114+
spinner,
115+
})
116+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { logger } from '@socketsecurity/registry/lib/logger'
2+
3+
import { outputPatchResult } from './output-patch-result.mts'
4+
import constants from '../../constants.mts'
5+
6+
import type { CResult, OutputKind } from '../../types.mts'
7+
8+
export interface HandlePatchConfig {
9+
cwd: string
10+
outputKind: OutputKind
11+
packages: string[]
12+
spinner: typeof constants.spinner
13+
}
14+
15+
export async function handlePatch({
16+
outputKind,
17+
packages,
18+
spinner,
19+
}: HandlePatchConfig): Promise<void> {
20+
spinner.start('Analyzing dependencies for security patches...')
21+
22+
try {
23+
// TODO: Implement actual patch logic
24+
// This is a stub implementation
25+
const result: CResult<{ patchedPackages: string[] }> = {
26+
ok: true,
27+
data: {
28+
patchedPackages: packages.length > 0 ? packages : ['example-package'],
29+
},
30+
}
31+
32+
spinner.stop()
33+
34+
logger.log('')
35+
if (packages.length > 0) {
36+
logger.info(`Checking patches for: ${packages.join(', ')}`)
37+
} else {
38+
logger.info('Scanning all dependencies for available patches')
39+
}
40+
logger.log('')
41+
42+
await outputPatchResult(result, outputKind)
43+
} catch (e) {
44+
spinner.stop()
45+
46+
const result: CResult<never> = {
47+
ok: false,
48+
code: 1,
49+
message: 'Failed to apply patches',
50+
cause: (e as Error)?.message || 'Unknown error',
51+
}
52+
53+
await outputPatchResult(result, outputKind)
54+
}
55+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { logger } from '@socketsecurity/registry/lib/logger'
2+
3+
import { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'
4+
import { serializeResultJson } from '../../utils/serialize-result-json.mts'
5+
6+
import type { CResult, OutputKind } from '../../types.mts'
7+
8+
export async function outputPatchResult(
9+
result: CResult<{ patchedPackages: string[] }>,
10+
outputKind: OutputKind,
11+
) {
12+
if (!result.ok) {
13+
process.exitCode = result.code ?? 1
14+
}
15+
16+
if (outputKind === 'json') {
17+
logger.log(serializeResultJson(result))
18+
return
19+
}
20+
21+
if (!result.ok) {
22+
logger.fail(failMsgWithBadge(result.message, result.cause))
23+
return
24+
}
25+
26+
const { patchedPackages } = result.data
27+
28+
if (patchedPackages.length > 0) {
29+
logger.success(
30+
`Successfully processed patches for ${patchedPackages.length} package(s):`,
31+
)
32+
for (const pkg of patchedPackages) {
33+
logger.success(pkg)
34+
}
35+
} else {
36+
logger.info('No packages found requiring patches')
37+
}
38+
39+
logger.log('')
40+
logger.success('Patch command completed!')
41+
}

src/utils/meow-with-subcommands.mts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ export async function meowWithSubcommands(
355355
'optimize',
356356
'organization',
357357
'package',
358+
'patch',
358359
'raw-npm',
359360
'raw-npx',
360361
'repository',
@@ -371,10 +372,7 @@ export async function meowWithSubcommands(
371372
if (commands.has(name)) {
372373
commands.delete(name)
373374
} else {
374-
logger.fail(
375-
'Received a visible command that was not added to the list here:',
376-
name,
377-
)
375+
logger.fail('Received an unknown command:', name)
378376
}
379377
})
380378
if (commands.size) {

0 commit comments

Comments
 (0)