Skip to content

Commit 7929faf

Browse files
authored
Merge branch 'main' into enterprise-filter
2 parents 4acc38f + a1dbb8d commit 7929faf

19 files changed

+731
-1136
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
._.DS_Store
33
Thumbs.db
44
/.claude
5+
/.custom-node-build
56
/.env
67
/.nvm
78
/.rollup.cache

.oxlintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
package-lock.json
22
dist/
3+
.custom-node-build/

biome.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"includes": [
55
"**",
66
"!**/.cache",
7+
"!**/.custom-node-build",
78
"!**/.DS_Store",
89
"!**/._.DS_Store",
910
"!**/.env",

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@
7575
"bs": "pnpm run build:dist:src; pnpm exec socket --",
7676
"s": "pnpm exec socket --",
7777
"test": "run-s check test:*",
78-
"test:unit": "dotenvx -q run -f .env.test -- vitest run --config .config/vitest.config.mts",
79-
"test:unit:update": "dotenvx -q run -f .env.test -- vitest run --config .config/vitest.config.mts --update",
80-
"test:unit:coverage": "dotenvx -q run -f .env.test -- vitest run --config .config/vitest.config.mts --coverage",
78+
"test:unit": "dotenvx -q run -f .env.test -- pnpm vitest run --config .config/vitest.config.mts",
79+
"test:unit:update": "dotenvx -q run -f .env.test -- pnpm vitest run --config .config/vitest.config.mts --update",
80+
"test:unit:coverage": "dotenvx -q run -f .env.test -- pnpm vitest run --config .config/vitest.config.mts --coverage",
8181
"test:validate": "node scripts/validate-tests.mjs",
8282
"test:wrapper": "node scripts/test-wrapper.mjs",
8383
"test-ci": "run-s test:*",

src/commands/fix/cmd-fix.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ async function run(
221221
parentName,
222222
importMeta,
223223
},
224-
{ allowUnknownFlags: false },
224+
{ allowUnknownFlags: true },
225225
)
226226

