User-triggered code paths have infinite-loop guards
Why it matters
Recursive functions without base cases or depth limits crash on cyclic data structures and blow the call stack on deeply nested inputs. CWE-674 (Uncontrolled Recursion) and CWE-835 (Loop with Unreachable Exit Condition) both apply. In a user-facing API, an attacker who can control a tree or graph structure — JSON, AST, category hierarchy, file system path — can trigger a stack overflow that crashes the Node process for all users. Even without adversarial input, an AI-generated recursive function that processes database-fetched hierarchical data will eventually encounter a self-referencing node in production and take down the server.
Severity rationale
Medium because recursion without a guard requires either user-controlled input or corrupt data to trigger, but the crash affects all users on the same process when it fires.
Remediation
Add a depth parameter with a hard limit to every recursive function. Throw an explicit error rather than returning silently, so the guard failure is visible in logs.
const MAX_DEPTH = 100
function processNode(node: TreeNode, depth = 0): ProcessedNode {
if (depth > MAX_DEPTH) {
throw new Error(`processNode: max depth ${MAX_DEPTH} exceeded — possible cycle`)
}
return {
...node,
children: node.children.map(child => processNode(child, depth + 1)),
}
}
For graph traversal (not trees), add a visited: Set<string> parameter and check visited.has(node.id) before recursing. This catches cycles that a depth limit alone would miss.
Detection
-
ID:
infinite-loop-guards-present -
Severity:
medium -
What to look for: Walk source files for recursive function definitions. Count all functions that call themselves by name. For each recursive function, verify the function body contains one of: a numeric depth parameter with a base case (
if (depth > MAX) return), a counter guard (if (count >= LIMIT) throw), an explicit early return on a base case (if (!node) return), OR is a tail-recursive function on a bounded data structure. -
Pass criteria: 100% of recursive functions have explicit base cases or depth limits. Report: "X recursive functions inspected, Y with guards, 0 unbounded."
-
Fail criteria: At least 1 recursive function has no visible base case or depth guard.
-
Skip (N/A) when: No recursive function definitions found in source.
-
Detail on fail:
"1 unguarded recursion: 'function processNode(node) { node.children.forEach(processNode) }' in src/lib/tree.ts — no cycle detection, will infinite-loop on a self-referencing node" -
Remediation: Recursion without a base case can blow the stack OR loop forever on cyclic data. Add a depth limit:
// Bad: no guard function processNode(node: Node) { node.children.forEach(processNode) } // Good: depth-limited function processNode(node: Node, depth = 0) { if (depth > 100) throw new Error('Max depth exceeded') node.children.forEach(child => processNode(child, depth + 1)) }
External references
- cwe · CWE-674 — Uncontrolled Recursion
- cwe · CWE-835 — Loop with Unreachable Exit Condition
- iso-25010:2011 · reliability
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ai-slop-cost-bombs·automated