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.
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.
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.
ID: ai-slop-cost-bombs.job-hygiene.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))
}