A GraalPy FileSystem that gives the Python sandbox REAL filesystem access
CONFINED to the session's filesystem roots.
Security model — every path-accessing operation canonicalizes its arguments and refuses anything that does not resolve UNDER a current filesystem root:
.. traversal is defeated by normalize./reload.GraalPy's own stdlib / internal resources live OUTSIDE the roots, so the
confined FS is wrapped with allowLanguageHomeAccess +
allowInternalResourceAccess (read-only access to the language home and
bundled resources) before it reaches the Context.
OUTBOX tap (DORMANT — the engine passes no outbox today, see
mpl-capture/incidental-capture-enabled?) — an optional engine-managed capture
directory ($VIS_OUTBOX, distinct from configured filesystem roots): the sandbox
may WRITE there and every file it closes is handed to on-close so the engine
can persist it as a session_iteration_attachment (the implicit twin of
attach). Reads, and writes anywhere else, are untouched. The machinery is kept
and tested for a future capture feature; today only attach records artifacts.
Empty/zero roots ⇒ DENY everything (fail closed).
A GraalPy `FileSystem` that gives the Python sandbox REAL filesystem access
CONFINED to the session's filesystem roots.
Security model — every path-accessing operation canonicalizes its arguments
and refuses anything that does not resolve UNDER a current filesystem root:
- `..` traversal is defeated by `normalize`.
- symlink escapes are defeated by resolving the path through the REAL path
of its nearest existing ancestor (so a symlink inside a root that points
outside is rejected, and a symlink whose target is inside is allowed).
- the root set is supplied by the current environment, rebuilt after `/reload`.
GraalPy's own stdlib / internal resources live OUTSIDE the roots, so the
confined FS is wrapped with `allowLanguageHomeAccess` +
`allowInternalResourceAccess` (read-only access to the language home and
bundled resources) before it reaches the Context.
OUTBOX tap (DORMANT — the engine passes no `outbox` today, see
`mpl-capture/incidental-capture-enabled?`) — an optional engine-managed capture
directory (`$VIS_OUTBOX`, distinct from configured filesystem roots): the sandbox
may WRITE there and every file it closes is handed to `on-close` so the engine
can persist it as a `session_iteration_attachment` (the implicit twin of
`attach`). Reads, and writes anywhere else, are untouched. The machinery is kept
and tested for a future capture feature; today only `attach` records artifacts.
Empty/zero roots ⇒ DENY everything (fail closed).(confined-filesystem roots-fn)(confined-filesystem roots-fn outbox)(confined-filesystem roots-fn outbox gate-fn)A GraalPy FileSystem confined to the filesystem roots returned by roots-fn
(a 0-arg fn → seq of root path strings). Delegates real I/O to the default FS
after confining every path argument. Wrapped so GraalPy's own stdlib / bundled
resources stay readable. Uses proxy (runtime dispatch) so the interface's
overloaded parsePath + varargs + void methods bind cleanly.
root-cache lives for the FS's lifetime and memoizes the per-root toRealPath
so confinement doesn't re-stat every root on every path operation.
outbox (optional, DORMANT — the engine passes nil, see
mpl-capture/incidental-capture-enabled?) —
{:dir <existing dir path string> :on-close (fn [^Path])}.
Its real path is treated as an always-allowed root (so the sandbox can write
there even though it is outside configured filesystem roots); a WRITE channel closed under it
fires on-close with the file path. The SAME on-close also fires for a
write closed under any system temp root (/tmp, $TMPDIR), so plain /tmp
scratch streams to the DB too, not just $VIS_OUTBOX. Nil ⇒ no tap.
A GraalPy `FileSystem` confined to the filesystem roots returned by `roots-fn`
(a 0-arg fn → seq of root path strings). Delegates real I/O to the default FS
after confining every path argument. Wrapped so GraalPy's own stdlib / bundled
resources stay readable. Uses `proxy` (runtime dispatch) so the interface's
overloaded `parsePath` + varargs + void methods bind cleanly.
`root-cache` lives for the FS's lifetime and memoizes the per-root `toRealPath`
so confinement doesn't re-stat every root on every path operation.
`outbox` (optional, DORMANT — the engine passes nil, see
`mpl-capture/incidental-capture-enabled?`) —
`{:dir <existing dir path string> :on-close (fn [^Path])}`.
Its real path is treated as an always-allowed root (so the sandbox can write
there even though it is outside configured filesystem roots); a WRITE channel closed under it
fires `on-close` with the file path. The SAME `on-close` also fires for a
write closed under any system temp root (`/tmp`, `$TMPDIR`), so plain /tmp
scratch streams to the DB too, not just `$VIS_OUTBOX`. Nil ⇒ no tap.cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |