CONCEPT Cited by 1 source
Safe expression language¶
A safe expression language is a domain-specific language designed explicitly to allow tenant-supplied code to execute in a shared process without compromising the process's safety or availability. The safety property is structural — the language cannot express the problematic behaviours — not merely a convention.
Canonical industrial instance on this wiki is Netflix SEL, used inside Maestro to evaluate user-injected expressions in parameterized workflows. (Source: sources/2024-07-22-netflix-maestro-netflixs-workflow-orchestrator)
The three structural properties¶
A language qualifies as safe when it provides all three:
1. Grammar subset of a familiar language¶
Users don't need to learn new syntax — the safe language is a proper subset of something they already know (usually Java, C-like, or Python-like). SEL is a subset of the Java Language Specification (JLS). Kubernetes CEL (Common Expression Language) is a Protobuf-language-subset. CEL, Rego, and JSONata all borrow familiar syntax.
2. Static + runtime bounded evaluation¶
Evaluation terminates in bounded time + bounded memory, enforced by the language runtime itself, not by best-effort caller checks. The three specific runtime limits SEL enforces (canonical for the orchestrator domain):
- Loop iteration limits — prevent infinite loops.
- Array size limits — prevent unbounded list/map growth.
- Object memory size limits — cap total evaluation footprint.
"SEL also includes additional runtime checks, such as loop iteration limits, array size checks, object memory size limits and so on, to enhance security and reliability." (Source: sources/2024-07-22-netflix-maestro-netflixs-workflow-orchestrator)
Other safe languages (CEL, Rego) achieve this via provably- terminating evaluation rules (no general loops, no recursion).
3. Platform-level sandbox¶
Even within the language, if the evaluator has access to JVM / OS capabilities (reflection, filesystem, network, class loading), a sufficiently creative expression can escape. A proper safe language runs inside a platform sandbox that denies these capabilities.
SEL uses the Java Security Manager to "restrict access, ensuring a secure and controlled environment for code execution." CEL runs in Go without reflection / filesystem access.
Why this is a distinct concept (not just "a DSL")¶
Many DSLs aren't safe — Groovy scripts in Jenkins, Lua scripts in Redis commands (before sandboxing), JavaScript in legacy template engines. What makes safe the discriminating property:
- A DSL can be compact + expressive without being bounded.
- A DSL can be bounded without having a platform sandbox.
- A DSL can have a sandbox but still leak via allocation-based DoS.
The safe-expression-language pattern requires all three because the security / availability contract is multilayered. Dropping any one layer opens a residual threat.
Industrial instances¶
| Language | Host | Use case | Layer 1: subset | Layer 2: runtime bounds | Layer 3: platform sandbox |
|---|---|---|---|---|---|
| Netflix SEL | Maestro | Parameterized workflows | JLS subset | Loop / array / memory limits | Java Security Manager |
| Kubernetes CEL | API server admission | Validation rules | Proto-lang subset | No unbounded loops, bounded eval | Go runtime (no reflect / fs) |
| OpenPolicyAgent Rego | Policy eval | Authorization | Datalog-inspired | Deterministic, provably terminating | Go sandbox |
| AWS IAM policy conditions | IAM | Policy eval | JSON DSL | No loops | AWS-managed runtime |
| CloudFormation intrinsic functions | CFN | Template eval | Limited function set | Bounded | AWS-managed runtime |
When you need one¶
The load-bearing architectural condition is: tenant-supplied logic must run inside a shared control plane. Any workflow orchestrator, admission controller, policy engine, or configuration-evaluation layer hits this condition.
The wrong answer is to embed a general-purpose interpreter (Groovy / Python / JavaScript) with ad-hoc guards. This fails by category — new attack surface is continuously discovered (prototype pollution in JS, reflection in Groovy, dunder methods in Python) and the guards always lag.
The right answer is the safe-expression-language pattern: pick a sufficiently-expressive subset, bound it structurally, and enforce at the platform layer. The cost is a one-time investment in the language + interpreter; the benefit is that the safety story becomes a property of the language rather than of each individual expression.
Seen in¶
- sources/2024-07-22-netflix-maestro-netflixs-workflow-orchestrator — SEL as the Maestro safe-expression layer