fix: keep agent sandbox sockets on short paths

This commit is contained in:
2026-07-11 02:22:19 +08:00
parent 38d9a9c6cb
commit 035c264179
7 changed files with 72 additions and 44 deletions
@@ -99,16 +99,17 @@ The boundary is enforced by the Claude Code SDK's built-in sandbox
in the run's workspace while `/bin`, shared libraries, CA certificates,
fonts and the configured `cph` executable remain available as the external
tool exception described above.
- `sandbox.filesystem.allowWrite: [workspaceDir]` confines every write to the
canonical ADR-0007 workspace. Config/cache/home stay beneath
`.cph/agent-runtime/`. `TMPDIR`, `TMP`, and `TEMP` use the absolute
workspace-local `.cph/t/` path so tools remain anchored after `cd`;
`CLAUDE_CODE_TMPDIR` uses the short relative `.cph/t` prefix, resolved from
the canonical workspace cwd, because the SDK's socat bridge otherwise falls
back to a host temp directory when its Unix-socket prefix is too long.
Root is denied for writes and only the canonical workspace is re-opened, so
`/tmp`, `/var/tmp`, sibling projects and every other host path are rejected.
There is no writable scratch exception outside the project directory.
- `sandbox.filesystem.allowWrite` confines deliverable writes to the canonical
ADR-0007 workspace and opens one service-owned SDK scratch directory under
`/tmp/cph-agent-<uid>/<project-hash>`. Config/cache/home stay beneath
`.cph/agent-runtime/`; `TMPDIR`, `TMP`, `TEMP`, and `CLAUDE_CODE_TMPDIR` all
point at the project-keyed scratch directory. Its canonical prefix is capped
at 64 bytes so the SDK can append randomized `socat` bridge socket names
without exceeding Linux `sockaddr_un.sun_path`. The per-Organization OS UID,
project hash, mode `0700`, symlink rejection, and exact sandbox allowlist keep
that scratch isolated; sibling scratch paths and ordinary `/tmp` remain
denied. Root is denied for writes and only the workspace plus that exact
internal scratch directory are re-opened.
- The SDK subprocess environment replaces rather than spreads `process.env`.
Only provider protocol variables and non-secret runtime variables cross the
boundary; database, Feishu and Hub session credentials never enter it.