--- phase: 04 plan: 1 subsystem: sandbox-script tags: [credentials, auth, audit, bwrap] dependency_graph: requires: [] provides: [credential-mount, unified-audit] affects: [claudebox.sh] tech_stack: added: [] patterns: [conditional-bwrap-args-array, unified-audit-prefixes] key_files: created: [] modified: - claudebox.sh decisions: - "Used BWRAP_ARGS array instead of inline exec bwrap to support conditional credential mount" - "Used [~]/[>]/[+] text prefixes (not color-only) for accessibility" - "print_audit depends on CREDS_MOUNT set earlier in script — no API change needed" metrics: duration: "2m 30s" completed: "2026-04-10" tasks_completed: 2 tasks_total: 2 files_changed: 1 --- # Phase 4 Plan 1: Credential Mount + Audit Redesign Summary **One-liner:** Read-write `~/.claude/.credentials.json` bind mount for OAuth passthrough plus unified `[~]/[>]/[+]` env audit with Mounts and Network sections. ## What Was Built ### Task 4.1.1 — Add credential file mount Added conditional detection and mounting of `~/.claude/.credentials.json` into the sandbox: - `CREDS_FILE` / `CREDS_MOUNT` variables set after `mkdir -p "$HOME/.claudebox"` - When `CREDS_MOUNT=true`: `--bind "$CREDS_FILE" "$HOME/.claude/.credentials.json"` added to bwrap args - Silent skip when file absent — no error or warning output - Uses `--bind` (not `--ro-bind`) so OAuth token refresh can write back to the file - `exec bwrap` refactored to use `BWRAP_ARGS` array to support the conditional mount cleanly - Credential bind mirrored in `--dry-run` display block ### Task 4.1.2 — Rewrite print_audit Rewrote `print_audit` from three separate sections to a unified list: - Single loop ordering: sandbox keys `[~]` (green) → host allowlisted `[>]` (yellow) → extra `[+]` (cyan) - Text prefixes readable without color (accessibility — D-07) - PATH retains multiline indented display - New `Mounts:` section shows CWD, `~/.claude`, and conditional credentials line - New `Network:` section shows `full (host network)` as Phase 6 placeholder - All print_audit output goes to stderr - `mask_value` called for every env var value in all three loops ## Decisions Made 1. **BWRAP_ARGS array:** The `exec bwrap ... \` inline form cannot have a conditional in the middle. Refactored to build a `BWRAP_ARGS` array and `exec bwrap "${BWRAP_ARGS[@]}"`. This is cleaner and extensible for future conditional mounts (network tiers, profile mounts). 2. **Text prefixes for accessibility:** `[~]`, `[>]`, `[+]` are printed as literal text (not just color differences). Color is additive — the prefix meaning is clear in monochrome terminals and when piped. 3. **CREDS_MOUNT scoping:** `CREDS_MOUNT` is set at script top-level (before `print_audit`), so the Mounts section in `print_audit` can read it without needing to re-check the filesystem. ## Commits | Task | Hash | Message | |-------|---------|---------| | 4.1.1 | 6465da8 | feat(04-01): add credential file mount for OAuth passthrough | | 4.1.2 | def8e67 | feat(04-01): rewrite print_audit to unified env list with Mounts and Network sections | ## Verification ``` bash -n claudebox.sh # SYNTAX OK grep 'CREDS_FILE' claudebox.sh # line 105: CREDS_FILE="$HOME/.claude/.credentials.json" grep 'CREDS_MOUNT' claudebox.sh # detection + dry-run + bwrap + Mounts section grep 'credentials.json' claudebox.sh # lines 105, 267, 331, 364 (dry-run + bwrap) grep 'ro-bind.*credentials' claudebox.sh # (no output — correct, uses --bind) grep '[~]' claudebox.sh # lines 239, 242, 248 grep '[>]' claudebox.sh # lines 239, 253 grep '[+]' claudebox.sh # lines 239, 257 grep 'Mounts:' claudebox.sh # line 263 grep 'Network:' claudebox.sh # line 273 grep 'full (host network)' claudebox.sh # line 274 ``` ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 1 - Refactor] Refactored exec bwrap to BWRAP_ARGS array** - **Found during:** Task 4.1.1 - **Issue:** The `exec bwrap ... \` inline multi-line form cannot include a conditional mount (`if [[ "$CREDS_MOUNT" == true ]]; then ... fi`) in the middle of the argument list. - **Fix:** Replaced the inline `exec bwrap` form with a `BWRAP_ARGS` array built up with conditional appends, then `exec bwrap "${BWRAP_ARGS[@]}"`. This preserves identical runtime behavior while enabling conditional mounts. - **Files modified:** claudebox.sh - **Commit:** 6465da8 ## Known Stubs - **Network section:** `full (host network)` in `print_audit` is an intentional Phase 4 placeholder. Network isolation tiers will replace this in Phase 6. ## Threat Flags | Flag | File | Description | |------|------|-------------| | threat_flag: credential-exfil | claudebox.sh | Read-write bind of `~/.claude/.credentials.json` gives sandbox read access to OAuth tokens; sandbox has full host network, so exfiltration is possible. Accepted risk per plan threat model — Phase 6 network tiers reduce surface. | ## Self-Check: PASSED - claudebox.sh exists and was modified: FOUND - Commit 6465da8 exists: FOUND - Commit def8e67 exists: FOUND - bash -n claudebox.sh: PASSES - credentials.json appears in both exec bwrap block and dry-run block: CONFIRMED (lines 364, 331) - [~]/[>]/[+] prefixes present in print_audit: CONFIRMED - Mounts: / Network: sections present: CONFIRMED - full (host network) present: CONFIRMED