Skip to content

VNX-837: Improper Enforcement of a Single, Unique Action

Detects Stripe calls that move money (charges, payment intents, refunds, transfers, payouts, top-ups) made without an idempotency key, so a retried or double-submitted request performs the action twice. Python and JavaScript/TypeScript.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-837
Severity Low
SARIF level note
Kind sast
Languages JavaScript / TypeScript, Python
CWE CWE-837
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 improper-enforcement-of-single-unique-action idempotency payments cwe-837
Source internal/sast/rules/vnx-837.rego

Overview

VNX-837 maps to CWE-837: Improper Enforcement of a Single, Unique Action. A payment must happen exactly once per order, but HTTP requests are retried by clients, proxies, job queues and impatient users clicking “Pay” twice. Stripe’s API makes a create call happen once only when it carries an idempotency key; without one, every retry is a new charge, refund or payout.

The rule reports stripe.Charge/PaymentIntent/Refund/Transfer/Payout/Topup.create(...) and the StripeClient charges/payment_intents/refunds/transfers/payouts services in Python, and stripe.charges/paymentIntents/refunds/transfers/payouts/topups.create(...) in JavaScript/TypeScript, when neither the call’s arguments nor the ten lines above it name an idempotency key.

What Gets Flagged

return stripe.Charge.create(amount=order.total_cents, currency="usd", source=token)   # FLAGGED
const intent = await stripe.paymentIntents.create({ amount: order.total, currency: 'eur' });   // FLAGGED

What Is Not Flagged

  • stripe.Charge.create(..., idempotency_key=f"order-{order.id}") and stripe.paymentIntents.create(params, { idempotencyKey }), including an options object built just above the call.
  • Read calls (retrieve, list) and creates that do not move money (customers, products).
  • Test code, and the call in comments or string literals.

Other single-use actions (coupon redemption, voting) have no recognisable API and are not judged by this rule.

Remediation

Derive a key from the business identity of the action (the order, refund request or payout id) and pass it on every money-moving call, so a retry of the same action is recognised and returns the original result:

stripe.Charge.create(amount=order.total_cents, currency="usd", source=token,
                     idempotency_key=f"order-{order.id}")

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

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

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