{"id":"CVE-2026-109383","kind":"vulnerability","aliases":["CVE-2026-109383","GHSA-2m5v-pch6-74hx"],"related":[],"title":"undici vulnerable to TLS certificate validation bypass via dropped tls options in Agent","description":"### Impact\n\nundici's `Agent` passes its constructor options through a JSON-based deep clone (`JSON.parse(JSON.stringify(...))`) before forwarding them to each per-origin `Pool`. JSON cannot represent functions, so a caller-supplied `tls` option containing a `checkServerIdentity` callback is silently dropped before it reaches the TLS layer. As a result, a TLS peer whose certificate a custom `checkServerIdentity` was written to reject, but which passes Node's default hostname and chain checks, is silently accepted when the request is made through `Agent`. Because `Agent` is the default dispatcher behind `request`, `fetch`, and `setGlobalDispatcher`, this is the path most applications use. `EnvHttpProxyAgent` is also affected on its direct and `NO_PROXY` routes, which delegate to an internal `Agent`.\n\nOnly applications that pass a function-valued `tls` option (such as a custom `checkServerIdentity` for certificate or public-key pinning) are affected. Passing the same options through `connect` is not affected. This is the `Agent` counterpart of the `BalancedPool` certificate validation bypass fixed in CVE-2026-84961.\n\n### Patches\n\nUpgrade to undici `6.29.1`, `7.30.1`, or `8.12.2`. `Agent` now preserves the `tls` option outside the JSON clone, so custom TLS verification callbacks are forwarded to each origin unchanged.\n\n### Workarounds\n\nPass the TLS options through `connect` instead of `tls`. For proxied `EnvHttpProxyAgent` destinations, use `requestTls` / `proxyTls`.","summary":"undici's Agent drops a caller-supplied checkServerIdentity callback from its tls options because it deep-clones options through JSON. A server certificate that the custom check was meant to reject is then accepted when requests go through Agent, the default dispatcher for fetch and request.","severity":"critical","cvss":9.1,"epss":0.00184,"exploited":false,"exploited_at":null,"products":[{"name":"undici","fixed":["6.29.1"],"affected":">= 4.0.0, < 6.29.1","ecosystem":"npm"},{"name":"undici","fixed":["7.30.1"],"affected":">= 7.0.0, < 7.30.1","ecosystem":"npm"},{"name":"undici","fixed":["8.12.2"],"affected":">= 8.0.0, < 8.12.2","ecosystem":"npm"},{"name":"nodejs undici","fixed":["6.29.1"],"affected":">= 4.0.0, < 6.29.1","ecosystem":"vendor"},{"name":"nodejs undici","fixed":["7.30.1"],"affected":">= 7.0.0, < 7.30.1","ecosystem":"vendor"},{"name":"nodejs undici","fixed":["8.12.2"],"affected":">= 8.0.0, < 8.12.2","ecosystem":"vendor"}],"ecosystems":["npm","vendor"],"tags":["certificate-pinning","cwe-295","fetch","node","npm","package","undici"],"not_affected_if":["connect-options-only","default-tls-config","no-custom-checkserveridentity"],"how_to_check":"- Search the codebase for `checkServerIdentity` or other function-valued `tls` options passed to `Agent`, `EnvHttpProxyAgent` or `setGlobalDispatcher`.\n- Run `npm ls undici` and compare the installed version against `6.29.1`, `7.30.1` or `8.12.2`.","fix":"- Upgrade undici to `6.29.1`, `7.30.1` or `8.12.2` on the line you are on.\n- Until upgraded, pass TLS options through `connect` instead of `tls`; for proxied `EnvHttpProxyAgent` destinations use `requestTls` / `proxyTls`.","status":"open","published_at":"2026-10-15T19:17:20+00:00","first_seen_at":"2026-10-27T19:04:55+00:00","triaged_at":"2026-10-27T19:05:51+00:00","updated_at":"2026-10-27T19:33:18+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-10-27T19:05:51Z","model":"claude-haiku-5-5","output":{"fix":"- Upgrade undici to `6.29.1`, `7.30.1` or `8.12.2` on the line you are on.\n- Until upgraded, pass TLS options through `connect` instead of `tls`; for proxied `EnvHttpProxyAgent` destinations use `requestTls` / `proxyTls`.","reach":"network","title":"undici: custom TLS certificate checks silently dropped by Agent","impact":"medium","summary":"undici's Agent drops a caller-supplied checkServerIdentity callback from its tls options because it deep-clones options through JSON. A server certificate that the custom check was meant to reject is then accepted when requests go through Agent, the default dispatcher for fetch and request.","category":"package","nicknames":[],"who_cares":["node","undici","fetch","certificate-pinning","npm"],"confidence":0.8,"needs_auth":"no","worst_case":"Man-in-the-middle connections accepted despite custom certificate pinning checks","analyst_note":"","how_to_check":"- Search the codebase for `checkServerIdentity` or other function-valued `tls` options passed to `Agent`, `EnvHttpProxyAgent` or `setGlobalDispatcher`.\n- Run `npm ls undici` and compare the installed version against `6.29.1`, `7.30.1` or `8.12.2`.","needs_analyst":false,"not_affected_if":["default-tls-config","connect-options-only","no-custom-checkServerIdentity"],"who_is_affected":"Applications that pass a function-valued tls option, such as a custom checkServerIdentity used for certificate or public-key pinning, to undici's Agent or EnvHttpProxyAgent are exposed. Affected routes include requests made through the default global dispatcher used by fetch and request. Applications that use only default TLS checks, or that pass options through connect, are not affected.","user_interaction":"no","needs_special_setup":"yes","severity_if_unknown":"high"},"cost_usd":0.000727,"input_tokens":3900,"output_tokens":673,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-109383':16A,24A '-2026':15A,23A,259C '-295':38B '-84961':260C '2m5v':19A '6.29.1':265C '7.30.1':266C '74hx':21A '8.12.2':268C 'accept':79B,169C 'affect':198C,235C,244C 'agent':13A,46B,84B,95C,176C,178C,211C,248C,269C 'also':197C 'applic':193C,213C 'balancedpool':252C 'base':104C 'behind':183C 'bypass':7A,255C 'callback':53B,132C,283C 'caller':50B,125C 'caller-suppli':49B,124C 'cannot':119C 'certif':5A,35B,68B,149C,228C,253C 'certificate-pin':34B 'chain':165C 'check':72B,166C 'checkserverident':52B,131C,152C,226C 'clone':62B,106C,278C 'connect':241C,296C 'constructor':98C 'contain':129C 'counterpart':249C 'custom':71B,151C,225C,280C 'cve':14A,22A,258C 'cwe':37B 'deep':61B,105C 'deep-clon':60B 'default':86B,162C,181C 'deleg':207C 'destin':303C 'direct':201C 'dispatch':87B,182C 'drop':9A,47B,135C 'envhttpproxyag':195C,302C 'fetch':39B,89B,185C 'fix':256C 'forward':110C,285C 'function':121C,218C 'function-valu':217C 'ghsa':18A 'ghsa-2m5v-pch6-74hx':17A 'go':82B 'hostnam':163C 'impact':92C 'instead':297C 'intern':210C 'json':65B,103C,118C,277C 'json-bas':102C 'json.parse':107C 'json.stringify':108C 'key':232C 'layer':141C 'made':174C 'meant':74B 'node':40B,160C 'nodejs':28A,30A,32A 'npm':41B 'option':11A,57B,63B,99C,128C,221C,239C,274C,294C 'origin':116C,288C 'outsid':275C 'packag':42B 'pass':96C,159C,215C,236C,291C 'patch':261C 'path':191C 'pch6':20A 'peer':147C 'per':115C 'per-origin':114C 'pin':36B,233C 'pool':117C 'preserv':271C 'proxi':204C,301C 'proxytl':306C 'public':231C 'public-key':230C 'reach':138C 'reject':76B,156C 'repres':120C 'request':81B,91B,172C,184C 'requesttl':305C 'result':144C 'rout':205C 'server':67B 'setglobaldispatch':187C 'silent':134C,168C 'suppli':51B,126C 'tls':4A,10A,56B,127C,140C,146C,220C,273C,281C,293C,299C 'unchang':289C 'undici':1A,25A,26A,27A,29A,31A,33A,43B,44B,93C,264C 'upgrad':262C 'use':194C,304C 'valid':6A,254C 'valu':219C 'verif':282C 'via':8A 'vulner':2A 'whose':148C 'workaround':290C 'written':154C","nicknames":[],"mitigations":[],"who_affected":"Applications that pass a function-valued tls option, such as a custom checkServerIdentity used for certificate or public-key pinning, to undici's Agent or EnvHttpProxyAgent are exposed. Affected routes include requests made through the default global dispatcher used by fetch and request. Applications that use only default TLS checks, or that pass options through connect, are not affected.","tier":"B","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-15T19:17:20+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-2m5v-pch6-74hx","url":"https://github.com/advisories/GHSA-2m5v-pch6-74hx","severity":"high","published_at":"2026-10-27T19:02:26+00:00","modified_at":"2026-10-27T19:02:27+00:00","first_seen_at":"2026-10-27T19:04:55+00:00","fetched_at":"2026-10-27T19:04:55+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-109383","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-109383","severity":null,"published_at":"2026-10-15T19:17:20+00:00","modified_at":"2026-10-19T20:45:52+00:00","first_seen_at":"2026-10-27T19:33:18+00:00","fetched_at":"2026-10-27T19:33:18+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}