Skip to content

Commit 9e57cdf

Browse files
committed
feat(security): prevent SIGUSR1 debugger signal handling
Integrate SIGUSR1 prevention flags across Socket CLI subprocess spawning: - shadow/npm-base.mts: Apply flags when spawning npm/npx with security scanning - optimize/agent-installer.mts: Apply via NODE_OPTIONS during package installation - bootstrap/node.mts: Apply when bootstrapping CLI from custom Node binary - bootstrap/shared/node-flags.mts: Minimal implementation for bootstrap (size-optimized) Uses @socketsecurity/lib/constants/node getNodeDisableSigusr1Flags() which returns: - --disable-sigusr1 for Node 22.14+, 23.7+, 24.8+ (prevents thread creation) - --no-inspect fallback for older Node 18+ (basic protection) This prevents SIGUSR1 from starting the debugger in production CLI usage.
1 parent 0d0d3d7 commit 9e57cdf

File tree

5 files changed

+60
-4
lines changed

5 files changed

+60
-4
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,4 @@ uild directory for temporary artifacts
5050
# Ignore socketbin package build artifacts (binaries and wasm files)
5151
# These are generated during build and should not be committed
5252
packages/socketbin-*/bin/
53+
packages/socketbin-*/build/

packages/cli/src/bootstrap/node.mts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
getCliPackageName,
2525
getDlxDir,
2626
} from './shared/paths.mjs'
27+
import { getNodeDisableSigusr1Flags } from './shared/node-flags.mjs'
2728

2829
/**
2930
* Check if CLI is installed.
@@ -128,10 +129,14 @@ async function main(): Promise<void> {
128129
const cliPath = getCliEntryPoint()
129130
const args = process.argv.slice(2)
130131

131-
const child = spawn(process.execPath, [cliPath, ...args], {
132-
stdio: 'inherit',
133-
env: process.env,
134-
})
132+
const child = spawn(
133+
process.execPath,
134+
[...getNodeDisableSigusr1Flags(), cliPath, ...args],
135+
{
136+
stdio: 'inherit',
137+
env: process.env,
138+
},
139+
)
135140

136141
child.on('error', error => {
137142
console.error('Failed to spawn CLI:', error)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Node.js flags for bootstrap (minimal implementation for size).
3+
* This file is bundled into bootstrap, not imported at runtime.
4+
*/
5+
6+
/**
7+
* Get Node major version number.
8+
*/
9+
function getNodeMajorVersion(): number {
10+
return Number.parseInt(process.version.slice(1).split('.')[0] || '0', 10)
11+
}
12+
13+
/**
14+
* Get Node minor version number.
15+
*/
16+
function getNodeMinorVersion(): number {
17+
return Number.parseInt(process.version.split('.')[1] || '0', 10)
18+
}
19+
20+
/**
21+
* Check if --disable-sigusr1 flag is supported.
22+
* Supported in v22.14.0+, v23.7.0+, v24.8.0+ (stable in v22.20.0+, v24.8.0+).
23+
*/
24+
function supportsDisableSigusr1(): boolean {
25+
const major = getNodeMajorVersion()
26+
const minor = getNodeMinorVersion()
27+
28+
if (major >= 24) {
29+
return minor >= 8
30+
}
31+
if (major === 23) {
32+
return minor >= 7
33+
}
34+
if (major === 22) {
35+
return minor >= 14
36+
}
37+
return false
38+
}
39+
40+
/**
41+
* Get flags to disable SIGUSR1 debugger signal handling.
42+
* Returns --disable-sigusr1 for newer Node, --no-inspect for older versions.
43+
*/
44+
export function getNodeDisableSigusr1Flags(): string[] {
45+
return supportsDisableSigusr1() ? ['--disable-sigusr1'] : ['--no-inspect']
46+
}

packages/cli/src/commands/optimize/agent-installer.mts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import { NPM, PNPM } from '@socketsecurity/lib/constants/agents'
2121
import {
22+
getNodeDisableSigusr1Flags,
2223
getNodeHardenFlags,
2324
getNodeNoWarningsFlags,
2425
} from '@socketsecurity/lib/constants/node'
@@ -96,6 +97,7 @@ export function runAgentInstall(
9697
NODE_OPTIONS: cmdFlagsToString([
9798
...(skipNodeHardenFlags ? [] : getNodeHardenFlags()),
9899
...getNodeNoWarningsFlags(),
100+
...getNodeDisableSigusr1Flags(),
99101
]),
100102
// @ts-expect-error - getOwn may return undefined, but spread handles it
101103
...getOwn(spawnOpts, 'env'),

packages/cli/src/shadow/npm-base.mts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
import {
1212
getExecPath,
1313
getNodeDebugFlags,
14+
getNodeDisableSigusr1Flags,
1415
getNodeHardenFlags,
1516
getNodeNoWarningsFlags,
1617
supportsNodePermissionFlag,
@@ -160,6 +161,7 @@ export default async function shadowNpmBase(
160161
...getNodeNoWarningsFlags(),
161162
...getNodeDebugFlags(),
162163
...getNodeHardenFlags(),
164+
...getNodeDisableSigusr1Flags(),
163165
// Memory flags commented out.
164166
// ...constants.nodeMemoryFlags,
165167
...(ENV.INLINED_SOCKET_CLI_SENTRY_BUILD

0 commit comments

Comments
 (0)