diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/isolate.rs | 19 | ||||
-rw-r--r-- | src/main.rs | 1 | ||||
-rw-r--r-- | src/ops.rs | 100 | ||||
-rw-r--r-- | src/permissions.rs | 86 |
4 files changed, 157 insertions, 49 deletions
diff --git a/src/isolate.rs b/src/isolate.rs index e222d280f..6530c396f 100644 --- a/src/isolate.rs +++ b/src/isolate.rs @@ -6,8 +6,10 @@ use deno_dir; use errors::DenoError; +use errors::DenoResult; use flags; use libdeno; +use permissions::DenoPermissions; use snapshot; use futures::Future; @@ -56,6 +58,7 @@ pub struct Isolate { pub struct IsolateState { pub dir: deno_dir::DenoDir, pub argv: Vec<String>, + pub permissions: Mutex<DenoPermissions>, pub flags: flags::DenoFlags, tx: Mutex<Option<mpsc::Sender<(i32, Buf)>>>, pub metrics: Mutex<Metrics>, @@ -71,6 +74,21 @@ impl IsolateState { tx.send((req_id, buf)).expect("tx.send error"); } + pub fn check_write(&self, filename: &str) -> DenoResult<()> { + let mut perm = self.permissions.lock().unwrap(); + perm.check_write(filename) + } + + pub fn check_env(&self) -> DenoResult<()> { + let mut perm = self.permissions.lock().unwrap(); + perm.check_env() + } + + pub fn check_net(&self, filename: &str) -> DenoResult<()> { + let mut perm = self.permissions.lock().unwrap(); + perm.check_net(filename) + } + fn metrics_op_dispatched( &self, bytes_sent_control: u64, @@ -143,6 +161,7 @@ impl Isolate { state: Arc::new(IsolateState { dir: deno_dir::DenoDir::new(flags.reload, custom_root).unwrap(), argv: argv_rest, + permissions: Mutex::new(DenoPermissions::new(&flags)), flags, tx: Mutex::new(Some(tx)), metrics: Mutex::new(Metrics::default()), diff --git a/src/main.rs b/src/main.rs index 91f566523..d524b94ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,6 +31,7 @@ mod http_util; mod isolate; mod libdeno; pub mod ops; +mod permissions; mod resources; mod snapshot; mod tokio_util; diff --git a/src/ops.rs b/src/ops.rs index 37cbd6826..b7a20a46e 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -1,6 +1,5 @@ // Copyright 2018 the Deno authors. All rights reserved. MIT license. use errors; -use errors::permission_denied; use errors::{DenoError, DenoResult, ErrorKind}; use fs as deno_fs; use http_util; @@ -333,11 +332,9 @@ fn op_set_env( let inner = base.inner_as_set_env().unwrap(); let key = inner.key().unwrap(); let value = inner.value().unwrap(); - - if !state.flags.allow_env { - return odd_future(permission_denied()); + if let Err(e) = state.check_env() { + return odd_future(e); } - std::env::set_var(key, value); ok_future(empty_buf()) } @@ -350,8 +347,8 @@ fn op_env( assert_eq!(data.len(), 0); let cmd_id = base.cmd_id(); - if !state.flags.allow_env { - return odd_future(permission_denied()); + if let Err(e) = state.check_env() { + return odd_future(e); } let builder = &mut FlatBufferBuilder::new(); @@ -399,8 +396,9 @@ fn op_fetch_req( let id = inner.id(); let url = inner.url().unwrap(); - if !state.flags.allow_net { - return odd_future(permission_denied()); + // FIXME use domain (or use this inside check_net) + if let Err(e) = state.check_net(url) { + return odd_future(e); } let url = url.parse::<hyper::Uri>().unwrap(); @@ -513,8 +511,9 @@ fn op_make_temp_dir( let inner = base.inner_as_make_temp_dir().unwrap(); let cmd_id = base.cmd_id(); - if !state.flags.allow_write { - return odd_future(permission_denied()); + // FIXME + if let Err(e) = state.check_write("make_temp") { + return odd_future(e); } let dir = inner.dir().map(PathBuf::from); @@ -562,10 +561,9 @@ fn op_mkdir( let mode = inner.mode(); let path = String::from(inner.path().unwrap()); - if !state.flags.allow_write { - return odd_future(permission_denied()); + if let Err(e) = state.check_write(&path) { + return odd_future(e); } - blocking!(base.sync(), || { debug!("op_mkdir {}", path); deno_fs::mkdir(Path::new(&path), mode)?; @@ -583,8 +581,8 @@ fn op_chmod( let _mode = inner.mode(); let path = String::from(inner.path().unwrap()); - if !state.flags.allow_write { - return odd_future(permission_denied()); + if let Err(e) = state.check_write(&path) { + return odd_future(e); } blocking!(base.sync(), || { @@ -766,11 +764,14 @@ fn op_remove( ) -> Box<Op> { assert_eq!(data.len(), 0); let inner = base.inner_as_remove().unwrap(); - let path = PathBuf::from(inner.path().unwrap()); + let path_ = inner.path().unwrap(); + let path = PathBuf::from(path_); let recursive = inner.recursive(); - if !state.flags.allow_write { - return odd_future(permission_denied()); + + if let Err(e) = state.check_write(path.to_str().unwrap()) { + return odd_future(e); } + blocking!(base.sync(), || { debug!("op_remove {}", path.display()); let metadata = fs::metadata(&path)?; @@ -831,10 +832,11 @@ fn op_copy_file( assert_eq!(data.len(), 0); let inner = base.inner_as_copy_file().unwrap(); let from = PathBuf::from(inner.from().unwrap()); - let to = PathBuf::from(inner.to().unwrap()); + let to_ = inner.to().unwrap(); + let to = PathBuf::from(to_); - if !state.flags.allow_write { - return odd_future(permission_denied()); + if let Err(e) = state.check_write(&to_) { + return odd_future(e); } debug!("op_copy_file {} {}", from.display(), to.display()); @@ -1015,14 +1017,13 @@ fn op_write_file( data: &'static mut [u8], ) -> Box<Op> { let inner = base.inner_as_write_file().unwrap(); - - if !state.flags.allow_write { - return odd_future(permission_denied()); - } - let filename = String::from(inner.filename().unwrap()); let perm = inner.perm(); + if let Err(e) = state.check_write(&filename) { + return odd_future(e); + } + blocking!(base.sync(), || -> OpResult { debug!("op_write_file {} {}", filename, data.len()); deno_fs::write_file(Path::new(&filename), data, perm)?; @@ -1036,12 +1037,13 @@ fn op_rename( data: &'static mut [u8], ) -> Box<Op> { assert_eq!(data.len(), 0); - if !state.flags.allow_write { - return odd_future(permission_denied()); - } let inner = base.inner_as_rename().unwrap(); let oldpath = PathBuf::from(inner.oldpath().unwrap()); - let newpath = PathBuf::from(inner.newpath().unwrap()); + let newpath_ = inner.newpath().unwrap(); + let newpath = PathBuf::from(newpath_); + if let Err(e) = state.check_write(&newpath_) { + return odd_future(e); + } blocking!(base.sync(), || -> OpResult { debug!("op_rename {} {}", oldpath.display(), newpath.display()); fs::rename(&oldpath, &newpath)?; @@ -1055,8 +1057,13 @@ fn op_symlink( data: &'static mut [u8], ) -> Box<Op> { assert_eq!(data.len(), 0); - if !state.flags.allow_write { - return odd_future(permission_denied()); + let inner = base.inner_as_symlink().unwrap(); + let oldname = PathBuf::from(inner.oldname().unwrap()); + let newname_ = inner.newname().unwrap(); + let newname = PathBuf::from(newname_); + + if let Err(e) = state.check_write(&newname_) { + return odd_future(e); } // TODO Use type for Windows. if cfg!(windows) { @@ -1065,10 +1072,6 @@ fn op_symlink( "Not implemented".to_string(), )); } - - let inner = base.inner_as_symlink().unwrap(); - let oldname = PathBuf::from(inner.oldname().unwrap()); - let newname = PathBuf::from(inner.newname().unwrap()); blocking!(base.sync(), || -> OpResult { debug!("op_symlink {} {}", oldname.display(), newname.display()); #[cfg(any(unix))] @@ -1118,13 +1121,14 @@ fn op_truncate( ) -> Box<Op> { assert_eq!(data.len(), 0); - if !state.flags.allow_write { - return odd_future(permission_denied()); - } - let inner = base.inner_as_truncate().unwrap(); let filename = String::from(inner.name().unwrap()); let len = inner.len(); + + if let Err(e) = state.check_write(&filename) { + return odd_future(e); + } + blocking!(base.sync(), || { debug!("op_truncate {} {}", filename, len); let f = fs::OpenOptions::new().write(true).open(&filename)?; @@ -1139,8 +1143,8 @@ fn op_listen( data: &'static mut [u8], ) -> Box<Op> { assert_eq!(data.len(), 0); - if !state.flags.allow_net { - return odd_future(permission_denied()); + if let Err(e) = state.check_net("listen") { + return odd_future(e); } let cmd_id = base.cmd_id(); @@ -1205,10 +1209,9 @@ fn op_accept( data: &'static mut [u8], ) -> Box<Op> { assert_eq!(data.len(), 0); - if !state.flags.allow_net { - return odd_future(permission_denied()); + if let Err(e) = state.check_net("accept") { + return odd_future(e); } - let cmd_id = base.cmd_id(); let inner = base.inner_as_accept().unwrap(); let server_rid = inner.rid(); @@ -1232,10 +1235,9 @@ fn op_dial( data: &'static mut [u8], ) -> Box<Op> { assert_eq!(data.len(), 0); - if !state.flags.allow_net { - return odd_future(permission_denied()); + if let Err(e) = state.check_net("dial") { + return odd_future(e); } - let cmd_id = base.cmd_id(); let inner = base.inner_as_dial().unwrap(); let network = inner.network().unwrap(); diff --git a/src/permissions.rs b/src/permissions.rs new file mode 100644 index 000000000..aeeb7df9a --- /dev/null +++ b/src/permissions.rs @@ -0,0 +1,86 @@ +extern crate atty; + +use flags::DenoFlags; + +use errors::permission_denied; +use errors::DenoResult; +use std::io; + +#[derive(Debug, Default, PartialEq)] +pub struct DenoPermissions { + pub allow_write: bool, + pub allow_net: bool, + pub allow_env: bool, +} + +impl DenoPermissions { + pub fn new(flags: &DenoFlags) -> DenoPermissions { + DenoPermissions { + allow_write: flags.allow_write, + allow_env: flags.allow_env, + allow_net: flags.allow_net, + } + } + + pub fn check_write(&mut self, filename: &str) -> DenoResult<()> { + if self.allow_write { + return Ok(()); + }; + // TODO get location (where access occurred) + let r = permission_prompt(format!( + "Deno requests write access to \"{}\".", + filename + ));; + if r.is_ok() { + self.allow_write = true; + } + r + } + + pub fn check_net(&mut self, domain_name: &str) -> DenoResult<()> { + if self.allow_net { + return Ok(()); + }; + // TODO get location (where access occurred) + let r = permission_prompt(format!( + "Deno requests network access to \"{}\".", + domain_name + )); + if r.is_ok() { + self.allow_net = true; + } + r + } + + pub fn check_env(&mut self) -> DenoResult<()> { + if self.allow_env { + return Ok(()); + }; + // TODO get location (where access occurred) + let r = permission_prompt( + "Deno requests access to environment variables.".to_string(), + ); + if r.is_ok() { + self.allow_env = true; + } + r + } +} + +fn permission_prompt(message: String) -> DenoResult<()> { + if !atty::is(atty::Stream::Stdin) || !atty::is(atty::Stream::Stderr) { + return Err(permission_denied()); + }; + // print to stderr so that if deno is > to a file this is still displayed. + eprint!("{} Grant? [yN] ", message); + let mut input = String::new(); + let stdin = io::stdin(); + let _nread = stdin.read_line(&mut input)?; + let ch = input.chars().next().unwrap(); + let is_yes = ch == 'y' || ch == 'Y'; + if is_yes { + Ok(()) + } else { + Err(permission_denied()) + } +} |