fix(sandbox): address PR #309 review — TOCTOU direction, denial metadata, shared roots, docs

- fs-sandbox: delegate the mutation with the freshly re-canonicalized target
  (not the stale one), so the checked identity is the mutated identity — a
  symlink swapped in after resolve() can no longer escape workspace-write.
- tool-fs: map a denial to an FsError carrying FS_SANDBOX_DENIED (not a plain
  Error), so ToolRegistry keeps the structured code on result.error for
  retry/observers while the message stays the shared marker.
- sandbox-local: derive the Seatbelt writable set from the shared
  writableRoots() helper, so the profile and the fs fence cannot drift.
- gen-doc-graphs: ctx.sandboxPolicy is owned by dsh-sandbox-policy and read only
  by the sandboxed executor/provider (the tool layers use the pure fold).
- docs: bash-sandbox/bash/permission READMEs and bash.md reflect the relocated
  policy home and the sandbox/mode rename; drop the stale stdout.golden.jsonl.
This commit is contained in:
kingwl
2026-07-20 13:59:18 +08:00
parent 1dd5757897
commit 2530bf8aa3
11 changed files with 65 additions and 107 deletions

View File

@@ -196,12 +196,12 @@ const SERVICE_ROLES: ServiceRole[] = [
},
{
key: 'sandboxPolicy',
pkg: 'sandbox',
pkg: 'sandbox-policy',
title: 'Sandbox policy home',
mode: 'core',
implementations: [],
consumers: ['bash-sandbox', 'fs-sandbox', 'tool-bash', 'tool-fs'],
note: 'The one home for the deployment default mode + workspace root and the per-session `sandbox/mode` override; both enforcing families read it so bash and fs cannot confine to different roots.',
consumers: ['bash-sandbox', 'fs-sandbox'],
note: 'The one home for the deployment default mode + workspace root; only the sandboxed executor and provider read the service (the tool layers use the pure `sandbox/mode` fold it also exports). Both enforcing families read it so bash and fs cannot confine to different roots.',
},
{
key: 'approval',