Repository navigation
fix(msb): skip the recursive home chown when the image baked the agent user - #432
Conversation
d0bc2ce to
01e6e95
Compare
wz-gsa
left a comment
There was a problem hiding this comment.
Adversarial review — the performance fix is genuinely necessary and correctly implemented, but found a real, live-reproduced gap in the safety claim before deciding approve vs. request-changes.
Performance claim verified, not just plausible: pulled the actual devenv image the linked issue references into a real msb sandbox and timed the pre-fix unconditional chown -R over its real baked Nix home (81,850 files, already correctly owned) — 759 seconds (12.6 minutes), worse than the issue's own quoted figure. The fix genuinely eliminates this on the "agent already exists" path. Not a theoretical justification.
Real gap found in the stated safety property. The PR body claims "a wrong skip fails loudly rather than degrading silently." I built the exact counterexample this implies should be caught: a base image where adduser agent succeeds but a baked subtree under /home/agent is root:root (simulating a COPY --chown=root:root in a custom image's Dockerfile). Loaded it into a real sandbox and ran the actual guest script verbatim:
- The top-level
chown "agent:${_agrp}" /home/agentfixes/home/agentitself. - The root-owned subtree underneath is never touched —
_acq_created_agentstays0becauseid agentsucceeded, so the recursive chown that used to silently heal this is skipped. - The
su agent -c "test -w /home/agent"backstop only probes the named directory's own write bit — it does not detect the unwritable subtree beneath it, so it passes trivially. - Result: the agent user gets a silent, permanent EACCES on that subtree — not the loud failure the PR body describes for this exact scenario.
Also checked the "kits already chown the subtrees they stage" mitigation directly against every kit in agentic-coding-patterns/integrations/isolation/acq-kits/ that stages files under /home/agent — none of them run their own chown; the coverage they get is from acq's own generic file-copy path (_acq_msb_copy_file_verified), which only covers files acq itself copies in via a kit's files: spec, not content the base image baked. So that mitigating claim doesn't actually cover the failure mode it's meant to.
Severity assessment, and why this is a comment not a block: this only affects a custom ACQ_MSB_IMAGE override that ships an agent user but violates the documented base-image contract (docs/BACKEND_GUIDE.md already states complete home ownership as an operator requirement for custom images). The default docker/sandbox-templates:shell-docker and the real devenv image I tested are both fully agent-owned, so the common path — and the actual bug this PR fixes — is unaffected. This is closer to "a documented contract violation is no longer silently papered over" than a new vulnerability, but the silent-vs-loud framing in the PR body is inaccurate for this specific edge case and there's no test covering it either way.
Suggested, non-blocking: a one-line note in docs/KNOWN_FAILURE_MODES.md about this edge case for custom-image operators, and/or a regression test for the half-baked scenario, would close the gap between the claim and the actual guarantee. Both new #422 bats tests are correct as far as they go (verified the refute_regex against both a real captured call log and a synthetic old-form line — sound, not vacuous) but they only cover the mechanical branch wiring, not this safety property.
No objection to merging as-is given the narrow, documented-contract-violation-only blast radius — flagging for the author's/maintainer's call on whether to add the doc note before or after merge.
01e6e95 to
e6caa9c
Compare
…iolation, not healed Review of #432 showed the writability backstop only probes /home/agent itself, so a custom image that ships the agent user with root-owned content beneath the home is no longer silently chowned. Say so in the base-image contract and drop the two inaccurate claims (loud failure, kits chowning subtrees) from the guest script comment.
|
Confirmed the backstop only probes |
wz-gsa
left a comment
There was a problem hiding this comment.
Converting my earlier comment-only review to an approval — my prior adversarial findings (performance claim verified live at 12.6 minutes eliminated; the silent-degradation gap on a custom base image with incompletely-owned baked subtrees) were deliberately non-blocking: the common path (default sandbox template, the real devenv image) is unaffected, and the edge case only bites a custom ACQ_MSB_IMAGE that already violates the documented home-ownership contract in docs/BACKEND_GUIDE.md. Nothing has changed since that assessment that would newly justify blocking.
All CI checks green (the one CANCELLED "Conventional Commit Title" run is a stale duplicate superseded by the SUCCESS run immediately after it). Approved.
…t user The first-provision agent-user setup ran chown -R /home/agent unconditionally; on images with a dense baked agent home (single-user Nix state, thousands of tiny files) the write-crawl grinds the guest ext4 journal for minutes with no output. Reserve the recursive chown for the path where acq itself created the user (the half-created/root-owned-home case); keep the top-level chown and the writability backstop on every path so a wrong skip still fails loudly. Wrap the step in a progress status like the neighboring provision steps. Fixes #422
…iolation, not healed Review of #432 showed the writability backstop only probes /home/agent itself, so a custom image that ships the agent user with root-owned content beneath the home is no longer silently chowned. Say so in the base-image contract and drop the two inaccurate claims (loud failure, kits chowning subtrees) from the guest script comment.
abab240 to
d7063c1
Compare
🤖 I have created a release *beep* *boop* --- ## [4.0.0](v3.1.0...v4.0.0) (2026-10-06) ### ⚠ BREAKING CHANGES * **kits:** acq no longer installs podman/OCI tooling by default. Users who need docker, docker compose, or rootless podman when the base image doesn't provide it must opt into the oci-engine kit with `acq configure`, `--kit`, or `ACQ_EXTRA_KITS`. * **kits:** acq no longer performs native OpenCode npm fallback installation. OpenCode is supplied by the pinned opencode agent kit from agentic-coding-patterns v1.12.0, currently delivering OpenCode v2.0.22. If the kit is unavailable or invalid, acq fails closed instead of installing opencode-ai from npm. * **kits:** existing acq sandboxes with adapter-installed podman are not migrated in place. Recreate affected sandboxes or add the oci-engine kit before relying on OCI behavior after stop/start. ### Features * **acq:** choose the host port per sandbox, and let --publish pin it ([#526](#526)) ([410f332](410f332)) * **acq:** export ACQ_WORKSPACE and ACQ_CLONE into the guest on both backends ([#457](#457)) ([e0b9a3b](e0b9a3b)) * **acq:** interactive `acq configure` UI for extra kits + token-scoping defaults ([#500](#500)) ([13d9d3a](13d9d3a)) * **kits:** adopt pinned agent and capability kits ([#474](#474)) ([6a67063](6a67063)) * **secrets:** add Windows-native DPAPI secret backend ([#464](#464)) ([907f404](907f404)) * **windows:** add acq preview install path ([#463](#463)) ([d6d76f4](d6d76f4)) ### Bug Fixes * **acq:** use sandbox workspace for github scoping ([#476](#476)) ([1610835](1610835)) * **ci:** bump js-yaml to 4.3.2 to clear a high-severity CPU-DoS advisory ([#454](#454)) ([3217b49](3217b49)) * **ci:** bump smol-toml to 1.7.1 to clear an infinite-loop DoS advisory ([#459](#459)) ([c2291bf](c2291bf)) * **ci:** centralize markdownlint coverage ([#548](#548)) ([a27c31e](a27c31e)) * **ci:** pin markdownlint-cli2 once, clear GHSA-253c, unify file selection ([#527](#527)) ([e712685](e712685)) * **ci:** unblock unrelated PRs from the bash32 required-check deadlock ([#513](#513)) ([187aa57](187aa57)) * **install:** keep curl | sh stdin intact and tap msb formula first ([#462](#462)) ([c808d74](c808d74)) * **install:** trust the microsandbox-acq dependency before brew install ([#534](#534)) ([ffac0fa](ffac0fa)) * **kits:** land collapsed kit stack implementation ([#549](#549)) ([bcd5b9c](bcd5b9c)) * **msb:** allow Playwright browser downloads ([#477](#477)) ([0c399c5](0c399c5)) * **msb:** block unsafe 0.7 migration versions ([#510](#510)) ([3634046](3634046)) * **msb:** carry every origin URL, keep the scratch private, fail closed on a copy miss ([#488](#488)) ([babcd49](babcd49)) * **msb:** carry the source checkout's git identity into the --clone scratch ([#443](#443)) ([320d784](320d784)) * **msb:** follow the host resolvers and disable DNS rebind protection in the guest ([#444](#444)) ([d2f4bdc](d2f4bdc)) * **msb:** point the --clone scratch's origin at the source checkout's remote ([#455](#455)) ([7f48b94](7f48b94)) * **msb:** refresh ssh authorization for port publish ([#446](#446)) ([72a6bd5](72a6bd5)) * **msb:** session parity for exec/attach/shell (cwd, terminal identity, login shell) ([#427](#427)) ([6261eda](6261eda)) * **msb:** skip the recursive home chown when the image baked the agent user ([#432](#432)) ([d8f66a4](d8f66a4)) * **msb:** thread the merged kit env onto kit lifecycle commands ([#519](#519)) ([2248a92](2248a92)) * **msb:** translate kit wildcard network rules ([#451](#451)) ([e22bbc3](e22bbc3)) * **msb:** verify post-hoc port forward listener ([#530](#530)) ([8ca50e1](8ca50e1)) * **msb:** warn on broadened kit wildcard rules ([#460](#460)) ([6ded3ce](6ded3ce)) * **msb:** warn when ssh-agent route is missing ([#448](#448)) ([3a767a4](3a767a4)) * **sbx:** emit valid setup.install shape for the git-identity mixin ([#492](#492)) ([8e6160c](8e6160c)) * **sbx:** raise the version floor to 0.39.0, where sbx create --env first exists ([#491](#491)) ([30f9ecf](30f9ecf)) * **sbx:** wait for startup barrier before attach ([#512](#512)) ([33c83c5](33c83c5)) * **secret:** reject multiline test secret values ([#447](#447)) ([f2da950](f2da950)) * **secrets:** propagate rotation across backends ([#509](#509)) ([9641958](9641958)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Bret Mogilefsky <[email protected]>
On the msb backend, first provision runs
chown -R agent:agent /home/agentunconditionally. On an image with a dense baked agent home (the devenv images ship single-user Nix state, thousands of tiny files) that write-crawl grinds the guest ext4 journal for minutes, with no output, right after "Waiting for the sandbox to finish booting… done". sbx never pays this: its template images bake the agent user and home ownership at image build time, so the create path has nothing to fix.Fix. The guest setup script now tracks whether acq itself created the
agentuser. The recursive chown runs only on that path (a plain OCI base can leave a half-created or root-owned home, the case the FATAL rework was about). When the image shipped the user, acq trusts the baked ownership: the top-levelchown /home/agentand thesu agent -c "test -w /home/agent"writability backstop still run on every path, but they cover the home directory itself, not its contents. A custom image that shipsagentyet bakes root-owned content under/home/agentviolates the base-image contract and is no longer papered over; the backend guide's contract section now says so and how to fix the image. The default templates and the devenv image are fully agent-owned, so the common path is unaffected.Spinner. The agent-user step now gets a progress status ("Preparing the agent user"), like the OCI-engine and agent-install steps, so whatever it costs no longer reads as a hang.
Fixes #422
AI-assisted