Skip to content

Add depth validation for ASTs loaded through ParsedExprToAst / Checke...#1334

Open
wilyan09007 wants to merge 4 commits into
cel-expr:masterfrom
wilyan09007:fix/issue-1333
Open

Add depth validation for ASTs loaded through ParsedExprToAst / Checke...#1334
wilyan09007 wants to merge 4 commits into
cel-expr:masterfrom
wilyan09007:fix/issue-1333

Conversation

@wilyan09007

Copy link
Copy Markdown

Summary

cel-go enforces a recursion/expression-depth limit while parsing CEL source, but ASTs ingested directly — through ParsedExprToAst / CheckedExprToAst — bypass that guard. A deeply nested loaded AST then flows into the recursive checker (Env.Check) and planner (Env.Program / PlanProgram), which can exhaust the Go stack and abort the process with fatal error: stack overflow instead of returning a recoverable error. Embedders that load stored or serialized CEL ASTs as policy objects are the most exposed.

This validates expression nesting depth before those recursive phases:

  • Adds ast.ExceedsMaxDepth(expr, maxDepth) in common/ast — a bounded traversal that never recurses past maxDepth+1 levels, so it stays safe on the same adversarially deep inputs it guards against. The default ast.MaxNestingDepth = 250 mirrors the parser's default maxRecursionDepth, keeping loaded ASTs consistent with parsed ones.
  • Env.Check and Env.PlanProgram now run that check up front and return an ordinary error/issue — input exceeds maximum expression nesting depth: 250 — rather than recursing into a stack overflow.
  • Adds a regression test that loads a synthetic over-deep AST and asserts Check and Program return an error (not a crash), while normal expressions remain unaffected.

Closes #1333

ASTs loaded via ParsedExprToAst / CheckedExprToAst bypass the parser's
recursion limit, so a deeply nested loaded AST could exhaust the Go
stack during checking or planning and crash the process instead of
returning a normal error.

Add ast.ExceedsMaxDepth, a bounded traversal (default limit 250, the
same as the parser's maxRecursionDepth) that Env.Check and
Env.PlanProgram run before recursing, returning an ordinary error when
the limit is exceeded. Includes a regression test that loads a
synthetic over-deep AST.

Closes cel-expr#1333
Comment thread common/ast/depth.go Outdated
wilyan09007 added a commit to wilyan09007/cel-go that referenced this pull request Jun 5, 2026
The depth bound for ASTs ingested outside the parser (via ParsedExprToAst / CheckedExprToAst) was an exported package constant, common/ast.MaxNestingDepth. Replace it with a cel.ExpressionNestingDepthLimit functional option on NewEnv so the behavior is user-configurable with a sensible default rather than a public package-level knob.

The option defaults to 250, matching the parser's maxRecursionDepth, and a negative value disables the check. Enforce it once during Program planning (PlanProgram) and drop the separate Env.Check guard, leaving a single configurable enforcement point.
Comment thread cel/options.go
Comment thread common/ast/depth.go Outdated
Comment thread cel/env.go Outdated
Comment thread cel/io_test.go Outdated
Address review feedback on the loaded-AST depth guard:

- common/ast: replace the bespoke depth.go traversal with ExceedsDepth in
  navigable.go, built on the existing NavigableExpr.Depth() visitor. The walk
  is bounded to maxDepth+1 levels so it stays safe on the adversarially deep
  inputs it guards against.
- cel: move the depth check out of the PlanProgram hot path and into the
  ParsedExprToAst / CheckedExprToAst proto-conversion helpers, the actual
  entry points for ASTs that bypass the parser. The expensive traversal now
  runs once at load; Check/Program only read the recorded error. Embedders in
  full control of their AST inputs can skip the check by constructing the AST
  through the common/ast package directly.
- cel: register limitMaxASTDepth in limitIDsToNames so the depth limit
  round-trips through env.Config export/import.

Updates the regression test to cover the parsed and checked conversion paths,
Check surfacing, the low-level bypass, and the env-config round-trip.
@TristonianJones

Copy link
Copy Markdown
Collaborator

/gcbrun

@TristonianJones

Copy link
Copy Markdown
Collaborator

/gcbrun

Comment thread common/ast/navigable.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add depth validation for ASTs loaded through ParsedExprToAst / CheckedExprToAst

2 participants