Skip to content

Sandbox: isolate tool cache from ~/.cache (security + usability) #26

Description

@nhuelstng

Problem

The default sandbox profile grants ~/.cache read-write to every harness session. ~/.cache is shared across all applications and can contain sensitive cached data:

  • ~/.cache/gh/ — GitHub CLI OAuth token cache, API response cache
  • ~/.cache/copilot/ — Copilot package cache
  • ~/.cache/claude-cli-nodejs/ — Claude CLI staging
  • Any other app's cached data

A compromised or buggy harness running inside the sandbox can read this data (cross-app leakage) or poison cached files for other apps.

Proposed approach

Replace the broad ~/.cache rw grant with a per-workdir persistent cache dir at ~/.cache/omac/<workdir-hash>/. Redirect specific tool caches into it via env vars:

func cacheEnvVars(cacheDir string) map[string]string {
    return map[string]string{
        "GOCACHE":         filepath.Join(cacheDir, "go-build"),
        "NPM_CONFIG_CACHE": filepath.Join(cacheDir, "npm"),
        "PIP_CACHE_DIR":   filepath.Join(cacheDir, "pip"),
        "CARGO_HOME":      filepath.Join(cacheDir, "cargo"),
    }
}

XDG_CACHE_HOME is not set — it would redirect ALL XDG-respecting tools, breaking those that need existing cache data from other apps (e.g. gh hardcodes ~/.cache/gh/).

The cache dir is created by omac at launch, granted --allow (rw), and persists across sessions for warm rebuilds. Tools without env var overrides keep their default paths — users add specific cache dirs to their profile if needed.

Implementation sketch

  1. createCacheDir(workdir) — creates ~/.cache/omac/<sha256(workdir)[:6]>/ (0o700)
  2. Grant --allow <cacheDir> in start.go + serve.go
  3. Set GOCACHE, NPM_CONFIG_CACHE, PIP_CACHE_DIR, CARGO_HOME in the sandbox env
  4. Also strip ~/.cache, ~/Library/Caches, ~/go, ~/.rustup, ~/.cargo, ~/.nvm, ~/.bun/bin from DefaultProfile (binary resolution now handled by shebang detection + resolveInnerBinaryPath)

Pros

  • Security: No cross-app cache leakage. Only omac's own subtree is exposed, not other apps' cached data (OAuth tokens, API responses, etc.)
  • Usability: Tools work out of the box — no config needed. Caches persist across sessions for fast rebuilds.
  • Isolation: Per-workdir, so different projects don't cross-contaminate.
  • Explicit: Only 4 well-known tool caches are redirected. Other tools keep their default paths — users add specific cache dirs to their profile when needed.

Cons

  • Cold start: First session for a workdir has empty caches (no reuse from system caches). Subsequent sessions are warm.
  • Incomplete coverage: Only Go, npm, pip, cargo are redirected. Tools without env var overrides (e.g. gh, docker, custom tools) need manual profile entries.
  • Disk usage: Per-workdir cache dirs accumulate. No automatic cleanup (could add GC later).
  • Poisoning: A malicious tool could plant poisoned cache files. Blast radius is limited to omac sessions for that workdir — next session reads its own poisoned cache, not the system's. Much better than poisoning ~/.cache/gh/.

Alternatives considered

  1. TMPDIR redirect only: Redirect all caches to ephemeral $TMPDIR. More secure (ephemeral) but cold rebuild every session. Bad usability.
  2. XDG_CACHE_HOME redirect: Set XDG_CACHE_HOME to the per-workdir dir. Catches all XDG-respecting tools automatically, but breaks tools that hardcode ~/.cache/<tool>/ and need existing data. Too broad.
  3. Status quo: Keep ~/.cache rw in default profile. Simple but insecure (cross-app leakage).

Context

Explored during PR #24 (codex+copilot harness support). Reverted to keep that PR focused on harness support. The SandboxDirs field (per-harness config/state dirs) stays — it is harness-related. This issue covers the non-harness cache isolation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions