ADR 0007: The DSL is a sandboxed AST interpreter¶
- Status:
Accepted
Context¶
Custom scoring strategies are authored in a browser by people who are not GAME developers, then run on the scoring hot path. Executing arbitrary user code there is a non-starter, both for safety (no arbitrary Python) and for stability (no unbounded CPU).
Decision¶
A custom strategy is a JSON AST walked node-by-node through a fixed handler
table (app/engine/dsl_interpreter.py). The design rests on four
guarantees:
No dynamic Python - no
eval,execorgetattron AST-supplied strings; an unknown node type is rejected.Whitelist - node types, operators, functions and field paths must each appear in an allow-list in
app/engine/dsl_ast.py, checked up front.Frozen precomputed context - field access is a lookup in a frozen dict that
ExecutionContext.build_for_astprecomputes; the walk never touches the database.Cooperative-yield cancellation - the walk awaits
asyncio.sleep(0)periodically so a CPU-bound tree can actually be cancelled by the timeout.
Consequences¶
Execution is bounded, deterministic, cancellable, and injection-proof.
The DSL is intentionally a small total language, not general-purpose; node and depth limits cap expressiveness.
See also¶
The DSL Strategy Engine - the interpreter, validator and limits in depth.
Security - why untrusted scoring logic is sandboxed.