JSON Toolkit

Sans additionalProperties, la validation de schéma ne valide rien

L’essentiel : Une validation réussie ne garantit pas des données propres. Pydantic ignore les champs inconnus par défaut, Zod les supprime, Ajv les laisse passer — les paramètres hallucinés et les clés mal orthographiées traversent en silence. Cet article montre les trois formes d'acceptation silencieuse et donne les recettes complètes : fermer les objets, contrôler la coercition, serrer chaque niveau d'imbrication.

Intermediate~8 min de lecturePublié le 2026-11-02Mis à jour le 2026-11-02
Un JSON avec des champs superflus qui passe la validation

Conclusion : par défaut, les validateurs ignorent les champs non déclarés au lieu de les rejeter. Sans additionalProperties: false, une validation réussie prouve seulement que les champs déclarés sont corrects.

Le comportement le plus dangereux d'un validateur n'est pas de lever une erreur, c'est de rester silencieux. Une clé mal orthographiée ou un paramètre inventé par un modèle traverse la validation sans un son et n'explose que dans la logique métier.

Un : une validation qui « passe »

{
  "type": "object",
  "properties": {
    "order_id": { "type": "string" },
    "amount": { "type": "number" }
  },
  "required": ["order_id", "amount"]
}

Envoyez {"order_id": "A1", "amount": 50, "reasn_code": "damaged"} — la troisième clé est une faute de frappe. Avec la configuration par défaut d'Ajv, le résultat est une réussite. Le champ superflu n'est ni rejeté ni signalé.

Dans la spécification JSON Schema, la valeur par défaut de additionalProperties est true : les champs non déclarés sont autorisés.

Deux : trois saveurs de silence

Runtime Comportement par défaut Conséquence
Ajv (JS) laisse passer données sales dans la logique métier
Zod z.object supprime le champ disparaît, l'objet raccourcit
Pydantic v2 ignore (extra="ignore") idem, aucune trace dans les logs

La suppression est plus furtive que le passage. Les deux bibliothèques, par défaut, échouent silencieusement de la même manière.

Trois : les appels d'outils LLM amplifient le problème

Les arguments d'appel d'outils sont générés par un modèle, qui invente des noms de champs et passe "3" pour un nombre. Les plateformes définissent les paramètres en JSON Schema mais ne forcent pas la fermeture du schéma par défaut — le mode strict doit être activé explicitement, et les contraintes grammaticales ne couvrent que la sortie directe du modèle.

{
  "order_id": 4411,
  "refund_amount_usd": 50,
  "reason_code": "damaged",
  "urgent": "yes"
}

Objet fermé, reason_code et urgent sont rejetés et l'erreur pointe vers /reason_code.

Quatre : comment fermer

Ajv : ajoutez additionalProperties: false sur le nœud objet.

import Ajv from "ajv";
const ajv = new Ajv({ allErrors: true });

const schema = {
  type: "object",
  properties: { order_id: { type: "string" }, amount: { type: "number" } },
  required: ["order_id", "amount"],
  additionalProperties: false,
};

const validate = ajv.compile(schema);
validate({ order_id: "A1", amount: 50, reasn_code: "x" }); // false

Zod : z.strictObject ou .strict(). Pydantic : ConfigDict(extra="forbid").

Règle commune : la fermeture se fait niveau par niveau. Chaque objet imbriqué exige son propre additionalProperties: false.

Cinq : la politique de coercition

Le mode lax de Pydantic convertit "5" en 5. Trois politiques : stricte (rejeter les types incorrects), contrôlée (conversions explicites bornées), permissive (données internes uniquement). Pour des entrées non fiables, choisissez la première ou la deuxième et encodez-la dans les tests.

Six : liste de contrôle d'imbrication

Point Action
Objet racine additionalProperties: false
Chaque objet imbriqué idem
Éléments de tableau fermer les objets dans items
Enums et plages ajouter enum, minimum/maximum
Schémas générés compléter à la main

Sept : après le rejet

allErrors: true signale tous les champs fautifs d'un coup ; verbose: true ajoute les chemins. Les problèmes de syntaxe relèvent du parseur, avant la validation de schéma.

FAQ

Trop strict ?

Cela dépend du contrat. Pour vos propres entrées, fermez ; pour des données tierces extensibles, fermez le noyau et isolez la zone d'extension.

Le strip de Zod est plus sûr ?

Non : il protège l'aval au détriment de l'appelant. Les erreurs doivent surgir tôt et explicitement.

Le mode strict de la plateforme suffit-il ?

Non. Le schéma lui-même peut rester ouvert et la grammaire peut ne pas couvrir les arguments d'outils.

Les anciennes données échouent en masse ?

C'est exactement ce qu'il fallait exposer. Inventoriez avec allErrors, ajoutez les champs inoffensifs, corrigez les autres.

Une solution en une ligne ?

Non, mais un geste minimal : vérifier que chaque type: "object" est suivi de additionalProperties: false.