Skip to content

fix(browser): a root caller operates the seat's store as the seat, and setup clears inherited setgid (DIVE-4348) - #64

Merged
5dive-bot merged 1 commit into
mainfrom
fix/DIVE-4348-browser-root-caller-and-setgid
Sep 12, 2026
Merged

fix(browser): a root caller operates the seat's store as the seat, and setup clears inherited setgid (DIVE-4348)#64
5dive-bot merged 1 commit into
mainfrom
fix/DIVE-4348-browser-root-caller-and-setgid

Conversation

@lodar

@lodar lodar commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

What was broken (measured on exact-swallow, 2026-09-12, while executing DIVE-4348 steps 1–3)

1. The dashboard's only path refused every verb but setup. GET /server/browser/sitesexecOnServer → shelld /shell/execsudo -n /usr/local/bin/5dive browser ls (root, SUDO_USER=claude). _seat resolves to claude, but _audit compares the store owner to id -u (0):

$ sudo -n /usr/local/bin/5dive browser ls
5dive browser: /var/lib/5dive/browser-profiles/claude is owned by uid 1000, not by you (uid 0) — refusing …
rc=77

So the API returned available:false on every box, and DIVE-4239's tile could never list a site. Both grades of #164 read the exec path against a mock.

2. Every seat store was 2700, and the audit wants 700. /var/lib/5dive is 2750 on every box; a directory made under it inherits setgid; GNU chmod 700 PRESERVES that bit on a directory. So setup left browser-profiles/<seat> at 2700:

$ 5dive browser ls        # as claude
5dive browser: /var/lib/5dive/browser-profiles/claude is mode 2700, want 700 — refusing …

The fix

  • Root with a non-root SUDO_USER re-executes as that seat (runuser -u) before any store is touched, for every verb except setup (root's by design — it chowns). runuser keeps the environment and sets HOME/USER to the seat's (verified on the control plane).
  • setup uses the 5-digit chmod form (00711 root, 00700 seat, 00700 site), which clears an inherited setgid. The audit stays strict and still never repairs.
  • browser 1.1.0 → 1.1.1; CHANGES entry.

Verified

  • tests/browser_plugin_unit.sh: 199 passed, 0 failed (6 new arms T2c2–T2c7: the setgid control + the 5-digit clear, and the static guards for the re-exec and for setup staying root).
  • On exact-swallow, clearing setgid by hand (chmod g-s) turned 5dive browser ls as claude from exit 77 to exit 0 — the mechanism in (2). The root-path fix (1) is verified on that box in DIVE-4348's body once the fixed binary is on it.

Rows: DIVE-4348 (the recipe that found it).

🤖 Generated with Claude Code

…d setup clears inherited setgid (DIVE-4348)

The dashboard reaches every browser verb through shelld's whitelist,
sudo -n /usr/local/bin/5dive browser ... — root with SUDO_USER=claude.
_seat resolved that to claude but _audit compared the store owner to
id -u (0), so every verb but setup exited 77 and GET /sites read every
box as unavailable. Root now re-executes as the seat via runuser.

Separately /var/lib/5dive is 2750 on every box; a directory made under
it inherits setgid and GNU chmod 700 preserves that bit, so setup left
every seat store 2700 and the audit (want 700) refused. setup now uses
the 5-digit chmod form, which clears it. Both measured on exact-swallow
2026-09-12. browser 1.1.0 -> 1.1.1.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@5dive-bot
5dive-bot merged commit 49505cb into main Sep 12, 2026
2 checks passed
@5dive-bot
5dive-bot deleted the fix/DIVE-4348-browser-root-caller-and-setgid branch September 12, 2026 03:01
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.

2 participants