Skip to main content

Deno Is Secure by Default and Makes You Grant Permissions Explicitly

In Deno, a script you just downloaded cannot read your files, open a network connection, read environment variables, or spawn a subprocess. Not because a policy forbids it, but because the runtime gives it no way to ask. Deno is deny by default. Access to operating system capabilities exists only when you pass a flag on the command line, which makes the set of privileges a program needs an explicit, reviewable part of how you run it.

Ryan Dahl introduced Deno at JSConf EU in 2018 in a talk titled 10 Things I Regret About Node.js. The security model was near the top of that list. In Node, any code in the process can call fs, net, and child_process pretty much at will, and because npm dependencies are ordinary code, a single compromised package two levels down the tree can read your environment variables and post them somewhere before your first log line prints. Deno 1.0, released in May 2020, was built around the opposite premise: the runtime starts locked down and the developer unlocks one capability at a time.

The flags are deliberately granular. deny net for networking, allow net to enable it, allow read and allow write for the filesystem, allow env for environment variables, allow run for subprocesses, allow ffi for native libraries. Most of them accept a scope. Running deno run --allow-net=api.stripe.com:443 billing.ts grants outbound HTTPS to one host on one port and nothing else. On the filesystem, --allow-read=/data/exports limits reads to a single directory instead of the whole disk. There is also an interactive mode: run a program without flags in a terminal and Deno asks whether to grant each permission as it is requested, which is useful when you are evaluating unfamiliar code.

The ergonomics matter more than the flag list, because most permission systems fail on developer experience and then get bypassed. Deno's defaults force the conversation into the open at the right moment. A pull request that adds a network call also has to touch the run script, and that diff is visible in review. A library that suddenly wants to write files cannot quietly do so inside a worker. When something is genuinely broad, the escape hatch exists: allow all, or its shorthand, grants everything, and there are equivalents for skipping dependency type checking and other checks. Seeing that flag in a deployment script is the signal, not the concealment.

There are limits to be honest about. Deno's permissions are capability based, not a full sandbox, and researchers have published escapes from the JavaScript sandbox layer over the years, so this is defense in depth rather than a proof. Permissions also apply per process and per worker, which means a build tool that needs to read your whole project still ends up with broad read access. For running genuinely untrusted third party code, Deno now offers a stronger answer: Deno Sandbox, announced in February 2026, runs untrusted code inside lightweight Linux microVMs on the Deno Deploy cloud so an escape from the language level sandbox is not the end of the story.

Where this changes day to day work is dependency risk. The current supply chain reality is that your dependency graph is larger than anyone can audit by hand, and npm install runs whatever install scripts it finds. Deno removes install scripts from the equation in the default configuration, since a package with no network and no filesystem access cannot do much damage on first run. Combined with built in TypeScript support and no separate transpile step, the result is a runtime where a fresh checkout is comparatively boring to execute, and boring is a feature.

For a senior developer choosing a runtime, the practical takeaway is that Deno makes privilege a first class artifact of deployment. Configuration files, container definitions, and CI jobs all have to name the capabilities they grant, which turns what is usually an invisible assumption in Node into something you can measure and shrink over time. You can also borrow the discipline without switching runtimes: run Node processes under a restricted user, a read only container filesystem, and a network policy, and audit install scripts in CI. Deno's model is worth understanding either way, because it is the clearest statement in the JavaScript ecosystem of what deny by default actually looks like in practice.

We use cookies for ads and analytics.