Keep secrets out of podman's argv, add env.default, --dry-run and a test suite #2
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/env-name-only-dry-run-tests"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Ports four improvements from claude-jail,
adapted to this repo's sessionless, single-shared-state model.
Secrets no longer appear in the podman command line
Container-level variables were forwarded as
-e KEY=value. Process argumentsare world-readable on Linux — any local user can run
ps auxor read/proc/<pid>/cmdline— so every secret in.env.codex,OPENAI_API_KEYincluded, leaked straight off the boundary this tool exists to enforce.
They are now passed by name only (
-e KEY). Podman reads each value fromthe environment it inherits from the wrapper, which is readable only by you and
root.
Two consequences worth reviewing:
script-internal name (
workspace,network,max_memory, …) wouldotherwise hand podman the wrapper's own value, so those are skipped with a
warning on stderr.
.env.codex, the container nowreceives the shell value. Previously the file's value leaked through in
argv even though the wrapper itself used the shell's. The new behaviour
matches the documented precedence.
Six variables stay inline as
KEY=value—SSH_AUTH_SOCK,CODEX_SSH,CODEX_WORKSPACE,TERM,COLUMNS,LINES. Their container value differsfrom the host's by design and none is secret.
Shared defaults:
~/.codex-jail/env.defaultEnv loading moves into
_load_env_file(), called for.env.codexthenenv.default, first definition winning. Cross-project settings live in oneplace; each workspace keeps only its deltas. Precedence, highest first:
CLI flag > host env > .env.codex > env.default > built-in default
This needed a change claude-jail does not have. There, only
sessions/<id>/configis mounted. Here the whole of~/.codex-jail/isbind-mounted read-write as
/home/codex/.codex, so a plainenv.defaultwould be rewritable from inside the jail: Codex appends
CODEX_JAIL_MOUNTS=/:/hostand escapes on the next run, in any workspace.The wrapper therefore mounts
/dev/nullread-only overenv.defaultinsidethe container, immediately after the jail mount. Unconditionally — masking only
when the file exists would let the agent create one a later run would honour.
The file is seeded with a commented header on first use so both mount endpoints
exist and podman never creates the destination itself inside the bind-mounted
host directory. The host file is untouched.
--dry-runPrints the assembled
podman runcommand to stdout and exits, touching nofilesystem state. Diagnostics move to stderr so stdout is exactly the command.
Documented as inspection-only: pasted
-e KEYarguments resolve against theshell you paste into, which has not loaded
.env.codex.Tests
37 tests across 6 files,
make test, nothing beyond bash. A stubpodmanearlier on
PATHrecords the argv and inherited environment the real one wouldhave received, then exits without starting a container — which is what makes
the property above directly assertable: the secret must be present in podman's
environment and absent from its argv.
Verified against the unmodified script first: 22 failures in exactly the
expected places, 0 after.
Incidental
.codexignorediagnostic counted array elements rather than paths, so italways reported double (
hiding 2 path(s)for a single directory).tests/anddocs/excluded from the Docker build context.Not ported:
.ai/prompt.md, and claude-jail'sinstall.shhelper ordering,which moved
info/errorbelow theHOMEvalidation that calls them.Design notes:
docs/superpowers/specs/2026-07-28-env-name-only-dry-run-tests-design.mdhttps://claude.ai/code/session_0134mePQ5EAQxnitf8Yst3ny