All posts

Node.js: randomUUID() repeating in snapshots, and permission model limits

Node.js · 3 min read ·

Most of the attention on the October 2026 Node.js security release goes to its high-severity bugs. Two of the smaller fixes are worth a separate look, because they correct assumptions developers make without thinking: that crypto.randomUUID() gives every process a different value, and that the permission model is a wall. For most applications neither changes anything. For two specific groups of people, it does.

When "random" is the same in every process

CVE-2026-106074, rated medium and fixed on the 26, 24 and 22 lines, is about startup snapshots and single executable applications (SEAs). The entropy caches behind crypto.randomUUID() and randomInt() were saved into user-land snapshots and SEAs, so every process started from the same snapshot or executable repeated the same values.

As general background: a snapshot or an SEA is an application image whose state is captured once and then restored in many processes. That is what makes it fast to start. It also means anything random that existed at capture time is shared by every copy. Here the runtime itself had cached randomness at the wrong moment, and the fix is in the runtime.

captured once, at build snapshot or SEA cached random process 1: value A process 2: value A process 3: value A three processes, one "random" value
Restoring a captured state is the point of a snapshot. Before the fix, it also restored the randomness.

Who is affected: people who build a user-land startup snapshot or ship an SEA and generate IDs, tokens or keys with these functions in it. Who is not: an ordinary node server.js app, which is how most Node applications run.

The lesson outlives the fix, as general practice: never derive IDs, tokens or secrets from values created at build time. Create them after the process starts. If you ship an SEA or a snapshot, a quick check is to log one value at startup, start the executable twice and compare:

// run at startup, start the executable twice, compare the output
console.log('boot id', crypto.randomUUID());

Two different lines means each process has its own randomness. Two identical lines means you are on an unpatched runtime or something is captured too early.

The permission model is one layer, not the wall

The Node.js permission model lets a process restrict its own access to the file system, the network and similar resources. This release fixes two bypasses in it, both rated low: CVE-2026-106073, a bypass through fs.realpathSync on the 26, 24 and 22 lines, and CVE-2026-106071, a file system over-grant through a wildcard radix lookup on 26 only. A third low fix, CVE-2026-106070 on 26 only, stops ZipFile returning attacker-controlled prefix bytes after a failed ZIP header check.

Low means low here, and I would not dress it up. But it is a useful reminder, in the general sense: a restriction inside the runtime is one layer of defence. If you use it to run code you do not trust, the real boundary should sit outside Node, at the operating system: a container, a dedicated unprivileged user, read-only mounts.

unprivileged user account container, read-only mounts Node.js permission model your process
If the innermost restriction has a gap, the layers outside it still hold. That is the reason to have them.

Who is affected: people who rely on the permission model to contain untrusted code. Who is not: apps that never turned it on, and apps that use it as an extra guard on top of OS-level isolation.

Create IDs and secrets after the process starts, never at build time, and keep the OS, not the runtime, as the boundary around untrusted code.

The next step is the same upgrade as for the rest of the release: 26.12.1, 24.22.1 or 22.23.4 for your line. If you ship an SEA or snapshot, run the two-start check after upgrading. Source: nodejs.org, Wednesday, October 21, 2026 Security Releases.

Notes from readers

  1. No notes yet. The first one is the bravest.
Building something like this? I can help. Message me on WhatsApp