Skip to content

Env-injection deny-list and allow-list SSH safe-allow fast path#37

Merged
morgaesis merged 4 commits into
mainfrom
salvage-server-hardening
Jul 2, 2026
Merged

Env-injection deny-list and allow-list SSH safe-allow fast path#37
morgaesis merged 4 commits into
mainfrom
salvage-server-hardening

Conversation

@morgaesis

Copy link
Copy Markdown
Owner

Adds a deny-list for environment variable names that can inject code into a child process (LD_PRELOAD, BASH_ENV, GIT_SSH_COMMAND, and similar) and a deterministic safe-allow fast path that lets a fixed set of read-only diagnostics skip the LLM evaluator. The ssh side of that fast path uses allow-list (deny-by-default) semantics: it is taken only when every ssh option is on a small vetted set and the remote command is a fixed read-only diagnostic, so any unrecognized option, external config, forwarding/proxy directive, or newline-smuggled -o forfeits to the evaluator. The option scan matches ssh's real getopt behavior (options between the destination and the remote command are honored, verified against ssh -G), StrictHostKeyChecking is permitted only in its security-preserving values, and the change is covered by table-driven tests.

morgaesis added 4 commits July 2, 2026 18:29
Block dangerous injected environment variable names (LD_PRELOAD, BASH_ENV,
GIT_SSH_COMMAND, PYTHONPATH, NODE_OPTIONS, the LD_AUDIT and GIT_CONFIG_KEY/
VALUE families, etc.) from --env/--secret injection, since a value under any
of them is code the child runs before its own logic.

Add a deterministic pre-LLM allow for a fixed, narrow set of trivially safe
read-only commands: local id/whoami/hostname/uptime and, over ssh, a fixed
read-only diagnostic remote command. Disabled in paranoid mode, never
applied when env/secrets are injected, and forfeited by any shell
metacharacter or risky ssh transport option (-L/-D/-J/-W/ProxyCommand/
LocalCommand/forwarding). Routes as a bypass allow like a trusted verb.

Salvaged and re-implemented fresh against current main from the
wsl-broker-wip snapshot.
The deterministic safe-allow fast path skipped the LLM evaluator for a
fixed set of read-only diagnostics, including one run over ssh. It
forfeited the fast path by matching a deny-list of risky ssh option
names, which is inherently incomplete: any unlisted option (an external
-F config, an -I PKCS#11 module, a newline-smuggled second -o directive,
RemoteCommand, or a future addition) slipped through.

Replace the deny-list with an allow-list: the ssh fast path is taken
only when every option in the invocation is on a small vetted set
(address family, compression, verbosity, no-tty and the restrictive
agent/X11/GSSAPI toggles; -p, -l, and an -o keyword whitelist covering
timeouts, batch mode, keepalive, and host-key handling). Anything else
forfeits to the evaluator. The -o value is rejected outright if it
contains a newline.

The scan covers the whole option zone, not just options before the
destination: ssh honors options placed between the destination and the
remote command, so the scan stops only at the command token (the second
positional). This behavior was verified against ssh's own -G dry run.

Adds table-driven tests for the vetted/forfeited option sets, the
between-host-and-command position, and newline-smuggled -o directives.
Review follow-up. The -o StrictHostKeyChecking directive was allowed on
the fast path regardless of value, so StrictHostKeyChecking=no would take
the deterministic path over an unauthenticated channel. Permit the
keyword only for its security-preserving values (yes, accept-new, or an
empty value that falls back to ssh's strict default); no/off/ask now
forfeit to the evaluator. accept-new, which the --hostkey mode injects,
stays on the fast path.

Also add tests for a value option (-p/-l) given without its value.
@morgaesis morgaesis merged commit ee256de into main Jul 2, 2026
7 checks passed
@morgaesis morgaesis deleted the salvage-server-hardening branch July 2, 2026 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant