Using Deno’s Permission Model to Safely Access the File System
Learn how Deno’s default sandbox blocks file access and how to use explicit, path‑specific permission flags to safely read and write files in scripts and compiled binaries.
01 Dec 2025, 09:56 UTC

The problem: Deno blocks file access by default
When you try to read or write a file in a Deno script, the runtime throws a PermissionDenied error unless you explicitly grant permission. This default sandbox protects your machine from accidental or malicious file system access, but it can feel like a hurdle when you just want to load a configuration file or write a log.
Thesis: Explicit, path‑specific flags let you grant the minimum needed access
Deno’s permission model is opt‑in. By adding flags such as --allow-read and --allow-write you tell the runtime exactly which directories a script may touch. The flags work with all Deno APIs, including the standard library, and are honoured when you produce a standalone executable with deno compile. This approach lets you start with zero privileges and elevate only when necessary.
Granting read and write access
The most basic form is a blanket grant:
deno run --allow-read --allow-write script.ts
This allows the script to read or write anywhere the user can, which reduces the security benefit. A safer alternative limits the grant to a specific directory:
deno run --allow-read=/home/user/project/config --allow-write=/home/user/project/logs script.ts
Here the script can only read files under /home/user/project/config and write under /home/user/project/logs. Any attempt to access a path outside those trees results in a PermissionDenied error.
Runtime prompts and CI considerations
If you omit the flags, Deno will show an interactive prompt the first time the script tries to access the file system:
⚠️ Deno requests read access to "/home/user/project/config/app.json". Grant? [y/n]
During development this can be useful for exploring what a script actually needs. In automated environments (CI/CD pipelines) the prompt will hang the job unless you pre‑grant the needed flags or set the environment variable DENO_INSTALL_ROOT to suppress prompts.
Worked example: reading a JSON configuration
Suppose you have a project layout:
project/
├─ src/
│ └─ main.ts
└─ config/
└─ app.json
The script src/main.ts needs to load ../config/app.json at start‑up. Instead of giving the script read access to the whole filesystem, you run:
deno run --allow-read=/full/path/to/project/config src/main.ts
Inside main.ts you can use the standard library:
import { readTextFile } from "https://deno.land/std@0.224.0/fs/read_text_file.ts";
const configPath = new URL("../config/app.json", import.meta.href);
const raw = await readTextFile(configPath);
const config = JSON.parse(raw);
console.log("Loaded config:", config);
If you change the flag to --allow-read=/full/path/to/project (a broader grant) the script will still work, but you have increased the attack surface. If you accidentally point the flag at a sibling directory that does not contain app.json, the read will fail with a permission error, confirming the limit is enforced.
Trade‑off and limitation
The main trade‑off is usability versus security. Blanket flags are quick to type but defeat the purpose of the sandbox. Path‑specific flags require you to know the exact directories your script needs, which can add a small amount of upfront work. Additionally, the permission system does not protect against side‑channel attacks or malicious code that runs within the granted permissions—it only gates access to the file system.
Actionable closing
- Start every new Deno script with no permission flags.
- Run the script and note any
PermissionDeniederrors; each error tells you a directory you need to allow. - Add the minimal
--allow-reador--allow-writeflags with the narrowest possible paths. - Check the behaviour in CI by pre‑granting those same flags; avoid relying on interactive prompts.
- When you produce a binary with
deno compile, carry the same flags forward so the executable inherits the same sandbox.
By treating permissions as a first‑class configuration detail, you keep the convenience of Deno’s standard library while preserving the security guarantees that make the runtime attractive for production tooling.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.