LearnSafety
Limits
The three limits every engine enforces, so that no input can exhaust a stack or a CPU.
Some languages cannot recover when a stack overflows. So every engine checks three limits before it recurses, and fails the same way everywhere:
- Expression depth: 128. A rule's expression has depth 1, its arguments depth 2, and so on. A
ruleset with a deeper expression fails to load with
EXPRESSION_TOO_DEEP. - Value depth: 64. The
data,original,actorandctxof a request may not be nested more than 64 deep. A deeper request is refused before anything is evaluated. - Pattern subject: 10000 characters.
matcheson a longer string is an evaluation error, so the rule fails closed.
Syntax
assert: # depth 1
op: not
args:
- op: lte # depth 2
args: [{ var: data.quantity }, 10] # depth 3Example
This rule wraps one comparison in 128 not operators. It means the same as the comparison, but it
is nested 130 deep, so the ruleset does not load.
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.limits, version: 1.0.0, title: Limits }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
- id: order.deep
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: not, args: [{ op: lte, args: [{ var: data.quantity }, 10] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }] }
severity: error
finding: { code: LRN-LIM-001, message: order.tooMany }
messages:
en:
order.tooMany: "You can order at most 10 items."
tests:
- name: eleven items are denied
entity: Order
operation: create
given:
data: { quantity: 11 }
expect:
decision: deny
findings:
- { rule: order.deep }
- name: ten items are allowed
entity: Order
operation: create
given:
data: { quantity: 10 }
expect: { decision: allow, findings: [] }{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 11
}
}Result, from the engine
does not loadThe engine refuses the ruleset before it evaluates anything.
EXPRESSION_TOO_DEEPan expression is nested more than 128 deep (line 10)
Try it YourselfOpens this ruleset and request in the playground. Nothing to install.
Common mistakes
- Generating rules by nesting. A tool that turns a long list into
and(a, and(b, and(c, ...)))hits the limit.andandortake any number of arguments: writeand(a, b, c, ...). - Accepting unbounded requests. The engine does not limit the number of list members or the
length of strings (except the
matchessubject). Limit the size of the request body in your API.
Exercise
Fix the rule so that the ruleset loads. Eleven items must still be denied and ten allowed.
Hint
An even number of not operators cancel out. Keep only the lte.
Show answer
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.limits, version: 1.0.0, title: Limits }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
- id: order.deep
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: lte, args: [{ var: data.quantity }, 10] }
severity: error
finding: { code: LRN-LIM-001, message: order.tooMany }
messages:
en:
order.tooMany: "You can order at most 10 items."
tests:
- name: eleven items are denied
entity: Order
operation: create
given:
data: { quantity: 11 }
expect:
decision: deny
findings:
- { rule: order.deep }
- name: ten items are allowed
entity: Order
operation: create
given:
data: { quantity: 10 }
expect: { decision: allow, findings: [] }{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 11
}
}Result, from the engine
Decisiondeny1 finding, server channel
LRN-LIM-001errorblockingYou can order at most 10 items./quantity