diff options
author | crowlKats <13135287+crowlKats@users.noreply.github.com> | 2021-03-17 22:45:12 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-17 17:45:12 -0400 |
commit | 0e70d9e59bc0e70f1921bb217ee00fc2e6facb69 (patch) | |
tree | d501399bd07855148ce817c568e7228bdfd87d87 /runtime/permissions.rs | |
parent | b3fe85163f303a1592335b23c25554dd0e39a4c4 (diff) |
refactor: clean up permission handling (#9367)
Diffstat (limited to 'runtime/permissions.rs')
-rw-r--r-- | runtime/permissions.rs | 1188 |
1 files changed, 581 insertions, 607 deletions
diff --git a/runtime/permissions.rs b/runtime/permissions.rs index 066f32747..5d28a0640 100644 --- a/runtime/permissions.rs +++ b/runtime/permissions.rs @@ -10,7 +10,6 @@ use deno_core::serde::Serialize; use deno_core::url; use deno_core::ModuleSpecifier; use std::collections::HashSet; -use std::env::current_dir; use std::fmt; use std::hash::Hash; #[cfg(not(test))] @@ -35,12 +34,21 @@ pub enum PermissionState { impl PermissionState { /// Check the permission state. - fn check(self, msg: &str, flag_name: &str) -> Result<(), AnyError> { + fn check(self, name: &str, info: Option<&str>) -> Result<(), AnyError> { if self == PermissionState::Granted { - log_perm_access(msg); + log_perm_access(&format!( + "{} access{}", + name, + info.map_or(Default::default(), |info| { format!(" to {}", info) }), + )); return Ok(()); } - let message = format!("{}, run again with the {} flag", msg, flag_name); + let message = format!( + "Requires {} access{}, run again with the --allow-{} flag", + name, + info.map_or(Default::default(), |info| { format!(" to {}", info) }), + name + ); Err(custom_error("PermissionDenied", message)) } } @@ -63,515 +71,505 @@ impl Default for PermissionState { #[derive(Clone, Debug, Default, Deserialize, PartialEq)] pub struct UnaryPermission<T: Eq + Hash> { + #[serde(skip)] + pub name: &'static str, + #[serde(skip)] + pub description: &'static str, pub global_state: PermissionState, pub granted_list: HashSet<T>, pub denied_list: HashSet<T>, } -#[derive(Clone, Debug, Default, PartialEq)] -pub struct Permissions { - pub read: UnaryPermission<PathBuf>, - pub write: UnaryPermission<PathBuf>, - pub net: UnaryPermission<String>, - pub env: PermissionState, - pub run: PermissionState, - pub plugin: PermissionState, - pub hrtime: PermissionState, -} +#[derive(Clone, Eq, PartialEq, Hash, Debug, Default, Deserialize)] +pub struct ReadPermission(pub PathBuf); -pub fn resolve_fs_allowlist(allow: &Option<Vec<PathBuf>>) -> HashSet<PathBuf> { - if let Some(v) = allow { - v.iter() - .map(|raw_path| resolve_from_cwd(Path::new(&raw_path)).unwrap()) - .collect() - } else { - HashSet::new() +impl UnaryPermission<ReadPermission> { + 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 + } } -} -#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] -pub struct PermissionsOptions { - pub allow_env: bool, - pub allow_hrtime: bool, - pub allow_net: Option<Vec<String>>, - pub allow_plugin: bool, - pub allow_read: Option<Vec<PathBuf>>, - pub allow_run: bool, - pub allow_write: Option<Vec<PathBuf>>, -} - -impl Permissions { - pub fn from_options(opts: &PermissionsOptions) -> Self { - fn global_state_from_flag_bool(flag: bool) -> PermissionState { - if flag { - PermissionState::Granted + 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 { + if permission_prompt(&format!( + "read access to \"{}\"", + display_path.display() + )) { + self + .granted_list + .retain(|path| !path.0.starts_with(&resolved_path)); + self.granted_list.insert(ReadPermission(resolved_path)); + PermissionState::Granted + } else { + self + .denied_list + .retain(|path| !resolved_path.starts_with(&path.0)); + self.denied_list.insert(ReadPermission(resolved_path)); + self.global_state = PermissionState::Denied; + PermissionState::Denied + } } else { - PermissionState::Prompt + state } - } - fn global_state_from_option<T>(flag: &Option<Vec<T>>) -> PermissionState { - if matches!(flag, Some(v) if v.is_empty()) { - PermissionState::Granted + } else { + let state = self.query(None); + if state == PermissionState::Prompt { + if permission_prompt("read access") { + self.granted_list.clear(); + self.global_state = PermissionState::Granted; + PermissionState::Granted + } else { + self.global_state = PermissionState::Denied; + PermissionState::Denied + } } else { - PermissionState::Prompt + state } } - Self { - read: UnaryPermission::<PathBuf> { - global_state: global_state_from_option(&opts.allow_read), - granted_list: resolve_fs_allowlist(&opts.allow_read), - ..Default::default() - }, - write: UnaryPermission::<PathBuf> { - global_state: global_state_from_option(&opts.allow_write), - granted_list: resolve_fs_allowlist(&opts.allow_write), - ..Default::default() - }, - net: UnaryPermission::<String> { - global_state: global_state_from_option(&opts.allow_net), - granted_list: opts - .allow_net - .as_ref() - .map(|v| v.iter().cloned().collect()) - .unwrap_or_else(HashSet::new), - ..Default::default() - }, - env: global_state_from_flag_bool(opts.allow_env), - run: global_state_from_flag_bool(opts.allow_run), - plugin: global_state_from_flag_bool(opts.allow_plugin), - hrtime: global_state_from_flag_bool(opts.allow_hrtime), - } } - /// Arbitrary helper. Resolves the path from CWD, and also gets a path that - /// can be displayed without leaking the CWD when not allowed. - fn resolved_and_display_path(&self, path: &Path) -> (PathBuf, PathBuf) { - let resolved_path = resolve_from_cwd(path).unwrap(); - let display_path = if path.is_absolute() { - path.to_path_buf() + 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_.0.starts_with(&path)); } else { - match self - .query_read(&Some(¤t_dir().unwrap())) - .check("", "") - { - Ok(_) => resolved_path.clone(), - Err(_) => path.to_path_buf(), + self.granted_list.clear(); + if self.global_state == PermissionState::Granted { + self.global_state = PermissionState::Prompt; } - }; - (resolved_path, display_path) + } + self.query(path) } - pub fn allow_all() -> Self { - Self { - read: UnaryPermission { - global_state: PermissionState::Granted, - ..Default::default() - }, - write: UnaryPermission { - global_state: PermissionState::Granted, - ..Default::default() - }, - net: UnaryPermission { - global_state: PermissionState::Granted, - ..Default::default() - }, - env: PermissionState::Granted, - run: PermissionState::Granted, - plugin: PermissionState::Granted, - hrtime: PermissionState::Granted, - } + pub fn check(&self, path: &Path) -> Result<(), AnyError> { + let (resolved_path, display_path) = resolved_and_display_path(path); + self + .query(Some(&resolved_path)) + .check(self.name, Some(&format!("\"{}\"", display_path.display()))) } - pub fn query_read(&self, path: &Option<&Path>) -> PermissionState { - let path = path.map(|p| resolve_from_cwd(p).unwrap()); - if self.read.global_state == PermissionState::Denied - && match path.as_ref() { - None => true, - Some(path) => check_path_blocklist(path, &self.read.denied_list), - } - { - return PermissionState::Denied; - } - if self.read.global_state == PermissionState::Granted - || match path.as_ref() { - None => false, - Some(path) => check_path_allowlist(path, &self.read.granted_list), - } - { - return PermissionState::Granted; - } - PermissionState::Prompt + /// As `check()`, but permission error messages will anonymize the path + /// by replacing it with the given `display`. + pub fn check_blind( + &self, + path: &Path, + display: &str, + ) -> Result<(), AnyError> { + let resolved_path = resolve_from_cwd(path).unwrap(); + self + .query(Some(&resolved_path)) + .check(self.name, Some(&format!("<{}>", display))) } +} + +#[derive(Clone, Eq, PartialEq, Hash, Debug, Default, Deserialize)] +pub struct WritePermission(pub PathBuf); - pub fn query_write(&self, path: &Option<&Path>) -> PermissionState { +impl UnaryPermission<WritePermission> { + pub fn query(&self, path: Option<&Path>) -> PermissionState { let path = path.map(|p| resolve_from_cwd(p).unwrap()); - if self.write.global_state == PermissionState::Denied + if self.global_state == PermissionState::Denied && match path.as_ref() { None => true, - Some(path) => check_path_blocklist(path, &self.write.denied_list), + Some(path) => self + .denied_list + .iter() + .any(|path_| path_.0.starts_with(path)), } { - return PermissionState::Denied; - } - if self.write.global_state == PermissionState::Granted + PermissionState::Denied + } else if self.global_state == PermissionState::Granted || match path.as_ref() { None => false, - Some(path) => check_path_allowlist(path, &self.write.granted_list), - } - { - return PermissionState::Granted; - } - PermissionState::Prompt - } - - pub fn query_net<T: AsRef<str>>( - &self, - host: &Option<&(T, Option<u16>)>, - ) -> PermissionState { - if self.net.global_state == PermissionState::Denied - && match host.as_ref() { - None => true, - Some(host) => check_host_blocklist(host, &self.net.denied_list), - } - { - return PermissionState::Denied; - } - if self.net.global_state == PermissionState::Granted - || match host.as_ref() { - None => false, - Some(host) => check_host_allowlist(host, &self.net.granted_list), + Some(path) => self + .granted_list + .iter() + .any(|path_| path.starts_with(&path_.0)), } { - return PermissionState::Granted; + PermissionState::Granted + } else { + PermissionState::Prompt } - PermissionState::Prompt - } - - pub fn query_env(&self) -> PermissionState { - self.env - } - - pub fn query_run(&self) -> PermissionState { - self.run } - pub fn query_plugin(&self) -> PermissionState { - self.plugin - } - - pub fn query_hrtime(&self) -> PermissionState { - self.hrtime - } - - pub fn request_read(&mut self, path: &Option<&Path>) -> PermissionState { + pub fn request(&mut self, path: Option<&Path>) -> PermissionState { if let Some(path) = path { - let (resolved_path, display_path) = self.resolved_and_display_path(path); - let state = self.query_read(&Some(&resolved_path)); + let (resolved_path, display_path) = resolved_and_display_path(path); + let state = self.query(Some(&resolved_path)); if state == PermissionState::Prompt { if permission_prompt(&format!( - "Deno requests read access to \"{}\"", + "write access to \"{}\"", display_path.display() )) { self - .read .granted_list - .retain(|path| !path.starts_with(&resolved_path)); - self.read.granted_list.insert(resolved_path); - return PermissionState::Granted; + .retain(|path| !path.0.starts_with(&resolved_path)); + self.granted_list.insert(WritePermission(resolved_path)); + PermissionState::Granted } else { self - .read .denied_list - .retain(|path| !resolved_path.starts_with(path)); - self.read.denied_list.insert(resolved_path); - self.read.global_state = PermissionState::Denied; - return PermissionState::Denied; + .retain(|path| !resolved_path.starts_with(&path.0)); + self.denied_list.insert(WritePermission(resolved_path)); + self.global_state = PermissionState::Denied; + PermissionState::Denied } + } else { + state } - state } else { - let state = self.query_read(&None); + let state = self.query(None); if state == PermissionState::Prompt { - if permission_prompt("Deno requests read access") { - self.read.granted_list.clear(); - self.read.global_state = PermissionState::Granted; - return PermissionState::Granted; + if permission_prompt("write access") { + self.granted_list.clear(); + self.global_state = PermissionState::Granted; + PermissionState::Granted } else { - self.read.global_state = PermissionState::Denied; - return PermissionState::Denied; + self.global_state = PermissionState::Denied; + PermissionState::Denied } + } else { + state } - state } } - pub fn request_write(&mut self, path: &Option<&Path>) -> PermissionState { + pub fn revoke(&mut self, path: Option<&Path>) -> PermissionState { if let Some(path) = path { - let (resolved_path, display_path) = self.resolved_and_display_path(path); - let state = self.query_write(&Some(&resolved_path)); - if state == PermissionState::Prompt { - if permission_prompt(&format!( - "Deno requests write access to \"{}\"", - display_path.display() - )) { - self - .write - .granted_list - .retain(|path| !path.starts_with(&resolved_path)); - self.write.granted_list.insert(resolved_path); - return PermissionState::Granted; - } else { - self - .write + let path = resolve_from_cwd(path).unwrap(); + self + .granted_list + .retain(|path_| !path_.0.starts_with(&path)); + } else { + self.granted_list.clear(); + if self.global_state == PermissionState::Granted { + self.global_state = PermissionState::Prompt; + } + } + self.query(path) + } + + pub fn check(&self, path: &Path) -> Result<(), AnyError> { + let (resolved_path, display_path) = resolved_and_display_path(path); + self + .query(Some(&resolved_path)) + .check(self.name, Some(&format!("\"{}\"", display_path.display()))) + } +} + +#[derive(Clone, Eq, PartialEq, Hash, Debug, Default, Deserialize)] +pub struct NetPermission(pub String, pub Option<u16>); + +impl NetPermission { + fn new<T: AsRef<str>>(host: &&(T, Option<u16>)) -> Self { + NetPermission(host.0.as_ref().to_string(), host.1) + } + + pub fn from_string(host: String) -> Self { + let url = url::Url::parse(&format!("http://{}", host)).unwrap(); + let hostname = url.host_str().unwrap().to_string(); + + NetPermission(hostname, url.port()) + } +} + +impl fmt::Display for NetPermission { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&match self.1 { + None => self.0.clone(), + Some(port) => format!("{}:{}", self.0, port), + }) + } +} + +impl UnaryPermission<NetPermission> { + pub fn query<T: AsRef<str>>( + &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 - .retain(|path| !resolved_path.starts_with(path)); - self.write.denied_list.insert(resolved_path); - self.write.global_state = PermissionState::Denied; - return PermissionState::Denied; - } + .iter() + .any(|host_| host.0.as_ref() == host_.0), + Some(_) => self.denied_list.contains(&NetPermission::new(host)), + }, } - state - } else { - let state = self.query_write(&None); - if state == PermissionState::Prompt { - if permission_prompt("Deno requests write access") { - self.write.granted_list.clear(); - self.write.global_state = PermissionState::Granted; - return PermissionState::Granted; - } else { - self.write.global_state = PermissionState::Denied; - return PermissionState::Denied; + { + PermissionState::Denied + } else if self.global_state == PermissionState::Granted + || match host.as_ref() { + None => false, + Some(host) => { + self.granted_list.contains(&NetPermission::new(&&( + host.0.as_ref().to_string(), + None, + ))) + || self.granted_list.contains(&NetPermission::new(host)) } } - state + { + PermissionState::Granted + } else { + PermissionState::Prompt } } - pub fn request_net<T: AsRef<str>>( + pub fn request<T: AsRef<str>>( &mut self, - host: &Option<&(T, Option<u16>)>, + host: Option<&(T, Option<u16>)>, ) -> PermissionState { if let Some(host) = host { - let state = self.query_net(&Some(host)); + let state = self.query(Some(host)); if state == PermissionState::Prompt { - let host_string = format_host(host); - if permission_prompt(&format!( - "Deno requests network access to \"{}\"", - host_string, - )) { + let host = NetPermission::new(&host); + if permission_prompt(&format!("network access to \"{}\"", host)) { if host.1.is_none() { - self - .net - .granted_list - .retain(|h| !h.starts_with(&format!("{}:", host.0.as_ref()))); + self.granted_list.retain(|h| h.0 != host.0); } - self.net.granted_list.insert(host_string); - return PermissionState::Granted; + self.granted_list.insert(host); + PermissionState::Granted } else { if host.1.is_some() { - self.net.denied_list.remove(host.0.as_ref()); + self.denied_list.remove(&host); } - self.net.denied_list.insert(host_string); - self.net.global_state = PermissionState::Denied; - return PermissionState::Denied; + self.denied_list.insert(host); + self.global_state = PermissionState::Denied; + PermissionState::Denied } + } else { + state } - state } else { - let state = self.query_net::<&str>(&None); + let state = self.query::<&str>(None); if state == PermissionState::Prompt { - if permission_prompt("Deno requests network access") { - self.net.granted_list.clear(); - self.net.global_state = PermissionState::Granted; - return PermissionState::Granted; + if permission_prompt("network access") { + self.granted_list.clear(); + self.global_state = PermissionState::Granted; + PermissionState::Granted } else { - self.net.global_state = PermissionState::Denied; - return PermissionState::Denied; + self.global_state = PermissionState::Denied; + PermissionState::Denied } + } else { + state } - state } } - pub fn request_env(&mut self) -> PermissionState { - if self.env == PermissionState::Prompt { - if permission_prompt("Deno requests access to environment variables") { - self.env = PermissionState::Granted; - } else { - self.env = PermissionState::Denied; + pub fn revoke<T: AsRef<str>>( + &mut self, + host: Option<&(T, Option<u16>)>, + ) -> PermissionState { + if let Some(host) = host { + self.granted_list.remove(&NetPermission::new(&host)); + if host.1.is_none() { + self.granted_list.retain(|h| h.0 != host.0.as_ref()); + } + } else { + self.granted_list.clear(); + if self.global_state == PermissionState::Granted { + self.global_state = PermissionState::Prompt; } } - self.env + self.query(host) } - pub fn request_run(&mut self) -> PermissionState { - if self.run == PermissionState::Prompt { - if permission_prompt("Deno requests to access to run a subprocess") { - self.run = PermissionState::Granted; - } else { - self.run = PermissionState::Denied; - } - } - self.run + pub fn check<T: AsRef<str>>( + &self, + host: &(T, Option<u16>), + ) -> Result<(), AnyError> { + self.query(Some(host)).check( + self.name, + Some(&format!("\"{}\"", NetPermission::new(&host))), + ) } - pub fn request_plugin(&mut self) -> PermissionState { - if self.plugin == PermissionState::Prompt { - if permission_prompt("Deno requests to open plugins") { - self.plugin = PermissionState::Granted; - } else { - self.plugin = PermissionState::Denied; - } - } - self.plugin + pub fn check_url(&self, url: &url::Url) -> Result<(), AnyError> { + let hostname = url + .host_str() + .ok_or_else(|| uri_error("Missing host"))? + .to_string(); + let display_host = match url.port() { + None => hostname.clone(), + Some(port) => format!("{}:{}", hostname, port), + }; + self + .query(Some(&(hostname, url.port_or_known_default()))) + .check(self.name, Some(&format!("\"{}\"", display_host))) } +} - pub fn request_hrtime(&mut self) -> PermissionState { - if self.hrtime == PermissionState::Prompt { - if permission_prompt("Deno requests access to high precision time") { - self.hrtime = PermissionState::Granted; - } else { - self.hrtime = PermissionState::Denied; - } - } - self.hrtime +#[derive(Clone, Debug, Default, PartialEq)] +pub struct BooleanPermission { + pub name: &'static str, + pub description: &'static str, + pub state: PermissionState, +} + +impl BooleanPermission { + pub fn query(&self) -> PermissionState { + self.state } - pub fn revoke_read(&mut self, path: &Option<&Path>) -> PermissionState { - if let Some(path) = path { - let path = resolve_from_cwd(path).unwrap(); - self - .read - .granted_list - .retain(|path_| !path_.starts_with(&path)); - } else { - self.read.granted_list.clear(); - if self.read.global_state == PermissionState::Granted { - self.read.global_state = PermissionState::Prompt; + pub fn request(&mut self) -> PermissionState { + if self.state == PermissionState::Prompt { + if permission_prompt(&format!("access to {}", self.description)) { + self.state = PermissionState::Granted; + } else { + self.state = PermissionState::Denied; } } - self.query_read(path) + self.state } - pub fn revoke_write(&mut self, path: &Option<&Path>) -> PermissionState { - if let Some(path) = path { - let path = resolve_from_cwd(path).unwrap(); - self - .write - .granted_list - .retain(|path_| !path_.starts_with(&path)); - } else { - self.write.granted_list.clear(); - if self.write.global_state == PermissionState::Granted { - self.write.global_state = PermissionState::Prompt; - } + pub fn revoke(&mut self) -> PermissionState { + if self.state == PermissionState::Granted { + self.state = PermissionState::Prompt; } - self.query_write(path) + self.state } - pub fn revoke_net<T: AsRef<str>>( - &mut self, - host: &Option<&(T, Option<u16>)>, - ) -> PermissionState { - if let Some(host) = host { - self.net.granted_list.remove(&format_host(host)); - if host.1.is_none() { - self - .net - .granted_list - .retain(|h| !h.starts_with(&format!("{}:", host.0.as_ref()))); - } - } else { - self.net.granted_list.clear(); - if self.net.global_state == PermissionState::Granted { - self.net.global_state = PermissionState::Prompt; - } - } - self.query_net(host) + pub fn check(&self) -> Result<(), AnyError> { + self.state.check(self.name, None) } +} - pub fn revoke_env(&mut self) -> PermissionState { - if self.env == PermissionState::Granted { - self.env = PermissionState::Prompt; +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Permissions { + pub read: UnaryPermission<ReadPermission>, + pub write: UnaryPermission<WritePermission>, + pub net: UnaryPermission<NetPermission>, + pub env: BooleanPermission, + pub run: BooleanPermission, + pub plugin: BooleanPermission, + pub hrtime: BooleanPermission, +} + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct PermissionsOptions { + pub allow_env: bool, + pub allow_hrtime: bool, + pub allow_net: Option<Vec<String>>, + pub allow_plugin: bool, + pub allow_read: Option<Vec<PathBuf>>, + pub allow_run: bool, + pub allow_write: Option<Vec<PathBuf>>, +} + +impl Permissions { + pub fn new_read( + state: &Option<Vec<PathBuf>>, + ) -> UnaryPermission<ReadPermission> { + UnaryPermission::<ReadPermission> { + name: "read", + description: "read the file system", + global_state: global_state_from_option(state), + granted_list: resolve_read_allowlist(&state), + denied_list: Default::default(), } - self.env } - pub fn revoke_run(&mut self) -> PermissionState { - if self.run == PermissionState::Granted { - self.run = PermissionState::Prompt; + pub fn new_write( + state: &Option<Vec<PathBuf>>, + ) -> UnaryPermission<WritePermission> { + UnaryPermission::<WritePermission> { + name: "write", + description: "write to the file system", + global_state: global_state_from_option(state), + granted_list: resolve_write_allowlist(&state), + denied_list: Default::default(), } - self.run } - pub fn revoke_plugin(&mut self) -> PermissionState { - if self.plugin == PermissionState::Granted { - self.plugin = PermissionState::Prompt; + pub fn new_net( + state: &Option<Vec<String>>, + ) -> UnaryPermission<NetPermission> { + UnaryPermission::<NetPermission> { + name: "net", + description: "network", + global_state: global_state_from_option(state), + granted_list: state + .as_ref() + .map(|v| { + v.iter() + .map(|x| NetPermission::from_string(x.clone())) + .collect() + }) + .unwrap_or_else(HashSet::new), + denied_list: Default::default(), } - self.plugin } - pub fn revoke_hrtime(&mut self) -> PermissionState { - if self.hrtime == PermissionState::Granted { - self.hrtime = PermissionState::Prompt; - } - self.hrtime + pub fn new_env(state: bool) -> BooleanPermission { + boolean_permission_from_flag_bool(state, "env", "environment variables") } - pub fn check_read(&self, path: &Path) -> Result<(), AnyError> { - let (resolved_path, display_path) = self.resolved_and_display_path(path); - self.query_read(&Some(&resolved_path)).check( - &format!("read access to \"{}\"", display_path.display()), - "--allow-read", - ) + pub fn new_run(state: bool) -> BooleanPermission { + boolean_permission_from_flag_bool(state, "run", "run a subprocess") } - /// As `check_read()`, but permission error messages will anonymize the path - /// by replacing it with the given `display`. - pub fn check_read_blind( - &self, - path: &Path, - display: &str, - ) -> Result<(), AnyError> { - let resolved_path = resolve_from_cwd(path).unwrap(); - self - .query_read(&Some(&resolved_path)) - .check(&format!("read access to <{}>", display), "--allow-read") + pub fn new_plugin(state: bool) -> BooleanPermission { + boolean_permission_from_flag_bool(state, "plugin", "open a plugin") } - pub fn check_write(&self, path: &Path) -> Result<(), AnyError> { - let (resolved_path, display_path) = self.resolved_and_display_path(path); - self.query_write(&Some(&resolved_path)).check( - &format!("write access to \"{}\"", display_path.display()), - "--allow-write", - ) + pub fn new_hrtime(state: bool) -> BooleanPermission { + boolean_permission_from_flag_bool(state, "hrtime", "high precision time") } - pub fn check_net<T: AsRef<str>>( - &self, - host: &(T, Option<u16>), - ) -> Result<(), AnyError> { - self.query_net(&Some(host)).check( - &format!("network access to \"{}\"", format_host(host)), - "--allow-net", - ) + pub fn from_options(opts: &PermissionsOptions) -> Self { + Self { + read: Permissions::new_read(&opts.allow_read), + write: Permissions::new_write(&opts.allow_write), + net: Permissions::new_net(&opts.allow_net), + env: Permissions::new_env(opts.allow_env), + run: Permissions::new_run(opts.allow_run), + plugin: Permissions::new_plugin(opts.allow_plugin), + hrtime: Permissions::new_hrtime(opts.allow_hrtime), + } } - pub fn check_net_url(&self, url: &url::Url) -> Result<(), AnyError> { - let hostname = url - .host_str() - .ok_or_else(|| uri_error("Missing host"))? - .to_string(); - let display_host = match url.port() { - None => hostname.clone(), - Some(port) => format!("{}:{}", hostname, port), - }; - self - .query_net(&Some(&(hostname, url.port_or_known_default()))) - .check( - &format!("network access to \"{}\"", display_host), - "--allow-net", - ) + pub fn allow_all() -> Self { + Self { + read: Permissions::new_read(&Some(vec![])), + write: Permissions::new_write(&Some(vec![])), + net: Permissions::new_net(&Some(vec![])), + env: Permissions::new_env(true), + run: Permissions::new_run(true), + plugin: Permissions::new_plugin(true), + hrtime: Permissions::new_hrtime(true), + } } /// A helper function that determines if the module specifier is a local or @@ -582,58 +580,101 @@ impl Permissions { ) -> Result<(), AnyError> { match specifier.scheme() { "file" => match specifier.to_file_path() { - Ok(path) => self.check_read(&path), + Ok(path) => self.read.check(&path), Err(_) => Err(uri_error(format!( "Invalid file path.\n Specifier: {}", specifier ))), }, "data" => Ok(()), - _ => self.check_net_url(specifier), + _ => self.net.check_url(specifier), } } +} - pub fn check_env(&self) -> Result<(), AnyError> { - self - .env - .check("access to environment variables", "--allow-env") +impl deno_fetch::FetchPermissions for Permissions { + fn check_net_url(&self, url: &url::Url) -> Result<(), AnyError> { + self.net.check_url(url) } - pub fn check_run(&self) -> Result<(), AnyError> { - self.run.check("access to run a subprocess", "--allow-run") + fn check_read(&self, path: &Path) -> Result<(), AnyError> { + self.read.check(path) } +} - pub fn check_plugin(&self, path: &Path) -> Result<(), AnyError> { - let (_, display_path) = self.resolved_and_display_path(path); - self.plugin.check( - &format!("access to open a plugin: {}", display_path.display()), - "--allow-plugin", - ) +impl deno_websocket::WebSocketPermissions for Permissions { + fn check_net_url(&self, url: &url::Url) -> Result<(), AnyError> { + self.net.check_url(url) } +} - pub fn check_hrtime(&self) -> Result<(), AnyError> { - self - .hrtime - .check("access to high precision time", "--allow-hrtime") +fn log_perm_access(message: &str) { + debug!( + "{}", + colors::bold(&format!("{}️ Granted {}", PERMISSION_EMOJI, message)) + ); +} + +fn boolean_permission_from_flag_bool( + flag: bool, + name: &'static str, + description: &'static str, +) -> BooleanPermission { + BooleanPermission { + name, + description, + state: if flag { + PermissionState::Granted + } else { + PermissionState::Prompt + }, } } -impl deno_fetch::FetchPermissions for Permissions { - fn check_net_url(&self, url: &url::Url) -> Result<(), AnyError> { - Permissions::check_net_url(self, url) +fn global_state_from_option<T>(flag: &Option<Vec<T>>) -> PermissionState { + if matches!(flag, Some(v) if v.is_empty()) { + PermissionState::Granted + } else { + PermissionState::Prompt } +} - fn check_read(&self, p: &Path) -> Result<(), AnyError> { - Permissions::check_read(self, p) +pub fn resolve_read_allowlist( + allow: &Option<Vec<PathBuf>>, +) -> HashSet<ReadPermission> { + if let Some(v) = allow { + v.iter() + .map(|raw_path| { + ReadPermission(resolve_from_cwd(Path::new(&raw_path)).unwrap()) + }) + .collect() + } else { + HashSet::new() } } -impl deno_websocket::WebSocketPermissions for Permissions { - fn check_net_url(&self, url: &url::Url) -> Result<(), AnyError> { - Permissions::check_net_url(self, url) +pub fn resolve_write_allowlist( + allow: &Option<Vec<PathBuf>>, +) -> HashSet<WritePermission> { + if let Some(v) = allow { + v.iter() + .map(|raw_path| { + WritePermission(resolve_from_cwd(Path::new(&raw_path)).unwrap()) + }) + .collect() + } else { + HashSet::new() } } +/// Arbitrary helper. Resolves the path from CWD, and also gets a path that +/// can be displayed without leaking the CWD when not allowed. +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) +} + /// Shows the permission prompt and returns the answer according to the user input. /// This loops until the user gives the proper input. #[cfg(not(test))] @@ -642,7 +683,7 @@ fn permission_prompt(message: &str) -> bool { return false; }; let msg = format!( - "️{} {}. Grant? [g/d (g = grant, d = deny)] ", + "{} ️Deno requests {}. Grant? [g/d (g = grant, d = deny)] ", PERMISSION_EMOJI, message ); // print to stderr so that if deno is > to a file this is still displayed. @@ -668,6 +709,13 @@ fn permission_prompt(message: &str) -> bool { } } +// When testing, permission prompt returns the value of STUB_PROMPT_VALUE +// which we set from the test functions. +#[cfg(test)] +fn permission_prompt(_message: &str) -> bool { + STUB_PROMPT_VALUE.load(Ordering::SeqCst) +} + #[cfg(test)] lazy_static! { /// Lock this when you use `set_prompt_result` in a test case. @@ -682,69 +730,6 @@ fn set_prompt_result(value: bool) { STUB_PROMPT_VALUE.store(value, Ordering::SeqCst); } -// When testing, permission prompt returns the value of STUB_PROMPT_VALUE -// which we set from the test functions. -#[cfg(test)] -fn permission_prompt(_message: &str) -> bool { - STUB_PROMPT_VALUE.load(Ordering::SeqCst) -} - -fn log_perm_access(message: &str) { - debug!( - "{}", - colors::bold(&format!("{}️ Granted {}", PERMISSION_EMOJI, message)) - ); -} - -fn check_path_allowlist(path: &Path, allowlist: &HashSet<PathBuf>) -> bool { - for path_ in allowlist { - if path.starts_with(path_) { - return true; - } - } - false -} - -fn check_path_blocklist(path: &Path, blocklist: &HashSet<PathBuf>) -> bool { - for path_ in blocklist { - if path_.starts_with(path) { - return true; - } - } - false -} - -fn check_host_allowlist<T: AsRef<str>>( - host: &(T, Option<u16>), - allowlist: &HashSet<String>, -) -> bool { - let (hostname, port) = host; - allowlist.contains(hostname.as_ref()) - || (port.is_some() && allowlist.contains(&format_host(host))) -} - -fn check_host_blocklist<T: AsRef<str>>( - host: &(T, Option<u16>), - blocklist: &HashSet<String>, -) -> bool { - let (hostname, port) = host; - match port { - None => blocklist.iter().any(|host| { - host == hostname.as_ref() - || host.starts_with(&format!("{}:", hostname.as_ref())) - }), - Some(_) => blocklist.contains(&format_host(host)), - } -} - -fn format_host<T: AsRef<str>>(host: &(T, Option<u16>)) -> String { - let (hostname, port) = host; - match port { - None => hostname.as_ref().to_string(), - Some(port) => format!("{}:{}", hostname.as_ref(), port), - } -} - #[cfg(test)] mod tests { use super::*; @@ -770,50 +755,55 @@ mod tests { }); // Inside of /a/specific and /a/specific/dir/name - assert!(perms.check_read(Path::new("/a/specific/dir/name")).is_ok()); - assert!(perms.check_write(Path::new("/a/specific/dir/name")).is_ok()); + assert!(perms.read.check(Path::new("/a/specific/dir/name")).is_ok()); + assert!(perms.write.check(Path::new("/a/specific/dir/name")).is_ok()); // Inside of /a/specific but outside of /a/specific/dir/name - assert!(perms.check_read(Path::new("/a/specific/dir")).is_ok()); - assert!(perms.check_write(Path::new("/a/specific/dir")).is_ok()); + assert!(perms.read.check(Path::new("/a/specific/dir")).is_ok()); + assert!(perms.write.check(Path::new("/a/specific/dir")).is_ok()); // Inside of /a/specific and /a/specific/dir/name assert!(perms - .check_read(Path::new("/a/specific/dir/name/inner")) + .read + .check(Path::new("/a/specific/dir/name/inner")) .is_ok()); assert!(perms - .check_write(Path::new("/a/specific/dir/name/inner")) + .write + .check(Path::new("/a/specific/dir/name/inner")) .is_ok()); // Inside of /a/specific but outside of /a/specific/dir/name - assert!(perms.check_read(Path::new("/a/specific/other/dir")).is_ok()); + assert!(perms.read.check(Path::new("/a/specific/other/dir")).is_ok()); assert!(perms - .check_write(Path::new("/a/specific/other/dir")) + .write + .check(Path::new("/a/specific/other/dir")) .is_ok()); // Exact match with /b/c - assert!(perms.check_read(Path::new("/b/c")).is_ok()); - assert!(perms.check_write(Path::new("/b/c")).is_ok()); + assert!(perms.read.check(Path::new("/b/c")).is_ok()); + assert!(perms.write.check(Path::new("/b/c")).is_ok()); // Sub path within /b/c - assert!(perms.check_read(Path::new("/b/c/sub/path")).is_ok()); - assert!(perms.check_write(Path::new("/b/c/sub/path")).is_ok()); + assert!(perms.read.check(Path::new("/b/c/sub/path")).is_ok()); + assert!(perms.write.check(Path::new("/b/c/sub/path")).is_ok()); // Sub path within /b/c, needs normalizing assert!(perms - .check_read(Path::new("/b/c/sub/path/../path/.")) + .read + .check(Path::new("/b/c/sub/path/../path/.")) .is_ok()); assert!(perms - .check_write(Path::new("/b/c/sub/path/../path/.")) + .write + .check(Path::new("/b/c/sub/path/../path/.")) .is_ok()); // Inside of /b but outside of /b/c - assert!(perms.check_read(Path::new("/b/e")).is_err()); - assert!(perms.check_write(Path::new("/b/e")).is_err()); + assert!(perms.read.check(Path::new("/b/e")).is_err()); + assert!(perms.write.check(Path::new("/b/e")).is_err()); // Inside of /a but outside of /a/specific - assert!(perms.check_read(Path::new("/a/b")).is_err()); - assert!(perms.check_write(Path::new("/a/b")).is_err()); + assert!(perms.read.check(Path::new("/a/b")).is_err()); + assert!(perms.write.check(Path::new("/a/b")).is_err()); } #[test] @@ -854,7 +844,7 @@ mod tests { ]; for (host, port, is_ok) in domain_tests { - assert_eq!(is_ok, perms.check_net(&(host, Some(port))).is_ok()); + assert_eq!(is_ok, perms.net.check(&(host, Some(port))).is_ok()); } } @@ -888,7 +878,7 @@ mod tests { ]; for (host, port) in domain_tests { - assert!(perms.check_net(&(host, Some(port))).is_ok()); + assert!(perms.net.check(&(host, Some(port))).is_ok()); } } @@ -922,7 +912,7 @@ mod tests { ]; for (host, port) in domain_tests { - assert!(!perms.check_net(&(host, Some(port))).is_ok()); + assert!(!perms.net.check(&(host, Some(port))).is_ok()); } } @@ -981,7 +971,7 @@ mod tests { for (url_str, is_ok) in url_tests { let u = url::Url::parse(url_str).unwrap(); - assert_eq!(is_ok, perms.check_net_url(&u).is_ok()); + assert_eq!(is_ok, perms.net.check_url(&u).is_ok()); } } @@ -1050,125 +1040,100 @@ mod tests { #[test] fn test_query() { - let perms1 = Permissions { + let perms1 = Permissions::allow_all(); + let perms2 = Permissions { read: UnaryPermission { - global_state: PermissionState::Granted, - ..Default::default() + global_state: PermissionState::Prompt, + ..Permissions::new_read(&Some(vec![PathBuf::from("/foo")])) }, write: UnaryPermission { - global_state: PermissionState::Granted, - ..Default::default() + global_state: PermissionState::Prompt, + ..Permissions::new_write(&Some(vec![PathBuf::from("/foo")])) }, net: UnaryPermission { - global_state: PermissionState::Granted, + global_state: PermissionState::Prompt, + ..Permissions::new_net(&Some(svec!["127.0.0.1:8000"])) + }, + env: BooleanPermission { + state: PermissionState::Prompt, ..Default::default() }, - env: PermissionState::Granted, - run: PermissionState::Granted, - plugin: PermissionState::Granted, - hrtime: PermissionState::Granted, - }; - let perms2 = Permissions { - read: UnaryPermission { - global_state: PermissionState::Prompt, - granted_list: resolve_fs_allowlist(&Some(vec![PathBuf::from("/foo")])), + run: BooleanPermission { + state: PermissionState::Prompt, ..Default::default() }, - write: UnaryPermission { - global_state: PermissionState::Prompt, - granted_list: resolve_fs_allowlist(&Some(vec![PathBuf::from("/foo")])), + plugin: BooleanPermission { + state: PermissionState::Prompt, ..Default::default() }, - net: UnaryPermission { - global_state: PermissionState::Prompt, - granted_list: ["127.0.0.1:8000".to_string()].iter().cloned().collect(), + hrtime: BooleanPermission { + state: PermissionState::Prompt, ..Default::default() }, - env: PermissionState::Prompt, - run: PermissionState::Prompt, - plugin: PermissionState::Prompt, - hrtime: PermissionState::Prompt, }; #[rustfmt::skip] { - assert_eq!(perms1.query_read(&None), PermissionState::Granted); - assert_eq!(perms1.query_read(&Some(&Path::new("/foo"))), PermissionState::Granted); - assert_eq!(perms2.query_read(&None), PermissionState::Prompt); - assert_eq!(perms2.query_read(&Some(&Path::new("/foo"))), PermissionState::Granted); - assert_eq!(perms2.query_read(&Some(&Path::new("/foo/bar"))), PermissionState::Granted); - assert_eq!(perms1.query_write(&None), PermissionState::Granted); - assert_eq!(perms1.query_write(&Some(&Path::new("/foo"))), PermissionState::Granted); - assert_eq!(perms2.query_write(&None), PermissionState::Prompt); - assert_eq!(perms2.query_write(&Some(&Path::new("/foo"))), PermissionState::Granted); - assert_eq!(perms2.query_write(&Some(&Path::new("/foo/bar"))), PermissionState::Granted); - assert_eq!(perms1.query_net::<&str>(&None), PermissionState::Granted); - assert_eq!(perms1.query_net(&Some(&("127.0.0.1", None))), PermissionState::Granted); - assert_eq!(perms2.query_net::<&str>(&None), PermissionState::Prompt); - assert_eq!(perms2.query_net(&Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted); - assert_eq!(perms1.query_env(), PermissionState::Granted); - assert_eq!(perms2.query_env(), PermissionState::Prompt); - assert_eq!(perms1.query_run(), PermissionState::Granted); - assert_eq!(perms2.query_run(), PermissionState::Prompt); - assert_eq!(perms1.query_plugin(), PermissionState::Granted); - assert_eq!(perms2.query_plugin(), PermissionState::Prompt); - assert_eq!(perms1.query_hrtime(), PermissionState::Granted); - assert_eq!(perms2.query_hrtime(), PermissionState::Prompt); + assert_eq!(perms1.read.query(None), PermissionState::Granted); + assert_eq!(perms1.read.query(Some(&Path::new("/foo"))), PermissionState::Granted); + 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!(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!(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!(perms1.env.query(), PermissionState::Granted); + assert_eq!(perms2.env.query(), PermissionState::Prompt); + assert_eq!(perms1.run.query(), PermissionState::Granted); + assert_eq!(perms2.run.query(), PermissionState::Prompt); + assert_eq!(perms1.plugin.query(), PermissionState::Granted); + assert_eq!(perms2.plugin.query(), PermissionState::Prompt); + assert_eq!(perms1.hrtime.query(), PermissionState::Granted); + assert_eq!(perms2.hrtime.query(), PermissionState::Prompt); }; } #[test] fn test_request() { - let mut perms = Permissions { - read: UnaryPermission { - global_state: PermissionState::Prompt, - ..Default::default() - }, - write: UnaryPermission { - global_state: PermissionState::Prompt, - ..Default::default() - }, - net: UnaryPermission { - global_state: PermissionState::Prompt, - ..Default::default() - }, - env: PermissionState::Prompt, - run: PermissionState::Prompt, - plugin: PermissionState::Prompt, - hrtime: PermissionState::Prompt, - }; + let mut perms: Permissions = Default::default(); #[rustfmt::skip] { let _guard = PERMISSION_PROMPT_GUARD.lock().unwrap(); set_prompt_result(true); - assert_eq!(perms.request_read(&Some(&Path::new("/foo"))), PermissionState::Granted); - assert_eq!(perms.query_read(&None), PermissionState::Prompt); + assert_eq!(perms.read.request(Some(&Path::new("/foo"))), PermissionState::Granted); + assert_eq!(perms.read.query(None), PermissionState::Prompt); set_prompt_result(false); - assert_eq!(perms.request_read(&Some(&Path::new("/foo/bar"))), PermissionState::Granted); + assert_eq!(perms.read.request(Some(&Path::new("/foo/bar"))), PermissionState::Granted); set_prompt_result(false); - assert_eq!(perms.request_write(&Some(&Path::new("/foo"))), PermissionState::Denied); - assert_eq!(perms.query_write(&Some(&Path::new("/foo/bar"))), PermissionState::Prompt); + assert_eq!(perms.write.request(Some(&Path::new("/foo"))), PermissionState::Denied); + assert_eq!(perms.write.query(Some(&Path::new("/foo/bar"))), PermissionState::Prompt); set_prompt_result(true); - assert_eq!(perms.request_write(&None), PermissionState::Denied); + assert_eq!(perms.write.request(None), PermissionState::Denied); set_prompt_result(true); - assert_eq!(perms.request_net(&Some(&("127.0.0.1", None))), PermissionState::Granted); + assert_eq!(perms.net.request(Some(&("127.0.0.1", None))), PermissionState::Granted); set_prompt_result(false); - assert_eq!(perms.request_net(&Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted); + assert_eq!(perms.net.request(Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted); set_prompt_result(true); - assert_eq!(perms.request_env(), PermissionState::Granted); + assert_eq!(perms.env.request(), PermissionState::Granted); set_prompt_result(false); - assert_eq!(perms.request_env(), PermissionState::Granted); + assert_eq!(perms.env.request(), PermissionState::Granted); set_prompt_result(false); - assert_eq!(perms.request_run(), PermissionState::Denied); + assert_eq!(perms.run.request(), PermissionState::Denied); set_prompt_result(true); - assert_eq!(perms.request_run(), PermissionState::Denied); + assert_eq!(perms.run.request(), PermissionState::Denied); set_prompt_result(true); - assert_eq!(perms.request_plugin(), PermissionState::Granted); + assert_eq!(perms.plugin.request(), PermissionState::Granted); set_prompt_result(false); - assert_eq!(perms.request_plugin(), PermissionState::Granted); + assert_eq!(perms.plugin.request(), PermissionState::Granted); set_prompt_result(false); - assert_eq!(perms.request_hrtime(), PermissionState::Denied); + assert_eq!(perms.hrtime.request(), PermissionState::Denied); set_prompt_result(true); - assert_eq!(perms.request_hrtime(), PermissionState::Denied); + assert_eq!(perms.hrtime.request(), PermissionState::Denied); }; } @@ -1177,38 +1142,47 @@ mod tests { let mut perms = Permissions { read: UnaryPermission { global_state: PermissionState::Prompt, - granted_list: resolve_fs_allowlist(&Some(vec![PathBuf::from("/foo")])), - ..Default::default() + ..Permissions::new_read(&Some(vec![PathBuf::from("/foo")])) }, write: UnaryPermission { global_state: PermissionState::Prompt, - granted_list: resolve_fs_allowlist(&Some(vec![PathBuf::from("/foo")])), - ..Default::default() + ..Permissions::new_write(&Some(vec![PathBuf::from("/foo")])) }, net: UnaryPermission { global_state: PermissionState::Prompt, - granted_list: svec!["127.0.0.1"].iter().cloned().collect(), + ..Permissions::new_net(&Some(svec!["127.0.0.1"])) + }, + env: BooleanPermission { + state: PermissionState::Granted, + ..Default::default() + }, + run: BooleanPermission { + state: PermissionState::Granted, + ..Default::default() + }, + plugin: BooleanPermission { + state: PermissionState::Prompt, + ..Default::default() + }, + hrtime: BooleanPermission { + state: PermissionState::Denied, ..Default::default() }, - env: PermissionState::Granted, - run: PermissionState::Granted, - plugin: PermissionState::Prompt, - hrtime: PermissionState::Denied, }; #[rustfmt::skip] { - assert_eq!(perms.revoke_read(&Some(&Path::new("/foo/bar"))), PermissionState::Granted); - assert_eq!(perms.revoke_read(&Some(&Path::new("/foo"))), PermissionState::Prompt); - assert_eq!(perms.query_read(&Some(&Path::new("/foo/bar"))), PermissionState::Prompt); - assert_eq!(perms.revoke_write(&Some(&Path::new("/foo/bar"))), PermissionState::Granted); - assert_eq!(perms.revoke_write(&None), PermissionState::Prompt); - assert_eq!(perms.query_write(&Some(&Path::new("/foo/bar"))), PermissionState::Prompt); - assert_eq!(perms.revoke_net(&Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted); - assert_eq!(perms.revoke_net(&Some(&("127.0.0.1", None))), PermissionState::Prompt); - assert_eq!(perms.revoke_env(), PermissionState::Prompt); - assert_eq!(perms.revoke_run(), PermissionState::Prompt); - assert_eq!(perms.revoke_plugin(), PermissionState::Prompt); - assert_eq!(perms.revoke_hrtime(), PermissionState::Denied); + assert_eq!(perms.read.revoke(Some(&Path::new("/foo/bar"))), PermissionState::Granted); + assert_eq!(perms.read.revoke(Some(&Path::new("/foo"))), PermissionState::Prompt); + assert_eq!(perms.read.query(Some(&Path::new("/foo/bar"))), PermissionState::Prompt); + assert_eq!(perms.write.revoke(Some(&Path::new("/foo/bar"))), PermissionState::Granted); + assert_eq!(perms.write.revoke(None), PermissionState::Prompt); + assert_eq!(perms.write.query(Some(&Path::new("/foo/bar"))), PermissionState::Prompt); + assert_eq!(perms.net.revoke(Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted); + assert_eq!(perms.net.revoke(Some(&("127.0.0.1", None))), PermissionState::Prompt); + assert_eq!(perms.env.revoke(), PermissionState::Prompt); + assert_eq!(perms.run.revoke(), PermissionState::Prompt); + assert_eq!(perms.plugin.revoke(), PermissionState::Prompt); + assert_eq!(perms.hrtime.revoke(), PermissionState::Denied); }; } } |