Repository navigation
Audit fixes, wave 2: sandboxed validation, batched Git snapshots, UI consistency pass - #14
Merged
Merged
Conversation
The Worker ran confined, but Foreman then executed validation commands (pnpm install, tests) directly on the host against Worker-authored files. With the default scope (every top-level path, including package.json) a Worker could add a preinstall script or a test and run code as the operator, reading ~/.ssh, gh credentials and other repos, before any human review. Every validation command now runs under bwrap by default and fails closed if bubblewrap is unusable. The host root is read-only; home directories, /tmp, /run, the Foreman data dir and the source checkout are hidden behind tmpfs; capabilities are dropped; toolchains under a hidden root are re-exposed read-only; a persistent owner-only cache keeps installs fast. Network access is still shared (documented). FOREMAN_VALIDATION_SANDBOX=none is an explicit, loudly logged opt-out for hosts without bubblewrap. Observations record the sandbox mode, and /api/status reports whether the sandbox is available. CI configuration (.github/ and similar) is no longer in the suggested Worker scope, since it runs with repository secrets once pushed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DXCvrGWmmRNDLmy7nYS2QP
snapshotGitCommit spawned `git cat-file` twice per file (~7 ms/file, about 53 s for a 5,000-file repo) and ran several times per run; promotion also ran `hash-object` once for every file in the repo and wrote the whole tree into a worktree it never read. Snapshots now use ls-tree -l (enforcing size limits before any content is read) plus a single `cat-file --batch` process with a streaming, strictly validated parser. Promotion reuses the base commit's blob ids for unchanged bytes and hashes only changed contents with one `hash-object --stdin-paths --no-filters`; the manifest is still fully validated and the result tree still independently re-verified. On a 5,050-file repo: snapshot 52.7 s -> 1.2 s, promotion 223 s -> 4.3 s, with identical result commit SHAs before and after. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DXCvrGWmmRNDLmy7nYS2QP
On hosts where /etc/resolv.conf links into /run/systemd/resolve (such as the Ubuntu CI runners), the sandbox deliberately re-exposes that one file, so /run is not empty. Assert instead that the only thing visible under /run and /var/run is the host's resolv.conf target and its parent directories, cover the systemd-resolved layout explicitly, and tighten the pure argv test to exactly one re-exposed bind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DXCvrGWmmRNDLmy7nYS2QP
On CI the home directory is /home/runner. The sandbox masks /home as a whole, so when nothing is re-exposed /home/runner does not exist inside it and `find` failed. A root that does not exist in the sandbox has nothing visible under it; errors from bwrap or find still fail, and the positive case still fails on an empty result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DXCvrGWmmRNDLmy7nYS2QP
Replace about ten ad-hoc pill, badge, dot and chip variants with one Badge component: one size, a marker plus text for every tone, six tones with at least 8:1 contrast, and a single status-to-tone mapping so the same status never shows in two colours. Rewrite the stylesheet around tokens (type, spacing, radii, controls, focus ring): 86 KB to 64 KB, text at least 11px, faint text at least 5.2:1. When a decision is pending, the review panel moves to the top so Approve and Reject are visible without scrolling at 1024 to 1920 wide. The project tree keeps status and run count on one row and the usage dock collapses to one line, so 7 tasks fit at 1440x900. The header and inspector no longer overlap or clip at 1024. awaiting_approval now shows the warning tone when validation has not passed or the Reviewer did not recommend. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DXCvrGWmmRNDLmy7nYS2QP
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.
Second batch of audit fixes.
Validation commands run inside bubblewrap (
2af8f82, test fixes259c623,107da21)The Worker ran confined, but Foreman then ran validation commands (
pnpm install, tests) directly on the host against Worker-authored files. With the default scope, which includespackage.json, a Worker could add apreinstallscript or a test and run code as the operator before any human review.What is sandboxed. Every validation command now runs under
bwrapby default:--cap-drop ALL, so root can't unmount the masks./home,/root,/tmp,/run,/mnt,/media,/srv, the Foreman data dir and the source checkout.~/.local, are re-exposed read-only. A re-expose can never reveal a hidden root, the repo or the data dir.Failure and opt-out.
FOREMAN_VALIDATION_SANDBOX=noneis an explicit, loudly logged opt-out for hosts without bubblewrap.FOREMAN_VALIDATION_SANDBOX_RO_PATHScovers unusual toolchain layouts.Reporting. Observations record the sandbox mode, and
/api/statusreports whether the sandbox is available.Default scope. CI configuration (
.github/and similar) is no longer in the suggested Worker scope, because it runs with repository secrets once pushed.Known residual. The network namespace is still shared, because installs need it. This is documented and will be followed up separately.
Test fixes. The follow-up commits fix two host-layout assumptions in the new sandbox tests that CI's runner exposed: systemd-resolved's
resolv.confunder/run, and a home directory under the masked/home.Batched Git snapshots and promotion (
89e33ca)snapshotGitCommitused to rungit cat-filetwice per file. It now runsls-tree -lplus onecat-file --batchprocess, with size limits enforced before any content is read.UI consistency and layout pass (
8c93753)Badgecomponent and one status-to-tone mapping replace about ten ad-hoc pill, badge and dot variants. Every badge has a marker plus text, and contrast is at least 8:1.Verification
pnpm typecheckandpnpm build: pass.pnpm test: 448/448.pnpm test:bridge: 54/54./home, which mirrors the CI runner.🤖 Generated with Claude Code
https://claude.ai/code/session_01DXCvrGWmmRNDLmy7nYS2QP