Bez additionalProperties walidacja schematu nic nie sprawdza
Podsumowanie: Zdana walidacja nie oznacza czystych danych. Pydantic domyślnie ignoruje nieznane pola, Zod je usuwa, Ajv przepuszcza — zmyślone parametry i literówki przechodzą po cichu. Artykuł pokazuje trzy formy cichej akceptacji i daje pełne przepisy: zamykać obiekty, kontrolować koercję, zaciskać każdy poziom zagnieżdżenia.

Wniosek: domyślnie walidatory ignorują niezadeklarowane pola zamiast je odrzucać. Bez additionalProperties: false zdana walidacja dowodzi tylko, że zadeklarowane pola są w porządku.
Najniebezpieczniejsze zachowanie walidatora to nie błąd, lecz cisza. Literówka w kluczu lub parametr wymyślony przez model przechodzi przez walidację bezgłośnie i wybucha dopiero w logice biznesowej.
Jeden: walidacja, która „przechodzi"
{
"type": "object",
"properties": {
"order_id": { "type": "string" },
"amount": { "type": "number" }
},
"required": ["order_id", "amount"]
}
Wyślij {"order_id": "A1", "amount": 50, "reasn_code": "damaged"} — trzeci klucz to literówka. Z domyślną konfiguracją Ajv wynik to sukces. Nadmiarowe pole nie jest ani odrzucone, ani oznaczone.
W specyfikacji JSON Schema domyślna wartość additionalProperties to true: niezadeklarowane pola są dozwolone.
Dwa: trzy odmiany ciszy
| Runtime | Domyślne zachowanie | Skutek |
|---|---|---|
| Ajv (JS) | przepuszcza | brudne dane docierają do logiki |
Zod z.object |
usuwa | pole znika, obiekt się kurczy |
| Pydantic v2 | ignoruje (extra="ignore") |
to samo, bez śladu w logach |
Usuwanie jest skrytsze niż przepuszczenie. Obie biblioteki domyślnie zawodzą tak samo — po cichu.
Trzy: wywołania narzędzi LLM pogłębiają problem
Argumenty generuje model: wymyśla nazwy pól i przekazuje "3" jako liczbę. Platformy definiują parametry w JSON Schema, ale domyślnie nie wymuszają zamknięcia schematu — tryb strict trzeba włączyć jawnie, a ograniczenia gramatyczne obejmują tylko bezpośrednie wyjście modelu.
{
"order_id": 4411,
"refund_amount_usd": 50,
"reason_code": "damaged",
"urgent": "yes"
}
Przy zamkniętym obiekcie reason_code i urgent są odrzucane, a błąd wskazuje na /reason_code.
Cztery: jak zamknąć
Ajv: dodaj additionalProperties: false w węźle obiektu.
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 lub .strict(). Pydantic: ConfigDict(extra="forbid").
Wspólna reguła: zamykanie odbywa się na każdym poziomie. Każdy zagnieżdżony obiekt wymaga własnego additionalProperties: false.
Pięć: polityka koercji
Tryb lax Pydantic zamienia "5" na 5. Trzy polityki: ścisła (odrzucać złe typy), kontrolowana (tylko jawne, ograniczone konwersje), swobodna (tylko dane wewnętrzne). Dla niezaufanych wejść wybierz pierwszą lub drugą i zapisz ją w testach.
Sześć: lista kontrolna zagnieżdżenia
| Punkt | Działanie |
|---|---|
| Obiekt główny | additionalProperties: false |
| Każdy zagnieżdżony obiekt | to samo |
| Elementy tablic | zamykać obiekty w items |
| Enumy i zakresy | dodać enum, minimum/maximum |
| Wygenerowane schematy | uzupełnić ręcznie |
Siedem: po odrzuceniu
allErrors: true zgłasza wszystkie pola naraz; verbose: true dodaje ścieżki. Problemy składniowe łapie parser przed walidacją schematu.
FAQ
Za surowo?
Zależy od kontraktu. Dla własnych wejść zamykaj; dla rozszerzalnych danych zewnętrznych zamknij tylko rdzeń i wydziel obszar rozszerzeń.
Strip w Zodzie jest bezpieczniejszy?
Nie: chroni downstream kosztem wywołującego. Błędy powinny pojawiać się wcześnie i jawnie.
Czy strict mode platformy zastępuje walidację lokalną?
Nie. Sam schemat może być otwarty, a gramatyka może nie obejmować argumentów narzędzi.
Po zamknięciu stare dane masowo padają?
To dokładnie to, co trzeba było ujawnić. Zinwentaryzuj przez allErrors, dodaj nieszkodliwe pola, popraw szkodliwe.
Rozwiązanie w jednej linii?
Nie, ale minimalny krok: sprawdzić, czy za każdym type: "object" następuje additionalProperties: false.