#![allow(unused)]
use deno_fs::FsError;
use deno_permissions::PermissionCheckError;
use parking_lot::RwLock;
use rustc_hash::FxHashSet;
use std::{
borrow::Cow,
path::{Path, PathBuf},
sync::Arc,
};
pub use deno_permissions::PermissionDeniedError;
fn to_io_err(err: PermissionDeniedError) -> std::io::Error {
std::io::Error::new(std::io::ErrorKind::PermissionDenied, err.to_string())
}
pub fn oops<T>(msg: impl std::fmt::Display) -> Result<T, PermissionDeniedError> {
use deno_permissions::PermissionDeniedError;
Err(PermissionDeniedError {
access: msg.to_string(),
name: "oops",
custom_message: None,
state: deno_permissions::PermissionState::Denied,
})
}
#[derive(Debug, Clone, Copy, Default)]
pub struct DefaultWebPermissions;
impl WebPermissions for DefaultWebPermissions {
fn allow_hrtime(&self) -> bool {
true
}
fn check_url(
&self,
url: &deno_core::url::Url,
api_name: &str,
) -> Result<(), PermissionDeniedError> {
Ok(())
}
fn check_open<'a>(
&self,
resolved: bool,
read: bool,
write: bool,
path: Cow<'a, Path>,
api_name: &str,
) -> Option<std::borrow::Cow<'a, Path>> {
Some(path)
}
fn check_read<'a>(
&self,
p: Cow<'a, Path>,
api_name: Option<&str>,
) -> Result<Cow<'a, Path>, PermissionDeniedError> {
Ok(p)
}
fn check_read_all(&self, api_name: Option<&str>) -> Result<(), PermissionDeniedError> {
Ok(())
}
fn check_read_blind(
&self,
p: &Path,
display: &str,
api_name: &str,
) -> Result<(), PermissionDeniedError> {
Ok(())
}
fn check_write<'a>(
&self,
p: Cow<'a, Path>,
api_name: Option<&str>,
) -> Result<Cow<'a, Path>, PermissionDeniedError> {
Ok(p)
}
fn check_write_all(&self, api_name: &str) -> Result<(), PermissionDeniedError> {
Ok(())
}
fn check_write_blind(
&self,
p: &Path,
display: &str,
api_name: &str,
) -> Result<(), PermissionDeniedError> {
Ok(())
}
fn check_write_partial(
&self,
path: &str,
api_name: &str,
) -> Result<std::path::PathBuf, PermissionDeniedError> {
Ok(PathBuf::from(path))
}
fn check_host(
&self,
host: &str,
port: Option<u16>,
api_name: &str,
) -> Result<(), PermissionDeniedError> {
Ok(())
}
fn check_sys(
&self,
kind: SystemsPermissionKind,
api_name: &str,
) -> Result<(), PermissionDeniedError> {
Ok(())
}
fn check_env(&self, var: &str) -> Result<(), PermissionDeniedError> {
Ok(())
}
fn check_exec(&self) -> Result<(), PermissionDeniedError> {
Ok(())
}
}
#[derive(Clone, Default, Debug)]
struct AllowlistWebPermissionsSet {
pub hrtime: bool,
pub exec: bool,
pub read_all: bool,
pub write_all: bool,
pub url: FxHashSet<String>,
pub openr_paths: FxHashSet<String>,
pub openw_paths: FxHashSet<String>,
pub envs: FxHashSet<String>,
pub sys: FxHashSet<SystemsPermissionKind>,
pub read_paths: FxHashSet<String>,
pub write_paths: FxHashSet<String>,
pub hosts: FxHashSet<String>,
}
#[derive(Clone, Default, Debug)]
pub struct AllowlistWebPermissions(Arc<RwLock<AllowlistWebPermissionsSet>>);
impl AllowlistWebPermissions {
fn borrow(&self) -> parking_lot::RwLockReadGuard<'_, AllowlistWebPermissionsSet> {
self.0.read()
}
}
impl WebPermissions for AllowlistWebPermissions {
fn allow_hrtime(&self) -> bool {
self.borrow().hrtime
}
fn check_host(
&self,
host: &str,
port: Option<u16>,
api_name: &str,
) -> Result<(), PermissionDeniedError> {
if self.borrow().hosts.contains(host) { Ok(()) } else { oops(host)? }
}
fn check_url(
&self,
url: &deno_core::url::Url,
api_name: &str,
) -> Result<(), PermissionDeniedError> {
if self.borrow().url.contains(url.as_str()) { Ok(()) } else { oops(url)? }
}
fn check_read<'a>(
&self,
p: Cow<'a, Path>,
api_name: Option<&str>,
) -> Result<Cow<'a, Path>, PermissionDeniedError> {
let inst = self.borrow();
if inst.read_all && p.to_str().is_some_and(|s| inst.read_paths.contains(s)) {
Ok(p)
} else {
oops(p.display())?
}
}
fn check_write<'a>(
&self,
p: Cow<'a, Path>,
api_name: Option<&str>,
) -> Result<Cow<'a, Path>, PermissionDeniedError> {
let inst = self.borrow();
if inst.write_all && p.to_str().is_some_and(|s| inst.write_paths.contains(s)) {
Ok(p)
} else {
oops(p.display())?
}
}
fn check_open<'a>(
&self,
resolved: bool,
read: bool,
write: bool,
path: Cow<'a, Path>,
api_name: &str,
) -> Option<std::borrow::Cow<'a, Path>> {
let path_str = path.to_str()?;
if read && !self.borrow().openr_paths.contains(path_str) {
return None;
}
if write && !self.borrow().openw_paths.contains(path_str) {
return None;
}
Some(path)
}
fn check_read_all(&self, api_name: Option<&str>) -> Result<(), PermissionDeniedError> {
if self.borrow().read_all { Ok(()) } else { oops("read_all")? }
}
fn check_read_blind(
&self,
p: &Path,
display: &str,
api_name: &str,
) -> Result<(), PermissionDeniedError> {
if !self.borrow().read_all {
return oops("read_all")?;
}
self.check_read(Cow::Borrowed(p), Some(api_name))?;
Ok(())
}
fn check_write_all(&self, api_name: &str) -> Result<(), PermissionDeniedError> {
if self.borrow().write_all { Ok(()) } else { oops("write_all")? }
}
fn check_write_blind(
&self,
path: &Path,
display: &str,
api_name: &str,
) -> Result<(), PermissionDeniedError> {
self.check_write(Cow::Borrowed(path), Some(api_name))?;
Ok(())
}
fn check_write_partial(
&self,
path: &str,
api_name: &str,
) -> Result<std::path::PathBuf, PermissionDeniedError> {
let p = self.check_write(Cow::Borrowed(Path::new(path)), Some(api_name))?;
Ok(p.into_owned())
}
fn check_sys(
&self,
kind: SystemsPermissionKind,
api_name: &str,
) -> Result<(), PermissionDeniedError> {
if self.borrow().sys.contains(&kind) { Ok(()) } else { oops(kind.as_str())? }
}
fn check_env(&self, var: &str) -> Result<(), PermissionDeniedError> {
if self.borrow().envs.contains(var) { Ok(()) } else { oops(var)? }
}
fn check_exec(&self) -> Result<(), PermissionDeniedError> {
if self.borrow().exec { Ok(()) } else { oops("ffi")? }
}
}
pub trait WebPermissions: std::fmt::Debug + Send + Sync {
fn allow_hrtime(&self) -> bool;
fn check_url(
&self,
url: &deno_core::url::Url,
api_name: &str,
) -> Result<(), PermissionDeniedError>;
fn check_open<'a>(
&self,
resolved: bool,
read: bool,
write: bool,
path: Cow<'a, Path>,
api_name: &str,
) -> Option<std::borrow::Cow<'a, Path>>;
fn check_read<'a>(
&self,
p: Cow<'a, Path>,
api_name: Option<&str>,
) -> Result<Cow<'a, Path>, PermissionDeniedError>;
fn check_read_all(&self, api_name: Option<&str>) -> Result<(), PermissionDeniedError>;
fn check_read_blind(
&self,
p: &Path,
display: &str,
api_name: &str,
) -> Result<(), PermissionDeniedError>;
fn check_write<'a>(
&self,
p: Cow<'a, Path>,
api_name: Option<&str>,
) -> Result<Cow<'a, Path>, PermissionDeniedError>;
fn check_write_all(&self, api_name: &str) -> Result<(), PermissionDeniedError>;
fn check_write_blind(
&self,
p: &Path,
display: &str,
api_name: &str,
) -> Result<(), PermissionDeniedError>;
fn check_write_partial(
&self,
path: &str,
api_name: &str,
) -> Result<std::path::PathBuf, PermissionDeniedError>;
fn check_host(
&self,
host: &str,
port: Option<u16>,
api_name: &str,
) -> Result<(), PermissionDeniedError>;
fn check_sys(
&self,
kind: SystemsPermissionKind,
api_name: &str,
) -> Result<(), PermissionDeniedError>;
fn check_env(&self, var: &str) -> Result<(), PermissionDeniedError>;
fn check_exec(&self) -> Result<(), PermissionDeniedError>;
}
macro_rules! impl_sys_permission_kinds {
($($kind:ident($name:literal)),+ $(,)?) => {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SystemsPermissionKind {
$(
#[doc = stringify!($kind)]
$kind,
)+
Other(String),
}
impl SystemsPermissionKind {
#[must_use]
pub fn new(s: &str) -> Self {
match s {
$( $name => Self::$kind, )+
_ => Self::Other(s.to_string()),
}
}
#[must_use]
pub fn as_str(&self) -> &str {
match self {
$( Self::$kind => $name, )+
Self::Other(s) => &s,
}
}
}
};
}
impl_sys_permission_kinds!(
LoadAvg("loadavg"),
Hostname("hostname"),
OsRelease("osRelease"),
Networkinterfaces("networkInterfaces"),
StatFs("statfs"),
GetPriority("getPriority"),
SystemMemoryInfo("systemMemoryInfo"),
Gid("gid"),
Uid("uid"),
OsUptime("osUptime"),
SetPriority("setPriority"),
UserInfo("userInfo"),
GetEGid("getegid"),
Cpus("cpus"),
HomeDir("homeDir"),
Inspector("inspector"),
);
#[derive(Clone, Debug)]
pub struct PermissionsContainer(pub Arc<dyn WebPermissions>);