Dynamic analysis sandbox — design (not implemented)¶
Warden analyses packages statically and never executes them. This document describes an optional
dynamic layer that could observe what an install actually does. None of it is built. The only
code that exists is configuration (SANDBOX_* settings) and the dynamic-sandbox provenance value
reserved for findings. Settings validation refuses SANDBOX_ENABLED=true, so GET /system/info
always reports sandbox: false and no deployment can claim a layer that does not run.
Why it is not the default¶
Running a suspected malicious package is exactly what its author wants. Static analysis covers the classic payloads (install hooks, start-up hooks, encoded execution, credential access) without that risk. A sandbox adds coverage for behaviour that only appears at run time — environment-dependent payloads, second-stage downloads, time bombs — at the cost of a much larger attack surface. It must therefore be opt-in, isolated from everything else, and its findings must never be required for a verdict.
Threat model¶
Assume the package is actively hostile and knows it may be sandboxed:
- it tries to escape the container or the kernel boundary;
- it tries to reach the network (exfiltration, C2, lateral movement to the database or Redis);
- it tries to exhaust resources (fork bombs, disk fill, memory);
- it detects the sandbox and behaves benignly (evasion);
- it produces output designed to exploit Warden's parsers (log injection, huge output).
Proposed architecture¶
- Separate executor. A worker process on a dedicated host or node pool — never inside the API
container — pulls sandbox jobs from
scan_jobs(kindsandbox). It has no database credentials beyond writing its own job result, and no access to Warden's secrets. - Strong isolation. Each job runs in a fresh gVisor (
runsc) container (SANDBOX_RUNTIME; validation should accept no other runtime once the sandbox exists). Read-only root filesystem, a size-capped tmpfs work directory,--network none, all capabilities dropped,no-new-privileges, the default seccomp profile, a non-root user, and the memory / CPU / PID limits fromSANDBOX_MEMORY_MB,SANDBOX_CPUS,SANDBOX_PIDS_LIMIT. A wall-clock limit (SANDBOX_TIMEOUT_SECONDS) kills the whole container. - Pinned image.
SANDBOX_IMAGEmust be an image pinned by digest containing only the Python interpreter and an in-container tracer; it is rebuilt through the same Trivy-gated pipeline as Warden's own images. - What runs.
pip install --no-deps --no-build-isolationof the already-downloaded, hash-verified artifact (never a fresh download inside the sandbox), thenpython -c "import <pkg>". - Observation. gVisor's syscall trace (
runsc --straceor its monitoring interface) records process execution, file writes outside the work directory, attempted socket connections (which fail, but are recorded), and reads of credential paths. A fake-credential environment (honeytoken values in~/.aws/credentials,GITHUB_TOKEN, …) makes credential access observable without real secrets. - Bounded, sanitised output. The executor parses the trace into a fixed schema, caps every list
and string, and emits standard
Findingobjects with provenancedynamic-sandbox(for exampleNETWORK_EGRESS,SUBPROCESS_EXEC,PERSISTENCE,ENV_HARVEST). Raw traces are never shown to users or stored unbounded. - Fail safe. A sandbox timeout, crash or unavailable runtime records the layer as unavailable; it never lowers risk and never blocks the static verdict.
Open questions before building it¶
- Detection of sandbox-aware payloads (time checks, CPU count, container markers) and how far to imitate a developer machine.
- Whether observed network attempts should be answered by a fake DNS / HTTP sink to capture second-stage URLs, and how to keep that sink from becoming an exfiltration path.
- Cost: a gVisor container per scan is far slower than static analysis; it probably belongs behind a policy trigger (for example only for packages that static analysis rated medium or above).
- Operating requirements: gVisor needs Linux hosts that allow it; many managed platforms do not.
Until those are answered and the design has an independent security review, Warden stays static-only.