diff options
Diffstat (limited to 'cli/permissions.rs')
-rw-r--r-- | cli/permissions.rs | 1095 |
1 files changed, 0 insertions, 1095 deletions
diff --git a/cli/permissions.rs b/cli/permissions.rs deleted file mode 100644 index cc3ce8242..000000000 --- a/cli/permissions.rs +++ /dev/null @@ -1,1095 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. - -use crate::colors; -use crate::flags::Flags; -use crate::fs_util::resolve_from_cwd; -use deno_core::error::custom_error; -use deno_core::error::uri_error; -use deno_core::error::AnyError; -use deno_core::url; -use deno_core::ModuleSpecifier; -use serde::Deserialize; -use std::collections::HashSet; -use std::env::current_dir; -use std::fmt; -use std::hash::Hash; -#[cfg(not(test))] -use std::io; -use std::path::{Path, PathBuf}; -#[cfg(test)] -use std::sync::atomic::AtomicBool; -#[cfg(test)] -use std::sync::atomic::Ordering; -#[cfg(test)] -use std::sync::Mutex; - -const PERMISSION_EMOJI: &str = "⚠️"; - -/// Tri-state value for storing permission state -#[derive(PartialEq, Debug, Clone, Copy, Deserialize)] -pub enum PermissionState { - Granted = 0, - Prompt = 1, - Denied = 2, -} - -impl PermissionState { - /// Check the permission state. - fn check(self, msg: &str, flag_name: &str) -> Result<(), AnyError> { - if self == PermissionState::Granted { - log_perm_access(msg); - return Ok(()); - } - let message = format!("{}, run again with the {} flag", msg, flag_name); - Err(custom_error("PermissionDenied", message)) - } -} - -impl fmt::Display for PermissionState { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - PermissionState::Granted => f.pad("granted"), - PermissionState::Prompt => f.pad("prompt"), - PermissionState::Denied => f.pad("denied"), - } - } -} - -impl Default for PermissionState { - fn default() -> Self { - PermissionState::Prompt - } -} - -#[derive(Clone, Debug, Default, Deserialize, PartialEq)] -pub struct UnaryPermission<T: Eq + Hash> { - pub global_state: PermissionState, - pub granted_list: HashSet<T>, - pub denied_list: HashSet<T>, -} - -#[derive(Clone, Debug, Default, Deserialize, 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, -} - -fn resolve_fs_allowlist(allowlist: &[PathBuf]) -> HashSet<PathBuf> { - allowlist - .iter() - .map(|raw_path| resolve_from_cwd(Path::new(&raw_path)).unwrap()) - .collect() -} - -impl Permissions { - pub fn from_flags(flags: &Flags) -> Self { - fn state_from_flag_bool(flag: bool) -> PermissionState { - if flag { - PermissionState::Granted - } else { - PermissionState::Prompt - } - } - Self { - read: UnaryPermission::<PathBuf> { - global_state: state_from_flag_bool(flags.allow_read), - granted_list: resolve_fs_allowlist(&flags.read_allowlist), - ..Default::default() - }, - write: UnaryPermission::<PathBuf> { - global_state: state_from_flag_bool(flags.allow_write), - granted_list: resolve_fs_allowlist(&flags.write_allowlist), - ..Default::default() - }, - net: UnaryPermission::<String> { - global_state: state_from_flag_bool(flags.allow_net), - granted_list: flags.net_allowlist.iter().cloned().collect(), - ..Default::default() - }, - env: state_from_flag_bool(flags.allow_env), - run: state_from_flag_bool(flags.allow_run), - plugin: state_from_flag_bool(flags.allow_plugin), - hrtime: state_from_flag_bool(flags.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() - } else { - match self - .query_read(&Some(¤t_dir().unwrap())) - .check("", "") - { - Ok(_) => resolved_path.clone(), - Err(_) => path.to_path_buf(), - } - }; - (resolved_path, display_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 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 - } - - pub fn query_write(&self, path: &Option<&Path>) -> PermissionState { - let path = path.map(|p| resolve_from_cwd(p).unwrap()); - if self.write.global_state == PermissionState::Denied - && match path.as_ref() { - None => true, - Some(path) => check_path_blocklist(path, &self.write.denied_list), - } - { - return PermissionState::Denied; - } - if self.write.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(&self, host: &str, port: Option<u16>) -> PermissionState { - if self.net.global_state == PermissionState::Denied - || check_host_and_port_list(host, port, &self.net.denied_list) - { - return PermissionState::Denied; - } - if self.net.global_state == PermissionState::Granted - || check_host_and_port_list(host, port, &self.net.granted_list) - { - return PermissionState::Granted; - } - PermissionState::Prompt - } - - pub fn query_net_url( - &self, - url: &Option<&str>, - ) -> Result<PermissionState, AnyError> { - if url.is_none() { - return Ok(self.net.global_state); - } - let url: &str = url.unwrap(); - // If url is invalid, then throw a TypeError. - let parsed = url::Url::parse(url)?; - // The url may be parsed correctly but still lack a host, i.e. "localhost:235" or "mailto:someone@somewhere.com" or "file:/1.txt" - // Note that host:port combos are parsed as scheme:path - if parsed.host().is_none() { - return Err(custom_error( - "URIError", - "invalid urlormat: <scheme>://<host>[:port][/subpath]", - )); - } - Ok(self.query_net( - &parsed.host().unwrap().to_string(), - parsed.port_or_known_default(), - )) - } - - 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 { - if let Some(path) = path { - let (resolved_path, display_path) = self.resolved_and_display_path(path); - let state = self.query_read(&Some(&resolved_path)); - if state == PermissionState::Prompt { - if permission_prompt(&format!( - "Deno requests read 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; - } 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; - } - } - state - } else { - let state = self.query_read(&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; - } else { - self.read.global_state = PermissionState::Denied; - return PermissionState::Denied; - } - } - state - } - } - - pub fn request_write(&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 - .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; - } - } - 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; - } - } - state - } - } - - pub fn request_net( - &mut self, - url: &Option<&str>, - ) -> Result<PermissionState, AnyError> { - if let Some(url) = url { - let state = self.query_net_url(&Some(url))?; - if state == PermissionState::Prompt { - if permission_prompt(&format!( - "Deno requests network access to \"{}\"", - url - )) { - self.net.granted_list.insert(url.to_string()); - return Ok(PermissionState::Granted); - } else { - self.net.denied_list.insert(url.to_string()); - self.net.global_state = PermissionState::Denied; - return Ok(PermissionState::Denied); - } - } - Ok(state) - } else { - let state = self.query_net_url(&None)?; - if state == PermissionState::Prompt { - if permission_prompt("Deno requests network access") { - self.net.granted_list.clear(); - self.net.global_state = PermissionState::Granted; - return Ok(PermissionState::Granted); - } else { - self.net.global_state = PermissionState::Denied; - return Ok(PermissionState::Denied); - } - } - Ok(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; - } - } - self.env - } - - 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 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 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 - } - - 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; - } - } - self.query_read(path) - } - - 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; - } - } - self.query_write(path) - } - - pub fn revoke_net( - &mut self, - url: &Option<&str>, - ) -> Result<PermissionState, AnyError> { - if let Some(url) = url { - self.net.granted_list.remove(*url); - } else { - self.net.granted_list.clear(); - if self.net.global_state == PermissionState::Granted { - self.net.global_state = PermissionState::Prompt; - } - } - self.query_net_url(url) - } - - pub fn revoke_env(&mut self) -> PermissionState { - if self.env == PermissionState::Granted { - self.env = PermissionState::Prompt; - } - self.env - } - - pub fn revoke_run(&mut self) -> PermissionState { - if self.run == PermissionState::Granted { - self.run = PermissionState::Prompt; - } - self.run - } - - pub fn revoke_plugin(&mut self) -> PermissionState { - if self.plugin == PermissionState::Granted { - self.plugin = PermissionState::Prompt; - } - self.plugin - } - - pub fn revoke_hrtime(&mut self) -> PermissionState { - if self.hrtime == PermissionState::Granted { - self.hrtime = PermissionState::Prompt; - } - self.hrtime - } - - 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", - ) - } - - /// 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 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 check_net(&self, hostname: &str, port: u16) -> Result<(), AnyError> { - self.query_net(hostname, Some(port)).check( - &format!("network access to \"{}:{}\"", hostname, port), - "--allow-net", - ) - } - - pub fn check_net_url(&self, url: &url::Url) -> Result<(), AnyError> { - let host = url.host_str().ok_or_else(|| uri_error("missing host"))?; - self - .query_net(host, url.port_or_known_default()) - .check(&format!("network access to \"{}\"", url), "--allow-net") - } - - /// A helper function that determines if the module specifier is a local or - /// remote, and performs a read or net check for the specifier. - pub fn check_specifier( - &self, - specifier: &ModuleSpecifier, - ) -> Result<(), AnyError> { - let url = specifier.as_url(); - if url.scheme() == "file" { - let path = url.to_file_path().unwrap(); - self.check_read(&path) - } else { - self.check_net_url(url) - } - } - - pub fn check_env(&self) -> Result<(), AnyError> { - self - .env - .check("access to environment variables", "--allow-env") - } - - pub fn check_run(&self) -> Result<(), AnyError> { - self.run.check("access to run a subprocess", "--allow-run") - } - - 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", - ) - } - - pub fn check_hrtime(&self) -> Result<(), AnyError> { - self - .hrtime - .check("access to high precision time", "--allow-hrtime") - } -} - -impl deno_fetch::FetchPermissions for Permissions { - fn check_net_url(&self, url: &url::Url) -> Result<(), AnyError> { - Permissions::check_net_url(self, url) - } - - fn check_read(&self, p: &PathBuf) -> Result<(), AnyError> { - Permissions::check_read(self, p) - } -} - -/// 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))] -fn permission_prompt(message: &str) -> bool { - if !atty::is(atty::Stream::Stdin) || !atty::is(atty::Stream::Stderr) { - return false; - }; - let msg = format!( - "️{} {}. 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. - eprint!("{}", colors::bold(&msg)); - loop { - let mut input = String::new(); - let stdin = io::stdin(); - let result = stdin.read_line(&mut input); - if result.is_err() { - return false; - }; - let ch = input.chars().next().unwrap(); - match ch.to_ascii_lowercase() { - 'g' => return true, - 'd' => return false, - _ => { - // If we don't get a recognized option try again. - let msg_again = - format!("Unrecognized option '{}' [g/d (g = grant, d = deny)] ", ch); - eprint!("{}", colors::bold(&msg_again)); - } - }; - } -} - -#[cfg(test)] -lazy_static! { - /// Lock this when you use `set_prompt_result` in a test case. - static ref PERMISSION_PROMPT_GUARD: Mutex<()> = Mutex::new(()); -} - -#[cfg(test)] -static STUB_PROMPT_VALUE: AtomicBool = AtomicBool::new(true); - -#[cfg(test)] -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_and_port_list( - host: &str, - port: Option<u16>, - allowlist: &HashSet<String>, -) -> bool { - allowlist.contains(host) - || (port.is_some() - && allowlist.contains(&format!("{}:{}", host, port.unwrap()))) -} - -#[cfg(test)] -mod tests { - use super::*; - use deno_core::serde_json; - - // Creates vector of strings, Vec<String> - macro_rules! svec { - ($($x:expr),*) => (vec![$($x.to_string()),*]); - } - - #[test] - fn check_paths() { - let allowlist = vec![ - PathBuf::from("/a/specific/dir/name"), - PathBuf::from("/a/specific"), - PathBuf::from("/b/c"), - ]; - - let perms = Permissions::from_flags(&Flags { - read_allowlist: allowlist.clone(), - write_allowlist: allowlist, - ..Default::default() - }); - - // 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()); - - // 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()); - - // Inside of /a/specific and /a/specific/dir/name - assert!(perms - .check_read(Path::new("/a/specific/dir/name/inner")) - .is_ok()); - assert!(perms - .check_write(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 - .check_write(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()); - - // 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()); - - // Sub path within /b/c, needs normalizing - assert!(perms - .check_read(Path::new("/b/c/sub/path/../path/.")) - .is_ok()); - assert!(perms - .check_write(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()); - - // 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()); - } - - #[test] - fn test_check_net() { - let perms = Permissions::from_flags(&Flags { - net_allowlist: svec![ - "localhost", - "deno.land", - "github.com:3000", - "127.0.0.1", - "172.16.0.2:8000", - "www.github.com:443" - ], - ..Default::default() - }); - - let domain_tests = vec![ - ("localhost", 1234, true), - ("deno.land", 0, true), - ("deno.land", 3000, true), - ("deno.lands", 0, false), - ("deno.lands", 3000, false), - ("github.com", 3000, true), - ("github.com", 0, false), - ("github.com", 2000, false), - ("github.net", 3000, false), - ("127.0.0.1", 0, true), - ("127.0.0.1", 3000, true), - ("127.0.0.2", 0, false), - ("127.0.0.2", 3000, false), - ("172.16.0.2", 8000, true), - ("172.16.0.2", 0, false), - ("172.16.0.2", 6000, false), - ("172.16.0.1", 8000, false), - // Just some random hosts that should err - ("somedomain", 0, false), - ("192.168.0.1", 0, false), - ]; - - let url_tests = vec![ - // Any protocol + port for localhost should be ok, since we don't specify - ("http://localhost", true), - ("https://localhost", true), - ("https://localhost:4443", true), - ("tcp://localhost:5000", true), - ("udp://localhost:6000", true), - // Correct domain + any port and protocol should be ok incorrect shouldn't - ("https://deno.land/std/example/welcome.ts", true), - ("https://deno.land:3000/std/example/welcome.ts", true), - ("https://deno.lands/std/example/welcome.ts", false), - ("https://deno.lands:3000/std/example/welcome.ts", false), - // Correct domain + port should be ok all other combinations should err - ("https://github.com:3000/denoland/deno", true), - ("https://github.com/denoland/deno", false), - ("https://github.com:2000/denoland/deno", false), - ("https://github.net:3000/denoland/deno", false), - // Correct ipv4 address + any port should be ok others should err - ("tcp://127.0.0.1", true), - ("https://127.0.0.1", true), - ("tcp://127.0.0.1:3000", true), - ("https://127.0.0.1:3000", true), - ("tcp://127.0.0.2", false), - ("https://127.0.0.2", false), - ("tcp://127.0.0.2:3000", false), - ("https://127.0.0.2:3000", false), - // Correct address + port should be ok all other combinations should err - ("tcp://172.16.0.2:8000", true), - ("https://172.16.0.2:8000", true), - ("tcp://172.16.0.2", false), - ("https://172.16.0.2", false), - ("tcp://172.16.0.2:6000", false), - ("https://172.16.0.2:6000", false), - ("tcp://172.16.0.1:8000", false), - ("https://172.16.0.1:8000", false), - // Testing issue #6531 (Network permissions check doesn't account for well-known default ports) so we dont regress - ("https://www.github.com:443/robots.txt", true), - ]; - - for (url_str, is_ok) in url_tests.iter() { - let u = url::Url::parse(url_str).unwrap(); - assert_eq!(*is_ok, perms.check_net_url(&u).is_ok()); - } - - for (host, port, is_ok) in domain_tests.iter() { - assert_eq!(*is_ok, perms.check_net(host, *port).is_ok()); - } - } - - #[test] - fn check_specifiers() { - let read_allowlist = if cfg!(target_os = "windows") { - vec![PathBuf::from("C:\\a")] - } else { - vec![PathBuf::from("/a")] - }; - let perms = Permissions::from_flags(&Flags { - read_allowlist, - net_allowlist: svec!["localhost"], - ..Default::default() - }); - - let mut fixtures = vec![ - ( - ModuleSpecifier::resolve_url_or_path("http://localhost:4545/mod.ts") - .unwrap(), - true, - ), - ( - ModuleSpecifier::resolve_url_or_path("http://deno.land/x/mod.ts") - .unwrap(), - false, - ), - ]; - - if cfg!(target_os = "windows") { - fixtures.push(( - ModuleSpecifier::resolve_url_or_path("file:///C:/a/mod.ts").unwrap(), - true, - )); - fixtures.push(( - ModuleSpecifier::resolve_url_or_path("file:///C:/b/mod.ts").unwrap(), - false, - )); - } else { - fixtures.push(( - ModuleSpecifier::resolve_url_or_path("file:///a/mod.ts").unwrap(), - true, - )); - fixtures.push(( - ModuleSpecifier::resolve_url_or_path("file:///b/mod.ts").unwrap(), - false, - )); - } - - for (specifier, expected) in fixtures { - assert_eq!(perms.check_specifier(&specifier).is_ok(), expected); - } - } - - #[test] - fn test_deserialize_perms() { - let json_perms = r#" - { - "read": { - "global_state": "Granted", - "granted_list": [], - "denied_list": [] - }, - "write": { - "global_state": "Granted", - "granted_list": [], - "denied_list": [] - }, - "net": { - "global_state": "Granted", - "granted_list": [], - "denied_list": [] - }, - "env": "Granted", - "run": "Granted", - "plugin": "Granted", - "hrtime": "Granted" - } - "#; - let perms0 = Permissions { - 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, - hrtime: PermissionState::Granted, - plugin: PermissionState::Granted, - }; - let deserialized_perms: Permissions = - serde_json::from_str(json_perms).unwrap(); - assert_eq!(perms0, deserialized_perms); - } - - #[test] - fn test_query() { - let perms1 = Permissions { - 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, - }; - let perms2 = Permissions { - read: UnaryPermission { - global_state: PermissionState::Prompt, - granted_list: resolve_fs_allowlist(&[PathBuf::from("/foo")]), - ..Default::default() - }, - write: UnaryPermission { - global_state: PermissionState::Prompt, - granted_list: resolve_fs_allowlist(&[PathBuf::from("/foo")]), - ..Default::default() - }, - net: UnaryPermission { - global_state: PermissionState::Prompt, - granted_list: ["127.0.0.1:8000".to_string()].iter().cloned().collect(), - ..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_url(&None).unwrap(), PermissionState::Granted); - assert_eq!(perms1.query_net_url(&Some("http://127.0.0.1:8000")).unwrap(), PermissionState::Granted); - assert_eq!(perms2.query_net_url(&None).unwrap(), PermissionState::Prompt); - assert_eq!(perms2.query_net_url(&Some("http://127.0.0.1:8000")).unwrap(), 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); - }; - } - - #[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, - }; - #[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); - set_prompt_result(false); - assert_eq!(perms.request_read(&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); - set_prompt_result(true); - assert_eq!(perms.request_write(&None), PermissionState::Denied); - set_prompt_result(true); - assert_eq!(perms.request_net(&None).unwrap(), PermissionState::Granted); - set_prompt_result(false); - assert_eq!(perms.request_net(&Some("http://127.0.0.1:8000")).unwrap(), PermissionState::Granted); - set_prompt_result(true); - assert_eq!(perms.request_env(), PermissionState::Granted); - set_prompt_result(false); - assert_eq!(perms.request_env(), PermissionState::Granted); - set_prompt_result(false); - assert_eq!(perms.request_run(), PermissionState::Denied); - set_prompt_result(true); - assert_eq!(perms.request_run(), PermissionState::Denied); - set_prompt_result(true); - assert_eq!(perms.request_plugin(), PermissionState::Granted); - set_prompt_result(false); - assert_eq!(perms.request_plugin(), PermissionState::Granted); - set_prompt_result(false); - assert_eq!(perms.request_hrtime(), PermissionState::Denied); - set_prompt_result(true); - assert_eq!(perms.request_hrtime(), PermissionState::Denied); - }; - } - - #[test] - fn test_revoke() { - let mut perms = Permissions { - read: UnaryPermission { - global_state: PermissionState::Prompt, - granted_list: resolve_fs_allowlist(&[PathBuf::from("/foo")]), - ..Default::default() - }, - write: UnaryPermission { - global_state: PermissionState::Prompt, - granted_list: resolve_fs_allowlist(&[PathBuf::from("/foo")]), - ..Default::default() - }, - net: UnaryPermission { - global_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(&None).unwrap(), PermissionState::Denied); - 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); - }; - } -} |