/** * Subprocess environment scrubbing (T11897 · S1 · security hardening). * * Building the environment a child process inherits is a SECURITY boundary, not * a convenience. Two distinct threats are closed here: * * 1. **Sandbox escape via Pi-controlled env** — a model-driven loop that can * set `LD_PRELOAD`, `NODE_OPTIONS`, `GIT_SSH_COMMAND`, `DYLD_*`, or a custom * `PATH` gains arbitrary native code execution outside any command allowlist * (the OS loader / Node honour them) and can satisfy a basename denylist with * a workspace-resident impostor on a hijacked `PATH`. The scrubber NEVER * forwards any of these dangerous variables and PINS `PATH` to a trusted * absolute value. * 2. **Daemon-secret exfiltration** — the daemon's own `process.env` carries * resolved provider credentials (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, …), * OAuth headers, and vault material (`CLEO_VAULT_*`). Inheriting the full * parent env into a Pi-spawned child means a single `env`/`printenv`/log line * exfiltrates them. The scrubber builds a MINIMAL, explicitly-constructed env * instead of inheriting `process.env`. * * The doctrine is **allowlist, not denylist**: the scrubbed env starts EMPTY and * only a small set of benign, named variables are copied through from the parent * (locale, terminal, home/user, tmp). Everything else — including every secret, * every loader hook, and `PATH` itself — is reconstructed from trusted inputs. * * Pure function of input — no global/session coupling, import-time side-effect * free (S1). * * @epic T10403 * @task T11761 * @task T11897 * @saga T11387 */ /** * The trusted, absolute `PATH` a scrubbed subprocess runs under. A fixed list of * standard system binary directories — NEVER the caller's (possibly Pi-poisoned) * `PATH`. Pinning this defeats the "workspace-resident impostor on a hijacked * PATH satisfies an allowed basename" escape: the workspace is not on it. */ export declare const TRUSTED_PATH: string; /** Options for {@link scrubSubprocessEnv}. */ export interface ScrubEnvOptions { /** * The parent environment to copy the benign {@link PASSTHROUGH_KEYS} from. * Defaults to `process.env`. Injectable for tests so a clean env can be * asserted without mutating the real process environment. */ readonly parentEnv?: Readonly>; /** * Extra variables to set on the scrubbed env (e.g. a caller-supplied `env` * merged ON TOP of the minimal base). These are themselves SCRUBBED — any * dangerous key (loader hooks, `PATH`, secrets) is dropped, so an untrusted * caller (Pi) cannot reintroduce an escape through this channel. */ readonly extra?: Readonly>; /** * The trusted `PATH` to pin. Defaults to {@link TRUSTED_PATH}. Callers MUST * pass an absolute, trusted value — never the inbound/Pi-controlled `PATH`. */ readonly path?: string; } /** * Whether a variable NAME is forbidden in a scrubbed subprocess env (a loader * hook, `PATH`, or a credential). * * @param name - The environment variable name. * @returns `true` when the variable must NOT be forwarded. */ export declare function isForbiddenEnvName(name: string): boolean; /** * Build a MINIMAL, explicitly-constructed subprocess environment. * * The result starts EMPTY; only the benign {@link PASSTHROUGH_KEYS} present in * `parentEnv` are copied, `PATH` is pinned to a trusted absolute value, and any * caller-supplied `extra` is merged ON TOP after being filtered through * {@link isForbiddenEnvName}. The daemon's secrets and any loader hooks in the * parent environment are therefore NEVER visible to the child, and an untrusted * caller cannot reintroduce an escape via `extra`. * * @param options - {@link ScrubEnvOptions}. * @returns A fresh `Record` safe to hand to `spawn(..., { env })`. * * @example * ```ts * // A Pi-supplied env that tries to inject a loader hook + hijack PATH: * const env = scrubSubprocessEnv({ * extra: { LD_PRELOAD: '/tmp/evil.so', PATH: '/workspace', SAFE: '1' }, * }); * // env has no LD_PRELOAD, PATH is the pinned TRUSTED_PATH, only SAFE survives. * ``` */ export declare function scrubSubprocessEnv(options?: ScrubEnvOptions): Record; //# sourceMappingURL=env-scrub.d.ts.map