summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
Diffstat (limited to 'cli')
-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
10 files changed, 756 insertions, 38 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: (