feat(evaluator): cap total node visits and recursion depth per evaluation call - #44
Merged
Merged
Conversation
…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
marked this pull request as ready for review
September 13, 2026 10:14
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
🎉 This PR is included in trilean@1.6.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.