Skip to content

feat(evaluator): cap total node visits and recursion depth per evaluation call - #44

Merged
Mearman merged 1 commit into
mainfrom
fix/evaluator-depth-node-caps
Sep 13, 2026
Merged

feat(evaluator): cap total node visits and recursion depth per evaluation call#44
Mearman merged 1 commit into
mainfrom
fix/evaluator-depth-node-caps

Conversation

@Mearman

@Mearman Mearman commented Sep 13, 2026

Copy link
Copy Markdown
Member

Fixes #43

MAX_TREE_REFERENCE_DEPTH only bounds a chain of separately-stored-tree treeReference hops. A single self-contained tree with no treeReference at all, built from ordinary and/or/fold/conditional/quantifier nesting, has no equivalent bound, so a sufficiently deep tree exhausts the call stack rather than resolving to an Evaluation. This matters for a consumer (wire-mesh) evaluating attacker-influenced signed predicate trees.

Add an EvaluationBudget threaded through every recursive call site in evaluatePredicateInternal/evaluateValueInternal, counting total nodes visited and current nesting depth. Either cap being exceeded returns indeterminate with a domain-error reason describing the resource exhaustion, matching the file's existing "never throw, always three-valued" convention and MAX_TREE_REFERENCE_DEPTH's own precedent for reporting a resource cap this way.

Both caps are configurable via createEvaluator's new maxNodes/maxNestingDepth options, defaulting to 10,000 nodes and 500 levels so the bare evaluatePredicate/evaluateValue exports are safe with no caller opting in.

New tests cover both caps independently, confirm a tree within budget evaluates correctly, confirm MAX_TREE_REFERENCE_DEPTH's own guard is unaffected, and confirm the default caps catch an oversized tree through both entry points.

…tion call

A single self-contained tree with no treeReference nodes had no bound
on ordinary recursive descent, so a sufficiently deep and/or/fold/
conditional/quantifier nesting could exhaust the call stack rather
than resolve to an Evaluation.

Add a per-call EvaluationBudget, threaded through every recursive
predicate/expression call site, that counts total nodes visited and
current nesting depth and returns indeterminate with a
resource-exhausted domain-error once either exceeds its cap.

Both caps are configurable via createEvaluator's new maxNodes and
maxNestingDepth options, defaulting to 10,000 nodes and 500 levels so
the bare evaluatePredicate/evaluateValue exports stay safe without any
caller opting in.

The nesting-depth cap is independent of MAX_TREE_REFERENCE_DEPTH,
which only advances on an actual treeReference hop and never bounded
ordinary node recursion.
@Mearman
Mearman marked this pull request as ready for review September 13, 2026 10:14
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 13, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review Completed 2026-09-13T10:23:11.702835Z d8d2704 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Mearman
Mearman merged commit ae05bf7 into main Sep 13, 2026
14 checks passed
@Mearman
Mearman deleted the fix/evaluator-depth-node-caps branch September 13, 2026 10:15
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in trilean@1.6.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Guard evaluation cost against a single untrusted, deeply-nested expression tree

1 participant