Skip to content

VNX-674: Unconditional self-recursion or a disabled recursion guard

Reports accessors and methods that call themselves unconditionally with the same arguments (Python, JavaScript/TypeScript, Java), and Python sys.setrecursionlimit() raised to 50000 or more.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-674
Severity Medium
SARIF level warning
Kind sast
Languages Java, JavaScript / TypeScript, Python
CWE CWE-674
CAPEC CAPEC-66
ATT&CK T1190
CWSS vector CWSS:1.0/TI:M/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C
Tags uncontrolled-recursion cwe-674
Source internal/sast/rules/vnx-674.rego

Overview

VNX-674 maps to CWE-674: Uncontrolled Recursion. It reports two concrete forms of recursion that nothing bounds:

  1. A function whose first statement calls itself with the same arguments. Every call recurses until the stack overflows. The common cause is an accessor that forgot its backing field:
    • Python: an @property returning self.<same name>, an @<name>.setter assigning self.<same name>, a method returning self.<same name>(<same parameters>).
    • JavaScript/TypeScript: a getter returning this.<same name>, a setter assigning this.<same name>, a method returning this.<same name>(<same parameters>).
    • Java: a method whose first statement is <same name>(<same parameters>) or this.<same name>(...).
  2. A disabled recursion guard. sys.setrecursionlimit() raised to 50000 or more (or 10**5+, 2**16+, sys.maxsize). Deeply nested attacker input (JSON, GraphQL queries, XML) then overflows the C stack and kills the process instead of raising RecursionError.

What Gets Flagged

@property
def name(self):
    return self.name          # FLAGGED: should be self._name

sys.setrecursionlimit(100000) # FLAGGED
set name(value) {
  this.name = value;          // FLAGGED: calls the setter again
}
public String getId() {
    return getId();           // FLAGGED: should be this.id
}

public String getName() { return getName(); }   // FLAGGED

What Is Not Flagged

  • Accessors backed by another field (self._name, this._name, this.id).
  • Delegation to another receiver or to super, overloads called with different or converted arguments (log(msg, null), count((int) n)).
  • Recursion with a base case or a depth check, smaller recursion limits, comments and string literals.

General recursion over untrusted nested data without a depth limit cannot be decided line by line and is not reported.

Remediation

Use the backing field in accessors. Keep the default recursion limit and bound depth explicitly when walking untrusted structures:

def walk(node, depth=0):
    if depth > MAX_DEPTH:
        raise ValueError("input nested too deeply")
    for child in node.children:
        walk(child, depth + 1)

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-674
// nosec vnx-674

Or record an ignore rule: vulnetix ignore add --rule vnx-674 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-674.rego, cli/internal/sast/docs/vnx-674.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.