בלי additionalProperties, אימות הסכמה לא מאמת כלום
שורה תחתונה: עמידה באימות לא אומרת שהנתונים נקיים. Pydantic מתעלם משדות לא מוכרים כברירת מחדל, Zod מסיר אותם, Ajv מרשה להם לעבור — פרמטרים מומצאים ומפתחות עם שגיאת הקלדה עוברים בשקט. המאמר מציג שלוש צורות של קבלה שקטה ונותן מתכונים מלאים: לסגור אובייקטים, לשלוט בהמרת טיפוסים ולהדק כל רמת קינון.

מסקנה: כברירת מחדל, מאמתים מתעלמים משדות שלא הוכרזו במקום לדחות אותם. בלי additionalProperties: false, אימות עובר רק מוכיח שהשדות שהוכרזו תקינים.
ההתנהגות המסוכנת ביותר של מאמת אינה זריקת שגיאה אלא שקט. מפתח עם שגיאת הקלדה או פרמטר שהמודל המציא עובר את האימות בלי קול ומתפוצץ רק בלוגיקה העסקית.
אחד: אימות ש"עובר"
{
"type": "object",
"properties": {
"order_id": { "type": "string" },
"amount": { "type": "number" }
},
"required": ["order_id", "amount"]
}
שלחו {"order_id": "A1", "amount": 50, "reasn_code": "damaged"} — המפתח השלישי הוא שגיאת הקלדה. עם התצורה המוגדרת של Ajv התוצאה היא הצלחה. השדה המיותר לא נדחה ולא מסומן.
במפרט JSON Schema ערך ברירת המחדל של additionalProperties הוא true: שדות שלא הוכרזו מותרים.
שניים: שלוש צורות של שקט
| סביבת ריצה | התנהגות ברירת מחדל | תוצאה |
|---|---|---|
| Ajv (JS) | מרשה לעבור | נתונים מלוכלכים מגיעים ללוגיקה |
Zod z.object |
מסיר | השדה נעלם, האובייקט מתקצר |
| Pydantic v2 | מתעלם (extra="ignore") |
כנ"ל, ללא עקבות בלוגים |
הסרה חמקמקה יותר מהעברה. שתי הספריות נכשלות כברירת מחדל באותו אופן — בשקט.
שלושה: קריאות כלים של LLM מגדילות את הבעיה
ארגומנטים נוצרים על ידי המודל: הוא ממציא שמות שדות בביטחון ומעביר "3" במקום מספר. הפלטפורמות מגדירות פרמטרים ב-JSON Schema אך אינן מכריחות סגירת הסכמה כברירת מחדל — מצב strict חייב להיות מופעל במפורש, והמגבלות הדקדוקיות חלות רק על הפלט הישיר של המודל.
{
"order_id": 4411,
"refund_amount_usd": 50,
"reason_code": "damaged",
"urgent": "yes"
}
עם אובייקט סגור, reason_code ו-urgent נדחים והשגיאה מצביעה על הנתיב /reason_code.
ארבעה: איך סוגרים
Ajv: הוסיפו additionalProperties: false על צומת האובייקט.
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.object ב-z.strictObject או שרשרו .strict(). Pydantic: הגדירו ConfigDict(extra="forbid").
כלל משותף: הסגירה נעשית לכל רמה. כל אובייקט מקונן זקוק ל-additionalProperties: false משלו.
חמישה: מדיניות המרת טיפוסים
מצב lax של Pydantic הופך "5" ל-5. שלוש מדיניות: מחמירה (לדחות טיפוסים שגויים), מבוקרת (רק המרות מפורשות ומוגבלות), פתוחה (נתונים פנימיים בלבד). לקלט לא מהימן בחרו בראשונה או בשנייה ורשמו זאת בבדיקות.
שישה: צ'קליסט קינון
| נקודה | פעולה |
|---|---|
| אובייקט שורש | additionalProperties: false |
| כל אובייקט מקונן | כנ"ל |
| איברי מערך | לסגור אובייקטים בתוך items |
| enum וטווחים | להוסיף enum, minimum/maximum |
| סכמות שנוצרו אוטומטית | להשלים ידנית |
שבעה: אחרי הדחייה
allErrors: true מדווח על כל השדות בבת אחת; verbose: true נותן נתיבים. בעיות תחביר נתפסות על ידי המנתח לפני אימות הסכמה.
שאלות נפוצות
קשוח מדי?
תלוי בחוזה. לקלט שאתם מגדירים — סגרו; לנתוני צד שלישי עם הרחבות — סגרו רק את הליבה ובדלו את אזור ההרחבה.
ה-strip של Zod בטוח יותר?
לא: הוא מגן על המורד במחיר הקורא. שגיאות צריכות להופיע מוקדם ובמפורש.
מצב strict של הפלטפורמה מחליף אימות מקומי?
לא. הסכמה עצמה עשויה להיות פתוחה, והדקדוק עשוי לא לכסות ארגומנטים של כלים.
אחרי הסגירה נתונים ישנים נופלים המונית?
זה בדיוק מה שהיה צריך לחשוף. בצעו מפקד עם allErrors, הוסיפו שדות לא מזיקים, תקנו את המזיקים.
פתרון בשורה אחת?
אין, אבל צעד מינימלי: לוודא שכל type: "object" מסתיים ב-additionalProperties: false.