Files
stack/docs/scratchpads/p8-016-tool-hardening.md
Jason Woltje 7f6464bbda
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
feat(gateway): tool path hardening + sandbox escape prevention (P8-016) (#177)
Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
2026-03-16 02:02:48 +00:00

1.6 KiB

P8-016: Security — Tool Path Hardening + Sandbox Escape Prevention

Status: in-progress

Branch: feat/p8-016-tool-hardening

Issue: #169

Scope

Harden file, git, and shell tool factories so no path operation escapes sandboxDir.

Files to Create

  • apps/gateway/src/agent/tools/path-guard.ts (new)
  • apps/gateway/src/agent/tools/path-guard.test.ts (new)

Files to Modify

  • apps/gateway/src/agent/tools/file-tools.ts
  • apps/gateway/src/agent/tools/git-tools.ts
  • apps/gateway/src/agent/tools/shell-tools.ts

Analysis

file-tools.ts

  • Has existing resolveSafe() function but uses weak containment check (relative path)
  • Replace with guardPath (for reads/lists on existing paths) and guardPathUnsafe (for writes)
  • Error pattern: return { content: [{ type: 'text', text: 'Error: ...' }], details: undefined }

git-tools.ts

  • Has clampCwd() that silently falls back to sandbox root on escape attempt
  • Replace with strict guardPath that throws SandboxEscapeError, caught and returned as error
  • Also need to guard the path parameter in git_diff

shell-tools.ts

  • Has clampCwd() same silent-fallback approach
  • Replace with strict guardPath that throws SandboxEscapeError

Key Design Decisions

  • guardPath: uses realpathSync.native to resolve symlinks, requires path to exist
  • guardPathUnsafe: lexical only (path.resolve), for paths that may not exist yet
  • Both throw SandboxEscapeError on escape attempt
  • Callers catch and return error result

Verification

  • pnpm typecheck
  • pnpm lint
  • pnpm format:check
  • pnpm test