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.
| 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/discountkey 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
- Compute amounts on the server from the order, catalogue and coupon records; accept only identifiers from the client.
- 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.
- 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.