diff options
Diffstat (limited to 'cli/permissions.rs')
-rw-r--r-- | cli/permissions.rs | 110 |
1 files changed, 82 insertions, 28 deletions
diff --git a/cli/permissions.rs b/cli/permissions.rs index 833dc28df..298259f6b 100644 --- a/cli/permissions.rs +++ b/cli/permissions.rs @@ -1,10 +1,12 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use crate::colors; use crate::flags::Flags; +use crate::fs::resolve_from_cwd; use crate::op_error::OpError; use serde::de; use serde::Deserialize; use std::collections::HashSet; +use std::env::current_dir; use std::fmt; #[cfg(not(test))] use std::io; @@ -149,20 +151,20 @@ pub struct Permissions { pub allow_hrtime: PermissionState, } +fn resolve_fs_whitelist(whitelist: &[PathBuf]) -> HashSet<PathBuf> { + whitelist + .iter() + .map(|raw_path| resolve_from_cwd(Path::new(&raw_path)).unwrap()) + .collect() +} + impl Permissions { pub fn from_flags(flags: &Flags) -> Self { - // assert each whitelist path is absolute, since the cwd may change. - for path in &flags.read_whitelist { - assert!(path.has_root()); - } - for path in &flags.write_whitelist { - assert!(path.has_root()); - } Self { allow_read: PermissionState::from(flags.allow_read), - read_whitelist: flags.read_whitelist.iter().cloned().collect(), + read_whitelist: resolve_fs_whitelist(&flags.read_whitelist), allow_write: PermissionState::from(flags.allow_write), - write_whitelist: flags.write_whitelist.iter().cloned().collect(), + write_whitelist: resolve_fs_whitelist(&flags.write_whitelist), allow_net: PermissionState::from(flags.allow_net), net_whitelist: flags.net_whitelist.iter().cloned().collect(), allow_env: PermissionState::from(flags.allow_env), @@ -172,6 +174,24 @@ impl Permissions { } } + /// 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 + .get_state_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 { allow_read: PermissionState::from(true), @@ -199,12 +219,26 @@ impl Permissions { } pub fn check_read(&self, path: &Path) -> Result<(), OpError> { - self.get_state_read(&Some(path)).check( - &format!("read access to \"{}\"", path.display()), + let (resolved_path, display_path) = self.resolved_and_display_path(path); + self.get_state_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<(), OpError> { + let resolved_path = resolve_from_cwd(path).unwrap(); + self + .get_state_read(&Some(&resolved_path)) + .check(&format!("read access to <{}>", display), "--allow-read") + } + fn get_state_write(&self, path: &Option<&Path>) -> PermissionState { if path.map_or(false, |f| check_path_white_list(f, &self.write_whitelist)) { return PermissionState::Allow; @@ -213,8 +247,9 @@ impl Permissions { } pub fn check_write(&self, path: &Path) -> Result<(), OpError> { - self.get_state_write(&Some(path)).check( - &format!("write access to \"{}\"", path.display()), + let (resolved_path, display_path) = self.resolved_and_display_path(path); + self.get_state_write(&Some(&resolved_path)).check( + &format!("write access to \"{}\"", display_path.display()), "--allow-write", ) } @@ -264,8 +299,9 @@ impl Permissions { } pub fn check_plugin(&self, path: &Path) -> Result<(), OpError> { + let (_, display_path) = self.resolved_and_display_path(path); self.allow_plugin.check( - &format!("access to open a plugin: {}", path.display()), + &format!("access to open a plugin: {}", display_path.display()), "--allow-plugin", ) } @@ -277,26 +313,34 @@ impl Permissions { } pub fn request_read(&mut self, path: &Option<&Path>) -> PermissionState { - if path.map_or(false, |f| check_path_white_list(f, &self.read_whitelist)) { - return PermissionState::Allow; + let paths = path.map(|p| self.resolved_and_display_path(p)); + if let Some((p, _)) = paths.as_ref() { + if check_path_white_list(&p, &self.read_whitelist) { + return PermissionState::Allow; + } }; - self.allow_read.request(&match path { + self.allow_read.request(&match paths { None => "Deno requests read access".to_string(), - Some(path) => { - format!("Deno requests read access to \"{}\"", path.display()) - } + Some((_, display_path)) => format!( + "Deno requests read access to \"{}\"", + display_path.display() + ), }) } pub fn request_write(&mut self, path: &Option<&Path>) -> PermissionState { - if path.map_or(false, |f| check_path_white_list(f, &self.write_whitelist)) { - return PermissionState::Allow; + let paths = path.map(|p| self.resolved_and_display_path(p)); + if let Some((p, _)) = paths.as_ref() { + if check_path_white_list(&p, &self.write_whitelist) { + return PermissionState::Allow; + } }; - self.allow_write.request(&match path { + self.allow_write.request(&match paths { None => "Deno requests write access".to_string(), - Some(path) => { - format!("Deno requests write access to \"{}\"", path.display()) - } + Some((_, display_path)) => format!( + "Deno requests write access to \"{}\"", + display_path.display() + ), }) } @@ -335,10 +379,12 @@ impl Permissions { url: &Option<&str>, path: &Option<&Path>, ) -> Result<PermissionState, OpError> { + let path = path.map(|p| resolve_from_cwd(p).unwrap()); + let path = path.as_deref(); match name { "run" => Ok(self.allow_run), - "read" => Ok(self.get_state_read(path)), - "write" => Ok(self.get_state_write(path)), + "read" => Ok(self.get_state_read(&path)), + "write" => Ok(self.get_state_write(&path)), "net" => self.get_state_net_url(url), "env" => Ok(self.allow_env), "plugin" => Ok(self.allow_plugin), @@ -486,6 +532,14 @@ mod tests { 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()); |