5.6 Permissions
The next step in running code is to build permissions. Permissions are one of the differentiators of Deno runtime. Deno offers a unique sandboxed environment to securely run programs. The sandboxing is offered through permissions.
let permissions = Permissions::from_options(&flags);
Permissions help Deno to offer a good and unique sandboxed environment. There are a lot of possible permissions and permission types in Deno. Examples of common permissions are to write, read, env, net, etc.
There are a lot of permissions supported by Deno. The more permissions it has, the more granular sandboxing gets. Here is the full list of all the permissions:
- Read
- Write
- Net
- Env
- Run
- Plugin
- Hrtime
That's a total of seven permission types at the time of writing.

All the above permission types are boolean i.e. they can have true or false value. In other words, it'd mean if permission is granted or not.
For some of the permissions types, a list is also maintained for more granular permission. This makes it a more strict sandboxing. Here are the permission types for which additional lists are maintained:
- Net allow list
- Read allow list
- Write allow list
There are three possible states for each of the permission. These states can be queried either from internal Deno code or from user-level TS/JS code.
The three states are:
- Prompt
- Granted
- Denied
Here is how any permission moves between the three possible states:

- If --allow-YYY is specified
- The permission is granted
- Else
- Prompt for permission
- If prompt resulted in G (or grant)
- The permission is granted
- Else
- The permission is denied
The from_options function is quite simple. It goes through the parsed flags and builds a permission object for each of the supported permissions. Some of the permissions are just true or false. While others could also have a list.
Here is a snippet of code for building permission with an optional list:
pub fn from_options(opts: &PermissionsOptions) -> Self {
fn state_from_flag_bool(flag: bool) -> PermissionState {
if flag {
PermissionState::Granted
} else {
PermissionState::Prompt
}
}
Self {
read: UnaryPermission::<PathBuf> {
global_state: state_from_flag_bool(opts.allow_read),
granted_list: resolve_fs_allowlist(&opts.read_allowlist),
..Default::default()
},
write: UnaryPermission::<PathBuf> {
global_state: state_from_flag_bool(opts.allow_write),
granted_list: resolve_fs_allowlist(&opts.write_allowlist),
..Default::default()
},
net: UnaryPermission::<String> {
global_state: state_from_flag_bool(opts.allow_net),
granted_list: opts.net_allowlist.iter().cloned().collect(),
..Default::default()
},
env: state_from_flag_bool(opts.allow_env),
run: state_from_flag_bool(opts.allow_run),
plugin: state_from_flag_bool(opts.allow_plugin),
hrtime: state_from_flag_bool(opts.allow_hrtime),
}
}
For read, write and net, there are two levels of permission:
- global_state
- permission is specified or not
- this is true or false
- granted_list
- go through the allow list if present
- build granted_list
For others like env, run, plugin, and hrtime, there is only boolean permission i.e. permission is specified or not.
Here is the code which converts any permission to boolean:
fn state_from_flag_bool(flag: bool) -> PermissionState {
if flag {
PermissionState::Granted
} else {
PermissionState::Prompt
}
}
If --allow-YYY is not there, permission state is granted, otherwise prompt.
Permissions are generally granted at the start of the main program unless Deno prompts at runtime. Once permissions are built, they get queried all the time.
All the permissions usually get built at startup. For a production system, it'd be impractical to prompt for permissions at runtime. Although Deno supports prompting for permissions, it's unlikely that it'd ever get used for production code.
The code to query simple boolean permission is very simple as it simply returns the permission state. Here are some examples:
pub fn query_env(&self) -> PermissionState {
self.env
}
pub fn query_run(&self) -> PermissionState {
self.run
}
pub fn query_plugin(&self) -> PermissionState {
self.plugin
}
pub fn query_hrtime(&self) -> PermissionState {
self.hrtime
}
The code to query permission with an optional list is a bit tricky. The input to that function differs for the type of permission:
- For read permission, input is path
- For write permission, input is path
- For net permission, input is URL
Here is the code for querying read permission:
pub fn query_read(&self, path: &Option<&Path>) -> PermissionState {
let path = path.map(|p| resolve_from_cwd(p).unwrap());
if self.read.global_state == PermissionState::Denied
&& match path.as_ref() {
None => true,
Some(path) => check_path_blocklist(path, &self.read.denied_list),
}
{
return PermissionState::Denied;
}
if self.read.global_state == PermissionState::Granted
|| match path.as_ref() {
None => false,
Some(path) => check_path_allowlist(path, &self.read.granted_list),
}
{
return PermissionState::Granted;
}
PermissionState::Prompt
}
Here are the steps required to query permission:
- Checks for the global_state first
- If the global state is denied,
- Check for blocklist and return denied if present in the block list
- If the global state is granted,
- If there is an optional list,
- Check path in the optional list
- If present,
- return granted
- Else,
- return denied
- Else
- just use the global_state
- If the global state is not denied
- return prompt so that Deno could possibly prompt at runtime.
It is also possible to request and revoke permissions at runtime. While this is impractical, it is supported by Deno. Let's quickly go over these two with a simple example of env permission:
pub fn request_env(&mut self) -> PermissionState {
if self.env == PermissionState::Prompt {
if permission_prompt("Deno requests access to environment variables") {
self.env = PermissionState::Granted;
} else {
self.env = PermissionState::Denied;
}
}
self.env
}
pub fn revoke_env(&mut self) -> PermissionState {
if self.env == PermissionState::Granted {
self.env = PermissionState::Prompt;
}
self.env
}
Revoke is straightforward. It simply moves permission from granted to prompt.
Request for permission moves permission from prompt to denied if prompt was responded with D (or denied). If prompt was responded with G (or granted), permission moves from prompt to granted.