{"id":"GHSA-3r8g-cqwr-jr29","kind":"vulnerability","aliases":["GHSA-3r8g-cqwr-jr29"],"related":[],"title":"Validation bypass of aliased fields in TypedDict and dataclasses allowing extra data","description":"### Summary\r\n\r\nWhen extra data is allowed, providing both the alias and the name of an aliased `TypedDict` or dataclass field makes the unvalidated value (provided under the name) overwrite the validated one (provided under the alias), resulting in validated data not matching the declared type.\r\n\r\n### Details\r\n\r\nExtra items are stored in the same dictionary as the fields (the returned dictionary for `TypedDict`, the instance `__dict__` for dataclasses). As only the alias is marked as used, the field name is treated as an extra item, and its unvalidated value overrides the validated one ([`pydantic-core/src/validators/typed_dict.rs`](https://github.com/pydantic/pydantic/blob/becee9cc0ef8132e854762448fde76aa56608c6b/pydantic-core/src/validators/typed_dict.rs#L204-L225)):\r\n\r\n```rust\r\n// First pass, for each field:\r\nused_keys.insert(lookup_path.first_key()); // only the alias `b` is marked as used\r\n// ...\r\noutput_dict.set_item(&field.name, value)?; // the validated value is stored under `a`\r\n\r\n// Second pass, for each input key:\r\nif self.used_keys.contains(cow.as_ref()) {\r\n    continue; // `a` isn't marked as used\r\n}\r\n// ...\r\nExtraBehavior::Allow => {\r\n    // the extra value is stored under `a`, overwriting the validated value:\r\n    self.output_dict.set_item(py_key, value.to_object(self.py)?)?;\r\n}\r\n```\r\n\r\nDataclasses have the same logic, in [`pydantic-core/src/validators/dataclass.rs`](https://github.com/pydantic/pydantic/blob/becee9cc0ef8132e854762448fde76aa56608c6b/pydantic-core/src/validators/dataclass.rs#L215-L342).\r\n\r\nThe issue was fixed in commit https://github.com/pydantic/pydantic/commit/34491f5328f168f14e3769cd5f0f83c0af0f4778.\r\n\r\n### Impact\r\n\r\nValidation bypass: data returned by validation doesn't match the declared type. Affected are applications validating untrusted data into `TypedDict` or dataclass types with aliased fields and extra data allowed (with `extra='allow'`, or with the `extra_items` argument for `TypedDict`), using Pydantic V2 (up to and including 2.14.1).\r\n\r\nPydantic models (`BaseModel`) are not affected, as extra data is stored separately from fields.","summary":"When extra data is allowed, passing both an alias and the field name for a TypedDict or dataclass field lets the unvalidated name-keyed value overwrite the validated one. The validated output then no longer matches its declared type.","severity":"medium","cvss":6.3,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"pydantic","fixed":["2.14.2"],"affected":">= 2.0, < 2.14.2","ecosystem":"pypi"}],"ecosystems":["pypi"],"tags":["cwe-20","dataclasses","package","pydantic","python","typeddict"],"not_affected_if":["basemodel-only","extra-forbidden","no-aliased-fields"],"how_to_check":"- Run `pip show pydantic` and check whether the version is below `2.14.2`.\n- Search the codebase for `extra='allow'` or `extra_items` used with TypedDict or dataclass types that have aliased fields.","fix":"- Upgrade `pydantic` to `2.14.2`.","status":"open","published_at":"2026-10-29T17:00:51+00:00","first_seen_at":"2026-11-09T22:53:21+00:00","triaged_at":"2026-11-09T22:54:56+00:00","updated_at":"2026-11-09T22:54:56+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T22:54:56Z","model":"claude-haiku-5-5","output":{"fix":"- Upgrade `pydantic` to `2.14.2`.","reach":"unknown","title":"Pydantic: aliased TypedDict and dataclass fields accept unvalidated overriding values","impact":"medium","summary":"When extra data is allowed, passing both an alias and the field name for a TypedDict or dataclass field lets the unvalidated name-keyed value overwrite the validated one. The validated output then no longer matches its declared type.","audience":"developers","category":"package","nicknames":[],"who_cares":["python","pydantic","typeddict","dataclasses"],"confidence":0.8,"fix_status":"available","needs_auth":"unknown","worst_case":"validated data silently replaced by unvalidated values, breaking type guarantees","analyst_note":"","how_to_check":"- Run `pip show pydantic` and check whether the version is below `2.14.2`.\n- Search the codebase for `extra='allow'` or `extra_items` used with TypedDict or dataclass types that have aliased fields.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["basemodel-only","extra-forbidden","no-aliased-fields"],"who_is_affected":"Applications that validate untrusted input into TypedDict or dataclass types with aliased fields while extra data is allowed (extra='allow' or extra_items). Pydantic BaseModel classes are not affected, since extra data is stored separately from fields.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"unknown"},"cost_usd":0.000797,"input_tokens":5344,"output_tokens":526,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-20':25B '/pydantic/pydantic/blob/becee9cc0ef8132e854762448fde76aa56608c6b/pydantic-core/src/validators/dataclass.rs#l215-l342).':247C '/pydantic/pydantic/blob/becee9cc0ef8132e854762448fde76aa56608c6b/pydantic-core/src/validators/typed_dict.rs#l204-l225)):':169C '/pydantic/pydantic/commit/34491f5328f168f14e3769cd5f0f83c0af0f4778.':256C '/src/validators/dataclass.rs':244C '/src/validators/typed_dict.rs':166C '2.14.1':306C '3r8g':15A,20A 'affect':270C,312C 'alia':39B,80C,106C,141C,181C 'alias':4A,86C,282C 'allow':10A,35B,76C,216C,287C,290C 'applic':272C 'argument':296C 'b':182C 'basemodel':309C 'bypass':2A,259C 'commit':253C 'continu':208C 'core':165C,243C 'cow.as':206C 'cqwr':16A,21A 'cwe':24B 'data':12A,33B,74C,110C,260C,275C,286C,315C 'dataclass':9A,26B,48B,89C,137C,235C,279C 'declar':69B,114C,268C 'detail':116C 'dict':135C 'dictionari':124C,130C 'doesn':264C 'extra':11A,32B,73C,117C,153C,218C,285C,289C,294C,314C 'extrabehavior':215C 'field':5A,42B,49B,90C,127C,147C,175C,283C,320C 'field.name':189C 'first':171C 'fix':251C 'ghsa':14A,19A 'ghsa-3r8g-cqwr-jr29':13A,18A 'github.com':168C,246C,255C 'github.com/pydantic/pydantic/blob/becee9cc0ef8132e854762448fde76aa56608c6b/pydantic-core/src/validators/dataclass.rs#l215-l342).':245C 'github.com/pydantic/pydantic/blob/becee9cc0ef8132e854762448fde76aa56608c6b/pydantic-core/src/validators/typed_dict.rs#l204-l225)):':167C 'github.com/pydantic/pydantic/commit/34491f5328f168f14e3769cd5f0f83c0af0f4778.':254C 'impact':257C 'includ':305C 'input':202C 'instanc':134C 'isn':210C 'issu':249C 'item':118C,154C,188C,229C,295C 'jr29':17A,22A 'key':55B,178C,203C,231C 'let':50B 'logic':239C 'longer':66B 'lookup_path.first':177C 'make':91C 'mark':143C,184C,212C 'match':67B,112C,266C 'model':308C 'name':43B,54B,83C,98C,148C 'name-key':53B 'object':233C 'one':60B,102C,162C 'output':63B 'output_dict.set':187C 'overrid':159C 'overwrit':57B,99C,224C 'packag':27B 'pass':36B,172C,199C 'provid':77C,95C,103C 'py':230C 'pydant':28B,164C,242C,300C,307C 'pydantic':23A 'pydantic-cor':163C,241C 'python':29B 'ref':207C 'result':107C 'return':129C,261C 'rust':170C 'second':198C 'self.output_dict.set':228C 'self.py':234C 'self.used_keys.contains':205C 'separ':318C 'store':120C,195C,221C,317C 'summari':71C 'treat':150C 'type':70B,115C,269C,280C 'typeddict':7A,30B,46B,87C,132C,277C,298C 'untrust':274C 'unvalid':52B,93C,157C 'use':145C,186C,214C,299C 'used_keys.insert':176C 'v2':301C 'valid':1A,59B,62B,101C,109C,161C,192C,226C,258C,263C,273C 'valu':56B,94C,158C,190C,193C,219C,227C 'value.to':232C","nicknames":[],"mitigations":[],"who_affected":"Applications that validate untrusted input into TypedDict or dataclass types with aliased fields while extra data is allowed (extra='allow' or extra_items). Pydantic BaseModel classes are not affected, since extra data is stored separately from fields.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["pydantic"],"ptokens":["pydantic"],"labels":["cwe20","dataclasses","medium","package","pydantic","pypi","python","typeddict"],"campaign_type":null,"search_a":"'3r8g':15A,20A 'alias':4A 'allow':10A 'bypass':2A 'cqwr':16A,21A 'data':12A 'dataclass':9A 'extra':11A 'field':5A 'ghsa':14A,19A 'ghsa-3r8g-cqwr-jr29':13A,18A 'jr29':17A,22A 'pydantic':23A 'typeddict':7A 'valid':1A","search_b":"'-20':25B 'alia':39B 'allow':35B 'cwe':24B 'data':33B 'dataclass':26B,48B 'declar':69B 'extra':32B 'field':42B,49B 'key':55B 'let':50B 'longer':66B 'match':67B 'name':43B,54B 'name-key':53B 'one':60B 'output':63B 'overwrit':57B 'packag':27B 'pass':36B 'pydant':28B 'python':29B 'type':70B 'typeddict':30B,46B 'unvalid':52B 'valid':59B,62B 'valu':56B","search_c":"'/pydantic/pydantic/blob/becee9cc0ef8132e854762448fde76aa56608c6b/pydantic-core/src/validators/dataclass.rs#l215-l342).':247C '/pydantic/pydantic/blob/becee9cc0ef8132e854762448fde76aa56608c6b/pydantic-core/src/validators/typed_dict.rs#l204-l225)):':169C '/pydantic/pydantic/commit/34491f5328f168f14e3769cd5f0f83c0af0f4778.':256C '/src/validators/dataclass.rs':244C '/src/validators/typed_dict.rs':166C '2.14.1':306C 'affect':270C,312C 'alia':80C,106C,141C,181C 'alias':86C,282C 'allow':76C,216C,287C,290C 'applic':272C 'argument':296C 'b':182C 'basemodel':309C 'bypass':259C 'commit':253C 'continu':208C 'core':165C,243C 'cow.as':206C 'data':74C,110C,260C,275C,286C,315C 'dataclass':89C,137C,235C,279C 'declar':114C,268C 'detail':116C 'dict':135C 'dictionari':124C,130C 'doesn':264C 'extra':73C,117C,153C,218C,285C,289C,294C,314C 'extrabehavior':215C 'field':90C,127C,147C,175C,283C,320C 'field.name':189C 'first':171C 'fix':251C 'github.com':168C,246C,255C 'github.com/pydantic/pydantic/blob/becee9cc0ef8132e854762448fde76aa56608c6b/pydantic-core/src/validators/dataclass.rs#l215-l342).':245C 'github.com/pydantic/pydantic/blob/becee9cc0ef8132e854762448fde76aa56608c6b/pydantic-core/src/validators/typed_dict.rs#l204-l225)):':167C 'github.com/pydantic/pydantic/commit/34491f5328f168f14e3769cd5f0f83c0af0f4778.':254C 'impact':257C 'includ':305C 'input':202C 'instanc':134C 'isn':210C 'issu':249C 'item':118C,154C,188C,229C,295C 'key':178C,203C,231C 'logic':239C 'lookup_path.first':177C 'make':91C 'mark':143C,184C,212C 'match':112C,266C 'model':308C 'name':83C,98C,148C 'object':233C 'one':102C,162C 'output_dict.set':187C 'overrid':159C 'overwrit':99C,224C 'pass':172C,199C 'provid':77C,95C,103C 'py':230C 'pydant':164C,242C,300C,307C 'pydantic-cor':163C,241C 'ref':207C 'result':107C 'return':129C,261C 'rust':170C 'second':198C 'self.output_dict.set':228C 'self.py':234C 'self.used_keys.contains':205C 'separ':318C 'store':120C,195C,221C,317C 'summari':71C 'treat':150C 'type':115C,269C,280C 'typeddict':87C,132C,277C,298C 'untrust':274C 'unvalid':93C,157C 'use':145C,186C,214C,299C 'used_keys.insert':176C 'v2':301C 'valid':101C,109C,161C,192C,226C,258C,263C,273C 'valu':94C,158C,190C,193C,219C,227C 'value.to':232C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-10-29T17:00:51+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-3r8g-cqwr-jr29","url":"https://github.com/pydantic/pydantic/security/advisories/GHSA-3r8g-cqwr-jr29","severity":"medium","published_at":"2026-10-29T17:00:51+00:00","modified_at":"2026-10-29T17:00:51+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":110523,"url":"https://github.com/pydantic/pydantic/security/advisories/GHSA-3r8g-cqwr-jr29","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"}