6/10/2026

Executing Untrusted Model Code Safely: Our Sandbox Design

The model outputs Python. Running unreviewed, model-generated Python is a remote-code-execution risk unless you take it seriously.

Once a fine-tuned model is generating CadQuery Python from a prompt, the pipeline has to actually run that code to produce a part — there's no way to turn a script into a STEP file without executing it. That's a real problem, not a hypothetical one: the model's output is, by construction, unreviewed, machine-generated Python, and executing arbitrary unreviewed Python is one of the textbook definitions of a remote-code-execution risk. A model that has never been prompted to write os.system("rm -rf /") can still emit it — through a bad sample, an adversarial prompt, or an outright bug — and if the executor just runs whatever string comes back, the entire host is exposed. The sandbox exists so that a bad generation is an annoyance, not an incident.

Defense in depth, not one clever trick

The design deliberately avoids relying on a single safety mechanism, because any one mechanism can have a hole in it. Instead there are four independent layers, each of which has to be defeated for anything to go wrong:

An AST allowlist, checked before anything runs. The generated code is parsed into a Python abstract syntax tree and walked before execution. Only a small set of import sources are permitted — cadquery, build123d, math, numpy — and only a small set of statement and expression types are permitted: assignments, expressions, function definitions, returns, for-loops, if-statements, binary operations, and calls. Anything outside that allowlist — a subprocess import, an eval, an attempt to reach os — is rejected before a single line executes, not caught after the fact.

Subprocess isolation. Code that passes the AST check still doesn't run inside the main process. It's launched in a fresh Python subprocess with a restricted working directory scoped to that job (/tmp/job-{id}), so even a bug that slips past the allowlist can't casually touch the rest of the filesystem.

Resource limits via resource.setrlimit. The subprocess has hard caps on CPU time, address space (virtual memory), and open file count, set through POSIX's resource module — worth noting this mechanism is POSIX-specific and doesn't carry over to a plain Windows process model, which is part of why the executor targets a Linux host. These limits catch the failure modes an AST check can't: not malice, but a pathological or accidental infinite loop or memory-hungry operation in otherwise "legal" geometry code.

A hard wall-clock timeout. Even with CPU-time and memory limits in place, a process can still hang — waiting on something, spinning without tripping the CPU-time cap in the way you'd expect, or just taking longer than any real generation should. At 60 seconds the process is sent SIGKILL unconditionally. That timeout also counts against the same budget as the repair loop's retry attempts, so a hung generation doesn't quietly consume unlimited wall-clock time across retries.

On top of all four layers, the subprocess is given no network access and an empty environment — no inherited secrets, no ambient configuration to probe.

Trusting the design means testing the design

None of these four layers is exotic engineering on its own — AST allowlisting, subprocess isolation, rlimits, and timeouts are all standard techniques. The part that actually builds confidence is not assuming they compose correctly just because each one sounds reasonable in isolation. The design calls for fuzz testing the sandbox directly: throwing a set of deliberately malicious payloads at it — code that tries os.system, __import__, eval, and similar escape attempts — and asserting that every single one is blocked.

That's a meaningfully different posture than writing the sandbox and moving on. Security mechanisms fail quietly and non-obviously; an allowlist can have a gap you didn't think of, a resource limit can be set against the wrong resource, a subprocess boundary can leak through an inherited file descriptor. Fuzzing the sandbox with known attack patterns before ever pointing a real model at it is how we get evidence, rather than an assumption, that the four layers actually hold together.