227227
const {

src/commands/manifest/cmd-manifest-cdxgen.mts

Lines changed: 30 additions & 275 deletions
Large diffs are not rendered by default.

src/commands/manifest/run-cdxgen.mts

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
/** @fileoverview CycloneDX cdxgen runner for Socket CLI. Executes cdxgen SBOM generator via npx. Converts yarn.lock to package-lock.json using synp for better accuracy. */
1+
/** @fileoverview CycloneDX cdxgen runner for Socket CLI. Executes cdxgen SBOM generator via npx. Converts yarn.lock to package-lock.json using synp for better accuracy. Applies Socket secure defaults for lifecycle and output. */
22

33
import { existsSync } from 'node:fs'
44
import path from 'node:path'
55

6+
import terminalLink from 'terminal-link'
67
import colors from 'yoctocolors-cjs'
78

89
import { removeSync } from '@socketsecurity/registry/lib/fs'
@@ -28,44 +29,28 @@ const nodejsPlatformTypes = new Set([
2829
'typescript',
2930
])
3031

31-
export type ArgvObject = {
32-
[key: string]: boolean | null | number | string | Array<string | number>
32+
function hasArg(args: readonly string[], ...flags: string[]): boolean {
33+
return flags.some(flag => args.includes(flag))
3334
}
3435

35-
function argvObjectToArray(argvObj: ArgvObject): string[] {
36-
if (argvObj['help']) {
37-
return [FLAG_HELP]
36+
function getArgValue(
37+
args: readonly string[],
38+
flag: string,
39+
): string | undefined {
40+
const idx = args.indexOf(flag)
41+
if (idx !== -1 && idx + 1 < args.length) {
42+
return args[idx + 1]
3843
}
39-
const result = []
40-
for (const { 0: key, 1: value } of Object.entries(argvObj)) {
41-
if (key === '_' || key === '--') {
42-
continue
43-
}
44-
if (key === 'babel' || key === 'install-deps' || key === 'validate') {
45-
// cdxgen documents no-babel, no-install-deps, and no-validate flags so
46-
// use them when relevant.
47-
result.push(`--${value ? key : `no-${key}`}`)
48-
} else if (value === true) {
49-
result.push(`--${key}`)
50-
} else if (typeof value === 'string') {
51-
result.push(`--${key}`, String(value))
52-
} else if (Array.isArray(value)) {
53-
result.push(`--${key}`, ...value.map(String))
54-
}
55-
}
56-
const pathArgs = argvObj['_'] as string[]
57-
if (Array.isArray(pathArgs)) {
58-
result.push(...pathArgs)
59-
}
60-
const argsAfterDoubleHyphen = argvObj['--'] as string[]
61-
if (Array.isArray(argsAfterDoubleHyphen)) {
62-
result.push('--', ...argsAfterDoubleHyphen)
63-
}
64-
return result
44+
// Check for --flag=value format.
45+
const prefix = `${flag}=`
46+
const arg = args.find(a => a.startsWith(prefix))
47+
return arg ? arg.slice(prefix.length) : undefined
6548
}
6649

67-
export async function runCdxgen(argvObj: ArgvObject): Promise<ShadowBinResult> {
68-
const argvMutable = { __proto__: null, ...argvObj } as ArgvObject
50+
export async function runCdxgen(
51+
args: readonly string[],
52+
): Promise<ShadowBinResult> {
53+
const argsMutable = [...args]
6954

7055
// Detect lockfiles for synp conversion.
7156
const npmLockPath = await findUp(PACKAGE_LOCK_JSON, { onlyFiles: true })
@@ -74,15 +59,16 @@ export async function runCdxgen(argvObj: ArgvObject): Promise<ShadowBinResult> {
7459
? undefined
7560
: await findUp(YARN_LOCK, { onlyFiles: true })
7661

62+
const typeValue =
63+
getArgValue(argsMutable, '--type') || getArgValue(argsMutable, '-t')
64+
7765
let cleanupPackageLock = false
7866
if (
7967
yarnLockPath &&
80-
argvMutable['type'] !== YARN &&
81-
nodejsPlatformTypes.has(argvMutable['type'] as string)
68+
typeValue !== YARN &&
69+
(!typeValue || nodejsPlatformTypes.has(typeValue))
8270
) {
83-
if (npmLockPath) {
84-
argvMutable['type'] = 'npm'
85-
} else {
71+
if (!npmLockPath) {
8672
// Use synp to create a package-lock.json from the yarn.lock,
8773
// based on the node_modules folder, for a more accurate SBOM.
8874
try {
@@ -103,22 +89,39 @@ export async function runCdxgen(argvObj: ArgvObject): Promise<ShadowBinResult> {
10389
},
10490
)
10591

106-
argvMutable['type'] = 'npm'
10792
cleanupPackageLock = true
10893
} catch {}
10994
}
11095
}
11196

97+
// Apply Socket secure defaults when not requesting help/version.
98+
const isHelpRequest = hasArg(argsMutable, FLAG_HELP, '-h', '--version', '-v')
99+
if (!isHelpRequest) {
100+
// Set lifecycle to 'pre-build' to avoid arbitrary code execution.
101+
// https://github.com/CycloneDX/cdxgen/issues/1328
102+
if (!hasArg(argsMutable, '--lifecycle')) {
103+
argsMutable.push('--lifecycle', 'pre-build')
104+
argsMutable.push('--no-install-deps')
105+
logger.info(
106+
`Setting cdxgen --lifecycle to "pre-build" to avoid arbitrary code execution on this scan.\n Pass "--lifecycle build" to generate a BOM consisting of information obtained during the build process.\n See cdxgen ${terminalLink(
107+
'BOM lifecycles documentation',
108+
'https://cyclonedx.github.io/cdxgen/#/ADVANCED?id=bom-lifecycles',
109+
)} for more details.\n`,
110+
)
111+
}
112+
113+
// Set default output filename.
114+
if (!hasArg(argsMutable, '--output', '-o')) {
115+
argsMutable.push('--output', 'socket-cdx.json')
116+
}
117+
}
118+
112119
// Run cdxgen via npx.
113120
const cdxgenVersion =
114121
constants.ENV['INLINED_SOCKET_CLI_CYCLONEDX_CDXGEN_VERSION']
115122
const cdxgenPackageSpec = `@cyclonedx/cdxgen@${cdxgenVersion}`
116-
const cdxgenArgs = argvObjectToArray(argvMutable)
117-
118-
// Check if this is a help/version request.
119-
const isHelpRequest = argvMutable['help'] || argvMutable['version']
120123

121-
const result = await runShadowCommand(cdxgenPackageSpec, cdxgenArgs, {
124+
const result = await runShadowCommand(cdxgenPackageSpec, argsMutable, {
122125
ipc: {
123126
[constants.SOCKET_CLI_SHADOW_ACCEPT_RISKS]: true,
124127
[constants.SOCKET_CLI_SHADOW_API_TOKEN]:
@@ -166,7 +169,8 @@ export async function runCdxgen(argvObj: ArgvObject): Promise<ShadowBinResult> {
166169
} catch {}
167170
}
168171

169-
const outputPath = argvMutable['output'] as string
172+
const outputPath =
173+
getArgValue(argsMutable, '--output') || getArgValue(argsMutable, '-o')
170174
if (outputPath) {
171175
const fullOutputPath = path.join(process.cwd(), outputPath)
172176
if (existsSync(fullOutputPath)) {

src/commands/package/fetch-purls-shallow-score.test.mts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ describe('fetchPurlsShallowScore', () => {
4343

4444
expect(mockWithSdk).toHaveBeenCalledWith(
4545
expect.any(Function),
46-
'batch package fetch',
46+
'looking up package',
4747
undefined,
4848
)
4949
expect(result).toEqual(successResult)
@@ -66,7 +66,7 @@ describe('fetchPurlsShallowScore', () => {
6666

6767
expect(mockWithSdk).toHaveBeenCalledWith(
6868
expect.any(Function),
69-
'batch package fetch',
69+
'looking up package',
7070
undefined,
7171
)
7272
expect(result).toEqual(error)
@@ -89,7 +89,7 @@ describe('fetchPurlsShallowScore', () => {
8989

9090
expect(mockWithSdk).toHaveBeenCalledWith(
9191
expect.any(Function),
92-
'batch package fetch',
92+
'looking up package',
9393
undefined,
9494
)
9595
expect(result.ok).toBe(false)
@@ -119,7 +119,7 @@ describe('fetchPurlsShallowScore', () => {
119119

120120
expect(mockWithSdk).toHaveBeenCalledWith(
121121
expect.any(Function),
122-
'batch package fetch',
122+
'looking up package',
123123
{ sdkOpts },
124124
)
125125
})
@@ -142,7 +142,7 @@ describe('fetchPurlsShallowScore', () => {
142142

143143
expect(mockWithSdk).toHaveBeenCalledWith(
144144
expect.any(Function),
145-
'batch package fetch',
145+
'looking up package',
146146
undefined,
147147
)
148148
expect(result.ok).toBe(true)
@@ -174,7 +174,7 @@ describe('fetchPurlsShallowScore', () => {
174174

175175
expect(mockWithSdk).toHaveBeenCalledWith(
176176
expect.any(Function),
177-
'batch package fetch',
177+
'looking up package',
178178
undefined,
179179
)
180180
expect(result.ok).toBe(true)
@@ -208,7 +208,7 @@ describe('fetchPurlsShallowScore', () => {
208208

209209
expect(mockWithSdk).toHaveBeenCalledWith(
210210
expect.any(Function),
211-
'batch package fetch',
211+
'looking up package',
212212
undefined,
213213
)
214214
expect(result.ok).toBe(true)

0 commit comments

Comments
 (0)