Skip to content

Commit ade6207

Browse files
committed
fix(cli): prevent ZX_* control-plane injection via --env file
loading a .env file via --env merges its contents into process.env, which resolveDefaults() then blindly promotes into live execution settings. An attacker who can modify the .env file (supply-chain PR, compromised dep) can set ZX_PREFIX/ZX_POSTFIX/ZX_SHELL to arbitrary shell code that runs for every call in an otherwise-trusted script. Fix: snapshot ZX_* keys present in process.env before dotenv.config() runs, then purge any ZX_* key introduced by the file before resolveDefaults() sees the environment. Legitimate ZX_* variables set by the operator (e.g. from the shell that launched zx) are preserved; only file-injected ones are stripped. Reported-by: LAKSHMIKANTHAN K (letchupkt) CWE: CWE-94 / CWE-77 (Command Injection)
1 parent 98531fc commit ade6207

File tree

1 file changed

+12
-0
lines changed

1 file changed

+12
-0
lines changed

src/cli.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,19 @@ export async function main(): Promise<void> {
112112
if (argv.cwd) $.cwd = argv.cwd
113113
if (argv.env) {
114114
const envfile = path.resolve($.cwd ?? process.cwd(), argv.env)
115+
// Security: snapshot the ZX_* keys that existed BEFORE loading the env
116+
// file so that attacker-controlled entries (e.g. ZX_PREFIX, ZX_POSTFIX,
117+
// ZX_SHELL) cannot pollute the execution control-plane.
118+
const zxKeysBefore = new Set(
119+
Object.keys(process.env).filter((k) => k.startsWith('ZX_'))
120+
)
115121
dotenv.config(envfile)
122+
// Purge any ZX_* key that was not present before the env-file was loaded.
123+
for (const k of Object.keys(process.env)) {
124+
if (k.startsWith('ZX_') && !zxKeysBefore.has(k)) {
125+
delete process.env[k]
126+
}
127+
}
116128
resolveDefaults()
117129
}
118130
if (argv.verbose) $.verbose = true

0 commit comments

Comments
 (0)