Skip to content

VNX-1060: Excessive Number of Inefficient Server-Side Data Accesses

Python database queries (cursor.execute, session.query, Model.objects.get/filter, asyncpg fetch) issued inside a loop or comprehension with the loop variable, or a name derived from it, as an argument: an N+1 query pattern. Chunked IN batches are not reported.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-1060
Severity Low
SARIF level note
Kind sast
Languages Python
CWE CWE-1060
CAPEC CAPEC-66
ATT&CK T1190
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 inefficient-server-side-access n-plus-one performance python cwe-1060
Source internal/sast/rules/vnx-1060.rego

Overview

VNX-1060 maps to CWE-1060: Excessive Number of Inefficient Server-Side Data Accesses. Python N+1 query: a database query (DB-API cursor/connection .execute(), SQLAlchemy session.query()/Model.query.get(), Django Model.objects.get()/filter(), asyncpg conn.fetch()/fetchrow()/fetchval()) issued inside the body of a for/async for loop, or inside a list/dict/set comprehension, with the loop variable, or a name the loop body assigns from it, in the query’s arguments. Each iteration makes one more round trip to the database where one batched query (WHERE id IN (…), select_related/joinedload, executemany) would do. Not reported: a query inside a loop that does not use the loop variable (a retry loop), a loop that hands each chunk to one IN/ANY/_in/in() query, a loop whose variable is the statement being executed (a schema or migration runner), executemany(), a query after the loop has ended, queries built and run once from a list the loop collected, and the pattern in strings or comments. Loops are recognised by indentation within 25 lines above the query; queries reached through helper functions are not followed.

Why This Matters

This weakness class (Excessive Number of Inefficient Server-Side Data Accesses) creates a concrete exploit surface: the rule searches for the concrete source-level patterns most commonly associated with CWE-1060 and surfaces them for review. Each finding should be evaluated in context, the rule catches the pattern, not the context.

What Gets Flagged

# FLAGGED: one query per row
for user_id in user_ids:
    cursor.execute("SELECT * FROM orders WHERE user_id = %s", (user_id,))

# FLAGGED: ORM lookup in a comprehension
users = [User.query.get(i) for i in ids]

# FLAGGED: a name derived from the loop variable, asyncpg
for inv in invoices:
    invoice_id = inv["id"]
    cursor.execute("SELECT sku FROM invoice_line WHERE invoice_id = %s", [invoice_id])

Not flagged: cursor.executemany(...), one WHERE id IN (...) query built from the collected ids, a chunked loop that runs WHERE id IN %s once per batch, for statement in SCHEMA: cur.execute(statement), or a retry loop whose query does not use the loop variable.

Remediation

  1. Review each flagged line and determine whether the pattern represents a real instance of CWE-1060 or a false positive.
  2. Replace the flagged construct with a documented safe alternative appropriate to your language and framework.
  3. For confirmed false positives, add a # vulnetix-ignore: VNX-1060 comment on the line.
  4. Ensure equivalent test coverage exists to prevent regression.

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

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

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