On this page · 12 sections
- Why the permission model matters in 2026
- What the permission model is, and what it is not
- The flags you actually use
- Using --permission-audit before you enforce
- The version trap: --allow-net is not in Node 24 LTS
- Checking permissions at runtime
- Where the model still needs help
- India-specific considerations
- A short rollout checklist
- FAQ
- How eCorpIT can help
- References
Summary. Node.js added a process permission model in v20.0.0 (April 2023), and Node.js 25.0.0, released on 15 October 2025, extended it with --allow-net so network access can be denied by default. That matters because 2026 has been a brutal year for the npm registry: attackers pushed malicious versions of axios (over 100 million weekly downloads) on 31 March 2026, of node-ipc (over 10 million weekly downloads) on 14 May 2026, and a wave of typosquatted packages documented by Microsoft on 28 May 2026 that scraped cloud and CI/CD secrets. Most of those payloads did one thing: read local secrets and phone home. With --permission --allow-fs-read and --allow-net scoped tightly, a hijacked dependency cannot quietly open ~/.aws/credentials or reach a command-and-control server. IBM's 2025 Cost of a Data Breach report put the global average breach at USD 4.44 million, down 9% year on year, so narrowing what your runtime is allowed to touch is cheap insurance. This guide covers the flags, the production invocation, the --permission-audit workflow, and the version trap that catches teams on Node 24 LTS.
Why the permission model matters in 2026
The threat has moved from your code to your dependencies. A modern Node.js service pulls in hundreds of transitive packages, and each one runs with the full authority of the process: it can read any file the user can read, open outbound sockets, and spawn child processes. For years that was accepted as the cost of the ecosystem. In 2026 the bill came due.
The pattern in every major incident this year is the same. An attacker compromises a maintainer account or typosquats a popular name, publishes a version with a post-install or import-time payload, and the payload harvests credentials before anyone notices. Palo Alto's Unit 42 has tracked a steady run of these through the year. Microsoft's security team documented typosquatted npm packages in May 2026 that scraped cloud and CI/CD secrets from AWS, Azure, GCP and Kubernetes environments and shipped them to an external server.
| npm incident (2026) | Date | Scale | What the payload targeted |
|---|---|---|---|
| axios maintainer hijack | 31 Mar 2026 | 100M+ weekly downloads | Credential and token exfiltration |
| node-ipc malicious versions | 14 May 2026 | 10M+ weekly downloads | Local developer secrets |
| Typosquat campaign (Microsoft) | 28 May 2026 | Multiple typosquats | AWS, Azure, GCP, Kubernetes secrets |
| Jscrambler packages (Rescana) | Jul 2026 | Coordinated set | Credential theft |
Read that table as an operations problem, not a news feed. Every one of those payloads needed two capabilities to succeed: file system read to find the secret, and network access to send it out. The permission model exists to take those capabilities away unless you grant them.
What the permission model is, and what it is not
The Node.js permission model is a process-level allow-list. You start the runtime with --permission, and from that point file system, network, child-process, worker and native-addon access are denied unless you explicitly grant them. The Node.js documentation calls it a "seat belt": it stops trusted code from doing things it was never meant to do.
Be honest about the boundary. Node's own security policy states that the runtime trusts any code it is asked to run, and the permission model documentation says plainly that it "does not provide security guarantees in the presence of malicious code." Determined malware with the right primitives can attempt to escape it. So the model is not a sandbox and not a replacement for vetting dependencies. What it does is narrow the blast radius and raise the cost of an attack: a credential-stealer that cannot open a socket is a far smaller problem than one that can.
Rafael Gonzaga, the Node.js Technical Steering Committee member who cut the release, framed the direction in the Node.js 25.0.0 announcement: "This release doubles down on secure-by-default apps and web-standard APIs: the permission model gains --allow-net, Web Storage is enabled by default, and ErrorEvent is now a global." The --allow-net addition is the piece that makes the model useful for a network service, because before it, turning the model on either blocked all networking or required workarounds.
The flags you actually use
Enable the model with --permission (the flag was called --experimental-permission before the feature stopped being experimental). Everything else is a grant. The important ones for a backend service:
| Flag | Controls | Example |
|---|---|---|
--permission |
Turns the model on; denies by default | node --permission app.js |
--allow-fs-read |
File system reads, scoped to paths | --allow-fs-read=/app |
--allow-fs-write |
File system writes, scoped to paths | --allow-fs-write=/app/tmp |
--allow-net |
Network access (added in v25.0.0) | --allow-net |
--allow-child-process |
child_process spawning |
--allow-child-process |
--allow-worker |
Worker threads | --allow-worker |
--allow-addons |
Native C++ addons | --allow-addons |
File system flags take path arguments and glob patterns, so you grant the narrowest tree the service needs rather than the whole disk. A typical production invocation for an HTTP API that reads its code from /app, writes only to a temp directory, and talks to the network looks like this:
node \
--permission \
--allow-fs-read=/app \
--allow-fs-write=/app/var \
--allow-net \
server.js
Anything the process was not granted throws ERR_ACCESS_DENIED at the point of use. If a compromised dependency tries to read /home/app/.ssh/id_ed25519, the read fails because that path is outside the --allow-fs-read grant. That is the whole point: the service keeps working, the exfiltration does not.
Note what is missing from that command. There is no --allow-child-process and no --allow-addons. Most web services never legitimately spawn a shell or load a native addon at runtime, so leaving those denied removes two of the most common escalation paths in one stroke. If your service genuinely needs to shell out, grant it and treat that grant as a code smell worth reviewing.
Using --permission-audit before you enforce
The reason teams avoid least-privilege flags is fear of breaking production. The permission model answers that with an audit mode. The --permission-audit flag, added in the Node 25 line, runs every permission check but, instead of throwing ERR_ACCESS_DENIED, emits a warning for each violation. You learn exactly what your application touches without denying anything.
The rollout that works in practice is three steps. First, run your full test suite and a soak period in staging under audit:
node --permission --permission-audit server.js
Second, collect the warnings. Each one names a scope and a reference, for example a file path the app read or a network call it made. That list is the real permission manifest of your service, derived from behaviour rather than guesswork. Third, translate the manifest into explicit --allow-* grants, remove --permission-audit, and redeploy in enforcing mode. From then on, any new capability a dependency tries to use shows up as a failed request in a controlled place instead of a silent secret leak.
This is also the cleanest way to catch a supply-chain change after the fact. If a routine dependency bump suddenly makes your service attempt an outbound connection it never made before, audit mode surfaces it in staging rather than production.
The version trap: --allow-net is not in Node 24 LTS
Here is the detail that catches teams. --allow-net landed in Node.js 25.0.0 as a semver-major change. It was not backported. Node 25 is an odd-numbered "Current" release with a short support window, and most production fleets run on an even-numbered Long Term Support line. As of July 2026 the latest LTS is the Node 24 line (24.18.0), and Node 24 does not have --allow-net. Node 26 is the current release (26.5.0) and does include it, and Node 26 is the line expected to become the next Active LTS under Node's even-numbered release schedule.
| Node.js line | --allow-net available |
Role in production (Jul 2026) |
|---|---|---|
| Node 22 | No | Maintenance LTS |
| Node 24 | No | Active LTS, latest 24.18.0 |
| Node 25 | Yes (added 25.0.0) | Current, short-lived, not LTS |
| Node 26 | Yes | Current 26.5.0, next Active LTS line |
The practical read for a CTO: if network-scoped permissions are part of your hardening plan, your target is Node 26 LTS, and the window to test it is now, on the current 26.x release, before it becomes the default LTS line. Running on Node 24 today, you can still use --permission with file system, child-process and worker grants; you just cannot scope the network yet, so keep egress control at the container or VPC layer until you move. If you are still planning that jump, our Bun vs Node.js backend runtime decision guide covers the runtime trade-offs, and the Node.js security release patch-response playbook covers how to keep any line patched.
Checking permissions at runtime
The model also exposes a runtime API, process.permission, so code can ask what it is allowed to do before it tries. This is useful for graceful degradation: a service can check for a capability and log a clear startup error rather than failing deep inside a request.
if (!process.permission.has('fs.read', '/app/config')) {
throw new Error('Missing fs.read grant for /app/config; check startup flags');
}
// Narrow a worker's own authority further at runtime
process.permission.has('net'); // false when started without --allow-net
Combined with startup checks, this turns permission configuration into something you can assert in tests. A small integration test that boots the server under --permission with the intended grants and confirms process.permission.has(...) returns the expected values will catch a misconfigured deployment before it ships.
Where the model still needs help
Least privilege at the process level is one layer, not the whole answer. Three limits are worth stating clearly.
First, --allow-net is coarse. In its current form it is closer to an on/off switch for networking than a per-host allow-list, and Node's 2026 security releases have been tightening edge cases, including applying network permission checks to Unix domain socket connections. Until fine-grained host scoping is dependable, keep an egress firewall or service-mesh policy in front of the service so a granted network permission still cannot reach an arbitrary destination.
Second, the model does not vet packages. It contains a bad package's actions; it does not stop you installing one. Signature verification, provenance and lockfile discipline still matter. Trusted publishing with OIDC is the complementary control here, covered in our npm OIDC trusted publishing hardening guide.
Third, the model protects the runtime, not the build. Post-install scripts during npm install run in your CI environment, often with more secrets in scope than production. Harden the pipeline separately, and treat the permission model as the last line inside the running container, part of the wider Interop 2026 web platform guide baseline for modern backends.
India-specific considerations
For teams building under India's Digital Personal Data Protection Act 2023 (DPDP), the permission model maps neatly onto a compliance argument. DPDP expects data fiduciaries to apply reasonable security safeguards and to limit access to personal data. A Node.js service that runs under --permission with a --allow-fs-read grant scoped to its own application tree can show, concretely, that the runtime cannot read arbitrary user data on the host. That is a stronger control statement than a policy document, and it is the kind of technical safeguard auditors respond to.
The same reasoning applies to lenders and fintechs under RBI expectations and to any organisation that processes health or payment data. Scoping file and network access does not make a service DPDP compliant on its own, but it is a low-cost, demonstrable safeguard that fits the least-privilege principle regulators keep returning to. Design the grants once, assert them in tests, and they hold across every deploy.
A short rollout checklist
Turning this into a plan rather than a demo takes five moves. Confirm your target Node line and whether it has --allow-net. Run the service under --permission --permission-audit in staging through a full test cycle. Convert the audit warnings into explicit --allow-fs-read, --allow-fs-write and, where available, --allow-net grants. Add a startup assertion using process.permission.has(...) so a bad config fails loudly. Keep container-level egress and CI hardening in place, because the runtime model is one layer of several. Done in that order, the change is boring, which is exactly what a security change should be.
FAQ
How eCorpIT can help
eCorpIT is a Gurugram-based engineering organisation, ISO 27001:2022 certified and assessed at CMMI Level 5, and we harden Node.js services for production as part of our backend and platform work. We map your service's real file, network and process needs under --permission-audit, convert them into scoped grants, add startup assertions, and layer them with CI and egress controls so the change is safe to ship. If a dependency incident has you reviewing your runtime posture, our software supply chain security service and secure development and AppSec service cover the full picture. Talk to our senior engineering team at /contact-us/.
References
- Node.js 25.0.0 release announcement — Node.js, 15 October 2025 (adds
--allow-net, V8 14.1, Web Storage by default).
- Permissions — Node.js v26.5.0 documentation — permission model, flags and runtime API.
- Node.js March 2026 security releases — network permission checks for Unix domain sockets.
- Axios npm package compromised — Trend Micro, March 2026.
- Malicious node-ipc versions published to npm — StepSecurity, May 2026.
- Typosquatted npm packages used to steal cloud and CI/CD secrets — Microsoft Security Blog, 28 May 2026.
- Monitoring npm supply chain attacks — Palo Alto Networks Unit 42, updated July 2026.
- Jscrambler npm packages compromised — Rescana, July 2026.
- Cost of a Data Breach 2025 — IBM, global average USD 4.44 million.
- Node.js 20 new permission model — InfoQ, April 2023.
- Node.js Security Policy (SECURITY.md) — the runtime trusts code it is asked to run.
_Last updated: 29 July 2026._