{"id":"GHSA-qpmf-fgh3-ff25","kind":"vulnerability","aliases":["GHSA-qpmf-fgh3-ff25"],"related":[],"title":"Validation bypass of extra data keys in JSON model validation","description":"### Summary\r\n\r\nWhen validating JSON data into a model allowing extra data, the keys of extra data aren't validated against the key type annotated on `__pydantic_extra__`, resulting in extra data not matching the declared type. This is the case if you define `__pydantic_extra__` as e.g. `dict[Annotated[str, Field(pattern=r'^x_')], int]`.\r\n\r\n### Details\r\n\r\nThe [`validate_json_by_iteration()`](https://github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/model_fields.rs#L540) method (in [`pydantic-core/src/validators/model_fields.rs`](https://github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/model_fields.rs)), used to validate JSON data into models, only validates the values of extra data, and stores the keys as is ([L605-L623](https://github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/model_fields.rs#L605-L623)):\r\n\r\n```rust\r\nExtraBehavior::Allow => {\r\n    // the key is stored as is, without being validated:\r\n    let py_key: Bound<'_, PyString> = new_py_string(py, key, state.cache_str());\r\n    if let Some(validator) = &self.extras_validator {\r\n        match validator.validate(py, value, state) {\r\n            Ok(value) => {\r\n                model_extra_dict.set_item(&py_key, value)?;\r\n                fields_set.add(py_key)?;\r\n            }\r\n            // ...\r\n        }\r\n    }\r\n    // ...\r\n}\r\n```\r\n\r\nPython mode validation isn't affected.\r\n\r\nThe issue was fixed in commit https://github.com/pydantic/pydantic/commit/4c65dbda9970a488e2ff21c8693fc6f6d70a3358.\r\n\r\n### Impact\r\n\r\nValidation bypass: extra data returned by validation doesn't match the declared type. Affected are applications validating untrusted JSON data into models allowing extra data (with `extra='allow'`), with a key type annotated on `__pydantic_extra__` (e.g. `dict[Annotated[str, Field(pattern=...)], int]`), using Pydantic from 2.13.0 up to (but excluding) 2.14.2.\r\n\r\nPython mode validation isn't affected.","summary":"When Pydantic validates JSON into a model with extra='allow', it stores extra keys without checking them against the key type declared on __pydantic_extra__. Extra data can therefore hold keys that do not match the declared type, bypassing validation for untrusted JSON input.","severity":"medium","cvss":6.3,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"pydantic","fixed":["2.14.2"],"affected":">= 2.13.0, < 2.14.2","ecosystem":"pypi"}],"ecosystems":["pypi"],"tags":["backend","cwe-20","package","pydantic","python"],"not_affected_if":["extra-forbid-or-ignore","no-constrained-extra-keys","python-mode-validation"],"how_to_check":"- Check the installed version with `pip show pydantic` and see whether it falls in `>= 2.13.0, < 2.14.2`.\n- Search the codebase for `extra='allow'` or `extra = \"allow\"` on models that validate JSON, and for `__pydantic_extra__` annotated with a constrained key type such as `dict[Annotated[str, Field(pattern=...)], ...]`.","fix":"- pydantic: upgrade to `2.14.2`","status":"open","published_at":"2026-10-29T17:01:40+00:00","first_seen_at":"2026-11-09T22:53:21+00:00","triaged_at":"2026-11-09T22:54:57+00:00","updated_at":"2026-11-09T22:54:57+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T22:54:57Z","model":"claude-haiku-5-5","output":{"fix":"- pydantic: upgrade to `2.14.2`","reach":"network","title":"Pydantic: JSON validation skips key checks on extra model fields","impact":"low","summary":"When Pydantic validates JSON into a model with extra='allow', it stores extra keys without checking them against the key type declared on __pydantic_extra__. Extra data can therefore hold keys that do not match the declared type, bypassing validation for untrusted JSON input.","audience":"developers","category":"package","nicknames":[],"who_cares":["python","pydantic","backend"],"confidence":0.8,"fix_status":"available","needs_auth":"unknown","worst_case":"validated data contains keys violating the declared type","analyst_note":"","how_to_check":"- Check the installed version with `pip show pydantic` and see whether it falls in `>= 2.13.0, < 2.14.2`.\n- Search the codebase for `extra='allow'` or `extra = \"allow\"` on models that validate JSON, and for `__pydantic_extra__` annotated with a constrained key type such as `dict[Annotated[str, Field(pattern=...)], ...]`.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["python-mode-validation","extra-forbid-or-ignore","no-constrained-extra-keys"],"who_is_affected":"Applications that parse untrusted JSON into Pydantic models that allow extra fields (extra='allow') and declare a constrained key type on __pydantic_extra__. Python-mode validation is not affected, and projects without those model settings can skip this.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"unknown"},"cost_usd":0.000816,"input_tokens":5286,"output_tokens":575,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-20':24B '/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/model_fields.rs#l540)':135C '/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/model_fields.rs#l605-l623)):':170C '/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/model_fields.rs)),':144C '/pydantic/pydantic/commit/4c65dbda9970a488e2ff21c8693fc6f6d70a3358.':230C '/src/validators/model_fields.rs':141C '2.13.0':278C '2.14.2':283C 'affect':221C,245C,289C 'allow':37B,80C,173C,254C,259C 'annot':95C,120C,264C,270C 'applic':247C 'aren':88C 'backend':22B 'bound':186C 'bypass':2A,66B,233C 'case':111C 'check':43B 'commit':227C 'core':140C 'cwe':23B 'data':5A,54B,76C,82C,87C,102C,149C,158C,235C,251C,256C 'declar':49B,64B,106C,243C 'defin':114C 'detail':127C 'dict':119C,269C 'doesn':239C 'e.g':118C,268C 'exclud':282C 'extra':4A,36B,40B,52B,53B,81C,86C,98C,101C,116C,157C,234C,255C,258C,267C 'extrabehavior':172C 'ff25':15A,20A 'fgh3':14A,19A 'field':122C,272C 'fields_set.add':213C 'fix':225C 'ghsa':12A,17A 'ghsa-qpmf-fgh3-ff25':11A,16A 'github.com':134C,143C,169C,229C 'github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/model_fields.rs#l540)':133C 'github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/model_fields.rs#l605-l623)):':168C 'github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/model_fields.rs)),':142C 'github.com/pydantic/pydantic/commit/4c65dbda9970a488e2ff21c8693fc6f6d70a3358.':228C 'hold':57B 'impact':231C 'input':71B 'int':126C,274C 'isn':219C,287C 'issu':223C 'item':209C 'iter':132C 'json':8A,31B,70B,75C,130C,148C,250C 'key':6A,41B,47B,58B,84C,93C,162C,175C,185C,192C,211C,215C,262C 'l605':166C 'l605-l623':165C 'l623':167C 'let':183C,196C 'match':62B,104C,201C,241C 'method':136C 'mode':217C,285C 'model':9A,34B,79C,151C,253C 'model_extra_dict.set':208C 'new':188C 'ok':206C 'packag':25B 'pattern':123C,273C 'py':184C,189C,191C,203C,210C,214C 'pydant':26B,29B,51B,97C,115C,139C,266C,276C 'pydantic':21A 'pydantic-cor':138C 'pystr':187C 'python':27B,216C,284C 'qpmf':13A,18A 'r':124C 'result':99C 'return':236C 'rust':171C 'self.extras':199C 'state':205C 'state.cache':193C 'store':39B,160C,177C 'str':121C,194C,271C 'string':190C 'summari':72C 'therefor':56B 'type':48B,65B,94C,107C,244C,263C 'untrust':69B,249C 'use':145C,275C 'valid':1A,10A,30B,67B,74C,90C,129C,147C,153C,182C,198C,200C,218C,232C,238C,248C,286C 'validator.validate':202C 'valu':155C,204C,207C,212C 'without':42B,180C 'x':125C","nicknames":[],"mitigations":[],"who_affected":"Applications that parse untrusted JSON into Pydantic models that allow extra fields (extra='allow') and declare a constrained key type on __pydantic_extra__. Python-mode validation is not affected, and projects without those model settings can skip this.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["pydantic"],"ptokens":["pydantic"],"labels":["backend","cwe20","medium","package","pydantic","pypi","python"],"campaign_type":null,"search_a":"'bypass':2A 'data':5A 'extra':4A 'ff25':15A,20A 'fgh3':14A,19A 'ghsa':12A,17A 'ghsa-qpmf-fgh3-ff25':11A,16A 'json':8A 'key':6A 'model':9A 'pydantic':21A 'qpmf':13A,18A 'valid':1A,10A","search_b":"'-20':24B 'allow':37B 'backend':22B 'bypass':66B 'check':43B 'cwe':23B 'data':54B 'declar':49B,64B 'extra':36B,40B,52B,53B 'hold':57B 'input':71B 'json':31B,70B 'key':41B,47B,58B 'match':62B 'model':34B 'packag':25B 'pydant':26B,29B,51B 'python':27B 'store':39B 'therefor':56B 'type':48B,65B 'untrust':69B 'valid':30B,67B 'without':42B","search_c":"'/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/model_fields.rs#l540)':135C '/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/model_fields.rs#l605-l623)):':170C '/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/model_fields.rs)),':144C '/pydantic/pydantic/commit/4c65dbda9970a488e2ff21c8693fc6f6d70a3358.':230C '/src/validators/model_fields.rs':141C '2.13.0':278C '2.14.2':283C 'affect':221C,245C,289C 'allow':80C,173C,254C,259C 'annot':95C,120C,264C,270C 'applic':247C 'aren':88C 'bound':186C 'bypass':233C 'case':111C 'commit':227C 'core':140C 'data':76C,82C,87C,102C,149C,158C,235C,251C,256C 'declar':106C,243C 'defin':114C 'detail':127C 'dict':119C,269C 'doesn':239C 'e.g':118C,268C 'exclud':282C 'extra':81C,86C,98C,101C,116C,157C,234C,255C,258C,267C 'extrabehavior':172C 'field':122C,272C 'fields_set.add':213C 'fix':225C 'github.com':134C,143C,169C,229C 'github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/model_fields.rs#l540)':133C 'github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/model_fields.rs#l605-l623)):':168C 'github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/model_fields.rs)),':142C 'github.com/pydantic/pydantic/commit/4c65dbda9970a488e2ff21c8693fc6f6d70a3358.':228C 'impact':231C 'int':126C,274C 'isn':219C,287C 'issu':223C 'item':209C 'iter':132C 'json':75C,130C,148C,250C 'key':84C,93C,162C,175C,185C,192C,211C,215C,262C 'l605':166C 'l605-l623':165C 'l623':167C 'let':183C,196C 'match':104C,201C,241C 'method':136C 'mode':217C,285C 'model':79C,151C,253C 'model_extra_dict.set':208C 'new':188C 'ok':206C 'pattern':123C,273C 'py':184C,189C,191C,203C,210C,214C 'pydant':97C,115C,139C,266C,276C 'pydantic-cor':138C 'pystr':187C 'python':216C,284C 'r':124C 'result':99C 'return':236C 'rust':171C 'self.extras':199C 'state':205C 'state.cache':193C 'store':160C,177C 'str':121C,194C,271C 'string':190C 'summari':72C 'type':94C,107C,244C,263C 'untrust':249C 'use':145C,275C 'valid':74C,90C,129C,147C,153C,182C,198C,200C,218C,232C,238C,248C,286C 'validator.validate':202C 'valu':155C,204C,207C,212C 'without':180C 'x':125C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-10-29T17:01:40+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-qpmf-fgh3-ff25","url":"https://github.com/pydantic/pydantic/security/advisories/GHSA-qpmf-fgh3-ff25","severity":"medium","published_at":"2026-10-29T17:01:40+00:00","modified_at":"2026-10-29T17:01:40+00:00","first_seen_at":"2026-11-09T22:53:21+00:00","fetched_at":"2026-11-09T22:53:21+00:00","note":null}],"documents":[{"id":110522,"url":"https://github.com/pydantic/pydantic/security/advisories/GHSA-qpmf-fgh3-ff25","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa-repo","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}