1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-24 15:19:26 -05:00

feat: log permission access (#2518)

Replaces -D/--log-debug flag with --log-level=debug

--log-level=info displays permission access
This commit is contained in:
Bartek Iwańczuk 2019-06-22 18:02:51 +02:00 committed by Ryan Dahl
parent 988bcbb884
commit b9fbd55214
4 changed files with 146 additions and 83 deletions

View file

@ -1,6 +1,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
use crate::deno_dir; use crate::deno_dir;
use log::Level;
// Creates vector of strings, Vec<String> // Creates vector of strings, Vec<String>
macro_rules! svec { macro_rules! svec {
@ -9,7 +10,7 @@ macro_rules! svec {
#[derive(Clone, Debug, PartialEq, Default)] #[derive(Clone, Debug, PartialEq, Default)]
pub struct DenoFlags { pub struct DenoFlags {
pub log_debug: bool, pub log_level: Option<Level>,
pub version: bool, pub version: bool,
pub reload: bool, pub reload: bool,
/// When the `--config`/`-c` flag is used to pass the name, this will be set /// When the `--config`/`-c` flag is used to pass the name, this will be set
@ -127,10 +128,12 @@ To get help on the another subcommands (run in this case):
deno help run") deno help run")
.arg( .arg(
Arg::with_name("log-debug") Arg::with_name("log-level")
.short("D") .short("L")
.long("log-debug") .long("log-level")
.help("Log debug output") .help("Set log level")
.takes_value(true)
.possible_values(&["debug", "info"])
.global(true), .global(true),
).arg( ).arg(
Arg::with_name("reload") Arg::with_name("reload")
@ -409,8 +412,12 @@ fn resolve_paths(paths: Vec<String>) -> Vec<String> {
pub fn parse_flags(matches: &ArgMatches) -> DenoFlags { pub fn parse_flags(matches: &ArgMatches) -> DenoFlags {
let mut flags = DenoFlags::default(); let mut flags = DenoFlags::default();
if matches.is_present("log-debug") { if matches.is_present("log-level") {
flags.log_debug = true; flags.log_level = match matches.value_of("log-level").unwrap() {
"debug" => Some(Level::Debug),
"info" => Some(Level::Info),
_ => unreachable!(),
};
} }
if matches.is_present("version") { if matches.is_present("version") {
flags.version = true; flags.version = true;
@ -743,11 +750,10 @@ mod tests {
#[test] #[test]
fn test_flags_from_vec_2() { fn test_flags_from_vec_2() {
let (flags, subcommand, argv) = let (flags, subcommand, argv) =
flags_from_vec(svec!["deno", "-r", "-D", "run", "script.ts"]); flags_from_vec(svec!["deno", "-r", "run", "script.ts"]);
assert_eq!( assert_eq!(
flags, flags,
DenoFlags { DenoFlags {
log_debug: true,
reload: true, reload: true,
..DenoFlags::default() ..DenoFlags::default()
} }
@ -758,19 +764,12 @@ mod tests {
#[test] #[test]
fn test_flags_from_vec_3() { fn test_flags_from_vec_3() {
let (flags, subcommand, argv) = flags_from_vec(svec![ let (flags, subcommand, argv) =
"deno", flags_from_vec(svec!["deno", "run", "-r", "--allow-write", "script.ts"]);
"run",
"-r",
"-D",
"--allow-write",
"script.ts"
]);
assert_eq!( assert_eq!(
flags, flags,
DenoFlags { DenoFlags {
reload: true, reload: true,
log_debug: true,
allow_write: true, allow_write: true,
..DenoFlags::default() ..DenoFlags::default()
} }
@ -782,11 +781,10 @@ mod tests {
#[test] #[test]
fn test_flags_from_vec_4() { fn test_flags_from_vec_4() {
let (flags, subcommand, argv) = let (flags, subcommand, argv) =
flags_from_vec(svec!["deno", "-Dr", "run", "--allow-write", "script.ts"]); flags_from_vec(svec!["deno", "-r", "run", "--allow-write", "script.ts"]);
assert_eq!( assert_eq!(
flags, flags,
DenoFlags { DenoFlags {
log_debug: true,
reload: true, reload: true,
allow_write: true, allow_write: true,
..DenoFlags::default() ..DenoFlags::default()
@ -1179,7 +1177,6 @@ mod tests {
let (flags, subcommand, argv) = flags_from_vec(svec![ let (flags, subcommand, argv) = flags_from_vec(svec![
"deno", "deno",
"-r", "-r",
"-D",
"--allow-net", "--allow-net",
"run", "run",
"--allow-read", "--allow-read",
@ -1189,7 +1186,6 @@ mod tests {
flags, flags,
DenoFlags { DenoFlags {
reload: true, reload: true,
log_debug: true,
allow_net: true, allow_net: true,
allow_read: true, allow_read: true,
..DenoFlags::default() ..DenoFlags::default()
@ -1381,4 +1377,19 @@ mod tests {
] ]
); );
} }
#[test]
fn test_flags_from_vec_31() {
let (flags, subcommand, argv) =
flags_from_vec(svec!["deno", "--log-level=debug", "script.ts"]);
assert_eq!(
flags,
DenoFlags {
log_level: Some(Level::Debug),
..DenoFlags::default()
}
);
assert_eq!(subcommand, DenoSubcommand::Run);
assert_eq!(argv, svec!["deno", "script.ts"])
}
} }

View file

@ -56,7 +56,9 @@ use flags::DenoSubcommand;
use futures::future; use futures::future;
use futures::lazy; use futures::lazy;
use futures::Future; use futures::Future;
use log::{LevelFilter, Metadata, Record}; use log::Level;
use log::Metadata;
use log::Record;
use std::env; use std::env;
static LOGGER: Logger = Logger; static LOGGER: Logger = Logger;
@ -333,11 +335,11 @@ fn main() {
v8_set_flags(v8_flags.clone()); v8_set_flags(v8_flags.clone());
} }
log::set_max_level(if flags.log_debug { let log_level = match flags.log_level {
LevelFilter::Debug Some(level) => level,
} else { None => Level::Warn,
LevelFilter::Warn };
}); log::set_max_level(log_level.to_level_filter());
match subcommand { match subcommand {
DenoSubcommand::Bundle => bundle_command(flags, argv), DenoSubcommand::Bundle => bundle_command(flags, argv),

View file

@ -42,6 +42,7 @@ use futures::Sink;
use futures::Stream; use futures::Stream;
use hyper; use hyper;
use hyper::rt::Future; use hyper::rt::Future;
use log;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use remove_dir_all::remove_dir_all; use remove_dir_all::remove_dir_all;
use std; use std;
@ -362,6 +363,11 @@ fn op_start(
.clone() .clone()
.map(|m| builder.create_string(&m)); .map(|m| builder.create_string(&m));
let debug_flag = state
.flags
.log_level
.map_or(false, |l| l == log::Level::Debug);
let inner = msg::StartRes::create( let inner = msg::StartRes::create(
&mut builder, &mut builder,
&msg::StartResArgs { &msg::StartResArgs {
@ -369,7 +375,7 @@ fn op_start(
pid: std::process::id(), pid: std::process::id(),
argv: Some(argv_off), argv: Some(argv_off),
main_module, main_module,
debug_flag: state.flags.log_debug, debug_flag,
version_flag: state.flags.version, version_flag: state.flags.version,
v8_version: Some(v8_version_off), v8_version: Some(v8_version_off),
deno_version: Some(deno_version_off), deno_version: Some(deno_version_off),

View file

@ -6,6 +6,7 @@ use crate::flags::DenoFlags;
use ansi_term::Style; use ansi_term::Style;
use crate::deno_error::permission_denied; use crate::deno_error::permission_denied;
use crate::deno_error::DenoResult; use crate::deno_error::DenoResult;
use log;
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt; use std::fmt;
use std::io; use std::io;
@ -13,6 +14,8 @@ use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
const PERMISSION_EMOJI: &str = "⚠️";
/// Tri-state value for storing permission state /// Tri-state value for storing permission state
pub enum PermissionAccessorState { pub enum PermissionAccessorState {
Allow = 0, Allow = 0,
@ -158,40 +161,50 @@ impl DenoPermissions {
} }
pub fn check_run(&self) -> DenoResult<()> { pub fn check_run(&self) -> DenoResult<()> {
let msg = "access to run a subprocess";
match self.allow_run.get_state() { match self.allow_run.get_state() {
PermissionAccessorState::Allow => Ok(()), PermissionAccessorState::Allow => {
PermissionAccessorState::Ask => { self.log_perm_access(msg);
match self.try_permissions_prompt("access to run a subprocess") { Ok(())
}
PermissionAccessorState::Ask => match self.try_permissions_prompt(msg) {
Err(e) => Err(e), Err(e) => Err(e),
Ok(v) => { Ok(v) => {
self.allow_run.update_with_prompt_result(&v); self.allow_run.update_with_prompt_result(&v);
v.check()?; v.check()?;
self.log_perm_access(msg);
Ok(()) Ok(())
} }
} },
}
PermissionAccessorState::Deny => Err(permission_denied()), PermissionAccessorState::Deny => Err(permission_denied()),
} }
} }
pub fn check_read(&self, filename: &str) -> DenoResult<()> { pub fn check_read(&self, filename: &str) -> DenoResult<()> {
let msg = &format!("read access to \"{}\"", filename);
match self.allow_read.get_state() { match self.allow_read.get_state() {
PermissionAccessorState::Allow => Ok(()), PermissionAccessorState::Allow => {
self.log_perm_access(msg);
Ok(())
}
state => { state => {
if check_path_white_list(filename, &self.read_whitelist) { if check_path_white_list(filename, &self.read_whitelist) {
self.log_perm_access(msg);
Ok(()) Ok(())
} else { } else {
match state { match state {
PermissionAccessorState::Ask => match self.try_permissions_prompt( PermissionAccessorState::Ask => {
&format!("read access to \"{}\"", filename), match self.try_permissions_prompt(msg) {
) {
Err(e) => Err(e), Err(e) => Err(e),
Ok(v) => { Ok(v) => {
self.allow_read.update_with_prompt_result(&v); self.allow_read.update_with_prompt_result(&v);
v.check()?; v.check()?;
self.log_perm_access(msg);
Ok(()) Ok(())
} }
}, }
}
PermissionAccessorState::Deny => Err(permission_denied()), PermissionAccessorState::Deny => Err(permission_denied()),
_ => unreachable!(), _ => unreachable!(),
} }
@ -201,23 +214,29 @@ impl DenoPermissions {
} }
pub fn check_write(&self, filename: &str) -> DenoResult<()> { pub fn check_write(&self, filename: &str) -> DenoResult<()> {
let msg = &format!("write access to \"{}\"", filename);
match self.allow_write.get_state() { match self.allow_write.get_state() {
PermissionAccessorState::Allow => Ok(()), PermissionAccessorState::Allow => {
self.log_perm_access(msg);
Ok(())
}
state => { state => {
if check_path_white_list(filename, &self.write_whitelist) { if check_path_white_list(filename, &self.write_whitelist) {
self.log_perm_access(msg);
Ok(()) Ok(())
} else { } else {
match state { match state {
PermissionAccessorState::Ask => match self.try_permissions_prompt( PermissionAccessorState::Ask => {
&format!("write access to \"{}\"", filename), match self.try_permissions_prompt(msg) {
) {
Err(e) => Err(e), Err(e) => Err(e),
Ok(v) => { Ok(v) => {
self.allow_write.update_with_prompt_result(&v); self.allow_write.update_with_prompt_result(&v);
v.check()?; v.check()?;
self.log_perm_access(msg);
Ok(()) Ok(())
} }
}, }
}
PermissionAccessorState::Deny => Err(permission_denied()), PermissionAccessorState::Deny => Err(permission_denied()),
_ => unreachable!(), _ => unreachable!(),
} }
@ -227,8 +246,12 @@ impl DenoPermissions {
} }
pub fn check_net(&self, host_and_port: &str) -> DenoResult<()> { pub fn check_net(&self, host_and_port: &str) -> DenoResult<()> {
let msg = &format!("network access to \"{}\"", host_and_port);
match self.allow_net.get_state() { match self.allow_net.get_state() {
PermissionAccessorState::Allow => Ok(()), PermissionAccessorState::Allow => {
self.log_perm_access(msg);
Ok(())
}
state => { state => {
let parts = host_and_port.split(':').collect::<Vec<&str>>(); let parts = host_and_port.split(':').collect::<Vec<&str>>();
if match parts.len() { if match parts.len() {
@ -244,17 +267,22 @@ impl DenoPermissions {
1 => self.net_whitelist.contains(parts[0]), 1 => self.net_whitelist.contains(parts[0]),
_ => panic!("Failed to parse origin string: {}", host_and_port), _ => panic!("Failed to parse origin string: {}", host_and_port),
} { } {
self.log_perm_access(msg);
Ok(()) Ok(())
} else { } else {
self.check_net_inner(state, host_and_port) self.check_net_inner(state, msg)
} }
} }
} }
} }
pub fn check_net_url(&self, url: url::Url) -> DenoResult<()> { pub fn check_net_url(&self, url: url::Url) -> DenoResult<()> {
let msg = &format!("network access to \"{}\"", url);
match self.allow_net.get_state() { match self.allow_net.get_state() {
PermissionAccessorState::Allow => Ok(()), PermissionAccessorState::Allow => {
self.log_perm_access(msg);
Ok(())
}
state => { state => {
let host = url.host().unwrap(); let host = url.host().unwrap();
let whitelist_result = { let whitelist_result = {
@ -270,9 +298,10 @@ impl DenoPermissions {
} }
}; };
if whitelist_result { if whitelist_result {
self.log_perm_access(msg);
Ok(()) Ok(())
} else { } else {
self.check_net_inner(state, &url.to_string()) self.check_net_inner(state, msg)
} }
} }
} }
@ -284,34 +313,38 @@ impl DenoPermissions {
prompt_str: &str, prompt_str: &str,
) -> DenoResult<()> { ) -> DenoResult<()> {
match state { match state {
PermissionAccessorState::Ask => match self.try_permissions_prompt( PermissionAccessorState::Ask => {
&format!("network access to \"{}\"", prompt_str), match self.try_permissions_prompt(prompt_str) {
) {
Err(e) => Err(e), Err(e) => Err(e),
Ok(v) => { Ok(v) => {
self.allow_net.update_with_prompt_result(&v); self.allow_net.update_with_prompt_result(&v);
v.check()?; v.check()?;
self.log_perm_access(prompt_str);
Ok(()) Ok(())
} }
}, }
}
PermissionAccessorState::Deny => Err(permission_denied()), PermissionAccessorState::Deny => Err(permission_denied()),
_ => unreachable!(), _ => unreachable!(),
} }
} }
pub fn check_env(&self) -> DenoResult<()> { pub fn check_env(&self) -> DenoResult<()> {
let msg = "access to environment variables";
match self.allow_env.get_state() { match self.allow_env.get_state() {
PermissionAccessorState::Allow => Ok(()), PermissionAccessorState::Allow => {
PermissionAccessorState::Ask => { self.log_perm_access(msg);
match self.try_permissions_prompt("access to environment variables") { Ok(())
}
PermissionAccessorState::Ask => match self.try_permissions_prompt(msg) {
Err(e) => Err(e), Err(e) => Err(e),
Ok(v) => { Ok(v) => {
self.allow_env.update_with_prompt_result(&v); self.allow_env.update_with_prompt_result(&v);
v.check()?; v.check()?;
self.log_perm_access(msg);
Ok(()) Ok(())
} }
} },
}
PermissionAccessorState::Deny => Err(permission_denied()), PermissionAccessorState::Deny => Err(permission_denied()),
} }
} }
@ -328,6 +361,17 @@ impl DenoPermissions {
permission_prompt(message) permission_prompt(message)
} }
fn log_perm_access(&self, message: &str) {
if log_enabled!(log::Level::Info) {
eprintln!(
"{}",
Style::new()
.bold()
.paint(format!("{} Granted {}", PERMISSION_EMOJI, message))
);
}
}
pub fn allows_run(&self) -> bool { pub fn allows_run(&self) -> bool {
self.allow_run.is_allow() self.allow_run.is_allow()
} }
@ -414,7 +458,7 @@ impl fmt::Display for PromptResult {
} }
fn permission_prompt(message: &str) -> DenoResult<PromptResult> { fn permission_prompt(message: &str) -> DenoResult<PromptResult> {
let msg = format!(" Deno requests {}. Grant? [a/y/n/d (a = allow always, y = allow once, n = deny once, d = deny always)] ", message); let msg = format!("{} Deno requests {}. Grant? [a/y/n/d (a = allow always, y = allow once, n = deny once, d = deny always)] ", PERMISSION_EMOJI, message);
// print to stderr so that if deno is > to a file this is still displayed. // print to stderr so that if deno is > to a file this is still displayed.
eprint!("{}", Style::new().bold().paint(msg)); eprint!("{}", Style::new().bold().paint(msg));
loop { loop {