summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/isolate.rs19
-rw-r--r--src/main.rs1
-rw-r--r--src/ops.rs100
-rw-r--r--src/permissions.rs86
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())
+ }
+}