summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAsher Gomez <ashersaupingomez@gmail.com>2023-08-03 21:19:19 +1000
committerGitHub <noreply@github.com>2023-08-03 13:19:19 +0200
commit6fb7e8d93bb9fd8cdd81130a394ae6061930c4f6 (patch)
tree2ec6dc2be234ef5a42023c1d75f1fc1316d80f06
parentdb287e216dd752bfcb3484cbfd93225e8463c363 (diff)
feat(permissions): add "--deny-*" flags (#19070)
This commit adds new "--deny-*" permission flags. These are complimentary to "--allow-*" flags. These flags can be used to restrict access to certain resources, even if they were granted using "--allow-*" flags or the "--allow-all" ("-A") flag. Eg. specifying "--allow-read --deny-read" will result in a permission error, while "--allow-read --deny-read=/etc" will allow read access to all FS but the "/etc" directory. Runtime permissions APIs ("Deno.permissions") were adjusted as well, mainly by adding, a new "PermissionStatus.partial" field. This field denotes that while permission might be granted to requested resource, it's only partial (ie. a "--deny-*" flag was specified that excludes some of the requested resources). Eg. specifying "--allow-read=foo/ --deny-read=foo/bar" and then querying for permissions like "Deno.permissions.query({ name: "read", path: "foo/" })" will return "PermissionStatus { state: "granted", onchange: null, partial: true }", denoting that some of the subpaths don't have read access. Closes #18804. --------- Co-authored-by: Bartek IwaƄczuk <biwanczuk@gmail.com> Co-authored-by: Nayeem Rahman <nayeemrmn99@gmail.com>
-rw-r--r--cli/args/flags.rs615
-rw-r--r--cli/args/flags_net.rs (renamed from cli/args/flags_allow_net.rs)0
-rw-r--r--cli/args/mod.rs10
-rw-r--r--cli/tests/integration/run_tests.rs70
-rw-r--r--cli/tests/testdata/run/deny_all_permission_args.js8
-rw-r--r--cli/tests/testdata/run/deny_all_permission_args.out8
-rw-r--r--cli/tests/testdata/run/deny_some_permission_args.js22
-rw-r--r--cli/tests/testdata/run/deny_some_permission_args.out22
-rw-r--r--cli/tests/testdata/run/permissions_cache.ts5
-rw-r--r--cli/tsc/dts/lib.deno.ns.d.ts34
-rw-r--r--ext/ffi/call.rs4
-rw-r--r--ext/ffi/callback.rs2
-rw-r--r--ext/ffi/dlfcn.rs2
-rw-r--r--ext/ffi/lib.rs2
-rw-r--r--ext/ffi/repr.rs40
-rw-r--r--ext/fs/lib.rs14
-rw-r--r--ext/fs/ops.rs13
-rw-r--r--runtime/build.rs10
-rw-r--r--runtime/js/10_permissions.js66
-rw-r--r--runtime/ops/permissions.rs33
-rw-r--r--runtime/permissions/mod.rs2279
21 files changed, 1804 insertions, 1455 deletions
diff --git a/cli/args/flags.rs b/cli/args/flags.rs
index 9674c68a6..790a9d83f 100644
--- a/cli/args/flags.rs
+++ b/cli/args/flags.rs
@@ -24,7 +24,7 @@ use std::str::FromStr;
use crate::util::fs::canonicalize_path;
-use super::flags_allow_net;
+use super::flags_net;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct FileFlags {
@@ -364,13 +364,21 @@ pub struct Flags {
pub allow_all: bool,
pub allow_env: Option<Vec<String>>,
+ pub deny_env: Option<Vec<String>>,
pub allow_hrtime: bool,
+ pub deny_hrtime: bool,
pub allow_net: Option<Vec<String>>,
+ pub deny_net: Option<Vec<String>>,
pub allow_ffi: Option<Vec<PathBuf>>,
+ pub deny_ffi: Option<Vec<PathBuf>>,
pub allow_read: Option<Vec<PathBuf>>,
+ pub deny_read: Option<Vec<PathBuf>>,
pub allow_run: Option<Vec<String>>,
+ pub deny_run: Option<Vec<String>>,
pub allow_sys: Option<Vec<String>>,
+ pub deny_sys: Option<Vec<String>>,
pub allow_write: Option<Vec<PathBuf>>,
+ pub deny_write: Option<Vec<PathBuf>>,
pub ca_stores: Option<Vec<String>>,
pub ca_data: Option<CaData>,
pub cache_blocklist: Vec<String>,
@@ -434,6 +442,17 @@ impl Flags {
_ => {}
}
+ match &self.deny_read {
+ Some(read_denylist) if read_denylist.is_empty() => {
+ args.push("--deny-read".to_string());
+ }
+ Some(read_denylist) => {
+ let s = format!("--deny-read={}", join_paths(read_denylist, ","));
+ args.push(s);
+ }
+ _ => {}
+ }
+
match &self.allow_write {
Some(write_allowlist) if write_allowlist.is_empty() => {
args.push("--allow-write".to_string());
@@ -445,6 +464,17 @@ impl Flags {
_ => {}
}
+ match &self.deny_write {
+ Some(write_denylist) if write_denylist.is_empty() => {
+ args.push("--deny-write".to_string());
+ }
+ Some(write_denylist) => {
+ let s = format!("--deny-write={}", join_paths(write_denylist, ","));
+ args.push(s);
+ }
+ _ => {}
+ }
+
match &self.allow_net {
Some(net_allowlist) if net_allowlist.is_empty() => {
args.push("--allow-net".to_string());
@@ -456,6 +486,17 @@ impl Flags {
_ => {}
}
+ match &self.deny_net {
+ Some(net_denylist) if net_denylist.is_empty() => {
+ args.push("--deny-net".to_string());
+ }
+ Some(net_denylist) => {
+ let s = format!("--deny-net={}", net_denylist.join(","));
+ args.push(s);
+ }
+ _ => {}
+ }
+
match &self.unsafely_ignore_certificate_errors {
Some(ic_allowlist) if ic_allowlist.is_empty() => {
args.push("--unsafely-ignore-certificate-errors".to_string());
@@ -481,6 +522,17 @@ impl Flags {
_ => {}
}
+ match &self.deny_env {
+ Some(env_denylist) if env_denylist.is_empty() => {
+ args.push("--deny-env".to_string());
+ }
+ Some(env_denylist) => {
+ let s = format!("--deny-env={}", env_denylist.join(","));
+ args.push(s);
+ }
+ _ => {}
+ }
+
match &self.allow_run {
Some(run_allowlist) if run_allowlist.is_empty() => {
args.push("--allow-run".to_string());
@@ -492,6 +544,17 @@ impl Flags {
_ => {}
}
+ match &self.deny_run {
+ Some(run_denylist) if run_denylist.is_empty() => {
+ args.push("--deny-run".to_string());
+ }
+ Some(run_denylist) => {
+ let s = format!("--deny-run={}", run_denylist.join(","));
+ args.push(s);
+ }
+ _ => {}
+ }
+
match &self.allow_sys {
Some(sys_allowlist) if sys_allowlist.is_empty() => {
args.push("--allow-sys".to_string());
@@ -503,6 +566,17 @@ impl Flags {
_ => {}
}
+ match &self.deny_sys {
+ Some(sys_denylist) if sys_denylist.is_empty() => {
+ args.push("--deny-sys".to_string());
+ }
+ Some(sys_denylist) => {
+ let s = format!("--deny-sys={}", sys_denylist.join(","));
+ args.push(s)
+ }
+ _ => {}
+ }
+
match &self.allow_ffi {
Some(ffi_allowlist) if ffi_allowlist.is_empty() => {
args.push("--allow-ffi".to_string());
@@ -514,10 +588,25 @@ impl Flags {
_ => {}
}
+ match &self.deny_ffi {
+ Some(ffi_denylist) if ffi_denylist.is_empty() => {
+ args.push("--deny-ffi".to_string());
+ }
+ Some(ffi_denylist) => {
+ let s = format!("--deny-ffi={}", join_paths(ffi_denylist, ","));
+ args.push(s);
+ }
+ _ => {}
+ }
+
if self.allow_hrtime {
args.push("--allow-hrtime".to_string());
}
+ if self.deny_hrtime {
+ args.push("--deny-hrtime".to_string());
+ }
+
args
}
@@ -607,26 +696,42 @@ impl Flags {
pub fn has_permission(&self) -> bool {
self.allow_all
|| self.allow_hrtime
+ || self.deny_hrtime
|| self.allow_env.is_some()
+ || self.deny_env.is_some()
|| self.allow_ffi.is_some()
+ || self.deny_ffi.is_some()
|| self.allow_net.is_some()
+ || self.deny_net.is_some()
|| self.allow_read.is_some()
+ || self.deny_read.is_some()
|| self.allow_run.is_some()
+ || self.deny_run.is_some()
|| self.allow_sys.is_some()
+ || self.deny_sys.is_some()
|| self.allow_write.is_some()
+ || self.deny_write.is_some()
}
pub fn has_permission_in_argv(&self) -> bool {
self.argv.iter().any(|arg| {
arg == "--allow-all"
|| arg == "--allow-hrtime"
+ || arg == "--deny-hrtime"
|| arg.starts_with("--allow-env")
+ || arg.starts_with("--deny-env")
|| arg.starts_with("--allow-ffi")
+ || arg.starts_with("--deny-ffi")
|| arg.starts_with("--allow-net")
+ || arg.starts_with("--deny-net")
|| arg.starts_with("--allow-read")
+ || arg.starts_with("--deny-read")
|| arg.starts_with("--allow-run")
+ || arg.starts_with("--deny-run")
|| arg.starts_with("--allow-sys")
+ || arg.starts_with("--deny-sys")
|| arg.starts_with("--allow-write")
+ || arg.starts_with("--deny-write")
})
}
}
@@ -2037,6 +2142,16 @@ static ALLOW_READ_HELP: &str = concat!(
" --allow-read=\"/etc,/var/log.txt\""
);
+static DENY_READ_HELP: &str = concat!(
+ "Deny file system read access. Optionally specify denied paths.\n",
+ "Docs: https://deno.land/manual@v",
+ env!("CARGO_PKG_VERSION"),
+ "/basics/permissions\n",
+ "Examples:\n",
+ " --deny-read\n",
+ " --deny-read=\"/etc,/var/log.txt\""
+);
+
static ALLOW_WRITE_HELP: &str = concat!(
"Allow file system write access. Optionally specify allowed paths.\n",
"Docs: https://deno.land/manual@v",
@@ -2047,6 +2162,16 @@ static ALLOW_WRITE_HELP: &str = concat!(
" --allow-write=\"/etc,/var/log.txt\""
);
+static DENY_WRITE_HELP: &str = concat!(
+ "Deny file system write access. Optionally specify denied paths.\n",
+ "Docs: https://deno.land/manual@v",
+ env!("CARGO_PKG_VERSION"),
+ "/basics/permissions\n",
+ "Examples:\n",
+ " --deny-write\n",
+ " --deny-write=\"/etc,/var/log.txt\""
+);
+
static ALLOW_NET_HELP: &str = concat!(
"Allow network access. Optionally specify allowed IP addresses and host names, with ports as necessary.\n",
"Docs: https://deno.land/manual@v",
@@ -2057,6 +2182,16 @@ static ALLOW_NET_HELP: &str = concat!(
" --allow-net=\"localhost:8080,deno.land\""
);
+static DENY_NET_HELP: &str = concat!(
+ "Deny network access. Optionally specify denied IP addresses and host names, with ports as necessary.\n",
+ "Docs: https://deno.land/manual@v",
+ env!("CARGO_PKG_VERSION"),
+ "/basics/permissions\n",
+ "Examples:\n",
+ " --deny-net\n",
+ " --deny-net=\"localhost:8080,deno.land\""
+);
+
static ALLOW_ENV_HELP: &str = concat!(
"Allow access to system environment information. Optionally specify accessible environment variables.\n",
"Docs: https://deno.land/manual@v",
@@ -2067,6 +2202,16 @@ static ALLOW_ENV_HELP: &str = concat!(
" --allow-env=\"PORT,HOME,PATH\""
);
+static DENY_ENV_HELP: &str = concat!(
+ "Deny access to system environment information. Optionally specify accessible environment variables.\n",
+ "Docs: https://deno.land/manual@v",
+ env!("CARGO_PKG_VERSION"),
+ "/basics/permissions\n",
+ "Examples:\n",
+ " --deny-env\n",
+ " --deny-env=\"PORT,HOME,PATH\""
+);
+
static ALLOW_SYS_HELP: &str = concat!(
"Allow access to OS information. Optionally allow specific APIs by function name.\n",
"Docs: https://deno.land/manual@v",
@@ -2077,6 +2222,16 @@ static ALLOW_SYS_HELP: &str = concat!(
" --allow-sys=\"systemMemoryInfo,osRelease\""
);
+static DENY_SYS_HELP: &str = concat!(
+ "Deny access to OS information. Optionally deny specific APIs by function name.\n",
+ "Docs: https://deno.land/manual@v",
+ env!("CARGO_PKG_VERSION"),
+ "/basics/permissions\n",
+ "Examples:\n",
+ " --deny-sys\n",
+ " --deny-sys=\"systemMemoryInfo,osRelease\""
+);
+
static ALLOW_RUN_HELP: &str = concat!(
"Allow running subprocesses. Optionally specify allowed runnable program names.\n",
"Docs: https://deno.land/manual@v",
@@ -2087,6 +2242,16 @@ static ALLOW_RUN_HELP: &str = concat!(
" --allow-run=\"whoami,ps\""
);
+static DENY_RUN_HELP: &str = concat!(
+ "Deny running subprocesses. Optionally specify denied runnable program names.\n",
+ "Docs: https://deno.land/manual@v",
+ env!("CARGO_PKG_VERSION"),
+ "/basics/permissions\n",
+ "Examples:\n",
+ " --deny-run\n",
+ " --deny-run=\"whoami,ps\""
+);
+
static ALLOW_FFI_HELP: &str = concat!(
"(Unstable) Allow loading dynamic libraries. Optionally specify allowed directories or files.\n",
"Docs: https://deno.land/manual@v",
@@ -2097,6 +2262,16 @@ static ALLOW_FFI_HELP: &str = concat!(
" --allow-ffi=\"./libfoo.so\""
);
+static DENY_FFI_HELP: &str = concat!(
+ "(Unstable) Deny loading dynamic libraries. Optionally specify denied directories or files.\n",
+ "Docs: https://deno.land/manual@v",
+ env!("CARGO_PKG_VERSION"),
+ "/basics/permissions\n",
+ "Examples:\n",
+ " --deny-ffi\n",
+ " --deny-ffi=\"./libfoo.so\""
+);
+
static ALLOW_HRTIME_HELP: &str = concat!(
"Allow high-resolution time measurement. Note: this can enable timing attacks and fingerprinting.\n",
"Docs: https://deno.land/manual@v",
@@ -2104,6 +2279,13 @@ static ALLOW_HRTIME_HELP: &str = concat!(
"/basics/permissions\n"
);
+static DENY_HRTIME_HELP: &str = concat!(
+ "Deny high-resolution time measurement. Note: this can prevent timing attacks and fingerprinting.\n",
+ "Docs: https://deno.land/manual@v",
+ env!("CARGO_PKG_VERSION"),
+ "/basics/permissions\n"
+);
+
static ALLOW_ALL_HELP: &str = concat!(
"Allow all permissions. Learn more about permissions in Deno:\n",
"https://deno.land/manual@v",
@@ -2125,6 +2307,17 @@ fn permission_args(app: Command) -> Command {
.value_hint(ValueHint::AnyPath),
)
.arg(
+ Arg::new("deny-read")
+ .long("deny-read")
+ .num_args(0..)
+ .use_value_delimiter(true)
+ .require_equals(true)
+ .value_name("PATH")
+ .help(DENY_READ_HELP)
+ .value_parser(value_parser!(PathBuf))
+ .value_hint(ValueHint::AnyPath),
+ )
+ .arg(
Arg::new("allow-write")
.long("allow-write")
.num_args(0..)
@@ -2136,6 +2329,17 @@ fn permission_args(app: Command) -> Command {
.value_hint(ValueHint::AnyPath),
)
.arg(
+ Arg::new("deny-write")
+ .long("deny-write")
+ .num_args(0..)
+ .use_value_delimiter(true)
+ .require_equals(true)
+ .value_name("PATH")
+ .help(DENY_WRITE_HELP)
+ .value_parser(value_parser!(PathBuf))
+ .value_hint(ValueHint::AnyPath),
+ )
+ .arg(
Arg::new("allow-net")
.long("allow-net")
.num_args(0..)
@@ -2143,7 +2347,17 @@ fn permission_args(app: Command) -> Command {
.require_equals(true)
.value_name("IP_OR_HOSTNAME")
.help(ALLOW_NET_HELP)
- .value_parser(flags_allow_net::validator),
+ .value_parser(flags_net::validator),
+ )
+ .arg(
+ Arg::new("deny-net")
+ .long("deny-net")
+ .num_args(0..)
+ .use_value_delimiter(true)
+ .require_equals(true)
+ .value_name("IP_OR_HOSTNAME")
+ .help(DENY_NET_HELP)
+ .value_parser(flags_net::validator),
)
.arg(unsafely_ignore_certificate_errors_arg())
.arg(
@@ -2167,6 +2381,26 @@ fn permission_args(app: Command) -> Command {
}),
)
.arg(
+ Arg::new("deny-env")
+ .long("deny-env")
+ .num_args(0..)
+ .use_value_delimiter(true)
+ .require_equals(true)
+ .value_name("VARIABLE_NAME")
+ .help(DENY_ENV_HELP)
+ .value_parser(|key: &str| {
+ if key.is_empty() || key.contains(&['=', '\0'] as &[char]) {
+ return Err(format!("invalid key \"{key}\""));
+ }
+
+ Ok(if cfg!(windows) {
+ key.to_uppercase()
+ } else {
+ key.to_string()
+ })
+ }),
+ )
+ .arg(
Arg::new("allow-sys")
.long("allow-sys")
.num_args(0..)
@@ -2177,6 +2411,16 @@ fn permission_args(app: Command) -> Command {
.value_parser(|key: &str| parse_sys_kind(key).map(ToString::to_string)),
)
.arg(
+ Arg::new("deny-sys")
+ .long("deny-sys")
+ .num_args(0..)
+ .use_value_delimiter(true)
+ .require_equals(true)
+ .value_name("API_NAME")
+ .help(DENY_SYS_HELP)
+ .value_parser(|key: &str| parse_sys_kind(key).map(ToString::to_string)),
+ )
+ .arg(
Arg::new("allow-run")
.long("allow-run")
.num_args(0..)
@@ -2186,6 +2430,15 @@ fn permission_args(app: Command) -> Command {
.help(ALLOW_RUN_HELP),
)
.arg(
+ Arg::new("deny-run")
+ .long("deny-run")
+ .num_args(0..)
+ .use_value_delimiter(true)
+ .require_equals(true)
+ .value_name("PROGRAM_NAME")
+ .help(DENY_RUN_HELP),
+ )
+ .arg(
Arg::new("allow-ffi")
.long("allow-ffi")
.num_args(0..)
@@ -2197,12 +2450,29 @@ fn permission_args(app: Command) -> Command {
.value_hint(ValueHint::AnyPath),
)
.arg(
+ Arg::new("deny-ffi")
+ .long("deny-ffi")
+ .num_args(0..)
+ .use_value_delimiter(true)
+ .require_equals(true)
+ .value_name("PATH")
+ .help(DENY_FFI_HELP)
+ .value_parser(value_parser!(PathBuf))
+ .value_hint(ValueHint::AnyPath),
+ )
+ .arg(
Arg::new("allow-hrtime")
.long("allow-hrtime")
.action(ArgAction::SetTrue)
.help(ALLOW_HRTIME_HELP),
)
.arg(
+ Arg::new("deny-hrtime")
+ .long("deny-hrtime")
+ .action(ArgAction::SetTrue)
+ .help(DENY_HRTIME_HELP),
+ )
+ .arg(
Arg::new("allow-all")
.short('A')
.long("allow-all")
@@ -2594,7 +2864,7 @@ fn unsafely_ignore_certificate_errors_arg() -> Arg {
.require_equals(true)
.value_name("HOSTNAMES")
.help("DANGER: Disables verification of TLS certificates")
- .value_parser(flags_allow_net::validator)
+ .value_parser(flags_net::validator)
}
fn bench_parse(flags: &mut Flags, matches: &mut ArgMatches) {
@@ -3189,38 +3459,76 @@ fn permission_args_parse(flags: &mut Flags, matches: &mut ArgMatches) {
flags.allow_read = Some(read_wl.collect());
}
+ if let Some(read_wl) = matches.remove_many::<PathBuf>("deny-read") {
+ flags.deny_read = Some(read_wl.collect());
+ }
+
if let Some(write_wl) = matches.remove_many::<PathBuf>("allow-write") {
flags.allow_write = Some(write_wl.collect());
}
+ if let Some(write_wl) = matches.remove_many::<PathBuf>("deny-write") {
+ flags.deny_write = Some(write_wl.collect());
+ }
+
if let Some(net_wl) = matches.remove_many::<String>("allow-net") {
- let net_allowlist = flags_allow_net::parse(net_wl.collect()).unwrap();
+ let net_allowlist = flags_net::parse(net_wl.collect()).unwrap();
flags.allow_net = Some(net_allowlist);
}
+ if let Some(net_wl) = matches.remove_many::<String>("deny-net") {
+ let net_denylist = flags_net::parse(net_wl.collect()).unwrap();
+ flags.deny_net = Some(net_denylist);
+ }
+
if let Some(env_wl) = matches.remove_many::<String>("allow-env") {
flags.allow_env = Some(env_wl.collect());
debug!("env allowlist: {:#?}", &flags.allow_env);
}
+ if let Some(env_wl) = matches.remove_many::<String>("deny-env") {
+ flags.deny_env = Some(env_wl.collect());
+ debug!("env denylist: {:#?}", &flags.deny_env);
+ }
+
if let Some(run_wl) = matches.remove_many::<String>("allow-run") {
flags.allow_run = Some(run_wl.collect());
debug!("run allowlist: {:#?}", &flags.allow_run);
}
+ if let Some(run_wl) = matches.remove_many::<String>("deny-run") {
+ flags.deny_run = Some(run_wl.collect());
+ debug!("run denylist: {:#?}", &flags.deny_run);
+ }
+
if let Some(sys_wl) = matches.remove_many::<String>("allow-sys") {
flags.allow_sys = Some(sys_wl.collect());
debug!("sys info allowlist: {:#?}", &flags.allow_sys);
}
+ if let Some(sys_wl) = matches.remove_many::<String>("deny-sys") {
+ flags.deny_sys = Some(sys_wl.collect());
+ debug!("sys info denylist: {:#?}", &flags.deny_sys);
+ }
+
if let Some(ffi_wl) = matches.remove_many::<PathBuf>("allow-ffi") {
flags.allow_ffi = Some(ffi_wl.collect());
debug!("ffi allowlist: {:#?}", &flags.allow_ffi);
}
+ if let Some(ffi_wl) = matches.remove_many::<PathBuf>("deny-ffi") {
+ flags.deny_ffi = Some(ffi_wl.collect());
+ debug!("ffi denylist: {:#?}", &flags.deny_ffi);
+ }
+
if matches.get_flag("allow-hrtime") {
flags.allow_hrtime = true;
}
+
+ if matches.get_flag("deny-hrtime") {
+ flags.deny_hrtime = true;
+ }
+
if matches.get_flag("allow-all") {
flags.allow_all = true;
flags.allow_read = Some(vec![]);
@@ -3232,6 +3540,7 @@ fn permission_args_parse(flags: &mut Flags, matches: &mut ArgMatches) {
flags.allow_ffi = Some(vec![]);
flags.allow_hrtime = true;
}
+
if matches.get_flag("no-prompt") {
flags.no_prompt = true;
}
@@ -3243,7 +3552,7 @@ fn unsafely_ignore_certificate_errors_parse(
if let Some(ic_wl) =
matches.remove_many::<String>("unsafely-ignore-certificate-errors")
{
- let ic_allowlist = flags_allow_net::parse(ic_wl.collect()).unwrap();
+ let ic_allowlist = flags_net::parse(ic_wl.collect()).unwrap();
flags.unsafely_ignore_certificate_errors = Some(ic_allowlist);
}
}
@@ -3694,6 +4003,9 @@ mod tests {
let r = flags_from_vec(svec!["deno", "run", "--allow-read", "x.ts"]);
assert_eq!(r.unwrap().has_permission(), true);
+ let r = flags_from_vec(svec!["deno", "run", "--deny-read", "x.ts"]);
+ assert_eq!(r.unwrap().has_permission(), true);
+
let r = flags_from_vec(svec!["deno", "run", "x.ts"]);
assert_eq!(r.unwrap().has_permission(), false);
}
@@ -3703,6 +4015,9 @@ mod tests {
let r = flags_from_vec(svec!["deno", "run", "x.ts", "--allow-read"]);
assert_eq!(r.unwrap().has_permission_in_argv(), true);
+ let r = flags_from_vec(svec!["deno", "run", "x.ts", "--deny-read"]);
+ assert_eq!(r.unwrap().has_permission_in_argv(), true);
+
let r = flags_from_vec(svec!["deno", "run", "x.ts"]);
assert_eq!(r.unwrap().has_permission_in_argv(), false);
}
@@ -3772,6 +4087,22 @@ mod tests {
}
#[test]
+ fn deny_read() {
+ let r = flags_from_vec(svec!["deno", "run", "--deny-read", "gist.ts"]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Run(RunFlags {
+ watch: None,
+ script: "gist.ts".to_string(),
+ }),
+ deny_read: Some(vec![]),
+ ..Flags::default()
+ }
+ );
+ }
+
+ #[test]
fn allow_hrtime() {
let r = flags_from_vec(svec!["deno", "run", "--allow-hrtime", "gist.ts"]);
assert_eq!(
@@ -3788,6 +4119,22 @@ mod tests {
}
#[test]
+ fn deny_hrtime() {
+ let r = flags_from_vec(svec!["deno", "run", "--deny-hrtime", "gist.ts"]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Run(RunFlags {
+ watch: None,
+ script: "gist.ts".to_string(),
+ }),
+ deny_hrtime: true,
+ ..Flags::default()
+ }
+ );
+ }
+
+ #[test]
fn double_hyphen() {
// notice that flags passed after double dash will not
// be parsed to Flags but instead forwarded to
@@ -4687,11 +5034,17 @@ mod tests {
allow_net: Some(vec![]),
unsafely_ignore_certificate_errors: None,
allow_env: Some(vec![]),
+ deny_env: None,
allow_run: Some(vec![]),
+ deny_run: None,
allow_read: Some(vec![]),
+ deny_read: None,
allow_sys: Some(vec![]),
+ deny_sys: None,
allow_write: Some(vec![]),
+ deny_write: None,
allow_ffi: Some(vec![]),
+ deny_ffi: None,
allow_hrtime: true,
..Flags::default()
}
@@ -4805,6 +5158,31 @@ mod tests {
}
#[test]
+ fn deny_read_denylist() {
+ use test_util::TempDir;
+ let temp_dir_guard = TempDir::new();
+ let temp_dir = temp_dir_guard.path().to_path_buf();
+
+ let r = flags_from_vec(svec![
+ "deno",
+ "run",
+ format!("--deny-read=.,{}", temp_dir.to_str().unwrap()),
+ "script.ts"
+ ]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ deny_read: Some(vec![PathBuf::from("."), temp_dir]),
+ subcommand: DenoSubcommand::Run(RunFlags {
+ watch: None,
+ script: "script.ts".to_string(),
+ }),
+ ..Flags::default()
+ }
+ );
+ }
+
+ #[test]
fn allow_write_allowlist() {
use test_util::TempDir;
let temp_dir_guard = TempDir::new();
@@ -4830,6 +5208,31 @@ mod tests {
}
#[test]
+ fn deny_write_denylist() {
+ use test_util::TempDir;
+ let temp_dir_guard = TempDir::new();
+ let temp_dir = temp_dir_guard.path().to_path_buf();
+
+ let r = flags_from_vec(svec![
+ "deno",
+ "run",
+ format!("--deny-write=.,{}", temp_dir.to_str().unwrap()),
+ "script.ts"
+ ]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ deny_write: Some(vec![PathBuf::from("."), temp_dir]),
+ subcommand: DenoSubcommand::Run(RunFlags {
+ watch: None,
+ script: "script.ts".to_string(),
+ }),
+ ..Flags::default()
+ }
+ );
+ }
+
+ #[test]
fn allow_net_allowlist() {
let r = flags_from_vec(svec![
"deno",
@@ -4851,6 +5254,23 @@ mod tests {
}
#[test]
+ fn deny_net_denylist() {
+ let r =
+ flags_from_vec(svec!["deno", "run", "--deny-net=127.0.0.1", "script.ts"]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Run(RunFlags {
+ watch: None,
+ script: "script.ts".to_string(),
+ }),
+ deny_net: Some(svec!["127.0.0.1"]),
+ ..Flags::default()
+ }
+ );
+ }
+
+ #[test]
fn allow_env_allowlist() {
let r =
flags_from_vec(svec!["deno", "run", "--allow-env=HOME", "script.ts"]);
@@ -4868,6 +5288,23 @@ mod tests {
}
#[test]
+ fn deny_env_denylist() {
+ let r =
+ flags_from_vec(svec!["deno", "run", "--deny-env=HOME", "script.ts"]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Run(RunFlags {
+ watch: None,
+ script: "script.ts".to_string(),
+ }),
+ deny_env: Some(svec!["HOME"]),
+ ..Flags::default()
+ }
+ );
+ }
+
+ #[test]
fn allow_env_allowlist_multiple() {
let r = flags_from_vec(svec![
"deno",
@@ -4889,6 +5326,23 @@ mod tests {
}
#[test]
+ fn deny_env_denylist_multiple() {
+ let r =
+ flags_from_vec(svec!["deno", "run", "--deny-env=HOME,PATH", "script.ts"]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Run(RunFlags {
+ watch: None,
+ script: "script.ts".to_string(),
+ }),
+ deny_env: Some(svec!["HOME", "PATH"]),
+ ..Flags::default()
+ }
+ );
+ }
+
+ #[test]
fn allow_env_allowlist_validator() {
let r =
flags_from_vec(svec!["deno", "run", "--allow-env=HOME", "script.ts"]);
@@ -4902,6 +5356,19 @@ mod tests {
}
#[test]
+ fn deny_env_denylist_validator() {
+ let r =
+ flags_from_vec(svec!["deno", "run", "--deny-env=HOME", "script.ts"]);
+ assert!(r.is_ok());
+ let r =
+ flags_from_vec(svec!["deno", "run", "--deny-env=H=ME", "script.ts"]);
+ assert!(r.is_err());
+ let r =
+ flags_from_vec(svec!["deno", "run", "--deny-env=H\0ME", "script.ts"]);
+ assert!(r.is_err());
+ }
+
+ #[test]
fn allow_sys() {
let r = flags_from_vec(svec!["deno", "run", "--allow-sys", "script.ts"]);
assert_eq!(
@@ -4918,6 +5385,22 @@ mod tests {
}
#[test]
+ fn deny_sys() {
+ let r = flags_from_vec(svec!["deno", "run", "--deny-sys", "script.ts"]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Run(RunFlags {
+ watch: None,
+ script: "script.ts".to_string(),
+ }),
+ deny_sys: Some(vec![]),
+ ..Flags::default()
+ }
+ );
+ }
+
+ #[test]
fn allow_sys_allowlist() {
let r =
flags_from_vec(svec!["deno", "run", "--allow-sys=hostname", "script.ts"]);
@@ -4935,6 +5418,23 @@ mod tests {
}
#[test]
+ fn deny_sys_denylist() {
+ let r =
+ flags_from_vec(svec!["deno", "run", "--deny-sys=hostname", "script.ts"]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Run(RunFlags {
+ watch: None,
+ script: "script.ts".to_string(),
+ }),
+ deny_sys: Some(svec!["hostname"]),
+ ..Flags::default()
+ }
+ );
+ }
+
+ #[test]
fn allow_sys_allowlist_multiple() {
let r = flags_from_vec(svec![
"deno",
@@ -4956,6 +5456,27 @@ mod tests {
}
#[test]
+ fn deny_sys_denylist_multiple() {
+ let r = flags_from_vec(svec![
+ "deno",
+ "run",
+ "--deny-sys=hostname,osRelease",
+ "script.ts"
+ ]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Run(RunFlags {
+ watch: None,
+ script: "script.ts".to_string(),
+ }),
+ deny_sys: Some(svec!["hostname", "osRelease"]),
+ ..Flags::default()
+ }
+ );
+ }
+
+ #[test]
fn allow_sys_allowlist_validator() {
let r =
flags_from_vec(svec!["deno", "run", "--allow-sys=hostname", "script.ts"]);
@@ -4980,6 +5501,29 @@ mod tests {
}
#[test]
+ fn deny_sys_denylist_validator() {
+ let r =
+ flags_from_vec(svec!["deno", "run", "--deny-sys=hostname", "script.ts"]);
+ assert!(r.is_ok());
+ let r = flags_from_vec(svec![
+ "deno",
+ "run",
+ "--deny-sys=hostname,osRelease",
+ "script.ts"
+ ]);
+ assert!(r.is_ok());
+ let r = flags_from_vec(svec!["deno", "run", "--deny-sys=foo", "script.ts"]);
+ assert!(r.is_err());
+ let r = flags_from_vec(svec![
+ "deno",
+ "run",
+ "--deny-sys=hostname,foo",
+ "script.ts"
+ ]);
+ assert!(r.is_err());
+ }
+
+ #[test]
fn reload_validator() {
let r = flags_from_vec(svec![
"deno",
@@ -5851,6 +6395,35 @@ mod tests {
}
#[test]
+ fn deny_net_denylist_with_ports() {
+ let r = flags_from_vec(svec![
+ "deno",
+ "run",
+ "--deny-net=deno.land,:8000,:4545",
+ "script.ts"
+ ]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Run(RunFlags {
+ watch: None,
+ script: "script.ts".to_string(),
+ }),
+ deny_net: Some(svec![
+ "deno.land",
+ "0.0.0.0:8000",
+ "127.0.0.1:8000",
+ "localhost:8000",
+ "0.0.0.0:4545",
+ "127.0.0.1:4545",
+ "localhost:4545"
+ ]),
+ ..Flags::default()
+ }
+ );
+ }
+
+ #[test]
fn allow_net_allowlist_with_ipv6_address() {
let r = flags_from_vec(svec![
"deno",
@@ -5883,6 +6456,38 @@ mod tests {
}
#[test]
+ fn deny_net_denylist_with_ipv6_address() {
+ let r = flags_from_vec(svec![
+ "deno",
+ "run",
+ "--deny-net=deno.land,deno.land:80,::,127.0.0.1,[::1],1.2.3.4:5678,:5678,[::1]:8080",
+ "script.ts"
+ ]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Run(RunFlags {
+ watch: None,
+ script: "script.ts".to_string(),
+ }),
+ deny_net: Some(svec![
+ "deno.land",
+ "deno.land:80",
+ "::",
+ "127.0.0.1",
+ "[::1]",
+ "1.2.3.4:5678",
+ "0.0.0.0:5678",
+ "127.0.0.1:5678",
+ "localhost:5678",
+ "[::1]:8080"
+ ]),
+ ..Flags::default()
+ }
+ );
+ }
+
+ #[test]
fn lock_write() {
let r = flags_from_vec(svec![
"deno",
diff --git a/cli/args/flags_allow_net.rs b/cli/args/flags_net.rs
index 9f8a6b9f9..9f8a6b9f9 100644
--- a/cli/args/flags_allow_net.rs
+++ b/cli/args/flags_net.rs
diff --git a/cli/args/mod.rs b/cli/args/mod.rs
index cea0c0ca1..7b3b0aa83 100644
--- a/cli/args/mod.rs
+++ b/cli/args/mod.rs
@@ -2,7 +2,7 @@
mod config_file;
mod flags;
-mod flags_allow_net;
+mod flags_net;
mod import_map;
mod lockfile;
pub mod package_json;
@@ -1105,13 +1105,21 @@ impl CliOptions {
pub fn permissions_options(&self) -> PermissionsOptions {
PermissionsOptions {
allow_env: self.flags.allow_env.clone(),
+ deny_env: self.flags.deny_env.clone(),
allow_hrtime: self.flags.allow_hrtime,
+ deny_hrtime: self.flags.deny_hrtime,
allow_net: self.flags.allow_net.clone(),
+ deny_net: self.flags.deny_net.clone(),
allow_ffi: self.flags.allow_ffi.clone(),
+ deny_ffi: self.flags.deny_ffi.clone(),
allow_read: self.flags.allow_read.clone(),
+ deny_read: self.flags.deny_read.clone(),
allow_run: self.flags.allow_run.clone(),
+ deny_run: self.flags.deny_run.clone(),
allow_sys: self.flags.allow_sys.clone(),
+ deny_sys: self.flags.deny_sys.clone(),
allow_write: self.flags.allow_write.clone(),
+ deny_write: self.flags.deny_write.clone(),
prompt: !self.no_prompt(),
}
}
diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs
index 9720f2577..e83932011 100644
--- a/cli/tests/integration/run_tests.rs
+++ b/cli/tests/integration/run_tests.rs
@@ -601,7 +601,7 @@ fn _090_run_permissions_request() {
.with_pty(|mut console| {
console.expect(concat!(
"┌ ⚠ Deno requests run access to \"ls\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-run to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)",
));
@@ -609,7 +609,7 @@ fn _090_run_permissions_request() {
console.expect("Granted run access to \"ls\".");
console.expect(concat!(
"┌ ⚠ Deno requests run access to \"cat\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-run to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)",
));
@@ -628,7 +628,7 @@ fn _090_run_permissions_request_sync() {
.with_pty(|mut console| {
console.expect(concat!(
"┌ ⚠ Deno requests run access to \"ls\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-run to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)",
));
@@ -636,7 +636,7 @@ fn _090_run_permissions_request_sync() {
console.expect("Granted run access to \"ls\".");
console.expect(concat!(
"┌ ⚠ Deno requests run access to \"cat\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-run to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)",
));
@@ -656,7 +656,7 @@ fn permissions_prompt_allow_all() {
// "run" permissions
console.expect(concat!(
"┌ ⚠ Deno requests run access to \"FOO\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-run to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)",
));
@@ -665,7 +665,7 @@ fn permissions_prompt_allow_all() {
// "read" permissions
console.expect(concat!(
"┌ ⚠ Deno requests read access to \"FOO\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-read to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
));
@@ -674,7 +674,7 @@ fn permissions_prompt_allow_all() {
// "write" permissions
console.expect(concat!(
"┌ ⚠ Deno requests write access to \"FOO\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-write to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all write permissions)",
));
@@ -682,8 +682,8 @@ fn permissions_prompt_allow_all() {
console.expect("✅ Granted all write access.");
// "net" permissions
console.expect(concat!(
- "┌ ⚠ Deno requests network access to \"foo\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "┌ ⚠ Deno requests net access to \"foo\".\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-net to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all net permissions)",
));
@@ -692,7 +692,7 @@ fn permissions_prompt_allow_all() {
// "env" permissions
console.expect(concat!(
"┌ ⚠ Deno requests env access to \"FOO\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-env to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions)",
));
@@ -701,7 +701,7 @@ fn permissions_prompt_allow_all() {
// "sys" permissions
console.expect(concat!(
"┌ ⚠ Deno requests sys access to \"loadavg\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-sys to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all sys permissions)",
));
@@ -710,7 +710,7 @@ fn permissions_prompt_allow_all() {
// "ffi" permissions
console.expect(concat!(
"┌ ⚠ Deno requests ffi access to \"FOO\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-ffi to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all ffi permissions)",
));
@@ -766,7 +766,7 @@ fn permissions_prompt_allow_all_lowercase_a() {
// "run" permissions
console.expect(concat!(
"┌ ⚠ Deno requests run access to \"FOO\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-run to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)",
));
@@ -775,6 +775,36 @@ fn permissions_prompt_allow_all_lowercase_a() {
});
}
+itest!(deny_all_permission_args {
+ args: "run --deny-env --deny-read --deny-write --deny-ffi --deny-run --deny-sys --deny-net --deny-hrtime run/deny_all_permission_args.js",
+ output: "run/deny_all_permission_args.out",
+});
+
+itest!(deny_some_permission_args {
+ args: "run --allow-env --deny-env=FOO --allow-read --deny-read=/foo --allow-write --deny-write=/foo --allow-ffi --deny-ffi=/foo --allow-run --deny-run=foo --allow-sys --deny-sys=hostname --allow-net --deny-net=127.0.0.1 --allow-hrtime --deny-hrtime run/deny_some_permission_args.js",
+ output: "run/deny_some_permission_args.out",
+});
+
+#[test]
+fn permissions_cache() {
+ TestContext::default()
+ .new_command()
+ .args_vec(["run", "--quiet", "run/permissions_cache.ts"])
+ .with_pty(|mut console| {
+ console.expect(concat!(
+ "prompt\r\n",
+ "┌ ⚠ Deno requests read access to \"foo\".\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
+ "├ Run again with --allow-read to bypass this prompt.\r\n",
+ "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
+ ));
+ console.write_line_raw("y");
+ console.expect("✅ Granted read access to \"foo\".");
+ console.expect("granted");
+ console.expect("prompt");
+ });
+}
+
itest!(_091_use_define_for_class_fields {
args: "run --check run/091_use_define_for_class_fields.ts",
output: "run/091_use_define_for_class_fields.ts.out",
@@ -2541,14 +2571,14 @@ mod permissions {
.with_pty(|mut console| {
console.expect(concat!(
"┌ ⚠ Deno requests read access to \"foo\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-read to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
));
console.write_line_raw("y");
console.expect(concat!(
"┌ ⚠ Deno requests read access to \"bar\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-read to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
));
@@ -2567,14 +2597,14 @@ mod permissions {
.with_pty(|mut console| {
console.expect(concat!(
"┌ ⚠ Deno requests read access to \"foo\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-read to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
));
console.write_line_raw("y");
console.expect(concat!(
"┌ ⚠ Deno requests read access to \"bar\".\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-read to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
));
@@ -2593,7 +2623,7 @@ mod permissions {
.with_pty(|mut console| {
console.expect(concat!(
"┌ ⚠ Deno requests read access.\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-read to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
));
@@ -2615,7 +2645,7 @@ mod permissions {
.with_pty(|mut console| {
console.expect(concat!(
"┌ ⚠ Deno requests read access.\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-read to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
));
@@ -2756,7 +2786,7 @@ fn issue9750() {
console.write_line_raw("yy");
console.expect(concat!(
"┌ ⚠ Deno requests env access.\r\n",
- "├ Requested by `Deno.permissions.query()` API.\r\n",
+ "├ Requested by `Deno.permissions.request()` API.\r\n",
"├ Run again with --allow-env to bypass this prompt.\r\n",
"└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions)",
));
diff --git a/cli/tests/testdata/run/deny_all_permission_args.js b/cli/tests/testdata/run/deny_all_permission_args.js
new file mode 100644
index 000000000..b0ca864fb
--- /dev/null
+++ b/cli/tests/testdata/run/deny_all_permission_args.js
@@ -0,0 +1,8 @@
+console.log(Deno.permissions.querySync({ name: "env" }));
+console.log(Deno.permissions.querySync({ name: "read" }));
+console.log(Deno.permissions.querySync({ name: "write" }));
+console.log(Deno.permissions.querySync({ name: "ffi" }));
+console.log(Deno.permissions.querySync({ name: "run" }));
+console.log(Deno.permissions.querySync({ name: "sys" }));
+console.log(Deno.permissions.querySync({ name: "net" }));
+console.log(Deno.permissions.querySync({ name: "hrtime" }));
diff --git a/cli/tests/testdata/run/deny_all_permission_args.out b/cli/tests/testdata/run/deny_all_permission_args.out
new file mode 100644
index 000000000..2a5228d62
--- /dev/null
+++ b/cli/tests/testdata/run/deny_all_permission_args.out
@@ -0,0 +1,8 @@
+PermissionStatus { state: "denied", onchange: null }
+PermissionStatus { state: "denied", onchange: null }
+PermissionStatus { state: "denied", onchange: null }
+PermissionStatus { state: "denied", onchange: null }
+PermissionStatus { state: "denied", onchange: null }
+PermissionStatus { state: "denied", onchange: null }
+PermissionStatus { state: "denied", onchange: null }
+PermissionStatus { state: "denied", onchange: null }
diff --git a/cli/tests/testdata/run/deny_some_permission_args.js b/cli/tests/testdata/run/deny_some_permission_args.js
new file mode 100644
index 000000000..320376b6f
--- /dev/null
+++ b/cli/tests/testdata/run/deny_some_permission_args.js
@@ -0,0 +1,22 @@
+console.log(Deno.permissions.querySync({ name: "env" }));
+console.log(Deno.permissions.querySync({ name: "env", variable: "FOO" }));
+console.log(Deno.permissions.querySync({ name: "env", variable: "BAR" }));
+console.log(Deno.permissions.querySync({ name: "read" }));
+console.log(Deno.permissions.querySync({ name: "read", path: "/foo" }));
+console.log(Deno.permissions.querySync({ name: "read", path: "/bar" }));
+console.log(Deno.permissions.querySync({ name: "write" }));
+console.log(Deno.permissions.querySync({ name: "write", path: "/foo" }));
+console.log(Deno.permissions.querySync({ name: "write", path: "/bar" }));
+console.log(Deno.permissions.querySync({ name: "ffi" }));
+console.log(Deno.permissions.querySync({ name: "ffi", path: "/foo" }));
+console.log(Deno.permissions.querySync({ name: "ffi", path: "/bar" }));
+console.log(Deno.permissions.querySync({ name: "run" }));
+console.log(Deno.permissions.querySync({ name: "run", command: "foo" }));
+console.log(Deno.permissions.querySync({ name: "run", command: "bar" }));
+console.log(Deno.permissions.querySync({ name: "sys" }));
+console.log(Deno.permissions.querySync({ name: "sys", kind: "hostname" }));
+console.log(Deno.permissions.querySync({ name: "sys", kind: "loadavg" }));
+console.log(Deno.permissions.querySync({ name: "net" }));
+console.log(Deno.permissions.querySync({ name: "net", host: "127.0.0.1" }));
+console.log(Deno.permissions.querySync({ name: "net", host: "192.168.0.1" }));
+console.log(Deno.permissions.querySync({ name: "hrtime" }));
diff --git a/cli/tests/testdata/run/deny_some_permission_args.out b/cli/tests/testdata/run/deny_some_permission_args.out
new file mode 100644
index 000000000..80c37159b
--- /dev/null
+++ b/cli/tests/testdata/run/deny_some_permission_args.out
@@ -0,0 +1,22 @@
+PermissionStatus { state: "granted", onchange: null, partial: true }
+PermissionStatus { state: "denied", onchange: null }
+PermissionStatus { state: "granted", onchange: null }
+PermissionStatus { state: "granted", onchange: null, partial: true }
+PermissionStatus { state: "denied", onchange: null }
+PermissionStatus { state: "granted", onchange: null }
+PermissionStatus { state: "granted", onchange: null, partial: true }
+PermissionStatus { state: "denied", onchange: null }
+PermissionStatus { state: "granted", onchange: null }
+PermissionStatus { state: "granted", onchange: null, partial: true }
+PermissionStatus { state: "denied", onchange: null }
+PermissionStatus { state: "granted", onchange: null }
+PermissionStatus { state: "granted", onchange: null, partial: true }
+PermissionStatus { state: "denied", onchange: null }
+PermissionStatus { state: "granted", onchange: null }
+PermissionStatus { state: "granted", onchange: null, partial: true }
+PermissionStatus { state: "denied", onchange: null }
+PermissionStatus { state: "granted", onchange: null }
+PermissionStatus { state: "granted", onchange: null, partial: true }
+PermissionStatus { state: "denied", onchange: null }
+PermissionStatus { state: "granted", onchange: null }
+PermissionStatus { state: "denied", onchange: null }
diff --git a/cli/tests/testdata/run/permissions_cache.ts b/cli/tests/testdata/run/permissions_cache.ts
new file mode 100644
index 000000000..c77ee0f36
--- /dev/null
+++ b/cli/tests/testdata/run/permissions_cache.ts
@@ -0,0 +1,5 @@
+const status = await Deno.permissions.query({ name: "read", path: "foo" });
+console.log(status.state);
+status.onchange = () => console.log(status.state);
+await Deno.permissions.request({ name: "read", path: "foo" }); // y
+await Deno.permissions.revoke({ name: "read", path: "foo" });
diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts
index 1c8d9db63..436387eba 100644
--- a/cli/tsc/dts/lib.deno.ns.d.ts
+++ b/cli/tsc/dts/lib.deno.ns.d.ts
@@ -4403,9 +4403,12 @@ declare namespace Deno {
*
* @category Permissions
*/
- export type PermissionState = "granted" | "denied" | "prompt";
+ export type PermissionState =
+ | "granted"
+ | "denied"
+ | "prompt";
- /** The permission descriptor for the `allow-run` permission, which controls
+ /** The permission descriptor for the `allow-run` and `deny-run` permissions, which controls
* access to what sub-processes can be executed by Deno. The option `command`
* allows scoping the permission to a specific executable.
*
@@ -4416,12 +4419,12 @@ declare namespace Deno {
* @category Permissions */
export interface RunPermissionDescriptor {
name: "run";
- /** The `allow-run` permission can be scoped to a specific executable,
+ /** An `allow-run` or `deny-run` permission can be scoped to a specific executable,
* which would be relative to the start-up CWD of the Deno CLI. */
command?: string | URL;
}
- /** The permission descriptor for the `allow-read` permissions, which controls
+ /** The permission descriptor for the `allow-read` and `deny-read` permissions, which controls
* access to reading resources from the local host. The option `path` allows
* scoping the permission to a specific path (and if the path is a directory
* any sub paths).
@@ -4432,12 +4435,12 @@ declare namespace Deno {
* @category Permissions */
export interface ReadPermissionDescriptor {
name: "read";
- /** The `allow-read` permission can be scoped to a specific path (and if
+ /** An `allow-read` or `deny-read` permission can be scoped to a specific path (and if
* the path is a directory, any sub paths). */
path?: string | URL;
}
- /** The permission descriptor for the `allow-write` permissions, which
+ /** The permission descriptor for the `allow-write` and `deny-write` permissions, which
* controls access to writing to resources from the local host. The option
* `path` allow scoping the permission to a specific path (and if the path is
* a directory any sub paths).
@@ -4448,12 +4451,12 @@ declare namespace Deno {
* @category Permissions */
export interface WritePermissionDescriptor {
name: "write";
- /** The `allow-write` permission can be scoped to a specific path (and if
+ /** An `allow-write` or `deny-write` permission can be scoped to a specific path (and if
* the path is a directory, any sub paths). */
path?: string | URL;
}
- /** The permission descriptor for the `allow-net` permissions, which controls
+ /** The permission descriptor for the `allow-net` and `deny-net` permissions, which controls
* access to opening network ports and connecting to remote hosts via the
* network. The option `host` allows scoping the permission for outbound
* connection to a specific host and port.
@@ -4469,7 +4472,7 @@ declare namespace Deno {
host?: string;
}
- /** The permission descriptor for the `allow-env` permissions, which controls
+ /** The permission descriptor for the `allow-env` and `deny-env` permissions, which controls
* access to being able to read and write to the process environment variables
* as well as access other information about the environment. The option
* `variable` allows scoping the permission to a specific environment
@@ -4482,7 +4485,7 @@ declare namespace Deno {
variable?: string;
}
- /** The permission descriptor for the `allow-sys` permissions, which controls
+ /** The permission descriptor for the `allow-sys` and `deny-sys` permissions, which controls
* access to sensitive host system information, which malicious code might
* attempt to exploit. The option `kind` allows scoping the permission to a
* specific piece of information.
@@ -4502,7 +4505,7 @@ declare namespace Deno {
| "gid";
}
- /** The permission descriptor for the `allow-ffi` permissions, which controls
+ /** The permission descriptor for the `allow-ffi` and `deny-ffi` permissions, which controls
* access to loading _foreign_ code and interfacing with it via the
* [Foreign Function Interface API](https://deno.land/manual/runtime/ffi_api)
* available in Deno. The option `path` allows scoping the permission to a
@@ -4515,7 +4518,7 @@ declare namespace Deno {
path?: string | URL;
}
- /** The permission descriptor for the `allow-hrtime` permission, which
+ /** The permission descriptor for the `allow-hrtime` and `deny-hrtime` permissions, which
* controls if the runtime code has access to high resolution time. High
* resolution time is considered sensitive information, because it can be used
* by malicious code to gain information about the host that it might not
@@ -4560,6 +4563,13 @@ declare namespace Deno {
// deno-lint-ignore no-explicit-any
onchange: ((this: PermissionStatus, ev: Event) => any) | null;
readonly state: PermissionState;
+ /**
+ * Describes if permission is only granted partially, eg. an access
+ * might be granted to "/foo" directory, but denied for "/foo/bar".
+ * In such case this field will be set to `true` when querying for
+ * read permissions of "/foo" directory.
+ */
+ readonly partial: boolean;
addEventListener<K extends keyof PermissionStatusEventMap>(
type: K,
listener: (
diff --git a/ext/ffi/call.rs b/ext/ffi/call.rs
index 21358d851..82f8dd822 100644
--- a/ext/ffi/call.rs
+++ b/ext/ffi/call.rs
@@ -289,7 +289,7 @@ where
{
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
};
let symbol = PtrSymbol::new(pointer, &def)?;
@@ -389,7 +389,7 @@ where
{
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
};
let symbol = PtrSymbol::new(pointer, &def)?;
diff --git a/ext/ffi/callback.rs b/ext/ffi/callback.rs
index e9d91855d..9a36815d0 100644
--- a/ext/ffi/callback.rs
+++ b/ext/ffi/callback.rs
@@ -546,7 +546,7 @@ where
{
check_unstable(state, "Deno.UnsafeCallback");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
let v8_value = cb.v8_value;
let cb = v8::Local::<v8::Function>::try_from(v8_value)?;
diff --git a/ext/ffi/dlfcn.rs b/ext/ffi/dlfcn.rs
index 3af9177bf..ced64bec5 100644
--- a/ext/ffi/dlfcn.rs
+++ b/ext/ffi/dlfcn.rs
@@ -144,7 +144,7 @@ where
check_unstable(state, "Deno.dlopen");
let permissions = state.borrow_mut::<FP>();
- permissions.check(Some(&PathBuf::from(&path)))?;
+ permissions.check_partial(Some(&PathBuf::from(&path)))?;
let lib = Library::open(&path).map_err(|e| {
dlopen::Error::OpeningLibraryError(std::io::Error::new(
diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs
index 2d3bb2866..b5a2a3bd5 100644
--- a/ext/ffi/lib.rs
+++ b/ext/ffi/lib.rs
@@ -64,7 +64,7 @@ pub fn check_unstable2(state: &Rc<RefCell<OpState>>, api_name: &str) {
}
pub trait FfiPermissions {
- fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError>;
+ fn check_partial(&mut self, path: Option<&Path>) -> Result<(), AnyError>;
}
pub(crate) type PendingFfiAsyncWork = Box<dyn FnOnce()>;
diff --git a/ext/ffi/repr.rs b/ext/ffi/repr.rs
index 0e2f88084..665e37186 100644
--- a/ext/ffi/repr.rs
+++ b/ext/ffi/repr.rs
@@ -24,7 +24,7 @@ where
{
check_unstable(state, "Deno.UnsafePointer#create");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
Ok(ptr_number as *mut c_void)
}
@@ -40,7 +40,7 @@ where
{
check_unstable(state, "Deno.UnsafePointer#equals");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
Ok(a == b)
}
@@ -55,7 +55,7 @@ where
{
check_unstable(state, "Deno.UnsafePointer#of");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
Ok(buf as *mut c_void)
}
@@ -71,7 +71,7 @@ where
{
check_unstable(state, "Deno.UnsafePointer#offset");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
if ptr.is_null() {
return Err(type_error("Invalid pointer to offset, pointer is null"));
@@ -99,7 +99,7 @@ where
{
check_unstable(state, "Deno.UnsafePointer#value");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
let outptr = out.as_ptr() as *mut usize;
let length = out.len();
@@ -129,7 +129,7 @@ where
check_unstable(state, "Deno.UnsafePointerView#getArrayBuffer");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
if ptr.is_null() {
return Err(type_error("Invalid ArrayBuffer pointer, pointer is null"));
@@ -164,7 +164,7 @@ where
check_unstable(state, "Deno.UnsafePointerView#copyInto");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
if src.is_null() {
Err(type_error("Invalid ArrayBuffer pointer, pointer is null"))
@@ -197,7 +197,7 @@ where
check_unstable(state, "Deno.UnsafePointerView#getCString");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
if ptr.is_null() {
return Err(type_error("Invalid CString pointer, pointer is null"));
@@ -227,7 +227,7 @@ where
check_unstable(state, "Deno.UnsafePointerView#getBool");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
if ptr.is_null() {
return Err(type_error("Invalid bool pointer, pointer is null"));
@@ -249,7 +249,7 @@ where
check_unstable(state, "Deno.UnsafePointerView#getUint8");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
if ptr.is_null() {
return Err(type_error("Invalid u8 pointer, pointer is null"));
@@ -273,7 +273,7 @@ where
check_unstable(state, "Deno.UnsafePointerView#getInt8");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
if ptr.is_null() {
return Err(type_error("Invalid i8 pointer, pointer is null"));
@@ -297,7 +297,7 @@ where
check_unstable(state, "Deno.UnsafePointerView#getUint16");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
if ptr.is_null() {
return Err(type_error("Invalid u16 pointer, pointer is null"));
@@ -321,7 +321,7 @@ where
check_unstable(state, "Deno.UnsafePointerView#getInt16");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
if ptr.is_null() {
return Err(type_error("Invalid i16 pointer, pointer is null"));
@@ -345,7 +345,7 @@ where
check_unstable(state, "Deno.UnsafePointerView#getUint32");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
if ptr.is_null() {
return Err(type_error("Invalid u32 pointer, pointer is null"));
@@ -367,7 +367,7 @@ where
check_unstable(state, "Deno.UnsafePointerView#getInt32");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
if ptr.is_null() {
return Err(type_error("Invalid i32 pointer, pointer is null"));
@@ -390,7 +390,7 @@ where
check_unstable(state, "Deno.UnsafePointerView#getBigUint64");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
let outptr = out.as_mut_ptr() as *mut u64;
@@ -425,7 +425,7 @@ where
check_unstable(state, "Deno.UnsafePointerView#getBigUint64");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
let outptr = out.as_mut_ptr() as *mut i64;
@@ -458,7 +458,7 @@ where
check_unstable(state, "Deno.UnsafePointerView#getFloat32");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
if ptr.is_null() {
return Err(type_error("Invalid f32 pointer, pointer is null"));
@@ -480,7 +480,7 @@ where
check_unstable(state, "Deno.UnsafePointerView#getFloat64");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
if ptr.is_null() {
return Err(type_error("Invalid f64 pointer, pointer is null"));
@@ -502,7 +502,7 @@ where
check_unstable(state, "Deno.UnsafePointerView#getPointer");
let permissions = state.borrow_mut::<FP>();
- permissions.check(None)?;
+ permissions.check_partial(None)?;
if ptr.is_null() {
return Err(type_error("Invalid pointer pointer, pointer is null"));
diff --git a/ext/fs/lib.rs b/ext/fs/lib.rs
index d27712927..b028b12c1 100644
--- a/ext/fs/lib.rs
+++ b/ext/fs/lib.rs
@@ -23,7 +23,8 @@ use std::path::Path;
use std::rc::Rc;
pub trait FsPermissions {
- fn check_read(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError>;
+ fn check_read(&mut self, path: &Path, api_name: &str)
+ -> Result<(), AnyError>;
fn check_read_all(&mut self, api_name: &str) -> Result<(), AnyError>;
fn check_read_blind(
&mut self,
@@ -31,7 +32,16 @@ pub trait FsPermissions {
display: &str,
api_name: &str,
) -> Result<(), AnyError>;
- fn check_write(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError>;
+ fn check_write(
+ &mut self,
+ path: &Path,
+ api_name: &str,
+ ) -> Result<(), AnyError>;
+ fn check_write_partial(
+ &mut self,
+ path: &Path,
+ api_name: &str,
+ ) -> Result<(), AnyError>;
fn check_write_all(&mut self, api_name: &str) -> Result<(), AnyError>;
fn check_write_blind(
&mut self,
diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs
index 083d1b15f..da52318a4 100644
--- a/ext/fs/ops.rs
+++ b/ext/fs/ops.rs
@@ -294,9 +294,16 @@ where
let fs = {
let mut state = state.borrow_mut();
- state
- .borrow_mut::<P>()
- .check_write(&path, "Deno.remove()")?;
+ if recursive {
+ state
+ .borrow_mut::<P>()
+ .check_write(&path, "Deno.remove()")?;
+ } else {
+ state
+ .borrow_mut::<P>()
+ .check_write_partial(&path, "Deno.remove()")?;
+ }
+
state.borrow::<FileSystemRc>().clone()
};
diff --git a/runtime/build.rs b/runtime/build.rs
index 828bc3c53..5b02f2202 100644
--- a/runtime/build.rs
+++ b/runtime/build.rs
@@ -111,7 +111,7 @@ mod startup_snapshot {
}
impl deno_ffi::FfiPermissions for Permissions {
- fn check(
+ fn check_partial(
&mut self,
_path: Option<&Path>,
) -> Result<(), deno_core::error::AnyError> {
@@ -204,6 +204,14 @@ mod startup_snapshot {
unreachable!("snapshotting!")
}
+ fn check_write_partial(
+ &mut self,
+ _path: &Path,
+ _api_name: &str,
+ ) -> Result<(), AnyError> {
+ unreachable!("snapshotting!")
+ }
+
fn check_write_all(&mut self, _api_name: &str) -> Result<(), AnyError> {
unreachable!("snapshotting!")
}
diff --git a/runtime/js/10_permissions.js b/runtime/js/10_permissions.js
index 79cbb7632..3004ab694 100644
--- a/runtime/js/10_permissions.js
+++ b/runtime/js/10_permissions.js
@@ -30,6 +30,7 @@ const illegalConstructorKey = Symbol("illegalConstructorKey");
* @typedef StatusCacheValue
* @property {PermissionState} state
* @property {PermissionStatus} status
+ * @property {boolean} partial
*/
/** @type {ReadonlyArray<"read" | "write" | "net" | "env" | "sys" | "run" | "ffi" | "hrtime">} */
@@ -69,27 +70,32 @@ function opRequest(desc) {
}
class PermissionStatus extends EventTarget {
- /** @type {{ state: Deno.PermissionState }} */
- #state;
+ /** @type {{ state: Deno.PermissionState, partial: boolean }} */
+ #status;
/** @type {((this: PermissionStatus, event: Event) => any) | null} */
onchange = null;
/** @returns {Deno.PermissionState} */
get state() {
- return this.#state.state;
+ return this.#status.state;
+ }
+
+ /** @returns {boolean} */
+ get partial() {
+ return this.#status.partial;
}
/**
- * @param {{ state: Deno.PermissionState }} state
+ * @param {{ state: Deno.PermissionState, partial: boolean }} status
* @param {unknown} key
*/
- constructor(state = null, key = null) {
+ constructor(status = null, key = null) {
if (key != illegalConstructorKey) {
throw new TypeError("Illegal constructor.");
}
super();
- this.#state = state;
+ this.#status = status;
}
/**
@@ -106,9 +112,9 @@ class PermissionStatus extends EventTarget {
}
[SymbolFor("Deno.privateCustomInspect")](inspect) {
- return `${this.constructor.name} ${
- inspect({ state: this.state, onchange: this.onchange })
- }`;
+ const object = { state: this.state, onchange: this.onchange };
+ if (this.partial) object.partial = this.partial;
+ return `${this.constructor.name} ${inspect(object)}`;
}
}
@@ -117,10 +123,10 @@ const statusCache = new SafeMap();
/**
* @param {Deno.PermissionDescriptor} desc
- * @param {Deno.PermissionState} state
+ * @param {{ state: Deno.PermissionState, partial: boolean }} rawStatus
* @returns {PermissionStatus}
*/
-function cache(desc, state) {
+function cache(desc, rawStatus) {
let { name: key } = desc;
if (
(desc.name === "read" || desc.name === "write" || desc.name === "ffi") &&
@@ -139,18 +145,24 @@ function cache(desc, state) {
key += "$";
}
if (MapPrototypeHas(statusCache, key)) {
- const status = MapPrototypeGet(statusCache, key);
- if (status.state !== state) {
- status.state = state;
- status.status.dispatchEvent(new Event("change", { cancelable: false }));
+ const cachedObj = MapPrototypeGet(statusCache, key);
+ if (
+ cachedObj.state !== rawStatus.state ||
+ cachedObj.partial !== rawStatus.partial
+ ) {
+ cachedObj.state = rawStatus.state;
+ cachedObj.partial = rawStatus.partial;
+ cachedObj.status.dispatchEvent(
+ new Event("change", { cancelable: false }),
+ );
}
- return status.status;
+ return cachedObj.status;
}
- /** @type {{ state: Deno.PermissionState; status?: PermissionStatus }} */
- const status = { state };
- status.status = new PermissionStatus(status, illegalConstructorKey);
- MapPrototypeSet(statusCache, key, status);
- return status.status;
+ /** @type {{ state: Deno.PermissionState, partial: boolean, status?: PermissionStatus }} */
+ const obj = rawStatus;
+ obj.status = new PermissionStatus(obj, illegalConstructorKey);
+ MapPrototypeSet(statusCache, key, obj);
+ return obj.status;
}
/**
@@ -200,8 +212,8 @@ class Permissions {
formDescriptor(desc);
- const state = opQuery(desc);
- return cache(desc, state);
+ const status = opQuery(desc);
+ return cache(desc, status);
}
revoke(desc) {
@@ -221,8 +233,8 @@ class Permissions {
formDescriptor(desc);
- const state = opRevoke(desc);
- return cache(desc, state);
+ const status = opRevoke(desc);
+ return cache(desc, status);
}
request(desc) {
@@ -242,8 +254,8 @@ class Permissions {
formDescriptor(desc);
- const state = opRequest(desc);
- return cache(desc, state);
+ const status = opRequest(desc);
+ return cache(desc, status);
}
}
diff --git a/runtime/ops/permissions.rs b/runtime/ops/permissions.rs
index 663b1d240..e5c55076c 100644
--- a/runtime/ops/permissions.rs
+++ b/runtime/ops/permissions.rs
@@ -1,6 +1,7 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use crate::permissions::parse_sys_kind;
+use crate::permissions::PermissionState;
use crate::permissions::PermissionsContainer;
use deno_core::error::custom_error;
use deno_core::error::uri_error;
@@ -9,6 +10,7 @@ use deno_core::op;
use deno_core::url;
use deno_core::OpState;
use serde::Deserialize;
+use serde::Serialize;
use std::path::Path;
deno_core::extension!(
@@ -30,11 +32,30 @@ pub struct PermissionArgs {
command: Option<String>,
}
+#[derive(Serialize)]
+pub struct PermissionStatus {
+ state: String,
+ partial: bool,
+}
+
+impl From<PermissionState> for PermissionStatus {
+ fn from(state: PermissionState) -> Self {
+ PermissionStatus {
+ state: if state == PermissionState::GrantedPartial {
+ PermissionState::Granted.to_string()
+ } else {
+ state.to_string()
+ },
+ partial: state == PermissionState::GrantedPartial,
+ }
+ }
+}
+
#[op]
pub fn op_query_permission(
state: &mut OpState,
args: PermissionArgs,
-) -> Result<String, AnyError> {
+) -> Result<PermissionStatus, AnyError> {
let permissions = state.borrow::<PermissionsContainer>().0.lock();
let path = args.path.as_deref();
let perm = match args.name.as_ref() {
@@ -61,14 +82,14 @@ pub fn op_query_permission(
))
}
};
- Ok(perm.to_string())
+ Ok(PermissionStatus::from(perm))
}
#[op]
pub fn op_revoke_permission(
state: &mut OpState,
args: PermissionArgs,
-) -> Result<String, AnyError> {
+) -> Result<PermissionStatus, AnyError> {
let mut permissions = state.borrow_mut::<PermissionsContainer>().0.lock();
let path = args.path.as_deref();
let perm = match args.name.as_ref() {
@@ -95,14 +116,14 @@ pub fn op_revoke_permission(
))
}
};
- Ok(perm.to_string())
+ Ok(PermissionStatus::from(perm))
}
#[op]
pub fn op_request_permission(
state: &mut OpState,
args: PermissionArgs,
-) -> Result<String, AnyError> {
+) -> Result<PermissionStatus, AnyError> {
let mut permissions = state.borrow_mut::<PermissionsContainer>().0.lock();
let path = args.path.as_deref();
let perm = match args.name.as_ref() {
@@ -129,7 +150,7 @@ pub fn op_request_permission(
))
}
};
- Ok(perm.to_string())
+ Ok(PermissionStatus::from(perm))
}
fn parse_host(host_str: &str) -> Result<(String, Option<u16>), AnyError> {
diff --git a/runtime/permissions/mod.rs b/runtime/permissions/mod.rs
index 56ebf8327..93294fc92 100644
--- a/runtime/permissions/mod.rs
+++ b/runtime/permissions/mod.rs
@@ -39,15 +39,41 @@ pub use prompter::PromptCallback;
static DEBUG_LOG_ENABLED: Lazy<bool> =
Lazy::new(|| log::log_enabled!(log::Level::Debug));
-/// Tri-state value for storing permission state
+/// Quadri-state value for storing permission state
#[derive(
Eq, PartialEq, Default, Debug, Clone, Copy, Deserialize, PartialOrd,
)]
pub enum PermissionState {
Granted = 0,
+ GrantedPartial = 1,
#[default]
- Prompt = 1,
- Denied = 2,
+ Prompt = 2,
+ Denied = 3,
+}
+
+/// `AllowPartial` prescribes how to treat a permission which is partially
+/// denied due to a `--deny-*` flag affecting a subscope of the queried
+/// permission.
+///
+/// `TreatAsGranted` is used in place of `TreatAsPartialGranted` when we don't
+/// want to wastefully check for partial denials when, say, checking read
+/// access for a file.
+#[derive(Debug, Eq, PartialEq)]
+#[allow(clippy::enum_variant_names)]
+enum AllowPartial {
+ TreatAsGranted,
+ TreatAsDenied,
+ TreatAsPartialGranted,
+}
+
+impl From<bool> for AllowPartial {
+ fn from(value: bool) -> Self {
+ if value {
+ Self::TreatAsGranted
+ } else {
+ Self::TreatAsDenied
+ }
+ }
}
impl PermissionState {
@@ -143,6 +169,7 @@ impl fmt::Display for PermissionState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PermissionState::Granted => f.pad("granted"),
+ PermissionState::GrantedPartial => f.pad("granted-partial"),
PermissionState::Prompt => f.pad("prompt"),
PermissionState::Denied => f.pad("denied"),
}
@@ -226,22 +253,263 @@ impl AsRef<str> for EnvVarName {
}
}
+pub trait Descriptor: Eq + Clone {
+ fn flag_name() -> &'static str;
+ fn name(&self) -> Cow<str>;
+ // By default, specifies no-stronger-than relationship.
+ // As this is not strict, it's only true when descriptors are the same.
+ fn stronger_than(&self, other: &Self) -> bool {
+ self == other
+ }
+}
+
#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct UnaryPermission<T: Eq + Hash> {
- pub name: &'static str,
- pub description: &'static str,
- pub global_state: PermissionState,
+pub struct UnaryPermission<T: Descriptor + Hash> {
+ pub granted_global: bool,
pub granted_list: HashSet<T>,
- pub denied_list: HashSet<T>,
+ pub flag_denied_global: bool,
+ pub flag_denied_list: HashSet<T>,
+ pub prompt_denied_global: bool,
+ pub prompt_denied_list: HashSet<T>,
pub prompt: bool,
}
+impl<T: Descriptor + Hash> Default for UnaryPermission<T> {
+ fn default() -> Self {
+ UnaryPermission {
+ granted_global: Default::default(),
+ granted_list: Default::default(),
+ flag_denied_global: Default::default(),
+ flag_denied_list: Default::default(),
+ prompt_denied_global: Default::default(),
+ prompt_denied_list: Default::default(),
+ prompt: Default::default(),
+ }
+ }
+}
+
+impl<T: Descriptor + Hash> UnaryPermission<T> {
+ fn check_desc(
+ &mut self,
+ desc: &Option<T>,
+ assert_non_partial: bool,
+ api_name: Option<&str>,
+ get_display_name: impl Fn() -> Option<String>,
+ ) -> Result<(), AnyError> {
+ let (result, prompted, is_allow_all) = self
+ .query_desc(desc, AllowPartial::from(assert_non_partial))
+ .check2(
+ T::flag_name(),
+ api_name,
+ || match get_display_name() {
+ Some(display_name) => Some(display_name),
+ None => desc.as_ref().map(|d| format!("\"{}\"", d.name())),
+ },
+ self.prompt,
+ );
+ if prompted {
+ if result.is_ok() {
+ if is_allow_all {
+ self.insert_granted(None);
+ } else {
+ self.insert_granted(desc.clone());
+ }
+ } else {
+ self.insert_prompt_denied(desc.clone());
+ }
+ }
+ result
+ }
+
+ fn query_desc(
+ &self,
+ desc: &Option<T>,
+ allow_partial: AllowPartial,
+ ) -> PermissionState {
+ if self.is_flag_denied(desc) || self.is_prompt_denied(desc) {
+ PermissionState::Denied
+ } else if self.is_granted(desc) {
+ match allow_partial {
+ AllowPartial::TreatAsGranted => PermissionState::Granted,
+ AllowPartial::TreatAsDenied => {
+ if self.is_partial_flag_denied(desc) {
+ PermissionState::Denied
+ } else {
+ PermissionState::Granted
+ }
+ }
+ AllowPartial::TreatAsPartialGranted => {
+ if self.is_partial_flag_denied(desc) {
+ PermissionState::GrantedPartial
+ } else {
+ PermissionState::Granted
+ }
+ }
+ }
+ } else if matches!(allow_partial, AllowPartial::TreatAsDenied)
+ && self.is_partial_flag_denied(desc)
+ {
+ PermissionState::Denied
+ } else {
+ PermissionState::Prompt
+ }
+ }
+
+ fn request_desc(
+ &mut self,
+ desc: &Option<T>,
+ get_display_name: impl Fn() -> Option<String>,
+ ) -> PermissionState {
+ let state = self.query_desc(desc, AllowPartial::TreatAsPartialGranted);
+ if state == PermissionState::Granted {
+ self.insert_granted(desc.clone());
+ return state;
+ }
+ if state != PermissionState::Prompt {
+ return state;
+ }
+ let mut message = String::with_capacity(40);
+ message.push_str(&format!("{} access", T::flag_name()));
+ match get_display_name() {
+ Some(display_name) => {
+ message.push_str(&format!(" to \"{}\"", display_name))
+ }
+ None => match desc {
+ Some(desc) => message.push_str(&format!(" to \"{}\"", desc.name())),
+ None => {}
+ },
+ }
+ match permission_prompt(
+ &message,
+ T::flag_name(),
+ Some("Deno.permissions.request()"),
+ true,
+ ) {
+ PromptResponse::Allow => {
+ self.insert_granted(desc.clone());
+ PermissionState::Granted
+ }
+ PromptResponse::Deny => {
+ self.insert_prompt_denied(desc.clone());
+ PermissionState::Denied
+ }
+ PromptResponse::AllowAll => {
+ self.insert_granted(None);
+ PermissionState::Granted
+ }
+ }
+ }
+
+ fn revoke_desc(&mut self, desc: &Option<T>) -> PermissionState {
+ match desc.as_ref() {
+ Some(desc) => self.granted_list.retain(|v| !v.stronger_than(desc)),
+ None => {
+ self.granted_global = false;
+ // Revoke global is a special case where the entire granted list is
+ // cleared. It's inconsistent with the granular case where only
+ // descriptors stronger than the revoked one are purged.
+ self.granted_list.clear();
+ }
+ }
+ self.query_desc(desc, AllowPartial::TreatAsPartialGranted)
+ }
+
+ fn is_granted(&self, desc: &Option<T>) -> bool {
+ Self::list_contains(desc, self.granted_global, &self.granted_list)
+ }
+
+ fn is_flag_denied(&self, desc: &Option<T>) -> bool {
+ Self::list_contains(desc, self.flag_denied_global, &self.flag_denied_list)
+ }
+
+ fn is_prompt_denied(&self, desc: &Option<T>) -> bool {
+ match desc.as_ref() {
+ Some(desc) => self
+ .prompt_denied_list
+ .iter()
+ .any(|v| desc.stronger_than(v)),
+ None => self.prompt_denied_global || !self.prompt_denied_list.is_empty(),
+ }
+ }
+
+ fn is_partial_flag_denied(&self, desc: &Option<T>) -> bool {
+ match desc {
+ None => !self.flag_denied_list.is_empty(),
+ Some(desc) => self.flag_denied_list.iter().any(|v| desc.stronger_than(v)),
+ }
+ }
+
+ fn list_contains(
+ desc: &Option<T>,
+ list_global: bool,
+ list: &HashSet<T>,
+ ) -> bool {
+ match desc.as_ref() {
+ Some(desc) => list_global || list.iter().any(|v| v.stronger_than(desc)),
+ None => list_global,
+ }
+ }
+
+ fn insert_granted(&mut self, desc: Option<T>) {
+ Self::list_insert(desc, &mut self.granted_global, &mut self.granted_list);
+ }
+
+ fn insert_prompt_denied(&mut self, desc: Option<T>) {
+ Self::list_insert(
+ desc,
+ &mut self.prompt_denied_global,
+ &mut self.prompt_denied_list,
+ );
+ }
+
+ fn list_insert(
+ desc: Option<T>,
+ list_global: &mut bool,
+ list: &mut HashSet<T>,
+ ) {
+ match desc {
+ Some(desc) => {
+ list.insert(desc);
+ }
+ None => *list_global = true,
+ }
+ }
+}
+
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct ReadDescriptor(pub PathBuf);
+impl Descriptor for ReadDescriptor {
+ fn flag_name() -> &'static str {
+ "read"
+ }
+
+ fn name(&self) -> Cow<str> {
+ Cow::from(self.0.display().to_string())
+ }
+
+ fn stronger_than(&self, other: &Self) -> bool {
+ other.0.starts_with(&self.0)
+ }
+}
+
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct WriteDescriptor(pub PathBuf);
+impl Descriptor for WriteDescriptor {
+ fn flag_name() -> &'static str {
+ "write"
+ }
+
+ fn name(&self) -> Cow<str> {
+ Cow::from(self.0.display().to_string())
+ }
+
+ fn stronger_than(&self, other: &Self) -> bool {
+ other.0.starts_with(&self.0)
+ }
+}
+
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct NetDescriptor(pub String, pub Option<u16>);
@@ -251,6 +519,20 @@ impl NetDescriptor {
}
}
+impl Descriptor for NetDescriptor {
+ fn flag_name() -> &'static str {
+ "net"
+ }
+
+ fn name(&self) -> Cow<str> {
+ Cow::from(format!("{}", self))
+ }
+
+ fn stronger_than(&self, other: &Self) -> bool {
+ self.0 == other.0 && (self.1.is_none() || self.1 == other.1)
+ }
+}
+
impl FromStr for NetDescriptor {
type Err = AnyError;
@@ -280,6 +562,16 @@ impl EnvDescriptor {
}
}
+impl Descriptor for EnvDescriptor {
+ fn flag_name() -> &'static str {
+ "env"
+ }
+
+ fn name(&self) -> Cow<str> {
+ Cow::from(self.0.as_ref())
+ }
+}
+
impl AsRef<str> for EnvDescriptor {
fn as_ref(&self) -> &str {
self.0.as_ref()
@@ -292,6 +584,16 @@ pub enum RunDescriptor {
Path(PathBuf),
}
+impl Descriptor for RunDescriptor {
+ fn flag_name() -> &'static str {
+ "run"
+ }
+
+ fn name(&self) -> Cow<str> {
+ Cow::from(self.to_string())
+ }
+}
+
impl FromStr for RunDescriptor {
type Err = ();
@@ -319,6 +621,16 @@ impl ToString for RunDescriptor {
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct SysDescriptor(pub String);
+impl Descriptor for SysDescriptor {
+ fn flag_name() -> &'static str {
+ "sys"
+ }
+
+ fn name(&self) -> Cow<str> {
+ Cow::from(self.0.to_string())
+ }
+}
+
pub fn parse_sys_kind(kind: &str) -> Result<&str, AnyError> {
match kind {
"hostname" | "osRelease" | "osUptime" | "loadavg" | "networkInterfaces"
@@ -330,135 +642,63 @@ pub fn parse_sys_kind(kind: &str) -> Result<&str, AnyError> {
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct FfiDescriptor(pub PathBuf);
+impl Descriptor for FfiDescriptor {
+ fn flag_name() -> &'static str {
+ "ffi"
+ }
+
+ fn name(&self) -> Cow<str> {
+ Cow::from(self.0.display().to_string())
+ }
+
+ fn stronger_than(&self, other: &Self) -> bool {
+ other.0.starts_with(&self.0)
+ }
+}
+
impl UnaryPermission<ReadDescriptor> {
pub fn query(&self, path: Option<&Path>) -> PermissionState {
- if self.global_state == PermissionState::Granted {
- return PermissionState::Granted;
- }
- let path = path.map(|p| resolve_from_cwd(p).unwrap());
- if self.global_state == PermissionState::Denied
- && match path.as_ref() {
- None => true,
- Some(path) => self
- .denied_list
- .iter()
- .any(|path_| path_.0.starts_with(path)),
- }
- {
- PermissionState::Denied
- } else if self.global_state == PermissionState::Granted
- || match path.as_ref() {
- None => false,
- Some(path) => self
- .granted_list
- .iter()
- .any(|path_| path.starts_with(&path_.0)),
- }
- {
- PermissionState::Granted
- } else {
- PermissionState::Prompt
- }
+ self.query_desc(
+ &path.map(|p| ReadDescriptor(resolve_from_cwd(p).unwrap())),
+ AllowPartial::TreatAsPartialGranted,
+ )
}
pub fn request(&mut self, path: Option<&Path>) -> PermissionState {
- if let Some(path) = path {
- let (resolved_path, display_path) = resolved_and_display_path(path);
- let state = self.query(Some(&resolved_path));
- if state == PermissionState::Prompt {
- match permission_prompt(
- &format!("read access to \"{}\"", display_path.display()),
- self.name,
- Some("Deno.permissions.query()"),
- true,
- ) {
- PromptResponse::Allow => {
- self.granted_list.insert(ReadDescriptor(resolved_path));
- PermissionState::Granted
- }
- PromptResponse::Deny => {
- self.denied_list.insert(ReadDescriptor(resolved_path));
- self.global_state = PermissionState::Denied;
- PermissionState::Denied
- }
- PromptResponse::AllowAll => {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- PermissionState::Granted
- }
- }
- } else if state == PermissionState::Granted {
- self.granted_list.insert(ReadDescriptor(resolved_path));
- PermissionState::Granted
- } else {
- state
- }
- } else {
- let state = self.query(None);
- if state == PermissionState::Prompt {
- if PromptResponse::Allow
- == permission_prompt(
- "read access",
- self.name,
- Some("Deno.permissions.query()"),
- true,
- )
- {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- PermissionState::Granted
- } else {
- self.global_state = PermissionState::Denied;
- PermissionState::Denied
- }
- } else {
- state
- }
- }
+ self.request_desc(
+ &path.map(|p| ReadDescriptor(resolve_from_cwd(p).unwrap())),
+ || Some(path?.display().to_string()),
+ )
}
pub fn revoke(&mut self, path: Option<&Path>) -> PermissionState {
- if let Some(path) = path {
- let path = resolve_from_cwd(path).unwrap();
- self
- .granted_list
- .retain(|path_| !path.starts_with(&path_.0));
- } else {
- self.granted_list.clear();
- }
- if self.global_state == PermissionState::Granted {
- self.global_state = PermissionState::Prompt;
- }
- self.query(path)
+ self
+ .revoke_desc(&path.map(|p| ReadDescriptor(resolve_from_cwd(p).unwrap())))
}
- #[inline]
pub fn check(
&mut self,
path: &Path,
api_name: Option<&str>,
) -> Result<(), AnyError> {
- let (result, prompted, is_allow_all) = self.query(Some(path)).check2(
- self.name,
+ self.check_desc(
+ &Some(ReadDescriptor(resolve_from_cwd(path)?)),
+ true,
api_name,
- || Some(format!("\"{}\"", path.to_path_buf().display())),
- self.prompt,
- );
- if prompted {
- let resolved_path = resolve_from_cwd(path)?;
- if result.is_ok() {
- if is_allow_all {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- } else {
- self.granted_list.insert(ReadDescriptor(resolved_path));
- }
- } else {
- self.denied_list.insert(ReadDescriptor(resolved_path));
- self.global_state = PermissionState::Denied;
- }
- }
- result
+ || Some(format!("\"{}\"", path.display())),
+ )
+ }
+
+ #[inline]
+ pub fn check_partial(
+ &mut self,
+ path: &Path,
+ api_name: Option<&str>,
+ ) -> Result<(), AnyError> {
+ let desc = ReadDescriptor(resolve_from_cwd(path)?);
+ self.check_desc(&Some(desc), false, api_name, || {
+ Some(format!("\"{}\"", path.display()))
+ })
}
/// As `check()`, but permission error messages will anonymize the path
@@ -469,205 +709,62 @@ impl UnaryPermission<ReadDescriptor> {
display: &str,
api_name: &str,
) -> Result<(), AnyError> {
- let resolved_path = resolve_from_cwd(path)?;
- let (result, prompted, is_allow_all) =
- self.query(Some(&resolved_path)).check(
- self.name,
- Some(api_name),
- Some(&format!("<{display}>")),
- self.prompt,
- );
- if prompted {
- if result.is_ok() {
- if is_allow_all {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- } else {
- self.granted_list.insert(ReadDescriptor(resolved_path));
- }
- } else {
- self.global_state = PermissionState::Denied;
- if !is_allow_all {
- self.denied_list.insert(ReadDescriptor(resolved_path));
- }
- }
- }
- result
+ let desc = ReadDescriptor(resolve_from_cwd(path)?);
+ self.check_desc(&Some(desc), false, Some(api_name), || {
+ Some(format!("<{display}>"))
+ })
}
pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> {
- let (result, prompted, _) =
- self
- .query(None)
- .check(self.name, api_name, Some("all"), self.prompt);
- if prompted {
- if result.is_ok() {
- self.global_state = PermissionState::Granted;
- } else {
- self.global_state = PermissionState::Denied;
- }
- }
- result
- }
-}
-
-impl Default for UnaryPermission<ReadDescriptor> {
- fn default() -> Self {
- UnaryPermission::<ReadDescriptor> {
- name: "read",
- description: "read the file system",
- global_state: Default::default(),
- granted_list: Default::default(),
- denied_list: Default::default(),
- prompt: false,
- }
+ self.check_desc(&None, false, api_name, || None)
}
}
impl UnaryPermission<WriteDescriptor> {
pub fn query(&self, path: Option<&Path>) -> PermissionState {
- if self.global_state == PermissionState::Granted {
- return PermissionState::Granted;
- }
- let path = path.map(|p| resolve_from_cwd(p).unwrap());
- if self.global_state == PermissionState::Denied
- && match path.as_ref() {
- None => true,
- Some(path) => self
- .denied_list
- .iter()
- .any(|path_| path_.0.starts_with(path)),
- }
- {
- PermissionState::Denied
- } else if self.global_state == PermissionState::Granted
- || match path.as_ref() {
- None => false,
- Some(path) => self
- .granted_list
- .iter()
- .any(|path_| path.starts_with(&path_.0)),
- }
- {
- PermissionState::Granted
- } else {
- PermissionState::Prompt
- }
+ self.query_desc(
+ &path.map(|p| WriteDescriptor(resolve_from_cwd(p).unwrap())),
+ AllowPartial::TreatAsPartialGranted,
+ )
}
pub fn request(&mut self, path: Option<&Path>) -> PermissionState {
- if let Some(path) = path {
- let (resolved_path, display_path) = resolved_and_display_path(path);
- let state = self.query(Some(&resolved_path));
- if state == PermissionState::Prompt {
- match permission_prompt(
- &format!("write access to \"{}\"", display_path.display()),
- self.name,
- Some("Deno.permissions.query()"),
- true,
- ) {
- PromptResponse::Allow => {
- self.granted_list.insert(WriteDescriptor(resolved_path));
- PermissionState::Granted
- }
- PromptResponse::Deny => {
- self.denied_list.insert(WriteDescriptor(resolved_path));
- self.global_state = PermissionState::Denied;
- PermissionState::Denied
- }
- PromptResponse::AllowAll => {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- PermissionState::Granted
- }
- }
- } else if state == PermissionState::Granted {
- self.granted_list.insert(WriteDescriptor(resolved_path));
- PermissionState::Granted
- } else {
- state
- }
- } else {
- let state = self.query(None);
- if state == PermissionState::Prompt {
- if PromptResponse::Allow
- == permission_prompt(
- "write access",
- self.name,
- Some("Deno.permissions.query()"),
- true,
- )
- {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- PermissionState::Granted
- } else {
- self.global_state = PermissionState::Denied;
- PermissionState::Denied
- }
- } else {
- state
- }
- }
+ self.request_desc(
+ &path.map(|p| WriteDescriptor(resolve_from_cwd(p).unwrap())),
+ || Some(path?.display().to_string()),
+ )
}
pub fn revoke(&mut self, path: Option<&Path>) -> PermissionState {
- if let Some(path) = path {
- let path = resolve_from_cwd(path).unwrap();
- self
- .granted_list
- .retain(|path_| !path.starts_with(&path_.0));
- } else {
- self.granted_list.clear();
- }
- if self.global_state == PermissionState::Granted {
- self.global_state = PermissionState::Prompt;
- }
- self.query(path)
+ self
+ .revoke_desc(&path.map(|p| WriteDescriptor(resolve_from_cwd(p).unwrap())))
}
- #[inline]
pub fn check(
&mut self,
path: &Path,
api_name: Option<&str>,
) -> Result<(), AnyError> {
- let (result, prompted, is_allow_all) = self.query(Some(path)).check2(
- self.name,
+ self.check_desc(
+ &Some(WriteDescriptor(resolve_from_cwd(path)?)),
+ true,
api_name,
- || Some(format!("\"{}\"", path.to_path_buf().display())),
- self.prompt,
- );
- if prompted {
- let resolved_path = resolve_from_cwd(path)?;
- if result.is_ok() {
- if is_allow_all {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- } else {
- self.granted_list.insert(WriteDescriptor(resolved_path));
- }
- } else {
- self.denied_list.insert(WriteDescriptor(resolved_path));
- self.global_state = PermissionState::Denied;
- }
- }
- result
+ || Some(format!("\"{}\"", path.display())),
+ )
}
- pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> {
- let (result, prompted, _) =
- self
- .query(None)
- .check(self.name, api_name, Some("all"), self.prompt);
- if prompted {
- if result.is_ok() {
- self.global_state = PermissionState::Granted;
- } else {
- self.global_state = PermissionState::Denied;
- }
- }
- result
+ #[inline]
+ pub fn check_partial(
+ &mut self,
+ path: &Path,
+ api_name: Option<&str>,
+ ) -> Result<(), AnyError> {
+ self.check_desc(
+ &Some(WriteDescriptor(resolve_from_cwd(path)?)),
+ false,
+ api_name,
+ || Some(format!("\"{}\"", path.display())),
+ )
}
/// As `check()`, but permission error messages will anonymize the path
@@ -678,43 +775,14 @@ impl UnaryPermission<WriteDescriptor> {
display: &str,
api_name: &str,
) -> Result<(), AnyError> {
- let resolved_path = resolve_from_cwd(path)?;
- let (result, prompted, is_allow_all) =
- self.query(Some(&resolved_path)).check(
- self.name,
- Some(api_name),
- Some(&format!("<{display}>")),
- self.prompt,
- );
- if prompted {
- if result.is_ok() {
- if is_allow_all {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- } else {
- self.granted_list.insert(WriteDescriptor(resolved_path));
- }
- } else {
- self.global_state = PermissionState::Denied;
- if !is_allow_all {
- self.denied_list.insert(WriteDescriptor(resolved_path));
- }
- }
- }
- result
+ let desc = WriteDescriptor(resolve_from_cwd(path)?);
+ self.check_desc(&Some(desc), false, Some(api_name), || {
+ Some(format!("<{display}>"))
+ })
}
-}
-impl Default for UnaryPermission<WriteDescriptor> {
- fn default() -> Self {
- UnaryPermission::<WriteDescriptor> {
- name: "write",
- description: "write to the file system",
- global_state: Default::default(),
- granted_list: Default::default(),
- denied_list: Default::default(),
- prompt: false,
- }
+ pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> {
+ self.check_desc(&None, false, api_name, || None)
}
}
@@ -723,116 +791,24 @@ impl UnaryPermission<NetDescriptor> {
&self,
host: Option<&(T, Option<u16>)>,
) -> PermissionState {
- if self.global_state == PermissionState::Denied
- && match host.as_ref() {
- None => true,
- Some(host) => match host.1 {
- None => self
- .denied_list
- .iter()
- .any(|host_| host.0.as_ref() == host_.0),
- Some(_) => self.denied_list.contains(&NetDescriptor::new(host)),
- },
- }
- {
- PermissionState::Denied
- } else if self.global_state == PermissionState::Granted
- || match host.as_ref() {
- None => false,
- Some(host) => {
- self.granted_list.contains(&NetDescriptor::new(&&(
- host.0.as_ref().to_string(),
- None,
- )))
- || self.granted_list.contains(&NetDescriptor::new(host))
- }
- }
- {
- PermissionState::Granted
- } else {
- PermissionState::Prompt
- }
+ self.query_desc(
+ &host.map(|h| NetDescriptor::new(&h)),
+ AllowPartial::TreatAsPartialGranted,
+ )
}
pub fn request<T: AsRef<str>>(
&mut self,
host: Option<&(T, Option<u16>)>,
) -> PermissionState {
- if let Some(host) = host {
- let state = self.query(Some(host));
- let host = NetDescriptor::new(&host);
- if state == PermissionState::Prompt {
- match permission_prompt(
- &format!("network access to \"{host}\""),
- self.name,
- Some("Deno.permissions.query()"),
- true,
- ) {
- PromptResponse::Allow => {
- self.granted_list.insert(host);
- PermissionState::Granted
- }
- PromptResponse::Deny => {
- self.denied_list.insert(host);
- self.global_state = PermissionState::Denied;
- PermissionState::Denied
- }
- PromptResponse::AllowAll => {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- PermissionState::Granted
- }
- }
- } else if state == PermissionState::Granted {
- self.granted_list.insert(host);
- PermissionState::Granted
- } else {
- state
- }
- } else {
- let state = self.query::<&str>(None);
- if state == PermissionState::Prompt {
- if PromptResponse::Allow
- == permission_prompt(
- "network access",
- self.name,
- Some("Deno.permissions.query()"),
- true,
- )
- {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- PermissionState::Granted
- } else {
- self.global_state = PermissionState::Denied;
- PermissionState::Denied
- }
- } else {
- state
- }
- }
+ self.request_desc(&host.map(|h| NetDescriptor::new(&h)), || None)
}
pub fn revoke<T: AsRef<str>>(
&mut self,
host: Option<&(T, Option<u16>)>,
) -> PermissionState {
- if let Some(host) = host {
- if host.1.is_some() {
- self
- .granted_list
- .remove(&NetDescriptor(host.0.as_ref().to_string(), host.1));
- }
- self
- .granted_list
- .remove(&NetDescriptor(host.0.as_ref().to_string(), None));
- } else {
- self.granted_list.clear();
- }
- if self.global_state == PermissionState::Granted {
- self.global_state = PermissionState::Prompt;
- }
- self.query(host)
+ self.revoke_desc(&Some(NetDescriptor::new(&host.unwrap())))
}
pub fn check<T: AsRef<str>>(
@@ -840,27 +816,7 @@ impl UnaryPermission<NetDescriptor> {
host: &(T, Option<u16>),
api_name: Option<&str>,
) -> Result<(), AnyError> {
- let new_host = NetDescriptor::new(&host);
- let (result, prompted, is_allow_all) = self.query(Some(host)).check(
- self.name,
- api_name,
- Some(&format!("\"{new_host}\"")),
- self.prompt,
- );
- if prompted {
- if result.is_ok() {
- if is_allow_all {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- } else {
- self.granted_list.insert(new_host);
- }
- } else {
- self.denied_list.insert(new_host);
- self.global_state = PermissionState::Denied;
- }
- }
- result
+ self.check_desc(&Some(NetDescriptor::new(&host)), false, api_name, || None)
}
pub fn check_url(
@@ -872,284 +828,60 @@ impl UnaryPermission<NetDescriptor> {
.host_str()
.ok_or_else(|| uri_error("Missing host"))?
.to_string();
+ let host = &(&hostname, url.port_or_known_default());
let display_host = match url.port() {
- None => Cow::Borrowed(&hostname),
- Some(port) => Cow::Owned(format!("{hostname}:{port}")),
+ None => hostname.clone(),
+ Some(port) => format!("{hostname}:{port}"),
};
- let host = &(&hostname, url.port_or_known_default());
- let (result, prompted, is_allow_all) = self.query(Some(host)).check(
- self.name,
- api_name,
- Some(&format!("\"{display_host}\"")),
- self.prompt,
- );
- if prompted {
- if result.is_ok() {
- if is_allow_all {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- } else {
- self.granted_list.insert(NetDescriptor::new(&host));
- }
- } else {
- self.denied_list.insert(NetDescriptor::new(&host));
- self.global_state = PermissionState::Denied;
- }
- }
- result
+ self.check_desc(&Some(NetDescriptor::new(&host)), false, api_name, || {
+ Some(format!("\"{}\"", display_host))
+ })
}
pub fn check_all(&mut self) -> Result<(), AnyError> {
- let (result, prompted, _) =
- self
- .query::<&str>(None)
- .check(self.name, None, Some("all"), self.prompt);
- if prompted {
- if result.is_ok() {
- self.global_state = PermissionState::Granted;
- } else {
- self.global_state = PermissionState::Denied;
- }
- }
- result
- }
-}
-
-impl Default for UnaryPermission<NetDescriptor> {
- fn default() -> Self {
- UnaryPermission::<NetDescriptor> {
- name: "net",
- description: "network",
- global_state: Default::default(),
- granted_list: Default::default(),
- denied_list: Default::default(),
- prompt: false,
- }
+ self.check_desc(&None, false, None, || None)
}
}
impl UnaryPermission<EnvDescriptor> {
pub fn query(&self, env: Option<&str>) -> PermissionState {
- let env = env.map(EnvVarName::new);
- if self.global_state == PermissionState::Denied
- && match env.as_ref() {
- None => true,
- Some(env) => self.denied_list.contains(&EnvDescriptor::new(env)),
- }
- {
- PermissionState::Denied
- } else if self.global_state == PermissionState::Granted
- || match env.as_ref() {
- None => false,
- Some(env) => self.granted_list.contains(&EnvDescriptor::new(env)),
- }
- {
- PermissionState::Granted
- } else {
- PermissionState::Prompt
- }
+ self.query_desc(
+ &env.map(EnvDescriptor::new),
+ AllowPartial::TreatAsPartialGranted,
+ )
}
pub fn request(&mut self, env: Option<&str>) -> PermissionState {
- if let Some(env) = env {
- let state = self.query(Some(env));
- if state == PermissionState::Prompt {
- match permission_prompt(
- &format!("env access to \"{env}\""),
- self.name,
- Some("Deno.permissions.query()"),
- true,
- ) {
- PromptResponse::Allow => {
- self.granted_list.insert(EnvDescriptor::new(env));
- PermissionState::Granted
- }
- PromptResponse::Deny => {
- self.denied_list.insert(EnvDescriptor::new(env));
- self.global_state = PermissionState::Denied;
- PermissionState::Denied
- }
- PromptResponse::AllowAll => {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- PermissionState::Granted
- }
- }
- } else if state == PermissionState::Granted {
- self.granted_list.insert(EnvDescriptor::new(env));
- PermissionState::Granted
- } else {
- state
- }
- } else {
- let state = self.query(None);
- if state == PermissionState::Prompt {
- if PromptResponse::Allow
- == permission_prompt(
- "env access",
- self.name,
- Some("Deno.permissions.query()"),
- true,
- )
- {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- PermissionState::Granted
- } else {
- self.global_state = PermissionState::Denied;
- PermissionState::Denied
- }
- } else {
- state
- }
- }
+ self.request_desc(&env.map(EnvDescriptor::new), || None)
}
pub fn revoke(&mut self, env: Option<&str>) -> PermissionState {
- if let Some(env) = env {
- self.granted_list.remove(&EnvDescriptor::new(env));
- } else {
- self.granted_list.clear();
- }
- if self.global_state == PermissionState::Granted {
- self.global_state = PermissionState::Prompt;
- }
- self.query(env)
+ self.revoke_desc(&env.map(EnvDescriptor::new))
}
pub fn check(&mut self, env: &str) -> Result<(), AnyError> {
- let (result, prompted, is_allow_all) = self.query(Some(env)).check(
- self.name,
- None,
- Some(&format!("\"{env}\"")),
- self.prompt,
- );
- if prompted {
- if result.is_ok() {
- if is_allow_all {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- } else {
- self.granted_list.insert(EnvDescriptor::new(env));
- }
- } else {
- self.denied_list.insert(EnvDescriptor::new(env));
- self.global_state = PermissionState::Denied;
- }
- }
- result
+ self.check_desc(&Some(EnvDescriptor::new(env)), false, None, || None)
}
pub fn check_all(&mut self) -> Result<(), AnyError> {
- let (result, prompted, _) =
- self
- .query(None)
- .check(self.name, None, Some("all"), self.prompt);
- if prompted {
- if result.is_ok() {
- self.global_state = PermissionState::Granted;
- } else {
- self.global_state = PermissionState::Denied;
- }
- }
- result
- }
-}
-
-impl Default for UnaryPermission<EnvDescriptor> {
- fn default() -> Self {
- UnaryPermission::<EnvDescriptor> {
- name: "env",
- description: "environment variables",
- global_state: Default::default(),
- granted_list: Default::default(),
- denied_list: Default::default(),
- prompt: false,
- }
+ self.check_desc(&None, false, None, || None)
}
}
impl UnaryPermission<SysDescriptor> {
pub fn query(&self, kind: Option<&str>) -> PermissionState {
- if self.global_state == PermissionState::Denied
- && match kind {
- None => true,
- Some(kind) => {
- self.denied_list.contains(&SysDescriptor(kind.to_string()))
- }
- }
- {
- PermissionState::Denied
- } else if self.global_state == PermissionState::Granted
- || match kind {
- None => false,
- Some(kind) => {
- self.granted_list.contains(&SysDescriptor(kind.to_string()))
- }
- }
- {
- PermissionState::Granted
- } else {
- PermissionState::Prompt
- }
+ self.query_desc(
+ &kind.map(|k| SysDescriptor(k.to_string())),
+ AllowPartial::TreatAsPartialGranted,
+ )
}
pub fn request(&mut self, kind: Option<&str>) -> PermissionState {
- let state = self.query(kind);
- if state != PermissionState::Prompt {
- return state;
- }
- if let Some(kind) = kind {
- let desc = SysDescriptor(kind.to_string());
- match permission_prompt(
- &format!("sys access to \"{kind}\""),
- self.name,
- Some("Deno.permissions.query()"),
- true,
- ) {
- PromptResponse::Allow => {
- self.granted_list.insert(desc);
- PermissionState::Granted
- }
- PromptResponse::Deny => {
- self.denied_list.insert(desc);
- self.global_state = PermissionState::Denied;
- PermissionState::Denied
- }
- PromptResponse::AllowAll => {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- PermissionState::Granted
- }
- }
- } else {
- if PromptResponse::Allow
- == permission_prompt(
- "sys access",
- self.name,
- Some("Deno.permissions.query()"),
- true,
- )
- {
- self.global_state = PermissionState::Granted;
- } else {
- self.granted_list.clear();
- self.global_state = PermissionState::Denied;
- }
- self.global_state
- }
+ self.request_desc(&kind.map(|k| SysDescriptor(k.to_string())), || None)
}
pub fn revoke(&mut self, kind: Option<&str>) -> PermissionState {
- if let Some(kind) = kind {
- self.granted_list.remove(&SysDescriptor(kind.to_string()));
- } else {
- self.granted_list.clear();
- }
- if self.global_state == PermissionState::Granted {
- self.global_state = PermissionState::Prompt;
- }
- self.query(kind)
+ self.revoke_desc(&kind.map(|k| SysDescriptor(k.to_string())))
}
pub fn check(
@@ -1157,155 +889,35 @@ impl UnaryPermission<SysDescriptor> {
kind: &str,
api_name: Option<&str>,
) -> Result<(), AnyError> {
- let (result, prompted, is_allow_all) = self.query(Some(kind)).check(
- self.name,
+ self.check_desc(
+ &Some(SysDescriptor(kind.to_string())),
+ false,
api_name,
- Some(&format!("\"{kind}\"")),
- self.prompt,
- );
- if prompted {
- if result.is_ok() {
- if is_allow_all {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- } else {
- self.granted_list.insert(SysDescriptor(kind.to_string()));
- }
- } else {
- self.denied_list.insert(SysDescriptor(kind.to_string()));
- self.global_state = PermissionState::Denied;
- }
- }
- result
+ || None,
+ )
}
pub fn check_all(&mut self) -> Result<(), AnyError> {
- let (result, prompted, _is_allow_all) =
- self
- .query(None)
- .check(self.name, None, Some("all"), self.prompt);
- if prompted {
- if result.is_ok() {
- self.global_state = PermissionState::Granted;
- } else {
- self.global_state = PermissionState::Denied;
- }
- }
- result
- }
-}
-
-impl Default for UnaryPermission<SysDescriptor> {
- fn default() -> Self {
- UnaryPermission::<SysDescriptor> {
- name: "sys",
- description: "system information",
- global_state: Default::default(),
- granted_list: Default::default(),
- denied_list: Default::default(),
- prompt: false,
- }
+ self.check_desc(&None, false, None, || None)
}
}
impl UnaryPermission<RunDescriptor> {
pub fn query(&self, cmd: Option<&str>) -> PermissionState {
- if self.global_state == PermissionState::Denied
- && match cmd {
- None => true,
- Some(cmd) => self
- .denied_list
- .contains(&RunDescriptor::from_str(cmd).unwrap()),
- }
- {
- PermissionState::Denied
- } else if self.global_state == PermissionState::Granted
- || match cmd {
- None => false,
- Some(cmd) => self
- .granted_list
- .contains(&RunDescriptor::from_str(cmd).unwrap()),
- }
- {
- PermissionState::Granted
- } else {
- PermissionState::Prompt
- }
+ self.query_desc(
+ &cmd.map(|c| RunDescriptor::from_str(c).unwrap()),
+ AllowPartial::TreatAsPartialGranted,
+ )
}
pub fn request(&mut self, cmd: Option<&str>) -> PermissionState {
- if let Some(cmd) = cmd {
- let state = self.query(Some(cmd));
- if state == PermissionState::Prompt {
- match permission_prompt(
- &format!("run access to \"{cmd}\""),
- self.name,
- Some("Deno.permissions.query()"),
- true,
- ) {
- PromptResponse::Allow => {
- self
- .granted_list
- .insert(RunDescriptor::from_str(cmd).unwrap());
- PermissionState::Granted
- }
- PromptResponse::Deny => {
- self
- .denied_list
- .insert(RunDescriptor::from_str(cmd).unwrap());
- self.global_state = PermissionState::Denied;
- PermissionState::Denied
- }
- PromptResponse::AllowAll => {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- PermissionState::Granted
- }
- }
- } else if state == PermissionState::Granted {
- self
- .granted_list
- .insert(RunDescriptor::from_str(cmd).unwrap());
- PermissionState::Granted
- } else {
- state
- }
- } else {
- let state = self.query(None);
- if state == PermissionState::Prompt {
- if PromptResponse::Allow
- == permission_prompt(
- "run access",
- self.name,
- Some("Deno.permissions.query()"),
- true,
- )
- {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- PermissionState::Granted
- } else {
- self.global_state = PermissionState::Denied;
- PermissionState::Denied
- }
- } else {
- state
- }
- }
+ self.request_desc(&cmd.map(|c| RunDescriptor::from_str(c).unwrap()), || {
+ Some(cmd?.to_string())
+ })
}
pub fn revoke(&mut self, cmd: Option<&str>) -> PermissionState {
- if let Some(cmd) = cmd {
- self
- .granted_list
- .remove(&RunDescriptor::from_str(cmd).unwrap());
- } else {
- self.granted_list.clear();
- }
- if self.global_state == PermissionState::Granted {
- self.global_state = PermissionState::Prompt;
- }
- self.query(cmd)
+ self.revoke_desc(&cmd.map(|c| RunDescriptor::from_str(c).unwrap()))
}
pub fn check(
@@ -1313,228 +925,63 @@ impl UnaryPermission<RunDescriptor> {
cmd: &str,
api_name: Option<&str>,
) -> Result<(), AnyError> {
- let (result, prompted, is_allow_all) = self.query(Some(cmd)).check(
- self.name,
+ self.check_desc(
+ &Some(RunDescriptor::from_str(cmd).unwrap()),
+ false,
api_name,
- Some(&format!("\"{cmd}\"")),
- self.prompt,
- );
- if prompted {
- if result.is_ok() {
- if is_allow_all {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- } else {
- self
- .granted_list
- .insert(RunDescriptor::from_str(cmd).unwrap());
- }
- } else {
- self
- .denied_list
- .insert(RunDescriptor::from_str(cmd).unwrap());
- self.global_state = PermissionState::Denied;
- }
- }
- result
+ || Some(format!("\"{}\"", cmd)),
+ )
}
pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> {
- let (result, prompted, _) =
- self
- .query(None)
- .check(self.name, api_name, Some("all"), self.prompt);
- if prompted {
- if result.is_ok() {
- self.global_state = PermissionState::Granted;
- } else {
- self.global_state = PermissionState::Denied;
- }
- }
- result
- }
-}
-
-impl Default for UnaryPermission<RunDescriptor> {
- fn default() -> Self {
- UnaryPermission::<RunDescriptor> {
- name: "run",
- description: "run a subprocess",
- global_state: Default::default(),
- granted_list: Default::default(),
- denied_list: Default::default(),
- prompt: false,
- }
+ self.check_desc(&None, false, api_name, || None)
}
}
impl UnaryPermission<FfiDescriptor> {
pub fn query(&self, path: Option<&Path>) -> PermissionState {
- let path = path.map(|p| resolve_from_cwd(p).unwrap());
- if self.global_state == PermissionState::Denied
- && match path.as_ref() {
- None => true,
- Some(path) => self
- .denied_list
- .iter()
- .any(|path_| path_.0.starts_with(path)),
- }
- {
- PermissionState::Denied
- } else if self.global_state == PermissionState::Granted
- || match path.as_ref() {
- None => false,
- Some(path) => self
- .granted_list
- .iter()
- .any(|path_| path.starts_with(&path_.0)),
- }
- {
- PermissionState::Granted
- } else {
- PermissionState::Prompt
- }
+ self.query_desc(
+ &path.map(|p| FfiDescriptor(resolve_from_cwd(p).unwrap())),
+ AllowPartial::TreatAsPartialGranted,
+ )
}
pub fn request(&mut self, path: Option<&Path>) -> PermissionState {
- if let Some(path) = path {
- let (resolved_path, display_path) = resolved_and_display_path(path);
- let state = self.query(Some(&resolved_path));
- if state == PermissionState::Prompt {
- match permission_prompt(
- &format!("ffi access to \"{}\"", display_path.display()),
- self.name,
- Some("Deno.permissions.query()"),
- true,
- ) {
- PromptResponse::Allow => {
- self.granted_list.insert(FfiDescriptor(resolved_path));
- PermissionState::Granted
- }
- PromptResponse::Deny => {
- self.denied_list.insert(FfiDescriptor(resolved_path));
- self.global_state = PermissionState::Denied;
- PermissionState::Denied
- }
- PromptResponse::AllowAll => {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- PermissionState::Granted
- }
- }
- } else if state == PermissionState::Granted {
- self.granted_list.insert(FfiDescriptor(resolved_path));
- PermissionState::Granted
- } else {
- state
- }
- } else {
- let state = self.query(None);
- if state == PermissionState::Prompt {
- if PromptResponse::Allow
- == permission_prompt(
- "ffi access",
- self.name,
- Some("Deno.permissions.query()"),
- true,
- )
- {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- PermissionState::Granted
- } else {
- self.global_state = PermissionState::Denied;
- PermissionState::Denied
- }
- } else {
- state
- }
- }
+ self.request_desc(
+ &path.map(|p| FfiDescriptor(resolve_from_cwd(p).unwrap())),
+ || Some(path?.display().to_string()),
+ )
}
pub fn revoke(&mut self, path: Option<&Path>) -> PermissionState {
- if let Some(path) = path {
- let path = resolve_from_cwd(path).unwrap();
- self
- .granted_list
- .retain(|path_| !path.starts_with(&path_.0));
- } else {
- self.granted_list.clear();
- }
- if self.global_state == PermissionState::Granted {
- self.global_state = PermissionState::Prompt;
- }
- self.query(path)
+ self.revoke_desc(&path.map(|p| FfiDescriptor(resolve_from_cwd(p).unwrap())))
}
- pub fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
- if let Some(path) = path {
- let (resolved_path, display_path) = resolved_and_display_path(path);
- let (result, prompted, is_allow_all) =
- self.query(Some(&resolved_path)).check(
- self.name,
- None,
- Some(&format!("\"{}\"", display_path.display())),
- self.prompt,
- );
-
- if prompted {
- if result.is_ok() {
- if is_allow_all {
- self.granted_list.clear();
- self.global_state = PermissionState::Granted;
- } else {
- self.granted_list.insert(FfiDescriptor(resolved_path));
- }
- } else {
- self.denied_list.insert(FfiDescriptor(resolved_path));
- self.global_state = PermissionState::Denied;
- }
- }
-
- result
- } else {
- let (result, prompted, _) =
- self.query(None).check(self.name, None, None, self.prompt);
-
- if prompted {
- if result.is_ok() {
- self.global_state = PermissionState::Granted;
- } else {
- self.global_state = PermissionState::Denied;
- }
- }
-
- result
- }
+ pub fn check(
+ &mut self,
+ path: &Path,
+ api_name: Option<&str>,
+ ) -> Result<(), AnyError> {
+ self.check_desc(
+ &Some(FfiDescriptor(resolve_from_cwd(path)?)),
+ true,
+ api_name,
+ || Some(format!("\"{}\"", path.display())),
+ )
}
- pub fn check_all(&mut self) -> Result<(), AnyError> {
- let (result, prompted, _) =
- self
- .query(None)
- .check(self.name, None, Some("all"), self.prompt);
- if prompted {
- if result.is_ok() {
- self.global_state = PermissionState::Granted;
- } else {
- self.global_state = PermissionState::Denied;
- }
- }
- result
+ pub fn check_partial(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
+ let desc = match path {
+ Some(path) => Some(FfiDescriptor(resolve_from_cwd(path)?)),
+ None => None,
+ };
+ self.check_desc(&desc, false, None, || {
+ Some(format!("\"{}\"", path?.display()))
+ })
}
-}
-impl Default for UnaryPermission<FfiDescriptor> {
- fn default() -> Self {
- UnaryPermission::<FfiDescriptor> {
- name: "ffi",
- description: "load a dynamic library",
- global_state: Default::default(),
- granted_list: Default::default(),
- denied_list: Default::default(),
- prompt: false,
- }
+ pub fn check_all(&mut self) -> Result<(), AnyError> {
+ self.check_desc(&None, false, Some("all"), || None)
}
}
@@ -1553,14 +1000,14 @@ pub struct Permissions {
impl Default for Permissions {
fn default() -> Self {
Self {
- read: Permissions::new_read(&None, false).unwrap(),
- write: Permissions::new_write(&None, false).unwrap(),
- net: Permissions::new_net(&None, false).unwrap(),
- env: Permissions::new_env(&None, false).unwrap(),
- sys: Permissions::new_sys(&None, false).unwrap(),
- run: Permissions::new_run(&None, false).unwrap(),
- ffi: Permissions::new_ffi(&None, false).unwrap(),
- hrtime: Permissions::new_hrtime(false),
+ read: Permissions::new_read(&None, &None, false).unwrap(),
+ write: Permissions::new_write(&None, &None, false).unwrap(),
+ net: Permissions::new_net(&None, &None, false).unwrap(),
+ env: Permissions::new_env(&None, &None, false).unwrap(),
+ sys: Permissions::new_sys(&None, &None, false).unwrap(),
+ run: Permissions::new_run(&None, &None, false).unwrap(),
+ ffi: Permissions::new_ffi(&None, &None, false).unwrap(),
+ hrtime: Permissions::new_hrtime(false, false),
}
}
}
@@ -1568,150 +1015,134 @@ impl Default for Permissions {
#[derive(Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize)]
pub struct PermissionsOptions {
pub allow_env: Option<Vec<String>>,
+ pub deny_env: Option<Vec<String>>,
pub allow_hrtime: bool,
+ pub deny_hrtime: bool,
pub allow_net: Option<Vec<String>>,
+ pub deny_net: Option<Vec<String>>,
pub allow_ffi: Option<Vec<PathBuf>>,
+ pub deny_ffi: Option<Vec<PathBuf>>,
pub allow_read: Option<Vec<PathBuf>>,
+ pub deny_read: Option<Vec<PathBuf>>,
pub allow_run: Option<Vec<String>>,
+ pub deny_run: Option<Vec<String>>,
pub allow_sys: Option<Vec<String>>,
+ pub deny_sys: Option<Vec<String>>,
pub allow_write: Option<Vec<PathBuf>>,
+ pub deny_write: Option<Vec<PathBuf>>,
pub prompt: bool,
}
impl Permissions {
pub fn new_read(
- state: &Option<Vec<PathBuf>>,
+ allow_list: &Option<Vec<PathBuf>>,
+ deny_list: &Option<Vec<PathBuf>>,
prompt: bool,
) -> Result<UnaryPermission<ReadDescriptor>, AnyError> {
Ok(UnaryPermission::<ReadDescriptor> {
- global_state: global_state_from_option(state),
- granted_list: resolve_read_allowlist(state)?,
+ granted_global: global_from_option(allow_list),
+ granted_list: parse_path_list(allow_list, ReadDescriptor)?,
+ flag_denied_global: global_from_option(deny_list),
+ flag_denied_list: parse_path_list(deny_list, ReadDescriptor)?,
prompt,
..Default::default()
})
}
pub fn new_write(
- state: &Option<Vec<PathBuf>>,
+ allow_list: &Option<Vec<PathBuf>>,
+ deny_list: &Option<Vec<PathBuf>>,
prompt: bool,
) -> Result<UnaryPermission<WriteDescriptor>, AnyError> {
- Ok(UnaryPermission::<WriteDescriptor> {
- global_state: global_state_from_option(state),
- granted_list: resolve_write_allowlist(state)?,
+ Ok(UnaryPermission {
+ granted_global: global_from_option(allow_list),
+ granted_list: parse_path_list(allow_list, WriteDescriptor)?,
+ flag_denied_global: global_from_option(deny_list),
+ flag_denied_list: parse_path_list(deny_list, WriteDescriptor)?,
prompt,
..Default::default()
})
}
pub fn new_net(
- state: &Option<Vec<String>>,
+ allow_list: &Option<Vec<String>>,
+ deny_list: &Option<Vec<String>>,
prompt: bool,
) -> Result<UnaryPermission<NetDescriptor>, AnyError> {
Ok(UnaryPermission::<NetDescriptor> {
- global_state: global_state_from_option(state),
- granted_list: state
- .as_ref()
- .map(|v| {
- v.iter()
- .map(|x| NetDescriptor::from_str(x))
- .collect::<Result<HashSet<NetDescriptor>, AnyError>>()
- })
- .unwrap_or_else(|| Ok(HashSet::new()))?,
+ granted_global: global_from_option(allow_list),
+ granted_list: parse_net_list(allow_list)?,
+ flag_denied_global: global_from_option(deny_list),
+ flag_denied_list: parse_net_list(deny_list)?,
prompt,
..Default::default()
})
}
pub fn new_env(
- state: &Option<Vec<String>>,
+ allow_list: &Option<Vec<String>>,
+ deny_list: &Option<Vec<String>>,
prompt: bool,
) -> Result<UnaryPermission<EnvDescriptor>, AnyError> {
Ok(UnaryPermission::<EnvDescriptor> {
- global_state: global_state_from_option(state),
- granted_list: state
- .as_ref()
- .map(|v| {
- v.iter()
- .map(|x| {
- if x.is_empty() {
- Err(AnyError::msg("Empty path is not allowed"))
- } else {
- Ok(EnvDescriptor::new(x))
- }
- })
- .collect()
- })
- .unwrap_or_else(|| Ok(HashSet::new()))?,
+ granted_global: global_from_option(allow_list),
+ granted_list: parse_env_list(allow_list)?,
+ flag_denied_global: global_from_option(deny_list),
+ flag_denied_list: parse_env_list(deny_list)?,
prompt,
..Default::default()
})
}
pub fn new_sys(
- state: &Option<Vec<String>>,
+ allow_list: &Option<Vec<String>>,
+ deny_list: &Option<Vec<String>>,
prompt: bool,
) -> Result<UnaryPermission<SysDescriptor>, AnyError> {
Ok(UnaryPermission::<SysDescriptor> {
- global_state: global_state_from_option(state),
- granted_list: state
- .as_ref()
- .map(|v| {
- v.iter()
- .map(|x| {
- if x.is_empty() {
- Err(AnyError::msg("empty"))
- } else {
- Ok(SysDescriptor(x.to_string()))
- }
- })
- .collect()
- })
- .unwrap_or_else(|| Ok(HashSet::new()))?,
+ granted_global: global_from_option(allow_list),
+ granted_list: parse_sys_list(allow_list)?,
+ flag_denied_global: global_from_option(deny_list),
+ flag_denied_list: parse_sys_list(deny_list)?,
prompt,
..Default::default()
})
}
pub fn new_run(
- state: &Option<Vec<String>>,
+ allow_list: &Option<Vec<String>>,
+ deny_list: &Option<Vec<String>>,
prompt: bool,
) -> Result<UnaryPermission<RunDescriptor>, AnyError> {
Ok(UnaryPermission::<RunDescriptor> {
- global_state: global_state_from_option(state),
- granted_list: state
- .as_ref()
- .map(|v| {
- v.iter()
- .map(|x| {
- if x.is_empty() {
- Err(AnyError::msg("Empty path is not allowed"))
- } else {
- Ok(RunDescriptor::from_str(x).unwrap())
- }
- })
- .collect()
- })
- .unwrap_or_else(|| Ok(HashSet::new()))?,
+ granted_global: global_from_option(allow_list),
+ granted_list: parse_run_list(allow_list)?,
+ flag_denied_global: global_from_option(deny_list),
+ flag_denied_list: parse_run_list(deny_list)?,
prompt,
..Default::default()
})
}
pub fn new_ffi(
- state: &Option<Vec<PathBuf>>,
+ allow_list: &Option<Vec<PathBuf>>,
+ deny_list: &Option<Vec<PathBuf>>,
prompt: bool,
) -> Result<UnaryPermission<FfiDescriptor>, AnyError> {
Ok(UnaryPermission::<FfiDescriptor> {
- global_state: global_state_from_option(state),
- granted_list: resolve_ffi_allowlist(state)?,
+ granted_global: global_from_option(allow_list),
+ granted_list: parse_path_list(allow_list, FfiDescriptor)?,
+ flag_denied_global: global_from_option(deny_list),
+ flag_denied_list: parse_path_list(deny_list, FfiDescriptor)?,
prompt,
..Default::default()
})
}
- pub fn new_hrtime(state: bool) -> UnitPermission {
- unit_permission_from_flag_bool(
- state,
+ pub fn new_hrtime(allow_state: bool, deny_state: bool) -> UnitPermission {
+ unit_permission_from_flag_bools(
+ allow_state,
+ deny_state,
"hrtime",
"high precision time",
false, // never prompt for hrtime
@@ -1720,27 +1151,35 @@ impl Permissions {
pub fn from_options(opts: &PermissionsOptions) -> Result<Self, AnyError> {
Ok(Self {
- read: Permissions::new_read(&opts.allow_read, opts.prompt)?,
- write: Permissions::new_write(&opts.allow_write, opts.prompt)?,
- net: Permissions::new_net(&opts.allow_net, opts.prompt)?,
- env: Permissions::new_env(&opts.allow_env, opts.prompt)?,
- sys: Permissions::new_sys(&opts.allow_sys, opts.prompt)?,
- run: Permissions::new_run(&opts.allow_run, opts.prompt)?,
- ffi: Permissions::new_ffi(&opts.allow_ffi, opts.prompt)?,
- hrtime: Permissions::new_hrtime(opts.allow_hrtime),
+ read: Permissions::new_read(
+ &opts.allow_read,
+ &opts.deny_read,
+ opts.prompt,
+ )?,
+ write: Permissions::new_write(
+ &opts.allow_write,
+ &opts.deny_write,
+ opts.prompt,
+ )?,
+ net: Permissions::new_net(&opts.allow_net, &opts.deny_net, opts.prompt)?,
+ env: Permissions::new_env(&opts.allow_env, &opts.deny_env, opts.prompt)?,
+ sys: Permissions::new_sys(&opts.allow_sys, &opts.deny_sys, opts.prompt)?,
+ run: Permissions::new_run(&opts.allow_run, &opts.deny_run, opts.prompt)?,
+ ffi: Permissions::new_ffi(&opts.allow_ffi, &opts.deny_ffi, opts.prompt)?,
+ hrtime: Permissions::new_hrtime(opts.allow_hrtime, opts.deny_hrtime),
})
}
pub fn allow_all() -> Self {
Self {
- read: Permissions::new_read(&Some(vec![]), false).unwrap(),
- write: Permissions::new_write(&Some(vec![]), false).unwrap(),
- net: Permissions::new_net(&Some(vec![]), false).unwrap(),
- env: Permissions::new_env(&Some(vec![]), false).unwrap(),
- sys: Permissions::new_sys(&Some(vec![]), false).unwrap(),
- run: Permissions::new_run(&Some(vec![]), false).unwrap(),
- ffi: Permissions::new_ffi(&Some(vec![]), false).unwrap(),
- hrtime: Permissions::new_hrtime(true),
+ read: Permissions::new_read(&Some(vec![]), &None, false).unwrap(),
+ write: Permissions::new_write(&Some(vec![]), &None, false).unwrap(),
+ net: Permissions::new_net(&Some(vec![]), &None, false).unwrap(),
+ env: Permissions::new_env(&Some(vec![]), &None, false).unwrap(),
+ sys: Permissions::new_sys(&Some(vec![]), &None, false).unwrap(),
+ run: Permissions::new_run(&Some(vec![]), &None, false).unwrap(),
+ ffi: Permissions::new_ffi(&Some(vec![]), &None, false).unwrap(),
+ hrtime: Permissions::new_hrtime(true, false),
}
}
@@ -1990,6 +1429,14 @@ impl deno_fs::FsPermissions for PermissionsContainer {
self.0.lock().write.check(path, Some(api_name))
}
+ fn check_write_partial(
+ &mut self,
+ path: &Path,
+ api_name: &str,
+ ) -> Result<(), AnyError> {
+ self.0.lock().write.check_partial(path, Some(api_name))
+ }
+
fn check_write_blind(
&mut self,
p: &Path,
@@ -2013,14 +1460,14 @@ impl deno_fs::FsPermissions for PermissionsContainer {
impl deno_napi::NapiPermissions for PermissionsContainer {
#[inline(always)]
fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
- self.0.lock().ffi.check(path)
+ self.0.lock().ffi.check(path.unwrap(), None)
}
}
impl deno_ffi::FfiPermissions for PermissionsContainer {
#[inline(always)]
- fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
- self.0.lock().ffi.check(path)
+ fn check_partial(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
+ self.0.lock().ffi.check_partial(path)
}
}
@@ -2036,8 +1483,9 @@ impl deno_kv::sqlite::SqliteDbHandlerPermissions for PermissionsContainer {
}
}
-fn unit_permission_from_flag_bool(
- flag: bool,
+fn unit_permission_from_flag_bools(
+ allow_flag: bool,
+ deny_flag: bool,
name: &'static str,
description: &'static str,
prompt: bool,
@@ -2045,7 +1493,9 @@ fn unit_permission_from_flag_bool(
UnitPermission {
name,
description,
- state: if flag {
+ state: if deny_flag {
+ PermissionState::Denied
+ } else if allow_flag {
PermissionState::Granted
} else {
PermissionState::Prompt
@@ -2054,24 +1504,32 @@ fn unit_permission_from_flag_bool(
}
}
-fn global_state_from_option<T>(flag: &Option<Vec<T>>) -> PermissionState {
- if matches!(flag, Some(v) if v.is_empty()) {
- PermissionState::Granted
+fn global_from_option<T>(flag: &Option<Vec<T>>) -> bool {
+ matches!(flag, Some(v) if v.is_empty())
+}
+
+fn parse_net_list(
+ list: &Option<Vec<String>>,
+) -> Result<HashSet<NetDescriptor>, AnyError> {
+ if let Some(v) = list {
+ v.iter()
+ .map(|x| NetDescriptor::from_str(x))
+ .collect::<Result<HashSet<NetDescriptor>, AnyError>>()
} else {
- PermissionState::Prompt
+ Ok(HashSet::new())
}
}
-pub fn resolve_read_allowlist(
- allow: &Option<Vec<PathBuf>>,
-) -> Result<HashSet<ReadDescriptor>, AnyError> {
- if let Some(v) = allow {
+fn parse_env_list(
+ list: &Option<Vec<String>>,
+) -> Result<HashSet<EnvDescriptor>, AnyError> {
+ if let Some(v) = list {
v.iter()
- .map(|raw_path| {
- if raw_path.as_os_str().is_empty() {
+ .map(|x| {
+ if x.is_empty() {
Err(AnyError::msg("Empty path is not allowed"))
} else {
- resolve_from_cwd(Path::new(&raw_path)).map(ReadDescriptor)
+ Ok(EnvDescriptor::new(x))
}
})
.collect()
@@ -2080,16 +1538,17 @@ pub fn resolve_read_allowlist(
}
}
-pub fn resolve_write_allowlist(
- allow: &Option<Vec<PathBuf>>,
-) -> Result<HashSet<WriteDescriptor>, AnyError> {
- if let Some(v) = allow {
+fn parse_path_list<T: Descriptor + Hash>(
+ list: &Option<Vec<PathBuf>>,
+ f: fn(PathBuf) -> T,
+) -> Result<HashSet<T>, AnyError> {
+ if let Some(v) = list {
v.iter()
.map(|raw_path| {
if raw_path.as_os_str().is_empty() {
Err(AnyError::msg("Empty path is not allowed"))
} else {
- resolve_from_cwd(Path::new(&raw_path)).map(WriteDescriptor)
+ resolve_from_cwd(Path::new(&raw_path)).map(f)
}
})
.collect()
@@ -2098,16 +1557,16 @@ pub fn resolve_write_allowlist(
}
}
-pub fn resolve_ffi_allowlist(
- allow: &Option<Vec<PathBuf>>,
-) -> Result<HashSet<FfiDescriptor>, AnyError> {
- if let Some(v) = allow {
+fn parse_sys_list(
+ list: &Option<Vec<String>>,
+) -> Result<HashSet<SysDescriptor>, AnyError> {
+ if let Some(v) = list {
v.iter()
- .map(|raw_path| {
- if raw_path.as_os_str().is_empty() {
- Err(AnyError::msg("Empty path is not allowed"))
+ .map(|x| {
+ if x.is_empty() {
+ Err(AnyError::msg("empty"))
} else {
- resolve_from_cwd(Path::new(&raw_path)).map(FfiDescriptor)
+ Ok(SysDescriptor(x.to_string()))
}
})
.collect()
@@ -2116,13 +1575,22 @@ pub fn resolve_ffi_allowlist(
}
}
-/// Arbitrary helper. Resolves the path from CWD, and also gets a path that
-/// can be displayed without leaking the CWD when not allowed.
-#[inline]
-fn resolved_and_display_path(path: &Path) -> (PathBuf, PathBuf) {
- let resolved_path = resolve_from_cwd(path).unwrap();
- let display_path = path.to_path_buf();
- (resolved_path, display_path)
+fn parse_run_list(
+ list: &Option<Vec<String>>,
+) -> Result<HashSet<RunDescriptor>, AnyError> {
+ if let Some(v) = list {
+ v.iter()
+ .map(|x| {
+ if x.is_empty() {
+ Err(AnyError::msg("Empty path is not allowed"))
+ } else {
+ Ok(RunDescriptor::from_str(x).unwrap())
+ }
+ })
+ .collect()
+ } else {
+ Ok(HashSet::new())
+ }
}
fn escalation_error() -> AnyError {
@@ -2397,12 +1865,11 @@ pub fn create_child_permissions(
if main_perms.env.check_all().is_err() {
return Err(escalation_error());
}
- worker_perms.env.global_state = PermissionState::Granted;
+ worker_perms.env.granted_global = true;
}
ChildUnaryPermissionArg::NotGranted => {}
ChildUnaryPermissionArg::GrantedList(granted_list) => {
- worker_perms.env.granted_list =
- Permissions::new_env(&Some(granted_list), false)?.granted_list;
+ worker_perms.env.granted_list = parse_env_list(&Some(granted_list))?;
if !worker_perms
.env
.granted_list
@@ -2413,10 +1880,11 @@ pub fn create_child_permissions(
}
}
}
- worker_perms.env.denied_list = main_perms.env.denied_list.clone();
- if main_perms.env.global_state == PermissionState::Denied {
- worker_perms.env.global_state = PermissionState::Denied;
- }
+ worker_perms.env.flag_denied_global = main_perms.env.flag_denied_global;
+ worker_perms.env.flag_denied_list = main_perms.env.flag_denied_list.clone();
+ worker_perms.env.prompt_denied_global = main_perms.env.prompt_denied_global;
+ worker_perms.env.prompt_denied_list =
+ main_perms.env.prompt_denied_list.clone();
worker_perms.env.prompt = main_perms.env.prompt;
match child_permissions_arg.sys {
ChildUnaryPermissionArg::Inherit => {
@@ -2426,12 +1894,11 @@ pub fn create_child_permissions(
if main_perms.sys.check_all().is_err() {
return Err(escalation_error());
}
- worker_perms.sys.global_state = PermissionState::Granted;
+ worker_perms.sys.granted_global = true;
}
ChildUnaryPermissionArg::NotGranted => {}
ChildUnaryPermissionArg::GrantedList(granted_list) => {
- worker_perms.sys.granted_list =
- Permissions::new_sys(&Some(granted_list), false)?.granted_list;
+ worker_perms.sys.granted_list = parse_sys_list(&Some(granted_list))?;
if !worker_perms
.sys
.granted_list
@@ -2442,10 +1909,11 @@ pub fn create_child_permissions(
}
}
}
- worker_perms.sys.denied_list = main_perms.sys.denied_list.clone();
- if main_perms.sys.global_state == PermissionState::Denied {
- worker_perms.sys.global_state = PermissionState::Denied;
- }
+ worker_perms.sys.flag_denied_global = main_perms.sys.flag_denied_global;
+ worker_perms.sys.flag_denied_list = main_perms.sys.flag_denied_list.clone();
+ worker_perms.sys.prompt_denied_global = main_perms.sys.prompt_denied_global;
+ worker_perms.sys.prompt_denied_list =
+ main_perms.sys.prompt_denied_list.clone();
worker_perms.sys.prompt = main_perms.sys.prompt;
match child_permissions_arg.hrtime {
ChildUnitPermissionArg::Inherit => {
@@ -2471,12 +1939,11 @@ pub fn create_child_permissions(
if main_perms.net.check_all().is_err() {
return Err(escalation_error());
}
- worker_perms.net.global_state = PermissionState::Granted;
+ worker_perms.net.granted_global = true;
}
ChildUnaryPermissionArg::NotGranted => {}
ChildUnaryPermissionArg::GrantedList(granted_list) => {
- worker_perms.net.granted_list =
- Permissions::new_net(&Some(granted_list), false)?.granted_list;
+ worker_perms.net.granted_list = parse_net_list(&Some(granted_list))?;
if !worker_perms
.net
.granted_list
@@ -2487,10 +1954,11 @@ pub fn create_child_permissions(
}
}
}
- worker_perms.net.denied_list = main_perms.net.denied_list.clone();
- if main_perms.net.global_state == PermissionState::Denied {
- worker_perms.net.global_state = PermissionState::Denied;
- }
+ worker_perms.net.flag_denied_global = main_perms.net.flag_denied_global;
+ worker_perms.net.flag_denied_list = main_perms.net.flag_denied_list.clone();
+ worker_perms.net.prompt_denied_global = main_perms.net.prompt_denied_global;
+ worker_perms.net.prompt_denied_list =
+ main_perms.net.prompt_denied_list.clone();
worker_perms.net.prompt = main_perms.net.prompt;
match child_permissions_arg.ffi {
ChildUnaryPermissionArg::Inherit => {
@@ -2500,29 +1968,29 @@ pub fn create_child_permissions(
if main_perms.ffi.check_all().is_err() {
return Err(escalation_error());
}
- worker_perms.ffi.global_state = PermissionState::Granted;
+ worker_perms.ffi.granted_global = true;
}
ChildUnaryPermissionArg::NotGranted => {}
ChildUnaryPermissionArg::GrantedList(granted_list) => {
- worker_perms.ffi.granted_list = Permissions::new_ffi(
+ worker_perms.ffi.granted_list = parse_path_list(
&Some(granted_list.iter().map(PathBuf::from).collect()),
- false,
- )?
- .granted_list;
+ FfiDescriptor,
+ )?;
if !worker_perms
.ffi
.granted_list
.iter()
- .all(|desc| main_perms.ffi.check(Some(&desc.0)).is_ok())
+ .all(|desc| main_perms.ffi.check(&desc.0, None).is_ok())
{
return Err(escalation_error());
}
}
}
- worker_perms.ffi.denied_list = main_perms.ffi.denied_list.clone();
- if main_perms.ffi.global_state == PermissionState::Denied {
- worker_perms.ffi.global_state = PermissionState::Denied;
- }
+ worker_perms.ffi.flag_denied_global = main_perms.env.flag_denied_global;
+ worker_perms.ffi.flag_denied_list = main_perms.ffi.flag_denied_list.clone();
+ worker_perms.ffi.prompt_denied_global = main_perms.ffi.prompt_denied_global;
+ worker_perms.ffi.prompt_denied_list =
+ main_perms.ffi.prompt_denied_list.clone();
worker_perms.ffi.prompt = main_perms.ffi.prompt;
match child_permissions_arg.read {
ChildUnaryPermissionArg::Inherit => {
@@ -2532,15 +2000,14 @@ pub fn create_child_permissions(
if main_perms.read.check_all(None).is_err() {
return Err(escalation_error());
}
- worker_perms.read.global_state = PermissionState::Granted;
+ worker_perms.read.granted_global = true;
}
ChildUnaryPermissionArg::NotGranted => {}
ChildUnaryPermissionArg::GrantedList(granted_list) => {
- worker_perms.read.granted_list = Permissions::new_read(
+ worker_perms.read.granted_list = parse_path_list(
&Some(granted_list.iter().map(PathBuf::from).collect()),
- false,
- )?
- .granted_list;
+ ReadDescriptor,
+ )?;
if !worker_perms
.read
.granted_list
@@ -2551,10 +2018,11 @@ pub fn create_child_permissions(
}
}
}
- worker_perms.read.denied_list = main_perms.read.denied_list.clone();
- if main_perms.read.global_state == PermissionState::Denied {
- worker_perms.read.global_state = PermissionState::Denied;
- }
+ worker_perms.read.flag_denied_global = main_perms.read.flag_denied_global;
+ worker_perms.read.flag_denied_list = main_perms.read.flag_denied_list.clone();
+ worker_perms.read.prompt_denied_global = main_perms.read.prompt_denied_global;
+ worker_perms.read.prompt_denied_list =
+ main_perms.read.prompt_denied_list.clone();
worker_perms.read.prompt = main_perms.read.prompt;
match child_permissions_arg.run {
ChildUnaryPermissionArg::Inherit => {
@@ -2564,12 +2032,11 @@ pub fn create_child_permissions(
if main_perms.run.check_all(None).is_err() {
return Err(escalation_error());
}
- worker_perms.run.global_state = PermissionState::Granted;
+ worker_perms.run.granted_global = true;
}
ChildUnaryPermissionArg::NotGranted => {}
ChildUnaryPermissionArg::GrantedList(granted_list) => {
- worker_perms.run.granted_list =
- Permissions::new_run(&Some(granted_list), false)?.granted_list;
+ worker_perms.run.granted_list = parse_run_list(&Some(granted_list))?;
if !worker_perms
.run
.granted_list
@@ -2580,10 +2047,11 @@ pub fn create_child_permissions(
}
}
}
- worker_perms.run.denied_list = main_perms.run.denied_list.clone();
- if main_perms.run.global_state == PermissionState::Denied {
- worker_perms.run.global_state = PermissionState::Denied;
- }
+ worker_perms.run.flag_denied_global = main_perms.run.flag_denied_global;
+ worker_perms.run.flag_denied_list = main_perms.run.flag_denied_list.clone();
+ worker_perms.run.prompt_denied_global = main_perms.run.prompt_denied_global;
+ worker_perms.run.prompt_denied_list =
+ main_perms.run.prompt_denied_list.clone();
worker_perms.run.prompt = main_perms.run.prompt;
match child_permissions_arg.write {
ChildUnaryPermissionArg::Inherit => {
@@ -2593,15 +2061,14 @@ pub fn create_child_permissions(
if main_perms.write.check_all(None).is_err() {
return Err(escalation_error());
}
- worker_perms.write.global_state = PermissionState::Granted;
+ worker_perms.write.granted_global = true;
}
ChildUnaryPermissionArg::NotGranted => {}
ChildUnaryPermissionArg::GrantedList(granted_list) => {
- worker_perms.write.granted_list = Permissions::new_write(
+ worker_perms.write.granted_list = parse_path_list(
&Some(granted_list.iter().map(PathBuf::from).collect()),
- false,
- )?
- .granted_list;
+ WriteDescriptor,
+ )?;
if !worker_perms
.write
.granted_list
@@ -2612,10 +2079,13 @@ pub fn create_child_permissions(
}
}
}
- worker_perms.write.denied_list = main_perms.write.denied_list.clone();
- if main_perms.write.global_state == PermissionState::Denied {
- worker_perms.write.global_state = PermissionState::Denied;
- }
+ worker_perms.write.flag_denied_global = main_perms.write.flag_denied_global;
+ worker_perms.write.flag_denied_list =
+ main_perms.write.flag_denied_list.clone();
+ worker_perms.write.prompt_denied_global =
+ main_perms.write.prompt_denied_global;
+ worker_perms.write.prompt_denied_list =
+ main_perms.write.prompt_denied_list.clone();
worker_perms.write.prompt = main_perms.write.prompt;
Ok(worker_perms)
}
@@ -2659,7 +2129,7 @@ mod tests {
.is_ok());
assert!(perms
.ffi
- .check(Some(Path::new("/a/specific/dir/name")))
+ .check(Path::new("/a/specific/dir/name"), None)
.is_ok());
// Inside of /a/specific but outside of /a/specific/dir/name
@@ -2668,7 +2138,7 @@ mod tests {
.write
.check(Path::new("/a/specific/dir"), None)
.is_ok());
- assert!(perms.ffi.check(Some(Path::new("/a/specific/dir"))).is_ok());
+ assert!(perms.ffi.check(Path::new("/a/specific/dir"), None).is_ok());
// Inside of /a/specific and /a/specific/dir/name
assert!(perms
@@ -2681,7 +2151,7 @@ mod tests {
.is_ok());
assert!(perms
.ffi
- .check(Some(Path::new("/a/specific/dir/name/inner")))
+ .check(Path::new("/a/specific/dir/name/inner"), None)
.is_ok());
// Inside of /a/specific but outside of /a/specific/dir/name
@@ -2695,18 +2165,18 @@ mod tests {
.is_ok());
assert!(perms
.ffi
- .check(Some(Path::new("/a/specific/other/dir")))
+ .check(Path::new("/a/specific/other/dir"), None)
.is_ok());
// Exact match with /b/c
assert!(perms.read.check(Path::new("/b/c"), None).is_ok());
assert!(perms.write.check(Path::new("/b/c"), None).is_ok());
- assert!(perms.ffi.check(Some(Path::new("/b/c"))).is_ok());
+ assert!(perms.ffi.check(Path::new("/b/c"), None).is_ok());
// Sub path within /b/c
assert!(perms.read.check(Path::new("/b/c/sub/path"), None).is_ok());
assert!(perms.write.check(Path::new("/b/c/sub/path"), None).is_ok());
- assert!(perms.ffi.check(Some(Path::new("/b/c/sub/path"))).is_ok());
+ assert!(perms.ffi.check(Path::new("/b/c/sub/path"), None).is_ok());
// Sub path within /b/c, needs normalizing
assert!(perms
@@ -2719,18 +2189,18 @@ mod tests {
.is_ok());
assert!(perms
.ffi
- .check(Some(Path::new("/b/c/sub/path/../path/.")))
+ .check(Path::new("/b/c/sub/path/../path/."), None)
.is_ok());
// Inside of /b but outside of /b/c
assert!(perms.read.check(Path::new("/b/e"), None).is_err());
assert!(perms.write.check(Path::new("/b/e"), None).is_err());
- assert!(perms.ffi.check(Some(Path::new("/b/e"))).is_err());
+ assert!(perms.ffi.check(Path::new("/b/e"), None).is_err());
// Inside of /a but outside of /a/specific
assert!(perms.read.check(Path::new("/a/b"), None).is_err());
assert!(perms.write.check(Path::new("/a/b"), None).is_err());
- assert!(perms.ffi.check(Some(Path::new("/a/b"))).is_err());
+ assert!(perms.ffi.check(Path::new("/a/b"), None).is_err());
}
#[test]
@@ -2773,7 +2243,13 @@ mod tests {
];
for (host, port, is_ok) in domain_tests {
- assert_eq!(is_ok, perms.net.check(&(host, Some(port)), None).is_ok());
+ assert_eq!(
+ is_ok,
+ perms.net.check(&(host, Some(port)), None).is_ok(),
+ "{}:{}",
+ host,
+ port,
+ );
}
}
@@ -2905,7 +2381,7 @@ mod tests {
for (url_str, is_ok) in url_tests {
let u = url::Url::parse(url_str).unwrap();
- assert_eq!(is_ok, perms.net.check_url(&u, None).is_ok());
+ assert_eq!(is_ok, perms.net.check_url(&u, None).is_ok(), "{}", u);
}
}
@@ -2954,7 +2430,12 @@ mod tests {
}
for (specifier, expected) in fixtures {
- assert_eq!(perms.check_specifier(&specifier).is_ok(), expected);
+ assert_eq!(
+ perms.check_specifier(&specifier).is_ok(),
+ expected,
+ "{}",
+ specifier,
+ );
}
}
@@ -2984,42 +2465,91 @@ mod tests {
set_prompter(Box::new(TestPrompter));
let perms1 = Permissions::allow_all();
let perms2 = Permissions {
- read: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Permissions::new_read(&Some(vec![PathBuf::from("/foo")]), false)
- .unwrap()
- },
- write: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Permissions::new_write(&Some(vec![PathBuf::from("/foo")]), false)
- .unwrap()
- },
- ffi: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Permissions::new_ffi(&Some(vec![PathBuf::from("/foo")]), false)
- .unwrap()
- },
-
- net: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Permissions::new_net(&Some(svec!["127.0.0.1:8000"]), false).unwrap()
- },
- env: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Permissions::new_env(&Some(svec!["HOME"]), false).unwrap()
- },
- sys: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Permissions::new_sys(&Some(svec!["hostname"]), false).unwrap()
- },
- run: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Permissions::new_run(&Some(svec!["deno"]), false).unwrap()
- },
- hrtime: UnitPermission {
- state: PermissionState::Prompt,
- ..Permissions::new_hrtime(false)
- },
+ read: Permissions::new_read(
+ &Some(vec![PathBuf::from("/foo")]),
+ &None,
+ false,
+ )
+ .unwrap(),
+ write: Permissions::new_write(
+ &Some(vec![PathBuf::from("/foo")]),
+ &None,
+ false,
+ )
+ .unwrap(),
+ ffi: Permissions::new_ffi(
+ &Some(vec![PathBuf::from("/foo")]),
+ &None,
+ false,
+ )
+ .unwrap(),
+ net: Permissions::new_net(&Some(svec!["127.0.0.1:8000"]), &None, false)
+ .unwrap(),
+ env: Permissions::new_env(&Some(svec!["HOME"]), &None, false).unwrap(),
+ sys: Permissions::new_sys(&Some(svec!["hostname"]), &None, false)
+ .unwrap(),
+ run: Permissions::new_run(&Some(svec!["deno"]), &None, false).unwrap(),
+ hrtime: Permissions::new_hrtime(false, false),
+ };
+ let perms3 = Permissions {
+ read: Permissions::new_read(
+ &None,
+ &Some(vec![PathBuf::from("/foo")]),
+ false,
+ )
+ .unwrap(),
+ write: Permissions::new_write(
+ &None,
+ &Some(vec![PathBuf::from("/foo")]),
+ false,
+ )
+ .unwrap(),
+ ffi: Permissions::new_ffi(
+ &None,
+ &Some(vec![PathBuf::from("/foo")]),
+ false,
+ )
+ .unwrap(),
+ net: Permissions::new_net(&None, &Some(svec!["127.0.0.1:8000"]), false)
+ .unwrap(),
+ env: Permissions::new_env(&None, &Some(svec!["HOME"]), false).unwrap(),
+ sys: Permissions::new_sys(&None, &Some(svec!["hostname"]), false)
+ .unwrap(),
+ run: Permissions::new_run(&None, &Some(svec!["deno"]), false).unwrap(),
+ hrtime: Permissions::new_hrtime(false, true),
+ };
+ let perms4 = Permissions {
+ read: Permissions::new_read(
+ &Some(vec![]),
+ &Some(vec![PathBuf::from("/foo")]),
+ false,
+ )
+ .unwrap(),
+ write: Permissions::new_write(
+ &Some(vec![]),
+ &Some(vec![PathBuf::from("/foo")]),
+ false,
+ )
+ .unwrap(),
+ ffi: Permissions::new_ffi(
+ &Some(vec![]),
+ &Some(vec![PathBuf::from("/foo")]),
+ false,
+ )
+ .unwrap(),
+ net: Permissions::new_net(
+ &Some(vec![]),
+ &Some(svec!["127.0.0.1:8000"]),
+ false,
+ )
+ .unwrap(),
+ env: Permissions::new_env(&Some(vec![]), &Some(svec!["HOME"]), false)
+ .unwrap(),
+ sys: Permissions::new_sys(&Some(vec![]), &Some(svec!["hostname"]), false)
+ .unwrap(),
+ run: Permissions::new_run(&Some(vec![]), &Some(svec!["deno"]), false)
+ .unwrap(),
+ hrtime: Permissions::new_hrtime(true, true),
};
#[rustfmt::skip]
{
@@ -3028,34 +2558,77 @@ mod tests {
assert_eq!(perms2.read.query(None), PermissionState::Prompt);
assert_eq!(perms2.read.query(Some(Path::new("/foo"))), PermissionState::Granted);
assert_eq!(perms2.read.query(Some(Path::new("/foo/bar"))), PermissionState::Granted);
+ assert_eq!(perms3.read.query(None), PermissionState::Prompt);
+ assert_eq!(perms3.read.query(Some(Path::new("/foo"))), PermissionState::Denied);
+ assert_eq!(perms3.read.query(Some(Path::new("/foo/bar"))), PermissionState::Denied);
+ assert_eq!(perms4.read.query(None), PermissionState::GrantedPartial);
+ assert_eq!(perms4.read.query(Some(Path::new("/foo"))), PermissionState::Denied);
+ assert_eq!(perms4.read.query(Some(Path::new("/foo/bar"))), PermissionState::Denied);
+ assert_eq!(perms4.read.query(Some(Path::new("/bar"))), PermissionState::Granted);
assert_eq!(perms1.write.query(None), PermissionState::Granted);
assert_eq!(perms1.write.query(Some(Path::new("/foo"))), PermissionState::Granted);
assert_eq!(perms2.write.query(None), PermissionState::Prompt);
assert_eq!(perms2.write.query(Some(Path::new("/foo"))), PermissionState::Granted);
assert_eq!(perms2.write.query(Some(Path::new("/foo/bar"))), PermissionState::Granted);
+ assert_eq!(perms3.write.query(None), PermissionState::Prompt);
+ assert_eq!(perms3.write.query(Some(Path::new("/foo"))), PermissionState::Denied);
+ assert_eq!(perms3.write.query(Some(Path::new("/foo/bar"))), PermissionState::Denied);
+ assert_eq!(perms4.write.query(None), PermissionState::GrantedPartial);
+ assert_eq!(perms4.write.query(Some(Path::new("/foo"))), PermissionState::Denied);
+ assert_eq!(perms4.write.query(Some(Path::new("/foo/bar"))), PermissionState::Denied);
+ assert_eq!(perms4.write.query(Some(Path::new("/bar"))), PermissionState::Granted);
assert_eq!(perms1.ffi.query(None), PermissionState::Granted);
assert_eq!(perms1.ffi.query(Some(Path::new("/foo"))), PermissionState::Granted);
assert_eq!(perms2.ffi.query(None), PermissionState::Prompt);
assert_eq!(perms2.ffi.query(Some(Path::new("/foo"))), PermissionState::Granted);
assert_eq!(perms2.ffi.query(Some(Path::new("/foo/bar"))), PermissionState::Granted);
+ assert_eq!(perms3.ffi.query(None), PermissionState::Prompt);
+ assert_eq!(perms3.ffi.query(Some(Path::new("/foo"))), PermissionState::Denied);
+ assert_eq!(perms3.ffi.query(Some(Path::new("/foo/bar"))), PermissionState::Denied);
+ assert_eq!(perms4.ffi.query(None), PermissionState::GrantedPartial);
+ assert_eq!(perms4.ffi.query(Some(Path::new("/foo"))), PermissionState::Denied);
+ assert_eq!(perms4.ffi.query(Some(Path::new("/foo/bar"))), PermissionState::Denied);
+ assert_eq!(perms4.ffi.query(Some(Path::new("/bar"))), PermissionState::Granted);
assert_eq!(perms1.net.query::<&str>(None), PermissionState::Granted);
assert_eq!(perms1.net.query(Some(&("127.0.0.1", None))), PermissionState::Granted);
assert_eq!(perms2.net.query::<&str>(None), PermissionState::Prompt);
assert_eq!(perms2.net.query(Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted);
+ assert_eq!(perms3.net.query::<&str>(None), PermissionState::Prompt);
+ assert_eq!(perms3.net.query(Some(&("127.0.0.1", Some(8000)))), PermissionState::Denied);
+ assert_eq!(perms4.net.query::<&str>(None), PermissionState::GrantedPartial);
+ assert_eq!(perms4.net.query(Some(&("127.0.0.1", Some(8000)))), PermissionState::Denied);
+ assert_eq!(perms4.net.query(Some(&("192.168.0.1", Some(8000)))), PermissionState::Granted);
assert_eq!(perms1.env.query(None), PermissionState::Granted);
assert_eq!(perms1.env.query(Some("HOME")), PermissionState::Granted);
assert_eq!(perms2.env.query(None), PermissionState::Prompt);
assert_eq!(perms2.env.query(Some("HOME")), PermissionState::Granted);
+ assert_eq!(perms3.env.query(None), PermissionState::Prompt);
+ assert_eq!(perms3.env.query(Some("HOME")), PermissionState::Denied);
+ assert_eq!(perms4.env.query(None), PermissionState::GrantedPartial);
+ assert_eq!(perms4.env.query(Some("HOME")), PermissionState::Denied);
+ assert_eq!(perms4.env.query(Some("AWAY")), PermissionState::Granted);
assert_eq!(perms1.sys.query(None), PermissionState::Granted);
assert_eq!(perms1.sys.query(Some("HOME")), PermissionState::Granted);
- assert_eq!(perms2.env.query(None), PermissionState::Prompt);
+ assert_eq!(perms2.sys.query(None), PermissionState::Prompt);
assert_eq!(perms2.sys.query(Some("hostname")), PermissionState::Granted);
+ assert_eq!(perms3.sys.query(None), PermissionState::Prompt);
+ assert_eq!(perms3.sys.query(Some("hostname")), PermissionState::Denied);
+ assert_eq!(perms4.sys.query(None), PermissionState::GrantedPartial);
+ assert_eq!(perms4.sys.query(Some("hostname")), PermissionState::Denied);
+ assert_eq!(perms4.sys.query(Some("uid")), PermissionState::Granted);
assert_eq!(perms1.run.query(None), PermissionState::Granted);
assert_eq!(perms1.run.query(Some("deno")), PermissionState::Granted);
assert_eq!(perms2.run.query(None), PermissionState::Prompt);
assert_eq!(perms2.run.query(Some("deno")), PermissionState::Granted);
+ assert_eq!(perms3.run.query(None), PermissionState::Prompt);
+ assert_eq!(perms3.run.query(Some("deno")), PermissionState::Denied);
+ assert_eq!(perms4.run.query(None), PermissionState::GrantedPartial);
+ assert_eq!(perms4.run.query(Some("deno")), PermissionState::Denied);
+ assert_eq!(perms4.run.query(Some("node")), PermissionState::Granted);
assert_eq!(perms1.hrtime.query(), PermissionState::Granted);
assert_eq!(perms2.hrtime.query(), PermissionState::Prompt);
+ assert_eq!(perms3.hrtime.query(), PermissionState::Denied);
+ assert_eq!(perms4.hrtime.query(), PermissionState::Denied);
};
}
@@ -3111,54 +2684,35 @@ mod tests {
fn test_revoke() {
set_prompter(Box::new(TestPrompter));
let mut perms = Permissions {
- read: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Permissions::new_read(
- &Some(vec![PathBuf::from("/foo"), PathBuf::from("/foo/baz")]),
- false,
- )
- .unwrap()
- },
- write: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Permissions::new_write(
- &Some(vec![PathBuf::from("/foo"), PathBuf::from("/foo/baz")]),
- false,
- )
- .unwrap()
- },
- ffi: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Permissions::new_ffi(
- &Some(vec![PathBuf::from("/foo"), PathBuf::from("/foo/baz")]),
- false,
- )
- .unwrap()
- },
- net: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Permissions::new_net(
- &Some(svec!["127.0.0.1", "127.0.0.1:8000"]),
- false,
- )
- .unwrap()
- },
- env: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Permissions::new_env(&Some(svec!["HOME"]), false).unwrap()
- },
- sys: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Permissions::new_sys(&Some(svec!["hostname"]), false).unwrap()
- },
- run: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Permissions::new_run(&Some(svec!["deno"]), false).unwrap()
- },
- hrtime: UnitPermission {
- state: PermissionState::Denied,
- ..Permissions::new_hrtime(false)
- },
+ read: Permissions::new_read(
+ &Some(vec![PathBuf::from("/foo"), PathBuf::from("/foo/baz")]),
+ &None,
+ false,
+ )
+ .unwrap(),
+ write: Permissions::new_write(
+ &Some(vec![PathBuf::from("/foo"), PathBuf::from("/foo/baz")]),
+ &None,
+ false,
+ )
+ .unwrap(),
+ ffi: Permissions::new_ffi(
+ &Some(vec![PathBuf::from("/foo"), PathBuf::from("/foo/baz")]),
+ &None,
+ false,
+ )
+ .unwrap(),
+ net: Permissions::new_net(
+ &Some(svec!["127.0.0.1", "127.0.0.1:8000"]),
+ &None,
+ false,
+ )
+ .unwrap(),
+ env: Permissions::new_env(&Some(svec!["HOME"]), &None, false).unwrap(),
+ sys: Permissions::new_sys(&Some(svec!["hostname"]), &None, false)
+ .unwrap(),
+ run: Permissions::new_run(&Some(svec!["deno"]), &None, false).unwrap(),
+ hrtime: Permissions::new_hrtime(false, true),
};
#[rustfmt::skip]
{
@@ -3185,14 +2739,14 @@ mod tests {
fn test_check() {
set_prompter(Box::new(TestPrompter));
let mut perms = Permissions {
- read: Permissions::new_read(&None, true).unwrap(),
- write: Permissions::new_write(&None, true).unwrap(),
- net: Permissions::new_net(&None, true).unwrap(),
- env: Permissions::new_env(&None, true).unwrap(),
- sys: Permissions::new_sys(&None, true).unwrap(),
- run: Permissions::new_run(&None, true).unwrap(),
- ffi: Permissions::new_ffi(&None, true).unwrap(),
- hrtime: Permissions::new_hrtime(false),
+ read: Permissions::new_read(&None, &None, true).unwrap(),
+ write: Permissions::new_write(&None, &None, true).unwrap(),
+ net: Permissions::new_net(&None, &None, true).unwrap(),
+ env: Permissions::new_env(&None, &None, true).unwrap(),
+ sys: Permissions::new_sys(&None, &None, true).unwrap(),
+ run: Permissions::new_run(&None, &None, true).unwrap(),
+ ffi: Permissions::new_ffi(&None, &None, true).unwrap(),
+ hrtime: Permissions::new_hrtime(false, false),
};
let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
@@ -3210,10 +2764,10 @@ mod tests {
assert!(perms.write.check(Path::new("/bar"), None).is_err());
prompt_value.set(true);
- assert!(perms.ffi.check(Some(Path::new("/foo"))).is_ok());
+ assert!(perms.ffi.check(Path::new("/foo"), None).is_ok());
prompt_value.set(false);
- assert!(perms.ffi.check(Some(Path::new("/foo"))).is_ok());
- assert!(perms.ffi.check(Some(Path::new("/bar"))).is_err());
+ assert!(perms.ffi.check(Path::new("/foo"), None).is_ok());
+ assert!(perms.ffi.check(Path::new("/bar"), None).is_err());
prompt_value.set(true);
assert!(perms.net.check(&("127.0.0.1", Some(8000)), None).is_ok());
@@ -3249,14 +2803,14 @@ mod tests {
fn test_check_fail() {
set_prompter(Box::new(TestPrompter));
let mut perms = Permissions {
- read: Permissions::new_read(&None, true).unwrap(),
- write: Permissions::new_write(&None, true).unwrap(),
- net: Permissions::new_net(&None, true).unwrap(),
- env: Permissions::new_env(&None, true).unwrap(),
- sys: Permissions::new_sys(&None, true).unwrap(),
- run: Permissions::new_run(&None, true).unwrap(),
- ffi: Permissions::new_ffi(&None, true).unwrap(),
- hrtime: Permissions::new_hrtime(false),
+ read: Permissions::new_read(&None, &None, true).unwrap(),
+ write: Permissions::new_write(&None, &None, true).unwrap(),
+ net: Permissions::new_net(&None, &None, true).unwrap(),
+ env: Permissions::new_env(&None, &None, true).unwrap(),
+ sys: Permissions::new_sys(&None, &None, true).unwrap(),
+ run: Permissions::new_run(&None, &None, true).unwrap(),
+ ffi: Permissions::new_ffi(&None, &None, true).unwrap(),
+ hrtime: Permissions::new_hrtime(false, false),
};
let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
@@ -3278,12 +2832,12 @@ mod tests {
assert!(perms.write.check(Path::new("/bar"), None).is_ok());
prompt_value.set(false);
- assert!(perms.ffi.check(Some(Path::new("/foo"))).is_err());
+ assert!(perms.ffi.check(Path::new("/foo"), None).is_err());
prompt_value.set(true);
- assert!(perms.ffi.check(Some(Path::new("/foo"))).is_err());
- assert!(perms.ffi.check(Some(Path::new("/bar"))).is_ok());
+ assert!(perms.ffi.check(Path::new("/foo"), None).is_err());
+ assert!(perms.ffi.check(Path::new("/bar"), None).is_ok());
prompt_value.set(false);
- assert!(perms.ffi.check(Some(Path::new("/bar"))).is_ok());
+ assert!(perms.ffi.check(Path::new("/bar"), None).is_ok());
prompt_value.set(false);
assert!(perms.net.check(&("127.0.0.1", Some(8000)), None).is_err());
@@ -3332,8 +2886,8 @@ mod tests {
let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
let mut perms = Permissions::allow_all();
perms.env = UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Permissions::new_env(&Some(svec!["HOME"]), false).unwrap()
+ granted_global: false,
+ ..Permissions::new_env(&Some(svec!["HOME"]), &None, false).unwrap()
};
prompt_value.set(true);
@@ -3504,9 +3058,10 @@ mod tests {
fn test_create_child_permissions() {
set_prompter(Box::new(TestPrompter));
let mut main_perms = Permissions {
- env: Permissions::new_env(&Some(vec![]), false).unwrap(),
- hrtime: Permissions::new_hrtime(true),
- net: Permissions::new_net(&Some(svec!["foo", "bar"]), false).unwrap(),
+ env: Permissions::new_env(&Some(vec![]), &None, false).unwrap(),
+ hrtime: Permissions::new_hrtime(true, false),
+ net: Permissions::new_net(&Some(svec!["foo", "bar"]), &None, false)
+ .unwrap(),
..Default::default()
};
assert_eq!(
@@ -3522,8 +3077,8 @@ mod tests {
)
.unwrap(),
Permissions {
- env: Permissions::new_env(&Some(vec![]), false).unwrap(),
- net: Permissions::new_net(&Some(svec!["foo"]), false).unwrap(),
+ env: Permissions::new_env(&Some(vec![]), &None, false).unwrap(),
+ net: Permissions::new_net(&Some(svec!["foo"]), &None, false).unwrap(),
..Default::default()
}
);
@@ -3591,18 +3146,36 @@ mod tests {
ChildPermissionsArg::none(),
)
.unwrap();
- assert_eq!(worker_perms.write.denied_list, main_perms.write.denied_list);
+ assert_eq!(
+ worker_perms.write.flag_denied_list,
+ main_perms.write.flag_denied_list
+ );
}
#[test]
fn test_handle_empty_value() {
set_prompter(Box::new(TestPrompter));
- assert!(Permissions::new_read(&Some(vec![PathBuf::new()]), false).is_err());
- assert!(Permissions::new_env(&Some(vec![String::new()]), false).is_err());
- assert!(Permissions::new_sys(&Some(vec![String::new()]), false).is_err());
- assert!(Permissions::new_run(&Some(vec![String::new()]), false).is_err());
- assert!(Permissions::new_ffi(&Some(vec![PathBuf::new()]), false).is_err());
- assert!(Permissions::new_net(&Some(svec![String::new()]), false).is_err());
- assert!(Permissions::new_write(&Some(vec![PathBuf::new()]), false).is_err());
+ assert!(
+ Permissions::new_read(&Some(vec![PathBuf::new()]), &None, false).is_err()
+ );
+ assert!(
+ Permissions::new_env(&Some(vec![String::new()]), &None, false).is_err()
+ );
+ assert!(
+ Permissions::new_sys(&Some(vec![String::new()]), &None, false).is_err()
+ );
+ assert!(
+ Permissions::new_run(&Some(vec![String::new()]), &None, false).is_err()
+ );
+ assert!(
+ Permissions::new_ffi(&Some(vec![PathBuf::new()]), &None, false).is_err()
+ );
+ assert!(
+ Permissions::new_net(&Some(svec![String::new()]), &None, false).is_err()
+ );
+ assert!(
+ Permissions::new_write(&Some(vec![PathBuf::new()]), &None, false)
+ .is_err()
+ );
}
}