anthropics / anthropics/sandbox-runtime
denyRead does not block access to individual files inside an allowRead directory
- Lingua principale
- TypeScript
- Stelle
- 5.2k
- Fork
- 439
- Merge medio
- 2g 9m
- PR unite (30g)
- 13
Descrizione
## Summary
When a directory is listed in `allowRead` (e.g. a project workspace), adding individual file paths to `denyRead` has no effect. The files remain fully readable inside the sandbox.
This makes it impossible to expose a working directory to the sandbox while hiding specific sensitive files within it (`.env`, `*_rsa`, `.secret`, etc.).
## Reproduction
```ts
import { spawnSync } from "child_process"
import fs from "fs"
fs.writeFileSync("secret.txt", "TOP SECRET DATA")
const config = {
network: {},
filesystem: {
denyRead: [process.cwd() + "/secret.txt"],
allowRead: [process.cwd()],
allowWrite: [process.cwd()],
denyWrite: []
}
}
fs.writeFileSync("/tmp/srt-test.json", JSON.stringify(config))
const res = spawnSync("srt", ["--settings", "/tmp/srt-test.json", "-c", "cat secret.txt"])
console.log("exit:", res.status) // 0 — expected non-zero
console.log("stdout:", res.stdout.toString()) // "TOP SECRET DATA" — expected empty/error
```
**Expected:** `cat secret.txt` fails with `Permission denied` or `No such file or directory`.
**Actual:** File is read successfully. `denyRead` is silently ignored.
## Root Cause
On Linux, `srt` uses `bubblewrap` (`bwrap`) to construct the mount namespace. The workspace directory is exposed via `--bind` or `--ro-bind`, which bind-mounts the entire directory tree. A subsequent `denyRead` entry for a file inside that tree has no mechanism to punch a hole in the existing bind mount.
`bwrap` does support file-level bind-mount overlays, which would solve this:
```bash
# This works natively with bwrap:
bwrap \
--bind /workspace /workspace \
--ro-bind /dev/null /workspace/.env \
-- cat /workspace/.env
# Result: empty read, secret is hidden
```
By bind-mounting `/dev/null` (or an empty tmpfs file) over each `denyRead` path, the file is effectively masked at the VFS level. The inode is replaced, so `cat`, `head`, `less`, and any other read operation sees an empty file. The original file is untouched on the host.
## Proposed Fix
When srt generates the bwrap command internally, for each denyRead path that falls inside an allowRead directory:
1. Check if the path is a regular file (not a directory).
2. If so, add `--ro-bind /dev/null ` **after** the parent directory's `--bind`/`--ro-bind` directive. Mount order matters — later mounts override earlier ones at the same path.
3. For directories in `denyRead`, the existing `--tmpfs ` approach should already work.
## Environment
- `srt` installed via `npm install -g @anthropic-ai/sandbox-runtime`
- Linux x64, kernel 6.x
- bubblewrap available in PATH
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Valutazione
Questa issue non è ancora stata valutata.