Sandbox hardening (a new non-LLM static pre-check), a decompression-bomb upload
fix, and correctness/perf fixes from a full code audit. No breaking changes.
Security
- Added a non-LLM static pre-check (
statlee/codecheck.py) that runs before
every sandbox execution on all three code paths (/chat,/run,/wrangle).
It is a deterministic AST denylist (Python) / regex denylist (R) that rejects
network and process imports,os/environment/file-exfiltration primitives,
dynamic code execution (eval/exec/compile/__import__), the classic
__subclasses__/__globals__sandbox-escape idioms, and file opens outside
the run directory. It is defense in depth added to the LLM moderation gates,
so in the defaultsubprocessmode the safety boundary is no longer solely
LLM-dependent. A blocked script is refused before execution and its credit
(when billing is on) is refunded. - The static pre-check resolves module aliases (
import os as o; o.system(...))
and blocks direct from-imports ofos/sys/shutilprimitives
(from os import system), closing two one-line evasions of the attribute
rules. - Docker Compose no longer bind-mounts a readable
.envsecret into the
untrusted-execution container, and the subprocess-isolation warning now fires
in development too, not only in production.
Fixed
/exportand/interpretnow use the script that produced the last run
instead of the most-recently-approved script. A/wrangleperformed after an
analysis no longer makes the project export bundle the one-line wrangle
transform asscript.py, or feed it to the auto-debugger, in place of the
analysis script.- Bounded upload parsing against a decompression bomb. Excel/Stata/SPSS uploads
are size/row/cell-capped before full materialization (zip central-directory
metadata for.xlsx, reader metadata for.sav/.dta, with a post-read cell
backstop) and rejected with a 413, so a small compressed file can no longer
expand to gigabytes and pin a worker.
Changed
/data_pagecaches the filtered frame per filter set and gets an explicit
tighter rate limit, removing the repeated full-frame rescan on the paging hot
path.- The container image now defaults
WEB_CONCURRENCYto 1 so the in-memory
rate-limit store and the monthly priority ceiling hold at their configured
numbers by default; running more than one worker is documented to require a
sharedRATELIMIT_STORAGE_URI.
Docs
- README and
docs/ARCHITECTURE.mdnow qualify the "network-less" and
"secret-free" isolation claims by mode (kernel-enforced indockermode; the
static pre-check plus moderation gates in the defaultsubprocessmode) and
document the static pre-check as the non-LLM boundary. - Documented the model-role mapping (
MODEL_PROis the default/draft tier;
MODEL_PRO_MAXis the Pro-mode model) inconfig.py,.env.example, and the
README to prevent misconfiguration.
Internal
- Test suite grew from 334 to 400 passing (plus 4 skip-marked Docker/POSIX tests
that run in CI); the README badge reflects the new count.