{"id":"GHSA-955g-x97g-8mcp","kind":"vulnerability","aliases":["GHSA-955g-x97g-8mcp"],"related":[],"title":"Quadratic time and memory multi-host URL validation","description":"### Summary\r\n\r\nValidating an untrusted URL with a long scheme and many hosts into a multi-host URL field (e.g. [`PostgresDsn`](https://pydantic.dev/docs/validation/latest/api/pydantic/networks/#pydantic.networks.PostgresDsn)) takes quadratic time and memory: an input of about a hundred kilobytes takes around a gigabyte of memory to validate.\r\n\r\n### Details\r\n\r\nThe [`parse_multihost_url()`](https://github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/url.rs#L537) function (in [`pydantic-core/src/validators/url.rs`](https://github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/url.rs)) parses each host separately, prepending the whole URL prefix (including the scheme) to every one of them ([L644-L649](https://github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/url.rs#L644-L649)):\r\n\r\n```rust\r\nlet extra_urls: Vec<Url> = hosts\r\n    .iter()\r\n    .map(|host| {\r\n        let reconstructed_url = format!(\"{prefix}{host}\");\r\n        parse_url(&reconstructed_url, input, strict)\r\n    })\r\n    .collect::<ValResult<_>>()?;\r\n```\r\n\r\nAs the scheme can be arbitrarily long, and is parsed and stored once per host, both time and memory grow quadratically with the input size. Allowed schemes (e.g. `postgres` for `PostgresDsn`) are only checked after parsing, so they don't prevent the issue.\r\n\r\nThe issue was fixed in commit https://github.com/pydantic/pydantic/commit/b4028f9d87e34750e53be0ba011d70f3c0fcc94e.\r\n\r\n### Impact\r\n\r\nDenial of service through validation of untrusted input. Affected are applications validating untrusted strings into multi-host URL fields ([`PostgresDsn`](https://pydantic.dev/docs/validation/latest/api/pydantic/networks/#pydantic.networks.PostgresDsn), [`MongoDsn`](https://pydantic.dev/docs/validation/latest/api/pydantic/networks/#pydantic.networks.MongoDsn), [`NatsDsn`](https://pydantic.dev/docs/validation/latest/api/pydantic/networks/#pydantic.networks.NatsDsn), or custom types built on `MultiHostUrl`).","summary":"Pydantic's multi-host URL validation parses the whole URL prefix once per host, so a long scheme with many hosts makes time and memory grow quadratically. An attacker who can submit a roughly 100 KB string to a PostgresDsn, MongoDsn or NatsDsn field can exhaust a server's memory and stall it, a denial of service.","severity":"medium","cvss":6.3,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"pydantic","note":"2.0.0,<2.14.0","fixed":["2.14.0"],"affected":">= 2.0.0, < 2.14.0","ecosystem":"pypi"}],"ecosystems":["pypi"],"tags":["cwe-407","fastapi","package","pydantic","python","web-api"],"not_affected_if":["no-multihost-url-fields","trusted-input-only"],"how_to_check":"- Check the installed version with `pip show pydantic` or `python -c \"import pydantic; print(pydantic.VERSION)\"`.\n- Search the code for `PostgresDsn`, `MongoDsn`, `NatsDsn` or custom types built on `MultiHostUrl` that validate untrusted input.","fix":"- `pydantic` `2.14.0` or later (fixed in 2.14.0; affected from 2.0.0 up to but not including 2.14.0).","status":"open","published_at":"2026-10-29T17:06:46+00:00","first_seen_at":"2026-11-09T22:53:21+00:00","triaged_at":"2026-11-09T22:54:53+00:00","updated_at":"2026-11-09T22:54:53+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T22:54:53Z","model":"claude-haiku-5-5","output":{"fix":"- `pydantic` `2.14.0` or later (fixed in 2.14.0; affected from 2.0.0 up to but not including 2.14.0).","reach":"network","title":"Pydantic: crafted multi-host URLs exhaust memory and CPU during validation","impact":"low","summary":"Pydantic's multi-host URL validation parses the whole URL prefix once per host, so a long scheme with many hosts makes time and memory grow quadratically. An attacker who can submit a roughly 100 KB string to a PostgresDsn, MongoDsn or NatsDsn field can exhaust a server's memory and stall it, a denial of service.","audience":"developers","category":"package","nicknames":[],"who_cares":["pydantic","python","fastapi","web-api"],"confidence":0.8,"fix_status":"available","needs_auth":"unknown","worst_case":"Server memory and CPU exhausted through a single crafted request","analyst_note":"","how_to_check":"- Check the installed version with `pip show pydantic` or `python -c \"import pydantic; print(pydantic.VERSION)\"`.\n- Search the code for `PostgresDsn`, `MongoDsn`, `NatsDsn` or custom types built on `MultiHostUrl` that validate untrusted input.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["trusted-input-only","no-multihost-url-fields"],"who_is_affected":"Applications that validate untrusted strings into multi-host URL fields such as PostgresDsn, MongoDsn, NatsDsn, or custom types built on MultiHostUrl are exposed. Code that only validates trusted configuration, or never uses multi-host URL types, is not affected in practice.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"no","severity_if_unknown":"unknown"},"cost_usd":0.000851,"input_tokens":5360,"output_tokens":630,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-407':22B '/docs/validation/latest/api/pydantic/networks/#pydantic.networks.mongodsn),':275C '/docs/validation/latest/api/pydantic/networks/#pydantic.networks.natsdsn),':279C '/docs/validation/latest/api/pydantic/networks/#pydantic.networks.postgresdsn))':111C '/docs/validation/latest/api/pydantic/networks/#pydantic.networks.postgresdsn),':271C '/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/url.rs#l537)':139C '/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/url.rs#l644-l649)):':171C '/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/url.rs))':148C '/pydantic/pydantic/commit/b4028f9d87e34750e53be0ba011d70f3c0fcc94e.':246C '/src/validators/url.rs':145C '100':65B '8mcp':14A,19A '955g':12A,17A 'affect':256C 'allow':220C 'api':29B 'applic':258C 'arbitrarili':200C 'around':125C 'attack':59B 'built':283C 'check':228C 'collect':193C 'commit':243C 'core':144C 'custom':281C 'cwe':21B 'denial':85B,248C 'detail':132C 'e.g':107C,222C 'everi':162C 'exhaust':76B 'extra':174C 'fastapi':23B 'field':74B,106C,267C 'fix':241C 'format':184C 'function':140C 'ghsa':11A,16A 'ghsa-955g-x97g-8mcp':10A,15A 'gigabyt':127C 'github.com':138C,147C,170C,245C 'github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/url.rs#l537)':137C 'github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/url.rs#l644-l649)):':169C 'github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/url.rs))':146C 'github.com/pydantic/pydantic/commit/b4028f9d87e34750e53be0ba011d70f3c0fcc94e.':244C 'grow':56B,214C 'host':7A,34B,44B,51B,99C,104C,151C,177C,180C,186C,209C,265C 'hundr':122C 'impact':247C 'includ':158C 'input':118C,191C,218C,255C 'issu':237C,239C 'iter':178C 'kb':66B 'kilobyt':123C 'l644':167C 'l644-l649':166C 'l649':168C 'let':173C,181C 'long':47B,95C,201C 'make':52B 'mani':50B,98C 'map':179C 'memori':4A,55B,80B,116C,129C,213C 'mongodsn':71B,272C 'multi':6A,33B,103C,264C 'multi-host':5A,32B,102C,263C 'multihost':135C 'multihosturl':285C 'natsdsn':73B,276C 'one':163C 'packag':24B 'pars':37B,134C,149C,187C,204C,230C 'per':43B,208C 'postgr':223C 'postgresdsn':70B,108C,225C,268C 'prefix':41B,157C,185C 'prepend':153C 'prevent':235C 'pydant':25B,30B,143C 'pydantic':20A 'pydantic-cor':142C 'pydantic.dev':110C,270C,274C,278C 'pydantic.dev/docs/validation/latest/api/pydantic/networks/#pydantic.networks.mongodsn),':273C 'pydantic.dev/docs/validation/latest/api/pydantic/networks/#pydantic.networks.natsdsn),':277C 'pydantic.dev/docs/validation/latest/api/pydantic/networks/#pydantic.networks.postgresdsn))':109C 'pydantic.dev/docs/validation/latest/api/pydantic/networks/#pydantic.networks.postgresdsn),':269C 'python':26B 'quadrat':1A,57B,113C,215C 'reconstruct':182C,189C 'rough':64B 'rust':172C 'scheme':48B,96C,160C,197C,221C 'separ':152C 'server':78B 'servic':87B,250C 'size':219C 'stall':82B 'store':206C 'strict':192C 'string':67B,261C 'submit':62B 'summari':88C 'take':112C,124C 'time':2A,53B,114C,211C 'type':282C 'untrust':91C,254C,260C 'url':8A,35B,40B,92C,105C,136C,156C,175C,183C,188C,190C,266C 'valid':9A,36B,89C,131C,252C,259C 'valresult':194C 'vec':176C 'web':28B 'web-api':27B 'whole':39B,155C 'x97g':13A,18A","nicknames":[],"mitigations":[],"who_affected":"Applications that validate untrusted strings into multi-host URL fields such as PostgresDsn, MongoDsn, NatsDsn, or custom types built on MultiHostUrl are exposed. Code that only validates trusted configuration, or never uses multi-host URL types, is not affected in practice.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["pydantic"],"ptokens":["pydantic"],"labels":["cwe407","fastapi","medium","package","pydantic","pypi","python","webapi"],"campaign_type":null,"search_a":"'8mcp':14A,19A '955g':12A,17A 'ghsa':11A,16A 'ghsa-955g-x97g-8mcp':10A,15A 'host':7A 'memori':4A 'multi':6A 'multi-host':5A 'pydantic':20A 'quadrat':1A 'time':2A 'url':8A 'valid':9A 'x97g':13A,18A","search_b":"'-407':22B '100':65B 'api':29B 'attack':59B 'cwe':21B 'denial':85B 'exhaust':76B 'fastapi':23B 'field':74B 'grow':56B 'host':34B,44B,51B 'kb':66B 'long':47B 'make':52B 'mani':50B 'memori':55B,80B 'mongodsn':71B 'multi':33B 'multi-host':32B 'natsdsn':73B 'packag':24B 'pars':37B 'per':43B 'postgresdsn':70B 'prefix':41B 'pydant':25B,30B 'python':26B 'quadrat':57B 'rough':64B 'scheme':48B 'server':78B 'servic':87B 'stall':82B 'string':67B 'submit':62B 'time':53B 'url':35B,40B 'valid':36B 'web':28B 'web-api':27B 'whole':39B","search_c":"'/docs/validation/latest/api/pydantic/networks/#pydantic.networks.mongodsn),':275C '/docs/validation/latest/api/pydantic/networks/#pydantic.networks.natsdsn),':279C '/docs/validation/latest/api/pydantic/networks/#pydantic.networks.postgresdsn))':111C '/docs/validation/latest/api/pydantic/networks/#pydantic.networks.postgresdsn),':271C '/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/url.rs#l537)':139C '/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/url.rs#l644-l649)):':171C '/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/url.rs))':148C '/pydantic/pydantic/commit/b4028f9d87e34750e53be0ba011d70f3c0fcc94e.':246C '/src/validators/url.rs':145C 'affect':256C 'allow':220C 'applic':258C 'arbitrarili':200C 'around':125C 'built':283C 'check':228C 'collect':193C 'commit':243C 'core':144C 'custom':281C 'denial':248C 'detail':132C 'e.g':107C,222C 'everi':162C 'extra':174C 'field':106C,267C 'fix':241C 'format':184C 'function':140C 'gigabyt':127C 'github.com':138C,147C,170C,245C 'github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/url.rs#l537)':137C 'github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/url.rs#l644-l649)):':169C 'github.com/pydantic/pydantic/blob/v2.13.5/pydantic-core/src/validators/url.rs))':146C 'github.com/pydantic/pydantic/commit/b4028f9d87e34750e53be0ba011d70f3c0fcc94e.':244C 'grow':214C 'host':99C,104C,151C,177C,180C,186C,209C,265C 'hundr':122C 'impact':247C 'includ':158C 'input':118C,191C,218C,255C 'issu':237C,239C 'iter':178C 'kilobyt':123C 'l644':167C 'l644-l649':166C 'l649':168C 'let':173C,181C 'long':95C,201C 'mani':98C 'map':179C 'memori':116C,129C,213C 'mongodsn':272C 'multi':103C,264C 'multi-host':102C,263C 'multihost':135C 'multihosturl':285C 'natsdsn':276C 'one':163C 'pars':134C,149C,187C,204C,230C 'per':208C 'postgr':223C 'postgresdsn':108C,225C,268C 'prefix':157C,185C 'prepend':153C 'prevent':235C 'pydant':143C 'pydantic-cor':142C 'pydantic.dev':110C,270C,274C,278C 'pydantic.dev/docs/validation/latest/api/pydantic/networks/#pydantic.networks.mongodsn),':273C 'pydantic.dev/docs/validation/latest/api/pydantic/networks/#pydantic.networks.natsdsn),':277C 'pydantic.dev/docs/validation/latest/api/pydantic/networks/#pydantic.networks.postgresdsn))':109C 'pydantic.dev/docs/validation/latest/api/pydantic/networks/#pydantic.networks.postgresdsn),':269C 'quadrat':113C,215C 'reconstruct':182C,189C 'rust':172C 'scheme':96C,160C,197C,221C 'separ':152C 'servic':250C 'size':219C 'store':206C 'strict':192C 'string':261C 'summari':88C 'take':112C,124C 'time':114C,211C 'type':282C 'untrust':91C,254C,260C 'url':92C,105C,136C,156C,175C,183C,188C,190C,266C 'valid':89C,131C,252C,259C 'valresult':194C 'vec':176C 'whole':155C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-10-29T17:06:46+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-955g-x97g-8mcp","url":"https://github.com/pydantic/pydantic/security/advisories/GHSA-955g-x97g-8mcp","severity":"medium","published_at":"2026-10-29T17:06:46+00:00","modified_at":"2026-10-29T17:06:46+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":110520,"url":"https://github.com/pydantic/pydantic/security/advisories/GHSA-955g-x97g-8mcp","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"}