Files
openchamber/packages/web/server/lib/inherited-env.js
T
Cursor AgentandSerhii Dziupin 5defd1af75 fix(terminal): drop native ARGV0 for bun-pty via env -u
bun-pty merges the OS environ into PTY children, so deleting ARGV0 from the
JS env object alone left the AppImage path in the shell. Wrap Linux PTY
spawns with env -u ARGV0, clear native ARGV0 under Bun via libc unsetenv,
and always clear process.env even when no login-shell snapshot exists.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-03 09:12:35 +00:00

75 lines
2.5 KiB
JavaScript

/**
* Sanitize environment objects inherited by user-facing child processes.
*
* Linux AppImage runtimes export `ARGV0` as the AppImage path before launching
* the packaged app. zsh treats an exported `ARGV0` as the argv[0] for every
* external command it spawns, which corrupts Python venv detection and any
* other program that reads argv[0]/$0 while leaving `/proc/self/exe` correct.
*
* See openchamber/openchamber#2588 and pingdotgg/t3code#2509.
*/
import { createRequire } from 'node:module';
import { existsSync } from 'node:fs';
const LINUX_ENV_BINARIES = ['/usr/bin/env', '/bin/env'];
/**
* Remove AppImage `ARGV0` from a mutable env object (or `process.env`).
* @param {NodeJS.ProcessEnv | Record<string, string | undefined> | null | undefined} env
* @returns {typeof env}
*/
export function stripAppImageArgv0Leak(env) {
if (!env || typeof env !== 'object') return env;
if (Object.prototype.hasOwnProperty.call(env, 'ARGV0')) {
delete env.ARGV0;
}
return env;
}
/**
* Clear AppImage `ARGV0` from this process.
*
* Bun keeps a native environ that `bun-pty` inherits even after
* `delete process.env.ARGV0`. On Linux under Bun we also call libc `unsetenv`.
*/
export function clearAppImageArgv0FromProcessEnv() {
delete process.env.ARGV0;
if (process.platform !== 'linux' || typeof Bun === 'undefined') return;
try {
const require = createRequire(import.meta.url);
const { dlopen } = require('bun:ffi');
const libc = dlopen('libc.so.6', {
unsetenv: { args: ['cstring'], returns: 'i32' },
});
libc.symbols.unsetenv(Buffer.from('ARGV0\0'));
} catch {
// Node/Electron and environments without bun:ffi rely on explicit child envs.
}
}
/**
* Resolve a Linux PTY launch that drops native `ARGV0` before the shell starts.
*
* `bun-pty` merges the OS environ into the child, so deleting `ARGV0` from the
* JS env object alone is not enough. Wrapping with `env -u ARGV0` unsets it
* before execing the real shell. No-op on non-Linux platforms.
*
* @param {string} executable
* @param {string[]} args
* @returns {{ executable: string, args: string[] }}
*/
export function resolveLinuxPtyLaunch(executable, args = []) {
if (process.platform !== 'linux') {
return { executable, args };
}
const envBinary = LINUX_ENV_BINARIES.find((candidate) => existsSync(candidate));
if (!envBinary) {
return { executable, args };
}
return {
executable: envBinary,
args: ['-u', 'ARGV0', executable, ...args],
};
}