docs(phase-05): add security threat verification
This commit is contained in:
parent
4af459c4a8
commit
dc3674c2fc
2 changed files with 179 additions and 0 deletions
|
|
@ -0,0 +1,118 @@
|
|||
---
|
||||
phase: 05-per-project-instance-isolation
|
||||
plan: "01"
|
||||
subsystem: sandbox-mount-architecture
|
||||
tags: [bwrap, mounts, isolation, per-project, instance-hash, worktree]
|
||||
dependency-graph:
|
||||
requires: []
|
||||
provides: [per-project-instance-isolation, direct-claude-bind, instance-hash-dirs]
|
||||
affects: [claudebox.sh, REQUIREMENTS.md]
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- sha256sum[:16] of canonical git root path for per-project instance identity
|
||||
- git rev-parse --git-common-dir for worktree-aware canonical root resolution
|
||||
- bwrap overlay mounts (last-mount-wins) on top of direct ~/.claude bind
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- claudebox.sh
|
||||
- .planning/REQUIREMENTS.md
|
||||
decisions:
|
||||
- D-01: Direct bind of ~/.claude (not ~/.claudebox symlink) gives plugins/skills/hooks/MCP full visibility
|
||||
- D-02: Per-project projects/ overlay via SHA-256[:16] of canonical root path
|
||||
- D-03: history.jsonl bind overlay from ~/.claudebox/history.jsonl
|
||||
- D-06: SANDBOX.md injected as file overlay; CLAUDE.md injection removed (user's real CLAUDE.md already has @SANDBOX.md)
|
||||
- D-08: compute_canonical_root uses git rev-parse --git-common-dir for worktree awareness
|
||||
- D-13: INST-03 satisfied architecturally — Claude Code manages its own file concurrency; no locking needed in claudebox.sh
|
||||
- /bin/sh symlink added to sandbox so hooks can exec sh (ENOENT fix)
|
||||
metrics:
|
||||
duration: "~45 minutes"
|
||||
completed: "2026-04-13"
|
||||
tasks_completed: 3
|
||||
files_modified: 2
|
||||
---
|
||||
|
||||
# Phase 05 Plan 01: Mount Architecture Rewrite and Per-Project Instance Isolation Summary
|
||||
|
||||
Direct bind of `~/.claude` into sandbox with SHA-256-keyed per-project overlay mounts, replacing the old `~/.claudebox` symlink approach that hid all plugins, skills, hooks, and MCP configs from Claude Code.
|
||||
|
||||
## What Was Built
|
||||
|
||||
### Mount Architecture Rewrite (claudebox.sh)
|
||||
|
||||
Replaced the old mount approach (`--bind ~/.claudebox ~/.claudebox` + `--symlink ~/.claudebox ~/.claude`) with a new architecture:
|
||||
|
||||
- `--bind "$HOME/.claude" "$HOME/.claude"` — direct bind, makes all Claude Code config (plugins, skills, hooks, MCP, commands, settings) visible inside the sandbox (D-01)
|
||||
- `--bind "$INSTANCE_DIR" "$HOME/.claude/projects"` — per-project overlay; each project gets its own isolated directory mounted over the real `~/.claude/projects/` (D-02, INST-01)
|
||||
- `--bind "$HOME/.claudebox/history.jsonl" "$HOME/.claude/history.jsonl"` — history overlay; conversation history stored sandbox-side (D-03)
|
||||
- `--bind "$HOME/.claudebox/SANDBOX.md" "$HOME/.claude/SANDBOX.md"` — SANDBOX.md injected as file overlay (D-06)
|
||||
- `--bind "$CREDS_FILE" "$HOME/.claude/.credentials.json"` — credential mount updated to new target path
|
||||
|
||||
### Per-Project Instance Isolation
|
||||
|
||||
Added `compute_canonical_root()` function using `git -C "$cwd" rev-parse --git-common-dir` to resolve worktree-aware canonical repo root. Git worktrees return a path pointing to the main worktree's `.git/`, so `dirname(readlink -f(git_common))` gives the main worktree root for any worktree.
|
||||
|
||||
Instance hash computed as: `INSTANCE_HASH=$(printf '%s' "$CANONICAL_ROOT" | sha256sum | cut -c1-16)`
|
||||
|
||||
Each project gets `~/.claudebox/projects/$INSTANCE_HASH/` with a `project-root` file recording the canonical path. Directory created at startup with `mkdir -p`.
|
||||
|
||||
### Removed
|
||||
|
||||
- Old `--symlink "$HOME/.claudebox" "$HOME/.claude"` (D-01 replacement)
|
||||
- Old `--bind "$HOME/.claudebox" "$HOME/.claudebox"` (D-01 replacement)
|
||||
- CLAUDE.md injection block (`CLAUDEMD="$HOME/.claudebox/CLAUDE.md"`) — user's real `~/.claude/CLAUDE.md` already has `@SANDBOX.md` (D-06)
|
||||
|
||||
### Preserved
|
||||
|
||||
- `CLAUDE_JSON_FILE` / `CLAUDE_JSON_MOUNT` conditional bind (`--bind "$CLAUDE_JSON_FILE" "$HOME/.claude.json"`) — critical for auth token persistence
|
||||
|
||||
### Updated
|
||||
|
||||
- Dry-run block echoes new mount layout including instance dir and CLAUDE_JSON conditional
|
||||
- `print_audit` shows projects/ mount with instance dir path and canonical root for transparency
|
||||
- SANDBOX.md heredoc updated to remove `~/.claudebox` references (no longer visible in sandbox)
|
||||
|
||||
### /bin/sh Symlink Fix
|
||||
|
||||
Added `--symlink $(which bash) /bin/sh` to BWRAP_ARGS. Without it, git hooks and other scripts that use `/bin/sh` fail with `posix_spawn '/bin/sh': ENOENT` inside the sandbox. Not in original plan scope — auto-fixed per deviation Rule 1 (bug) and confirmed approved by user at checkpoint.
|
||||
|
||||
### Requirements Registration
|
||||
|
||||
Added INST-01 through INST-04 to `.planning/REQUIREMENTS.md` under new `### Instance Isolation` section, with traceability table entries mapping all four to Phase 5.
|
||||
|
||||
## Verification Results
|
||||
|
||||
- `bash -n claudebox.sh` passes (syntax clean)
|
||||
- `compute_canonical_root` present in claudebox.sh
|
||||
- `INSTANCE_HASH` computation present in claudebox.sh
|
||||
- New mount lines confirmed present via search
|
||||
- Old symlink/claudebox bind lines confirmed absent
|
||||
- Human checkpoint approved by user
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 1 - Bug] Added /bin/sh symlink so hooks can exec sh**
|
||||
- **Found during:** Task 1 (anticipated based on bwrap behavior + user confirmation at checkpoint)
|
||||
- **Issue:** Sandbox has no `/bin/sh` — git hooks and POSIX scripts that call `/bin/sh` fail with `posix_spawn '/bin/sh': ENOENT`
|
||||
- **Fix:** Added `--symlink $(which bash) /bin/sh` to BWRAP_ARGS
|
||||
- **Files modified:** claudebox.sh
|
||||
- **Commit:** 4baf576
|
||||
|
||||
## Known Stubs
|
||||
|
||||
None. All mount architecture changes are fully wired. Per-project instance dirs are created and used at runtime. No placeholder data flows to any UI or output.
|
||||
|
||||
## Threat Flags
|
||||
|
||||
None. No new network endpoints, auth paths, or unplanned trust boundary crossings introduced. The STRIDE mitigations in the plan's threat model (T-05-01 through T-05-04) were all implemented: `readlink -f` for symlink resolution, correct overlay mount order, hex-only INSTANCE_HASH path construction, and per-project isolation of `~/.claude/projects/`.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- FOUND: claudebox.sh (syntax check passed, compute_canonical_root present, INSTANCE_HASH present)
|
||||
- FOUND: .planning/REQUIREMENTS.md (INST-01 through INST-04 present)
|
||||
- FOUND: commit c5e8cca (mount architecture rewrite)
|
||||
- FOUND: commit 6eb3b46 (INST-01 through INST-04 registration)
|
||||
- FOUND: commit 4baf576 (/bin/sh symlink fix)
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
phase: "05"
|
||||
slug: per-project-instance-isolation
|
||||
status: verified
|
||||
threats_open: 0
|
||||
asvs_level: 1
|
||||
created: 2026-04-16
|
||||
---
|
||||
|
||||
# Phase 05 — Security
|
||||
|
||||
> Per-phase security contract: threat register, accepted risks, and audit trail.
|
||||
|
||||
---
|
||||
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description | Data Crossing |
|
||||
|----------|-------------|---------------|
|
||||
| Host → Sandbox | bwrap mount namespace | `~/.claude` config, per-project projects/ dir, history.jsonl, credentials |
|
||||
| Sandbox → Host FS | Per-project instance dir | Conversation history, project state (scoped to hash dir) |
|
||||
|
||||
---
|
||||
|
||||
## Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Disposition | Mitigation | Status |
|
||||
|-----------|----------|-----------|-------------|------------|--------|
|
||||
| T-05-01 | Tampering | Symlink resolution in `compute_canonical_root` | mitigate | `readlink -f` used to resolve symlinks before hashing; prevents symlink-based path manipulation | closed |
|
||||
| T-05-02 | Tampering | bwrap overlay mount ordering | mitigate | Direct `~/.claude` bind applied first; per-project projects/ overlay applied after — last-mount-wins semantics correctly isolate per-project state | closed |
|
||||
| T-05-03 | Injection | INSTANCE_HASH used in filesystem path | mitigate | Hash is hex-only (sha256sum output, `cut -c1-16`); no user-controlled input enters path construction | closed |
|
||||
| T-05-04 | Information Disclosure | Cross-project Claude projects/ data | mitigate | Each project gets its own `~/.claudebox/projects/$INSTANCE_HASH/` mounted over `~/.claude/projects/`; project A data invisible in project B sandbox | closed |
|
||||
| T-05-07 | Tampering | GC function path traversal | mitigate | `gc_instances()` scoped exclusively to `$HOME/.claudebox/projects/*/`; cannot escape to arbitrary filesystem paths | closed |
|
||||
|
||||
*Status: open · closed*
|
||||
*Disposition: mitigate (implementation required) · accept (documented risk) · transfer (third-party)*
|
||||
|
||||
---
|
||||
|
||||
## Accepted Risks Log
|
||||
|
||||
No accepted risks.
|
||||
|
||||
---
|
||||
|
||||
## Security Audit Trail
|
||||
|
||||
| Audit Date | Threats Total | Closed | Open | Run By |
|
||||
|------------|---------------|--------|------|--------|
|
||||
| 2026-04-16 | 5 | 5 | 0 | gsd-secure-phase (from summaries) |
|
||||
|
||||
---
|
||||
|
||||
## Sign-Off
|
||||
|
||||
- [x] All threats have a disposition (mitigate / accept / transfer)
|
||||
- [x] Accepted risks documented in Accepted Risks Log
|
||||
- [x] `threats_open: 0` confirmed
|
||||
- [x] `status: verified` set in frontmatter
|
||||
|
||||
**Approval:** verified 2026-04-16
|
||||
Loading…
Add table
Reference in a new issue