diff options
author | Asher Gomez <ashersaupingomez@gmail.com> | 2023-08-03 21:19:19 +1000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-03 13:19:19 +0200 |
commit | 6fb7e8d93bb9fd8cdd81130a394ae6061930c4f6 (patch) | |
tree | 2ec6dc2be234ef5a42023c1d75f1fc1316d80f06 /cli/args/flags.rs | |
parent | db287e216dd752bfcb3484cbfd93225e8463c363 (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>
Diffstat (limited to 'cli/args/flags.rs')
-rw-r--r-- | cli/args/flags.rs | 615 |
1 files changed, 610 insertions, 5 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", |