Skip to content

fix(user-mapping): don't chown the workspace to root (corrupts host repo)#176

Merged
pofallon merged 3 commits into
mainfrom
fix/workspace-chown-root
Jun 2, 2026
Merged

fix(user-mapping): don't chown the workspace to root (corrupts host repo)#176
pofallon merged 3 commits into
mainfrom
fix/workspace-chown-root

Conversation

@pofallon

@pofallon pofallon commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Problem

Running the test suite kept flipping /workspaces/deacon to root:root ownership, and it came back after every fix.

Root cause

deacon up bind-mounts the host workspace into the container. Per the git-root mount default, an in-repo workspace folder resolves to the git root (/workspaces/deacon), so the whole repo gets mounted. Then user_mapping::set_workspace_ownership runs chown <uid>:<gid> <workspaceFolder> as root inside the container whenever a remoteUser is set (needs_user_mapping() = remote_user.is_some()). The feature-and-dotfiles / single-container fixtures set "remoteUser": "root", so it did chown 0:0 /workspace — which, on a bind mount, rewrites the host repo dir to root:root. The up_prebuild tests run for real in the docker integration job, so it recurred every run.

Fix (two layers)

  1. Production guard (crates/core/src/user_mapping.rs): skip the workspace ownership adjustment when the target user is root (uid 0) — chowning to root is pointless (root already has full access) and actively corrupts host ownership on bind mounts. Non-root users still get the adjustment. Adds test_workspace_ownership_skipped_for_root and test_workspace_ownership_adjusted_for_nonroot. This protects real users and canaries, not just tests.
  2. Hermetic tests (up_prebuild.rs, up_dotfiles.rs): copy fixtures into a TempDir outside the repo and run up there, so the git root is never mounted regardless. Drops the now-obsolete git-root .devcontainer-state cleanup.

integration_up_with_features.rs already passed --mount-workspace-git-root=false; no other in-repo up tests were unguarded.

Verification

  • cargo test -p deacon-core --lib user_mapping — 17 passed (incl. both new tests).
  • cargo nextest run -E 'binary(=up_prebuild)' — 8 passed; no root-owned artifacts created in /tmp or the repo during the run (TempDirs were created and cleaned by the unprivileged test user, proving no chown fired).
  • cargo fmt --all -- --check and cargo clippy --all-targets --all-features -- -D warnings clean.

Note: the repo dir is currently still root:root from earlier (pre-fix) runs; a one-time sudo chown -R vscode:vscode /workspaces/deacon is needed, after which it will stay correct.

🤖 Generated with Claude Code

pofallon and others added 3 commits June 2, 2026 02:13
chown 0:0 on a bind-mounted workspace rewrites the HOST directory to
root:root (corrupting the dev workspace). Skip the adjustment when the
target user is root; non-root users still get it. Adds unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Keeps the workspace outside the repo so up never bind-mounts the git
root into the container.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Keeps the workspace outside the repo so up never bind-mounts the git
root into the container; drops the now-obsolete git-root state cleanup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pofallon pofallon merged commit 7a889c4 into main Jun 2, 2026
11 checks passed
@pofallon pofallon deleted the fix/workspace-chown-root branch June 2, 2026 02:26
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