Skip to content

VNX-1048: Invokable Control Element with Large Number of Outward Calls

Detects a single function or method in Go, Java, JavaScript/TypeScript, PHP, Python or Ruby whose body calls 40 or more distinct functions or methods (excessive fan-out).

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-1048
Severity Low
SARIF level note
Kind sast
Languages Go, Java, JavaScript / TypeScript, PHP, Python, Ruby
CWE CWE-1048
CWSS vector CWSS:1.0/TI:L/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L
Tags fan-out outward-calls code-quality maintainability cwe-1048
Source internal/sast/rules/vnx-cwe1048-auto.rego

Overview

VNX-1048 reports a function or method that calls 40 or more distinct functions or methods in its own body (CWE-1048, excessive fan-out). Each distinct callee name counts once, with its receiver, so db.Close() and store.Close() are two callees and a call repeated ten times is one. String literals and comment lines are removed before counting and language keywords (if, for, return, len, isset…) are ignored. In Ruby a method sent without parentheses (account.suspended?) counts as a call.

The body runs to the function’s closing brace (Go, Java, PHP, JavaScript/TypeScript), its dedent (Python) or its end (Ruby), measured over at most 300 lines.

Why This Matters

A unit that reaches 40 other units does many jobs at once: it is hard to review as one, hard to test exhaustively, and a security check placed among the calls (an authorisation test, a rate limit, a fraud screen) is easy to miss, reorder or bypass when the function is edited.

What Gets Flagged

// FLAGGED: main wires config, database, cache, auth, metrics, routes and shutdown itself
func main() {
	cfg, err := config.Load(*cfgPath)
	db, err := sql.Open("postgres", cfg.DatabaseURL)
	store := cache.NewRedis(cfg.RedisAddr)
	verifier, err := auth.NewVerifier(cfg.JWKSURL)
	// ... 40+ distinct calls
}
// FLAGGED: a route handler that authenticates, checks quota, scans, stores, queues and notifies
router.post('/documents', upload.single('file'), async (req, res, next) => {
  const quota = await db.quotas.findOne({ userId: req.user.id });
  const verdict = await scanFile(req.file.path);
  await s3.putObject({ Bucket: process.env.BUCKET, Key: key, Body: buffer }).promise();
  // ... 40+ distinct calls
});

What Is Not Flagged

  • The same work split into helpers that each call fewer than 40 things (validate, price, persist, notifyParties).
  • A long function that repeats a few calls (out.write(format_row(...)) sixty times).
  • SQL functions inside a query string or a PHP/Ruby heredoc (COUNT(, COALESCE(), and calls in comments.
  • Test files, vendored or minified code, front-end libraries in static/js/libs and similar, AMD define([...], function () {...}) module wrappers and whole-page DOM-ready wrappers ($(document).ready(function () {...}), DOMContentLoaded listeners).

Remediation

Split the function by responsibility: keep the orchestration in one short function and move each step (validation, pricing, persistence, notification) into its own named function that can be read and tested alone. Keep security checks at the top of the orchestrating function where a reviewer sees them first.

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-1048
// nosec vnx-1048

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

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