Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
### Changed
- **Bazel diagnostics** — `socket manifest bazel --verbose` now emits bounded subprocess traces with argv, cwd, duration, exit status, output sizes, and failure stderr tails to make customer log-only triage safer and faster.

## [1.1.104](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.104) - 2026-05-26

### Fixed
- Coana CLI invocation: strip `npm_package_*` env vars before spawning the npm-install fallback. Prevents `spawn E2BIG` failures in large monorepos where the parent process has hundreds of `npm_package_*` env vars populated from the root `package.json`. Preserves `npm_config_*` (registry / proxy / cache from `.npmrc`).

## [1.1.103](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.103) - 2026-05-26

### Changed
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "socket",
"version": "1.1.103",
"version": "1.1.104",
"description": "CLI for Socket.dev",
"homepage": "https://github.com/SocketDev/socket-cli",
"license": "MIT AND OFL-1.1",
Expand Down
30 changes: 28 additions & 2 deletions src/utils/dlx.mts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,32 @@ export type CoanaDlxOptions = DlxOptions & {
*/
const installedCoanaScriptPathsByVersion = new Map<string, string>()

/**
* Strip npm-injected `npm_package_*` env vars before spawning a Coana
* subprocess. npm (and pnpm/yarn classic) populate one env var per leaf in
* the cwd's package.json — `npm_package_dependencies_*`, `npm_package_scripts_*`,
* etc. In big monorepos with hundreds of deps this can easily account for
* 50KB+ of environment, pushing combined argv + env past Linux ARG_MAX
* (~128KB) and causing `spawn` to fail with E2BIG before Coana even starts.
*
* Coana does not read `npm_package_*` itself, so dropping them is safe. We
* intentionally keep `npm_config_*` (registry, cache, proxy settings sourced
* from .npmrc), `npm_lifecycle_*`, and everything else untouched — those can
* matter for outbound network behavior of nested `npm install` calls.
*/
function sanitizeEnvForCoanaSubprocess(
env: NodeJS.ProcessEnv,
): NodeJS.ProcessEnv {
const out: NodeJS.ProcessEnv = {}
for (const key of Object.keys(env)) {
if (key.startsWith('npm_package_')) {
continue
}
out[key] = env[key]
}
return out
}

/**
* Spawn an installed Coana entry point via `node` (or directly, if it's a
* native binary). Shared by the SOCKET_CLI_COANA_LOCAL_PATH branch and the
Expand All @@ -210,7 +236,7 @@ async function spawnCoanaScriptViaNode(
const spawnArgs = isBinary ? args : [scriptPath, ...args]
const spawnResult = await spawn(isBinary ? scriptPath : 'node', spawnArgs, {
cwd: options.cwd,
env: finalEnv,
env: sanitizeEnvForCoanaSubprocess(finalEnv),
stdio: spawnExtra?.['stdio'] || 'inherit',
})

Expand Down Expand Up @@ -278,7 +304,7 @@ async function installCoanaToTmpdir(
`@coana-tech/cli@${version}`,
],
{
env: finalEnv,
env: sanitizeEnvForCoanaSubprocess(finalEnv),
stdio: 'inherit',
},
)
Expand Down
49 changes: 49 additions & 0 deletions src/utils/dlx.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -468,5 +468,54 @@ describe('utils/dlx', () => {
)
expect(npmInstallCalls).toHaveLength(1)
})

it('strips npm_package_* env vars in the fallback to avoid E2BIG in big monorepos', async () => {
// Simulate a parent env polluted with npm_package_* (as set by npm/pnpm
// when running inside a project with a populated package.json). The
// fallback must not pass these through to its npm install or node
// spawns, or the same ARG_MAX overflow that broke the dlx path would
// recur.
process.env['npm_package_name'] = 'forge'
process.env['npm_package_dependencies_react'] = '^18.2.0'
process.env['npm_package_devDependencies_typescript'] = '^5.0.0'
// npm_config_* must be preserved — these carry registry/proxy settings
// sourced from .npmrc and are needed for the nested npm install.
process.env['npm_config_registry'] = 'https://artifactory.example/npm/'

try {
const result = await spawnCoanaDlx(['run', '.'], 'acme', {
coanaVersion: nextVersion(),
})
expect(result.ok).toBe(true)

const npmInstallCall = mockSpawn.mock.calls.find(
([cmd, args]) => cmd === 'npm' && (args as string[])[0] === 'install',
)!
const nodeCall = mockSpawn.mock.calls.find(([cmd]) => cmd === 'node')!

const npmEnv = (npmInstallCall[2] as { env: NodeJS.ProcessEnv }).env
const nodeEnv = (nodeCall[2] as { env: NodeJS.ProcessEnv }).env

// npm_package_* are stripped from both spawns.
expect(npmEnv['npm_package_name']).toBeUndefined()
expect(npmEnv['npm_package_dependencies_react']).toBeUndefined()
expect(npmEnv['npm_package_devDependencies_typescript']).toBeUndefined()
expect(nodeEnv['npm_package_name']).toBeUndefined()
expect(nodeEnv['npm_package_dependencies_react']).toBeUndefined()

// npm_config_* is preserved (registry override survives).
expect(npmEnv['npm_config_registry']).toBe(
'https://artifactory.example/npm/',
)
expect(nodeEnv['npm_config_registry']).toBe(
'https://artifactory.example/npm/',
)
} finally {
delete process.env['npm_package_name']
delete process.env['npm_package_dependencies_react']
delete process.env['npm_package_devDependencies_typescript']
delete process.env['npm_config_registry']
}
})
})
})
Loading