Skip to content

VNX-840: Business Logic Error: Client-Controlled Monetary Value

Reports a monetary value read from the HTTP request (amount, price, total, discount, cost, fee, balance) used as the amount charged, an order total or discount, or an account balance change, in JavaScript/TypeScript and Python.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-840
Severity Low
SARIF level note
Kind sast
Languages JavaScript / TypeScript, Python
CWE CWE-840
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 business-logic price-tampering payments cwe-840
Source internal/sast/rules/vnx-840.rego

Overview

VNX-840 maps to CWE-840: Business Logic Errors. CWE-840 is a category; this rule reports its most concrete, statically recognisable form: the server lets the client decide how much money moves.

A finding is a request field named like money (amount, price, total, subtotal, discount, cost, fee, with suffixes such as _percent or _cents) that reaches, directly or through one assignment:

  • an amount / unit_amount / total / subtotal / discount key or keyword argument, such as a payment-provider call (stripe.paymentIntents.create({ amount }), stripe.Charge.create(amount=...));
  • an assignment to an order’s .total, .amount, .subtotal, .discount, or an account’s .balance (including += and -=);
  • a total computed by arithmetic on the client value.

Why This Matters

If the client sends the price, the client sets the price. A tampered request pays one cent, applies a 100% discount, or transfers a negative amount that credits the sender.

What Gets Flagged

// FLAGGED: the client chooses what it is charged
const intent = await stripe.paymentIntents.create({ amount: req.body.amount, currency: 'usd' });

// FLAGGED: total reduced by a client-supplied discount
const total = cart.subtotal - req.body.discount;
# FLAGGED: balance moved by an unchecked client amount (a negative value reverses it)
amount = int(request.form["amount"])
src.balance -= amount

Not flagged: a price looked up on the server (product.price_cents * qty), request identifiers and codes (productId, coupon_code, quantity), a client value compared with a server value or range-checked in an if before use, an admin catalogue edit of product.price, and non-payment code that happens to use a total keyword.

Remediation

  1. Compute amounts on the server from the order, catalogue and coupon records; accept only identifiers from the client.
  2. Where the client must send an amount (a donation, a partial refund), validate it: positive, within the allowed range, and no larger than what is owed or paid.
  3. Never apply a balance change before checking sign and sufficient funds.

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

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

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