Rule Cascade
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, actor and ctx of a request may not be nested more than 64 deep. A deeper request is refused before anything is evaluated.
  • Pattern subject: 10000 characters. matches on a longer string is an evaluation error, so the rule fails closed.

Syntax

how depth is counted
assert:                          # depth 1
  op: not
  args:
    - op: lte                    # depth 2
      args: [{ var: data.quantity }, 10]   # depth 3

Example

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.

limits.ruleset.yaml
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: [] }
request.json
{
  "entity": "Order",
  "operation": "create",
  "data": {
    "quantity": 11
  }
}

Result, from the engine

does not loadThe engine refuses the ruleset before it evaluates anything.

  • EXPRESSION_TOO_DEEP an 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. and and or take any number of arguments: write and(a, b, c, ...).
  • Accepting unbounded requests. The engine does not limit the number of list members or the length of strings (except the matches subject). 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
limits.ruleset.yaml
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: [] }
request.json
{
  "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
Course overview

On this page