Module A-17·11 min read

Runtime supply chain defense, --allow-fs-read capability delegation, and isolating execution contexts against compromised dependencies.

JJS
Written by Jatin Jain Saraf · Senior Software Engineer

Module 16 — Zero-Trust Runtime Architecture & The Node.js Permission Model

What this module covers: The Node.js ecosystem has a supply chain problem. A single compromised transitive dependency can exfiltrate environment variables, read SSH keys, or establish outbound connections to attacker infrastructure — and your application has no defense. npm audit finds known vulnerabilities. It does not stop unknown ones. The Node.js Permission Model (v20+) provides runtime defense: a process cannot read/write files it hasn't been granted access to, spawn subprocesses, create worker threads, or load native addons unless explicitly permitted. It does not restrict network access — that's a deliberate scope limit you need to design around, not an oversight. This module covers the Permission Model's actual architecture, capability delegation patterns, and how to structure an ingestion pipeline so that a compromised analytics dependency cannot reach your database credentials on disk.


The Supply Chain Attack Surface

A typical blockchain indexer has:

bash

847 packages. You audited and trust maybe 20 of them directly. The other 827 are transitive dependencies of your dependencies. Each one can:

  • Read /etc/passwd, ~/.ssh/id_rsa, .env
  • Open TCP connections to attacker.com:443
  • Execute shell commands
  • Write to the filesystem

npm audit only catches packages with known CVEs. A new supply chain attack (a freshly compromised package, a typosquatting attack, a malicious update to a legitimate package) is invisible to npm audit until it is reported.

The Permission Model closes this gap by restricting what the Node.js process can do at runtime, regardless of what any code tries to do.


The Node.js Permission Model (v20+)

The Permission Model is activated via command-line flags. Without a flag, the capability is denied.

bash

Everything not explicitly granted is denied by default once --permission is active — including child_process, worker_threads, and native addons, since no --allow-child-process/--allow-worker/--allow-addons flags were passed above. Network access is the one resource the model has no concept of at all: there's no flag that would restrict it, granted or not.

Any code (yours or a dependency's) that tries to access a resource outside these permissions gets a runtime error:

text

Available Permission Flags

bash

There is no --allow-net flag. Network sockets, fetch, http/https requests — none of it is gated by the Permission Model, in any Node.js version. If you need to restrict which hosts a process can reach, that has to happen outside the runtime (an egress firewall rule, a network policy in Kubernetes, an explicit allowlist in your own HTTP client wrapper) — not through this flag set.

Using --permission without any --allow-* flags enables the model with no permissions granted for the resources it does cover (fs, child_process, worker, wasi, addons) — a deny-all sandbox for those five things specifically, with network access left exactly as open as it would be without the flag at all. (--permission is the stable flag name since Node.js v23.5.0; older Node versions used --experimental-permission.)


Capability Delegation: Layered Trust

Sign in to keep reading

The rest of this module is free — sign in with Google to unlock it and track your progress.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.