fix(tool-shell): seed PATH in the sandbox-host engine so full-access resolves bare names#203
Merged
Merged
Conversation
…resolves bare names The sandbox-host engine (HostShellTool, ADR 0019) spawns `/bin/sh -c <cmd>` through `ConfinedCommand`, whose shared spawn does `env_clear()` — so no ambient environment (including `PATH`) crosses the boundary. It then only re-applies the caller-supplied `env`. When the caller sends no `PATH` (e.g. newt's structured env seam is empty absent a venv), the child shell had no `PATH` and fell back to the shell's compiled `_CS_PATH` (dash: `/usr/local/sbin:…:/usr/bin:/sbin:/bin`). That resolves standard `/usr/bin` tools by luck, but silently drops every tool in a non-standard dir the user actually has on `PATH` — `~/.cargo/bin`, `~/.local/bin`, `/opt/homebrew/bin`, language shims — and is fragile across non-dash `/bin/sh` and macOS. The result: full-access sessions where bare `grep`/`ls`/`find`-style names (and the user's own tools) fail to resolve. Fix: when building the child env, seed `PATH` from a new `agent_bridle_core::default_exec_path()` (ambient `$PATH` if set, else the conventional `RootfsPolicy` search dirs, platform-joined) unless the caller already provided one. This is authority-consistent: the engine only reaches env construction when `exec` is unrestricted (a restricted `exec`/`net` grant is refused above, ADR 0019 D2), so the child is already permitted to run anything on the host — seeding `PATH` grants nothing new. `env_clear()` still scrubs everything else; a caller-provided `PATH` wins. Regression test `full_access_seeds_default_path_into_the_child` has the child echo its own `$PATH` and asserts it equals `default_exec_path()` — empty/minimal pre-fix, the full path post-fix (verified failing on the pre-fix engine). `bare_name_resolves_when_path_includes_its_dir` proves a bare program name resolves and runs when its dir is on `PATH`. Existing host-shell tests (incl. the real-Landlock keystone) unchanged and green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HRv5vteHCw4jUayHGL5esx
This was referenced Jul 6, 2026
hartsock
added a commit
that referenced
this pull request
Jul 6, 2026
Cut the 0.7.0-rc.1 line carrying the OCAP shell-engine work: the sandbox-host engine + full-access PATH seed (#203), the carried brush engine + carried coreutils (#204/#206, via the published brush-ocap-* crates), and the honest per-axis L3 disclosure. The stale crates.io `agent-bridle-core 0.1.0` predated the macos-seatbelt / windows-appcontainer / host-shell features, so republishing the workspace at 0.7.0-rc.1 is what lets `agent-bridle-tool-shell` publish with those features (the pre-existing release blocker F surfaced during the brush dep-swap). Claude-Session: https://claude.ai/code/session_01HRv5vteHCw4jUayHGL5esx Co-authored-by: Shawn Hartsock <hartsock@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The sandbox-host engine (
HostShellTool, ADR 0019 / #199) spawns/bin/sh -c <cmd>throughConfinedCommand, whose shared spawn doesenv_clear()— so no ambient environment (includingPATH) crosses the boundary; only the caller-suppliedenvis re-applied. When the caller sends noPATH(e.g. an embedder whose structured env seam is empty), the child shell had noPATHand fell back to the shell's compiled_CS_PATH(dash:/usr/local/sbin:…:/usr/bin:/sbin:/bin).That resolves standard
/usr/bintools by luck, but silently drops every tool in a non-standard dir the user actually has onPATH—~/.cargo/bin,~/.local/bin,/opt/homebrew/bin, language shims — and is fragile across non-dash/bin/shand macOS. Net effect: full-access sessions where baregrep/ls/find-style names (and the user's own tools) fail to resolve.Fix: seed
PATHfrom a newagent_bridle_core::default_exec_path()(ambient$PATHif set, else the conventionalRootfsPolicysearch dirs, platform-joined) unless the caller already provided one. This is authority-consistent: the engine only reaches env construction whenexecis unrestricted (a restrictedexec/netgrant is refused above, ADR 0019 D2), so the child is already permitted to run anything on the host — seedingPATHgrants nothing new.env_clear()still scrubs everything else; a caller-providedPATHwins.This unblocks the newt-side adoption (newt #868) of the host-shell engine for
--full-access, where the user "often needs find/grep/ls and other shell tools."Test plan
full_access_seeds_default_path_into_the_child(regression): the child echoes its own$PATH; asserts it equalsdefault_exec_path()— verified failing on the pre-fix engine (child saw only dash's minimal_CS_PATH), passing after.bare_name_resolves_when_path_includes_its_dir: a bare program name resolves and runs when its dir is onPATH.dynamic_construct_runs_but_out_of_scope_write_is_kernel_denied(gnuc, 6.8 kernel).just checkgreen (fmt + clippy-D warningsall-features & no-default-features + tests both ways);just publish-checkgreen (core stays publishable — no new deps).risk:low — additive, single concern, regression-tested, no CI/hooks/build changes, deletes nothing.