Quantité négative (CWE-840) : valeurs négatives ou en débordement pour inverser l'arithmétique de commande — transformant un achat en crédit ou réduisant le total à zéro.
TL;DR
price × quantity, produisant un sous-total négatif qui réduit à zéro ou crédite le panier.qty=-1), transferts financiers négatifs (amount=-500) et wrap-around par integer overflow (qty=2^63) qui contourne les filtres naïfs sur le signe moins.quantity > 0 côté serveur avec une validation typée et une contrainte CHECK en base de données comme défense en profondeur.L'abus de quantité négative est une faille de logique métier dans laquelle le système de commande, de panier ou de transfert financier d'une application n'impose pas que les entrées de quantité ou de montant soient strictement positives. Comme le serveur calcule total = price × quantity, une quantité négative produit un sous-total négatif — et dans de nombreuses implémentations de panier, le total général tombe sous zéro, générant un crédit effectif pour l'attaquant. La cause racine est CWE-20 : Improper Input Validation, fréquemment aggravée par CWE-190 : Integer Overflow or Wraparound lorsque l'attaquant contourne les filtres sur le signe moins en soumettant des valeurs qui dépassent la plage des entiers signés.
Cette vulnérabilité est parfois confondue avec la manipulation de prix, mais les surfaces d'attaque sont distinctes. La manipulation de prix altère la valeur d'un article (par exemple, changer unit_price de $499.99 à $0.01). L'abus de quantité négative altère plutôt le signe du multiplicateur — laissant le prix intact tout en inversant l'arithmétique. Les défenses diffèrent également : la manipulation de prix nécessite une recherche de prix côté serveur, tandis que les attaques par quantité négative nécessitent une validation stricte de plage numérique et des contraintes en base de données. Toutes deux relèvent de OWASP A04:2021 — Insecure Design car elles exploitent une logique que le développeur n'a jamais voulu rendre accessible.
La vulnérabilité est particulièrement insidieuse car l'entrée de l'attaquant est techniquement un entier valide. Un validateur naïf qui accepte « n'importe quel nombre » laissera passer la requête. Les Web Application Firewalls ne signalent pas la requête, la coercition de type réussit et l'INSERT en base de données s'exécute sans erreur. Le seul signal que quelque chose ne va pas est le sous-total négatif dans la réponse — un signal que les scanners DAST automatisés interprètent rarement comme une découverte car ils manquent du contexte métier pour savoir que total: -218.00 est anormal.
La surface d'attaque s'étend bien au-delà des paiements e-commerce. Partout où une quantité numérique alimente une multiplication, une accumulation ou une mise à jour de solde — rachat de points de fidélité, recharge de carte cadeau, partage de tarif de course, paiement vendeur de marketplace, consommation de budget publicitaire, échange de monnaie de jeu — la même inversion arithmétique s'applique. La vulnérabilité recoupe également CWE-682 (Incorrect Calculation) et CWE-840 (Business Logic Errors) dans les systèmes de classification qui suivent les failles logiques séparément de la validation d'entrée, mais le défaut sous-jacent et la défense sont identiques.
Un endpoint de panier vulnérable fait confiance à la quantité fournie par le client et la transmet directement dans l'arithmétique. Le flux haut niveau d'une interaction d'exploitation unique est illustré ci-dessous :
L'inversion arithmétique ne nécessite que trois ingrédients : une entrée numérique contrôlée par l'attaquant, une multiplication qui propage le signe et une couche de persistance qui accepte le résultat. L'ensemble des langages et ORMs qui satisfont ces conditions par défaut est énorme. La coercition int lâche de PHP, les primitifs int et long de Java, le Number non typé de JavaScript et les attributs de classe Python sans annotations de type acceptent tous silencieusement les entrées négatives. Côté persistance, SQLAlchemy Integer, Prisma Int, Sequelize INTEGER, Django IntegerField, ActiveRecord :integer et TypeORM int acceptent toute valeur dans la plage signée de la colonne — y compris toute la moitié négative — sauf si le développeur attache explicitement un validateur à borne positive. Le résultat pratique est que la configuration par défaut de chaque pile web grand public est livrée vulnérable à cette classe d'attaque, et seule une défense en couches intentionnelle comble le fossé.
Trois voies d'exploitation découlent de cette cause racine unique :
"quantity": 1 en "quantity": -1. Le serveur calcule price × -1 et soustrait la ligne du total courant. Enchaîner cela avec des articles de grande valeur réduit à zéro le panier ou génère un solde négatif que certaines plateformes convertissent en crédit boutique.POST /api/transfer, POST /api/refund ou POST /api/payment acceptent un champ amount. Sans validation de signe, envoyer amount=-500 inverse la sémantique de transfert — créditant le compte de l'attaquant et débitant soit la victime, soit la trésorerie de la plateforme.+2,147,483,647 à -2,147,483,648 lorsqu'il est incrémenté. Soumettre qty=2147483648 (ou 9223372036854775808 pour int64) produit une quantité légitimement négative sans jamais envoyer de signe moins. Cela contourne les filtres regex qui ne bloquent que le caractère -.Une requête vulnérable représentative :
POST /api/cart/update HTTP/1.1
Host: shop.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGc...
{
"cart_id": "a3f9b2",
"items": [
{ "product_id": 101, "quantity": 1, "unit_price": 299.99 },
{ "product_id": 204, "quantity": -1, "unit_price": 499.99 }
]
}La réponse vulnérable confirme l'inversion :
HTTP/1.1 200 OK
Content-Type: application/json
{
"cart_id": "a3f9b2",
"subtotal": -200.00,
"tax": -18.00,
"total": -218.00,
"message": "Cart updated successfully"
}Le serveur a accepté un total négatif. Selon le traitement en aval, l'étape de paiement se terminera soit à $0.00, refusera la commande avec une erreur obscure, ou — pire cas — émettra un crédit boutique de $218.00 utilisable sur une commande ultérieure.
Une regex telle que ^-?\d+$ ou un filtre qui supprime le caractère - ne bloque pas l'integer overflow. Soumettre qty=9223372036854775808 ne contient aucun signe moins et passe tout filtre basé sur les caractères, pourtant boucle vers -9223372036854775808 une fois converti en entier signé 64 bits. La validation de plage, et non la validation de motif, est la seule défense correcte.
| Variante | Technique | Impact |
|---|---|---|
| Quantité négative directe | Définir qty=-1 sur une ligne de grande valeur | Réduit à zéro ou inverse le total du panier ; peut produire un crédit boutique |
| Contournement par quantité zéro | Définir qty=0 pour ignorer les règles d'achat minimum | Abus de remises BOGO/bundle, déblocage promo gratuit |
| Integer overflow 32 bits | Soumettre qty=2147483648 pour faire boucler l'int signé | Le total tombe à -INT_MIN × price, ex. -$21,474,836 |
| Integer overflow 64 bits | Soumettre qty=9223372036854775808 pour faire boucler l'int64 | Total catastrophiquement négatif ; crédit de remboursement complet |
| Transfert avec montant négatif | POST /api/transfer avec amount=-500 | Flux de fonds inversé ; l'attaquant crédite son propre compte |
| Replay de remboursement négatif | POST /api/refund avec refund_amount=-100 | Remboursement inversé en charge supplémentaire, ou crédit surdimensionné |
Les variantes overflow 32 bits et 64 bits sont les plus dangereuses dans les piles legacy. Les colonnes Java int, C# int et MySQL INT bouclent silencieusement en cas de débordement sans exception, sans entrée de log et sans indication au niveau applicatif. PHP sur systèmes d'exploitation 64 bits convertit au-delà de PHP_INT_MAX en float, ce qui introduit des erreurs de précision mais produit typiquement encore une primitive d'attaque utilisable dans les chemins de code à forte arithmétique. La largeur native int de Go dépend de la plateforme et boucle silencieusement dans les builds release ; Rust panique en cas de débordement en mode debug mais boucle en release sauf si le développeur utilise explicitement checked_mul ou le flag de profil overflow-checks = true. JavaScript échappe à l'integer overflow car tous les nombres sont des doubles IEEE 754, mais perd la précision entière au-dessus de Number.MAX_SAFE_INTEGER (2^53 − 1), ce qui peut produire des totaux incorrects sur de grandes quantités même sans inversion de signe.
La variante de replay de remboursement négatif mérite une attention particulière car elle échappe souvent à la revue de code. Un relecteur lisant if (refund_amount > original_charge) reject() conclura que la comparaison est saine — mais la comparaison passe lorsque refund_amount = -100 (car -100 < original_charge), et l'écriture de grand livre en aval applique le montant négatif comme une charge supplémentaire contre le client ou comme un crédit gonflé, selon la convention de signe de la couche comptable. Le correctif nécessite à la fois refund_amount > 0 ET refund_amount <= original_charge comme assertions séparées, jamais combinées en une seule comparaison bornée.
Les attaques par montant négatif contre les API financières (transfer, refund, withdrawal, initiation ACH) ne sont pas académiques. Des chercheurs testant des endpoints d'open-banking en 2021 ont démontré le crédit non autorisé de comptes contrôlés par l'attaquant via amount=-N sur plusieurs API de production. Combiné à une faiblesse BOLA/IDOR sur le sélecteur de compte source, le même défaut permet le vol direct depuis des comptes tiers. Traitez chaque endpoint monétaire comme critique pour la validation de signe.
CVE-2020-11007 — Shopizer (e-commerce Java Spring, < 2.11.0). Shopizer acceptait un paramètre de requête qty négatif sur son endpoint d'ajout au panier sans validation côté serveur. La preuve de concept est un GET d'une ligne : GET /shop/cart/addToCart?merchantId=1&productId=43&qty=-240. Le sous-total du panier devenait négatif, et un attaquant pouvait ouvrir un second onglet pendant un paiement actif pour appliquer le total manipulé. CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N — score 6.5 (Medium). Le correctif en 2.11.0 était une vérification backend d'une seule ligne qui réinitialise qty à 1 chaque fois qu'une valeur négative est détectée.
CVE-2025-56426 — Bagisto CMS v2.3.6 (Laravel/PHP). La plateforme e-commerce Bagisto permettait à un attaquant d'intercepter une requête de mise à jour du panier et de définir la quantité d'un produit à -1. La logique de calcul défectueuse soustrayait le prix de l'article du total du panier, permettant un paiement à $0 pour des marchandises valant des centaines de dollars. Au-delà de la perte financière directe, la quantité négative corrompait les compteurs d'inventaire — des articles qui auraient dû être marqués comme épuisés restaient listés comme disponibles, provoquant des échecs de fulfillment. CVSS v3 score 6.5 (Medium).
CVE-2023-45854 — Shopkit 1.0. Un cas d'école de faille d'integer overflow dans la fonctionnalité d'ajout au panier. Un attaquant soumettant une valeur de quantité spécialement conçue déclenchait un wrap-around d'entier signé dans le calcul du sous-total du panier, produisant un prix négatif non autorisé. Classée comme vulnérabilité critique par l'autorité d'attribution car aucune authentification n'était requise et l'exploit était une seule requête HTTP.
Bug Bounty — grande plateforme e-commerce (2021, divulguée via HackerOne). Un chercheur a soumis une commande contenant 9223427036854775808 articles (légèrement au-dessus de 2^63, la valeur maximale d'un entier signé 64 bits plus un). La gestion interne des entiers de PHP a bouclé la valeur vers un nombre négatif proche de PHP_INT_MIN. Le total du panier a été calculé à un grand chiffre négatif, et le pipeline de paiement de la plateforme a accepté la commande, émettant un crédit de remboursement de plusieurs milliers de dollars sur le compte de l'attaquant. Le chercheur a reçu une prime importante ; le patch a plafonné la quantité à 999 côté serveur. L'incident est largement cité dans le matériel de formation sur la logique métier comme l'attaque canonique d'overflow 2^63 contre l'e-commerce moderne.
Recherche sur API d'open-banking (divulgation BusinessWire, 2021). Des chercheurs indépendants testant un échantillon d'endpoints de transfert d'open-banking en production ont démontré que plusieurs implémentations acceptaient les champs amount négatifs sans validation. Les chaînes d'attaque rapportées comprenaient le crédit non autorisé de comptes contrôlés par l'attaquant, le vol depuis des comptes tiers lorsqu'enchaîné à une faiblesse BOLA/IDOR sur le sélecteur de compte source, et l'inflation de solde sans dépôt correspondant. La divulgation a poussé plusieurs banques à pousser des hotfixes d'urgence ajoutant une validation stricte de montant positif sur leurs API publiques.
Au-delà des cas CVE-numérotés, plusieurs divulgations bug bounty ont établi que la quantité négative est un pattern récurrent à travers les plateformes e-commerce. HackerOne #364843 (Upserve/OLO, 250 $ Medium) — inclure un article de menu avec quantity: -1 a réduit le total de la commande car le serveur validait le SKU mais pas le signe de la quantité. HackerOne #403783 (Zomato, 250 $ Medium) — un chercheur a changé le champ support_rider_amount en valeur décimale négative, et le serveur a accepté la valeur, réduisant le total de la commande à chaque achat. HackerOne #1562515 (Glovo, prime non divulguée) — un integer overflow dans le calcul du prix de la commande a produit un total de panier proche de zéro ou négatif. Ces rapports correspondent au mécanisme CVE-2020-11007 / CVE-2025-56426 / CVE-2023-45854 documenté plus haut et confirment que la classe est activement exploitée à grande échelle.
Une session Burp Suite ciblée est le moyen le plus rapide de confirmer ou réfuter la vulnérabilité.
POST /cart/add, PUT /cart/update, PATCH /cart/item/:id, POST /order/create, POST /checkout, POST /transfer, POST /payment, POST /refund. Tout champ nommé qty, quantity, amount, count, units ou volume est un candidat.POST /cart/add résultant dans Burp Proxy, et notez le total du panier dans la réponse."quantity": 1 par "quantity": -1 dans Repeater. Transmettez la requête et inspectez la réponse. Si le total du panier diminue ou devient négatif avec HTTP 200, l'endpoint est vulnérable.qty=0. Si l'article est ajouté sans satisfaire la règle de quantité minimum de la plateforme, vous avez un contournement partiel même sans inversion arithmétique.2147483647, 2147483648, 9223372036854775807 et 9223372036854775808. Surveillez l'inversion de signe dans la réponse, un HTTP 500 (qui révèle le type entier), ou un changement soudain du total rendu."amount": -100 aux endpoints transfer/refund/payment. Toute réponse 200 qui rapporte un changement de solde est une découverte confirmée.[-1000, -100, -1, 0, 2147483647, 2147483648, 9223372036854775807, 9223372036854775808] et ciblez chaque champ numérique sur les endpoints découverts. Triez les résultats par longueur de réponse et champ total pour trouver rapidement les anomalies.Pour le balayage le plus approfondi, lancez une attaque Intruder dédiée contre chaque paramètre numérique découvert à l'étape 1. La wordlist [-1, 0, -0, 0.5, -999999999, 2147483648, 9223372036854775808, NaN, Infinity] exerce l'inversion de signe, le contournement par zéro, l'évasion regex par zéro signé, l'arrondi fractionnaire, la plage très négative, l'overflow 32 bits, l'overflow 64 bits, et les deux valeurs limites JSON (NaN et Infinity) que certains parsers acceptent sans erreur.
quantity (ou amount, count, units, volume) et cliquez sur Add §. Intruder mutera uniquement cette position, gardant le reste du corps intact.-1, 0, -0, 0.5, -999999999, 2147483648, 9223372036854775808, NaN, Infinity. Désactivez l'URL-encoding pour les caractères de payload afin que les jetons numériques JSON parviennent au serveur intacts."total":\s*(-?\d+(?:\.\d+)?)). Intruder fera apparaître la valeur extraite comme une colonne dans la table de résultats, rendant l'inversion de signe immédiatement visible./api/refund et /api/transfer. Les endpoints financiers dupliquent souvent le même handler d'entrée que les endpoints de panier ; si le panier échoue, les endpoints financiers échouent presque certainement aussi.Les scanners DAST génériques peinent avec cette classe de faille pour deux raisons. Premièrement, l'entrée de l'attaquant est un entier syntaxiquement valide, donc les règles de validation d'entrée la laissent passer. Deuxièmement, la vulnérabilité ne se manifeste que dans un calcul en aval — le scanner devrait analyser la réponse, localiser le total du panier, et reconnaître qu'une valeur négative est anormale dans ce champ. La plupart des scanners ne maintiennent pas ce niveau de contexte métier.
Les signaux de détection qui fonctionnent sont :
total entre les réponses de référence (qty=1) et d'attaque (qty=-1). Si total_attack < total_baseline, l'endpoint inverse l'arithmétique.qty=2147483648 dont le champ total a le signe opposé à la référence est un indicateur à haute confiance.qty=2147483648 (sans erreur sur qty=2147483647) signale une limite de type INT — la colonne déborde mais l'application capture l'exception. L'endpoint est limite-vulnérable selon la façon dont l'exception est gérée.qty=-5 qui diminue le stock affiché d'un produit de -5 (c'est-à-dire augmente le stock) signale la même faille sur un champ différent.BreachVex détecte cela via le fuzzing numérique typé sur les endpoints panier, commande et financiers — soumettant des valeurs négatives, zéro et les limites d'overflow 32/64 bits contre chaque paramètre nommé qty, quantity, amount, count, units, ou correspondant aux heuristiques définies par le projet, puis comparant les deltas arithmétiques dans le corps de la réponse pour signaler l'inversion de signe ou les totaux anormaux. Le scanner croise chaque réponse avec une référence capturée depuis une requête connue comme bonne, donc un total négatif dans un champ qui était positif dans la référence est signalé indépendamment du nom du champ ou de la forme de la réponse environnante.
La défense est une combinaison en couches de validation d'entrée typée à la frontière de l'API et d'une contrainte CHECK au niveau de la base de données comme garde-fou. Aucune des deux couches seule ne suffit.
Validez chaque champ numérique à la frontière de l'API avec à la fois la coercition de type et les bornes de plage. Deux exemples fonctionnels — d'abord vulnérable, puis corrigé — pour les deux piles backend les plus courantes.
Python (FastAPI + Pydantic) — vulnérable :
from pydantic import BaseModel
class CartItem(BaseModel):
product_id: int
quantity: int # No range check — accepts -1, 0, 2^63Python (FastAPI + Pydantic) — corrigé :
from pydantic import BaseModel, Field, conint
class CartItem(BaseModel):
product_id: int = Field(..., gt=0)
quantity: conint(ge=1, le=999) # Strictly positive, capped business maxNode.js (Express + zod) — vulnérable :
import { z } from "zod";
const cartItemSchema = z.object({
product_id: z.number(),
quantity: z.number(), // Accepts negatives and overflow
});Node.js (Express + zod) — corrigé :
import { z } from "zod";
const cartItemSchema = z.object({
product_id: z.number().int().positive(),
quantity: z.number().int().positive().max(999),
});Le plafond de 999 est une heuristique de défense en profondeur : toute commande de détail légitime sera bien en dessous de ce nombre, et toute entrée au-dessus est traitée comme une attaque. Le plafond exact est une décision de politique métier, mais un plafond fini doit exister pour empêcher les attaques par overflow sur la colonne de stockage sous-jacente.
Pour les champs monétaires, n'utilisez jamais de types à virgule flottante. Utilisez Decimal (Python), BigDecimal (Java), ou NUMERIC (PostgreSQL) avec des contraintes positives explicites :
from decimal import Decimal
from pydantic import BaseModel, condecimal
class TransferRequest(BaseModel):
from_account: str
to_account: str
amount: condecimal(gt=Decimal("0"), max_digits=12, decimal_places=2)La validation à la frontière de l'API peut être contournée par des services internes qui écrivent directement en base de données, par SQL injection, ou par des failles de mass-assignment. Une contrainte CHECK au niveau de la colonne comble cette lacune :
-- PostgreSQL: prevent negative quantities at the storage layer
ALTER TABLE order_items
ADD CONSTRAINT chk_quantity_positive CHECK (quantity > 0);
ALTER TABLE cart_items
ADD CONSTRAINT chk_cart_qty_positive CHECK (quantity > 0);
-- Prevent negative amounts in financial tables
ALTER TABLE transactions
ADD CONSTRAINT chk_amount_positive CHECK (amount > 0);La contrainte CHECK provoque une IntegrityError sur tout INSERT ou UPDATE qui stockerait une valeur non positive, indépendamment du service applicatif qui a initié l'écriture. Associez-la à des types de colonne BIGINT ou NUMERIC — jamais INT — pour tout champ de quantité susceptible de recevoir des attaques par overflow. Le coût de BIGINT par rapport à INT est de quatre octets supplémentaires par ligne ; le coût d'un INT non protégé est l'inventaire entier des marchandises.
Les contraintes CHECK en base de données ne remplacent pas la validation API — elles se déclenchent trop tard dans le flux de requête pour retourner un message d'erreur utile au client, et elles entraînent le coût d'un aller-retour vers la base de données. Utilisez la validation Pydantic/zod au niveau API comme défense principale et la contrainte CHECK comme garde-fou.
Pour les piles Java et C# spécifiquement, préférez BigInteger / BigDecimal aux int/long natifs pour tout champ de quantité ou de montant qui traverse une frontière de confiance, et activez Math.addExact (Java) ou les blocs checked { ... } (C#) dans l'arithmétique elle-même. Ceux-ci produisent une ArithmeticException explicite en cas de débordement plutôt qu'un wrap-around silencieux, convertissant un bug de logique furtif en un échec runtime bruyant.
Une posture défensive complète inclut également la détection d'anomalies sur le pipeline de commande. Toute commande dont le total final est en dessous d'un plancher raisonnable (par exemple, total < 0.50 × sum_of_unit_prices) doit être mise en quarantaine pour une revue manuelle avant fulfillment, et tout changement dans les compteurs d'inventaire qui augmente le stock sans événement de réapprovisionnement correspondant doit déclencher une alerte. Ces contrôles attrapent la classe résiduelle d'attaques où une entrée d'apparence légitime se combine à une faille logique que les validateurs n'ont pas anticipée — par exemple, des interactions de cumul de coupons qui poussent le total post-remise négatif même lorsque chaque quantité individuelle est positive. La défense en profondeur aux couches d'acceptation et de fulfillment des commandes transforme un seul validateur manquant en un incident récupérable plutôt qu'en un événement de remboursement-de-l'entrepôt.
-0) partage le pattern d'évasion regex.L'abus de quantité négative est une faille de logique métier où un attaquant soumet un entier négatif (par exemple, qty=-1) à un endpoint de panier, de commande ou financier qui n'impose pas une contrainte strictement positive. Le serveur multiplie le prix par la quantité négative et produit un sous-total négatif, ce qui peut réduire le panier à zéro ou générer un crédit boutique. La cause racine est CWE-20 (Improper Input Validation), souvent aggravée par CWE-190 (Integer Overflow) lorsque les attaquants utilisent 2^31 ou 2^63 pour faire boucler des entiers signés sans jamais envoyer de signe moins.
Les types entiers signés en Java, C#, MySQL INT et PHP sur les plateformes 32 bits bouclent silencieusement lorsqu'une valeur dépasse leur plage. Soumettre qty=2147483648 (un de plus que INT_MAX) à une colonne 32 bits fait basculer la valeur stockée à -2147483648, et le panier calcule prix × -2147483648, produisant un total catastrophiquement négatif. Cela contourne les filtres naïfs sur le signe moins car le corps de la requête ne contient jamais le caractère `-`.
Cela fonctionne chaque fois que le développeur fait confiance au champ de quantité fourni par le client et le transmet directement dans l'arithmétique sans vérification de plage Pydantic/zod/Joi ni contrainte CHECK en base de données. L'entrée passe le parsing JSON comme un entier valide, le WAF ne la signale pas (aucun caractère SQL ou script), et le seul signal de compromission est le sous-total négatif dans la réponse — un signal que les scanners DAST génériques n'interprètent pas comme une vulnérabilité.
CVE-2020-11007 est un cas d'école de faille de quantité négative dans Shopizer (plateforme e-commerce Java Spring, < 2.11.0). L'endpoint GET /shop/cart/addToCart?merchantId=1&productId=43&qty=-240 acceptait un paramètre qty négatif sans validation côté serveur, faisant inverser le sous-total du panier. CVSS 6.5 Medium. Le correctif en 2.11.0 était une vérification backend d'une seule ligne qui réinitialise qty à 1 chaque fois qu'une valeur négative est détectée.
En Python avec FastAPI, utilisez Pydantic conint(ge=1, le=999) ou Field(..., gt=0, le=999) sur le champ quantity. En Node.js avec Express, utilisez zod : z.number().int().positive().max(999). Pour les champs monétaires, utilisez les types Decimal/BigDecimal avec condecimal(gt=Decimal('0'), max_digits=12, decimal_places=2). Ajoutez une contrainte CHECK (quantity > 0) en base de données comme défense en profondeur afin que les services internes et les SQL injection ne puissent pas contourner le validateur de la couche API.
Soumettre qty=9223372036854775808 (un de plus que 2^63 − 1, le maximum d'un int64 signé) fait boucler la valeur stockée à -9223372036854775808 en Java long, C# long, PostgreSQL BIGINT et MySQL BIGINT. Un chercheur en bug bounty aurait utilisé 9223427036854775808 contre une grande plateforme e-commerce en 2021, déclenchant un crédit de remboursement de plusieurs milliers de dollars. La plateforme a corrigé en plafonnant la quantité à 999 côté serveur.
Oui. Définir qty=0 sur une ligne d'article contourne souvent les règles d'achat minimum, BOGO ou de bundle de remise car le validateur ne vérifie que le signe, pas le zéro. L'article reste attaché au panier pour le calcul d'éligibilité aux promotions mais ne contribue en rien au total — débloquant des articles promo gratuits ou contournant les seuils de dépense minimum. Imposez toujours qty >= 1 avec Pydantic conint(ge=1) ou zod .int().positive(), jamais .nonnegative() ou .gte(0).