diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs index 3988acf9e3..a7daaa63ae 100644 --- a/ext/fetch/lib.rs +++ b/ext/fetch/lib.rs @@ -167,8 +167,12 @@ impl FetchHandler for DefaultFileFetchHandler { } pub trait FetchPermissions { - fn check_net_url(&mut self, _url: &Url) -> Result<(), AnyError>; - fn check_read(&mut self, _p: &Path) -> Result<(), AnyError>; + fn check_net_url( + &mut self, + _url: &Url, + api_name: &str, + ) -> Result<(), AnyError>; + fn check_read(&mut self, _p: &Path, api_name: &str) -> Result<(), AnyError>; } pub fn get_declaration() -> PathBuf { @@ -215,7 +219,7 @@ where type_error("NetworkError when attempting to fetch resource.") })?; let permissions = state.borrow_mut::(); - permissions.check_read(&path)?; + permissions.check_read(&path, "fetch()")?; if method != Method::GET { return Err(type_error(format!( @@ -240,7 +244,7 @@ where } "http" | "https" => { let permissions = state.borrow_mut::(); - permissions.check_net_url(&url)?; + permissions.check_net_url(&url, "fetch()")?; let mut request = client.request(method.clone(), url); @@ -535,7 +539,7 @@ where if let Some(proxy) = args.proxy.clone() { let permissions = state.borrow_mut::(); let url = Url::parse(&proxy.url)?; - permissions.check_net_url(&url)?; + permissions.check_net_url(&url, "Deno.createHttpClient()")?; } let client_cert_chain_and_key = { diff --git a/ext/flash/lib.rs b/ext/flash/lib.rs index 957c011bf9..93c95f84b2 100644 --- a/ext/flash/lib.rs +++ b/ext/flash/lib.rs @@ -1135,7 +1135,7 @@ where check_unstable(state, "Deno.serve"); state .borrow_mut::

() - .check_net(&(&opts.hostname, Some(opts.port)))?; + .check_net(&(&opts.hostname, Some(opts.port)), "Deno.serve()")?; let addr = resolve_addr_sync(&opts.hostname, opts.port)? .next() @@ -1377,6 +1377,7 @@ pub trait FlashPermissions { fn check_net>( &mut self, _host: &(T, Option), + _api_name: &str, ) -> Result<(), AnyError>; } diff --git a/ext/net/lib.rs b/ext/net/lib.rs index 2491700607..35d6125988 100644 --- a/ext/net/lib.rs +++ b/ext/net/lib.rs @@ -21,9 +21,11 @@ pub trait NetPermissions { fn check_net>( &mut self, _host: &(T, Option), + _api_name: &str, ) -> Result<(), AnyError>; - fn check_read(&mut self, _p: &Path) -> Result<(), AnyError>; - fn check_write(&mut self, _p: &Path) -> Result<(), AnyError>; + fn check_read(&mut self, _p: &Path, _api_name: &str) -> Result<(), AnyError>; + fn check_write(&mut self, _p: &Path, _api_name: &str) + -> Result<(), AnyError>; } /// `UnstableChecker` is a struct so it can be placed inside `GothamState`; diff --git a/ext/net/ops.rs b/ext/net/ops.rs index 36786cd861..41d04467ea 100644 --- a/ext/net/ops.rs +++ b/ext/net/ops.rs @@ -252,8 +252,10 @@ where } if transport == "udp" => { { let mut s = state.borrow_mut(); - s.borrow_mut::() - .check_net(&(&args.hostname, Some(args.port)))?; + s.borrow_mut::().check_net( + &(&args.hostname, Some(args.port)), + "Deno.DatagramConn.send()", + )?; } let addr = resolve_addr(&args.hostname, args.port) .await? @@ -278,7 +280,8 @@ where let address_path = Path::new(&args.path); { let mut s = state.borrow_mut(); - s.borrow_mut::().check_write(address_path)?; + s.borrow_mut::() + .check_write(address_path, "Deno.DatagramConn.send()")?; } let resource = state .borrow() @@ -319,7 +322,7 @@ where let mut state_ = state.borrow_mut(); state_ .borrow_mut::() - .check_net(&(&args.hostname, Some(args.port)))?; + .check_net(&(&args.hostname, Some(args.port)), "Deno.connect()")?; } let addr = resolve_addr(&args.hostname, args.port) .await? @@ -354,8 +357,12 @@ where super::check_unstable2(&state, "Deno.connect"); { let mut state_ = state.borrow_mut(); - state_.borrow_mut::().check_read(address_path)?; - state_.borrow_mut::().check_write(address_path)?; + state_ + .borrow_mut::() + .check_read(address_path, "Deno.connect()")?; + state_ + .borrow_mut::() + .check_write(address_path, "Deno.connect()")?; } let path = args.path; let unix_stream = net_unix::UnixStream::connect(Path::new(&path)).await?; @@ -494,9 +501,10 @@ where if transport == "udp" { super::check_unstable(state, "Deno.listenDatagram"); } - state - .borrow_mut::() - .check_net(&(&args.hostname, Some(args.port)))?; + state.borrow_mut::().check_net( + &(&args.hostname, Some(args.port)), + "Deno.listenDatagram()", + )?; } let addr = resolve_addr_sync(&args.hostname, args.port)? .next() @@ -540,9 +548,14 @@ where if transport == "unixpacket" { super::check_unstable(state, "Deno.listenDatagram"); } + let api_name = if transport == "unix" { + "Deno.listen()" + } else { + "Deno.listenDatagram()" + }; let permissions = state.borrow_mut::(); - permissions.check_read(address_path)?; - permissions.check_write(address_path)?; + permissions.check_read(address_path, api_name)?; + permissions.check_write(address_path, api_name)?; } let (rid, local_addr) = if transport == "unix" { net_unix::listen_unix(state, address_path)? @@ -678,7 +691,7 @@ where let socker_addr = &ns.socket_addr; let ip = socker_addr.ip().to_string(); let port = socker_addr.port(); - perm.check_net(&(ip, Some(port)))?; + perm.check_net(&(ip, Some(port)), "Deno.resolveDns()")?; } } @@ -1010,15 +1023,24 @@ mod tests { fn check_net>( &mut self, _host: &(T, Option), + _api_name: &str, ) -> Result<(), AnyError> { Ok(()) } - fn check_read(&mut self, _p: &Path) -> Result<(), AnyError> { + fn check_read( + &mut self, + _p: &Path, + _api_name: &str, + ) -> Result<(), AnyError> { Ok(()) } - fn check_write(&mut self, _p: &Path) -> Result<(), AnyError> { + fn check_write( + &mut self, + _p: &Path, + _api_name: &str, + ) -> Result<(), AnyError> { Ok(()) } } diff --git a/ext/net/ops_tls.rs b/ext/net/ops_tls.rs index 1c91674df0..230f4359eb 100644 --- a/ext/net/ops_tls.rs +++ b/ext/net/ops_tls.rs @@ -799,7 +799,7 @@ where { let mut s = state.borrow_mut(); let permissions = s.borrow_mut::(); - permissions.check_net(&(hostname, Some(0)))?; + permissions.check_net(&(hostname, Some(0)), "Deno.startTls()")?; } let ca_certs = args @@ -904,9 +904,9 @@ where { let mut s = state.borrow_mut(); let permissions = s.borrow_mut::(); - permissions.check_net(&(hostname, Some(port)))?; + permissions.check_net(&(hostname, Some(port)), "Deno.connectTls()")?; if let Some(path) = cert_file { - permissions.check_read(Path::new(path))?; + permissions.check_read(Path::new(path), "Deno.connectTls()")?; } } @@ -1051,12 +1051,12 @@ where { let permissions = state.borrow_mut::(); - permissions.check_net(&(hostname, Some(port)))?; + permissions.check_net(&(hostname, Some(port)), "Deno.listenTls()")?; if let Some(path) = cert_file { - permissions.check_read(Path::new(path))?; + permissions.check_read(Path::new(path), "Deno.listenTls()")?; } if let Some(path) = key_file { - permissions.check_read(Path::new(path))?; + permissions.check_read(Path::new(path), "Deno.listenTls()")?; } } diff --git a/ext/websocket/01_websocket.js b/ext/websocket/01_websocket.js index bdb29526e3..f7bd820c0c 100644 --- a/ext/websocket/01_websocket.js +++ b/ext/websocket/01_websocket.js @@ -191,6 +191,7 @@ this[_url] = wsURL.href; ops.op_ws_check_permission_and_cancel_handle( + "WebSocket.abort()", this[_url], false, ); @@ -227,6 +228,7 @@ PromisePrototypeThen( core.opAsync( "op_ws_create", + "new WebSocket()", wsURL.href, ArrayPrototypeJoin(protocols, ", "), ), diff --git a/ext/websocket/02_websocketstream.js b/ext/websocket/02_websocketstream.js index cf83fe4c7d..598816d052 100644 --- a/ext/websocket/02_websocketstream.js +++ b/ext/websocket/02_websocketstream.js @@ -133,6 +133,7 @@ } const cancelRid = ops.op_ws_check_permission_and_cancel_handle( + "WebSocketStream.abort()", this[_url], true, ); @@ -150,6 +151,7 @@ PromisePrototypeThen( core.opAsync( "op_ws_create", + "new WebSocketStream()", this[_url], options.protocols ? ArrayPrototypeJoin(options.protocols, ", ") diff --git a/ext/websocket/lib.rs b/ext/websocket/lib.rs index fad217585b..e8ada74a2d 100644 --- a/ext/websocket/lib.rs +++ b/ext/websocket/lib.rs @@ -61,7 +61,11 @@ pub struct WsRootStore(pub Option); pub struct WsUserAgent(pub String); pub trait WebSocketPermissions { - fn check_net_url(&mut self, _url: &url::Url) -> Result<(), AnyError>; + fn check_net_url( + &mut self, + _url: &url::Url, + _api_name: &str, + ) -> Result<(), AnyError>; } /// `UnsafelyIgnoreCertificateErrors` is a wrapper struct so it can be placed inside `GothamState`; @@ -211,6 +215,7 @@ impl Resource for WsCancelResource { #[op] pub fn op_ws_check_permission_and_cancel_handle( state: &mut OpState, + api_name: String, url: String, cancel_handle: bool, ) -> Result, AnyError> @@ -219,7 +224,7 @@ where { state .borrow_mut::() - .check_net_url(&url::Url::parse(&url)?)?; + .check_net_url(&url::Url::parse(&url)?, &api_name)?; if cancel_handle { let rid = state @@ -242,6 +247,7 @@ pub struct CreateResponse { #[op] pub async fn op_ws_create( state: Rc>, + api_name: String, url: String, protocols: String, cancel_handle: Option, @@ -253,7 +259,7 @@ where { let mut s = state.borrow_mut(); s.borrow_mut::() - .check_net_url(&url::Url::parse(&url)?) + .check_net_url(&url::Url::parse(&url)?, &api_name) .expect( "Permission check should have been done in op_ws_check_permission", ); diff --git a/runtime/build.rs b/runtime/build.rs index a9ba09825f..d45c4a2a81 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -72,6 +72,7 @@ mod not_docs { fn check_net_url( &mut self, _url: &deno_core::url::Url, + _api_name: &str, ) -> Result<(), deno_core::error::AnyError> { unreachable!("snapshotting!") } @@ -79,6 +80,7 @@ mod not_docs { fn check_read( &mut self, _p: &Path, + _api_name: &str, ) -> Result<(), deno_core::error::AnyError> { unreachable!("snapshotting!") } @@ -88,6 +90,7 @@ mod not_docs { fn check_net_url( &mut self, _url: &deno_core::url::Url, + _api_name: &str, ) -> Result<(), deno_core::error::AnyError> { unreachable!("snapshotting!") } @@ -120,6 +123,7 @@ mod not_docs { fn check_net>( &mut self, _host: &(T, Option), + _api_name: &str, ) -> Result<(), deno_core::error::AnyError> { unreachable!("snapshotting!") } @@ -138,6 +142,7 @@ mod not_docs { fn check_net>( &mut self, _host: &(T, Option), + _api_name: &str, ) -> Result<(), deno_core::error::AnyError> { unreachable!("snapshotting!") } @@ -145,6 +150,7 @@ mod not_docs { fn check_read( &mut self, _p: &Path, + _api_name: &str, ) -> Result<(), deno_core::error::AnyError> { unreachable!("snapshotting!") } @@ -152,6 +158,7 @@ mod not_docs { fn check_write( &mut self, _p: &Path, + _api_name: &str, ) -> Result<(), deno_core::error::AnyError> { unreachable!("snapshotting!") } diff --git a/runtime/js/40_process.js b/runtime/js/40_process.js index e3f6fc7a08..76ce57fe38 100644 --- a/runtime/js/40_process.js +++ b/runtime/js/40_process.js @@ -16,8 +16,12 @@ String, } = window.__bootstrap.primordials; - function opKill(pid, signo) { - ops.op_kill(pid, signo); + function opKill(pid, signo, apiName) { + ops.op_kill(pid, signo, apiName); + } + + function kill(pid, signo) { + opKill(pid, signo, "Deno.kill()"); } function opRunStatus(rid) { @@ -91,7 +95,7 @@ } kill(signo) { - opKill(this.pid, signo); + opKill(this.pid, signo, "Deno.Process.kill()"); } } @@ -126,6 +130,6 @@ window.__bootstrap.process = { run, Process, - kill: opKill, + kill, }; })(this); diff --git a/runtime/js/40_spawn.js b/runtime/js/40_spawn.js index 4fae9e6b77..daa4f8ff88 100644 --- a/runtime/js/40_spawn.js +++ b/runtime/js/40_spawn.js @@ -21,7 +21,7 @@ const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId"); - function spawnChild(command, { + function spawnChildInner(command, apiName, { args = [], cwd = undefined, clearEnv = false, @@ -44,13 +44,17 @@ stdin, stdout, stderr, - }); + }, apiName); return new Child(illegalConstructorKey, { ...child, signal, }); } + function spawnChild(command, options = {}) { + return spawnChildInner(command, "Deno.spawnChild()", options); + } + async function collectOutput(readableStream) { if (!(readableStream instanceof ReadableStream)) { return null; @@ -204,7 +208,7 @@ if (this.#rid === null) { throw new TypeError("Child process has already terminated."); } - ops.op_kill(this.#pid, signo); + ops.op_kill(this.#pid, signo, "Deno.Child.kill()"); } ref() { @@ -228,7 +232,7 @@ "Piped stdin is not supported for this function, use 'Deno.spawnChild()' instead", ); } - return spawnChild(command, options).output(); + return spawnChildInner(command, "Deno.spawn()", options).output(); } function spawnSync(command, { diff --git a/runtime/ops/fs.rs b/runtime/ops/fs.rs index f5e96e7e21..9b0737fe95 100644 --- a/runtime/ops/fs.rs +++ b/runtime/ops/fs.rs @@ -126,6 +126,7 @@ fn open_helper( path: &str, mode: Option, options: Option<&OpenOptions>, + api_name: &str, ) -> Result<(PathBuf, std::fs::OpenOptions), AnyError> { let path = Path::new(path).to_path_buf(); @@ -147,7 +148,7 @@ fn open_helper( match options { None => { - permissions.read.check(&path)?; + permissions.read.check(&path, Some(api_name))?; open_options .read(true) .create(false) @@ -158,11 +159,11 @@ fn open_helper( } Some(options) => { if options.read { - permissions.read.check(&path)?; + permissions.read.check(&path, Some(api_name))?; } if options.write || options.append { - permissions.write.check(&path)?; + permissions.write.check(&path, Some(api_name))?; } open_options @@ -185,7 +186,8 @@ fn op_open_sync( options: Option, mode: Option, ) -> Result { - let (path, open_options) = open_helper(state, &path, mode, options.as_ref())?; + let (path, open_options) = + open_helper(state, &path, mode, options.as_ref(), "Deno.openSync()")?; let std_file = open_options.open(&path).map_err(|err| { Error::new(err.kind(), format!("{}, open '{}'", err, path.display())) })?; @@ -201,8 +203,13 @@ async fn op_open_async( options: Option, mode: Option, ) -> Result { - let (path, open_options) = - open_helper(&mut state.borrow_mut(), &path, mode, options.as_ref())?; + let (path, open_options) = open_helper( + &mut state.borrow_mut(), + &path, + mode, + options.as_ref(), + "Deno.open()", + )?; let std_file = tokio::task::spawn_blocking(move || { open_options.open(path.clone()).map_err(|err| { Error::new(err.kind(), format!("{}, open '{}'", err, path.display())) @@ -240,6 +247,7 @@ fn op_write_file_sync( &path, mode, Some(&write_open_options(create, append)), + "Deno.writeFileSync()", )?; write_file(&path, open_options, mode, data) } @@ -267,6 +275,7 @@ async fn op_write_file_async( &path, mode, Some(&write_open_options(create, append)), + "Deno.writeFile()", )?; let write_future = tokio::task::spawn_blocking(move || { write_file(&path, open_options, mode, data) @@ -517,7 +526,10 @@ fn op_umask(state: &mut OpState, mask: Option) -> Result { #[op] fn op_chdir(state: &mut OpState, directory: String) -> Result<(), AnyError> { let d = PathBuf::from(&directory); - state.borrow_mut::().read.check(&d)?; + state + .borrow_mut::() + .read + .check(&d, Some("Deno.chdir()"))?; set_current_dir(&d).map_err(|err| { Error::new(err.kind(), format!("{}, chdir '{}'", err, directory)) })?; @@ -536,7 +548,10 @@ pub struct MkdirArgs { fn op_mkdir_sync(state: &mut OpState, args: MkdirArgs) -> Result<(), AnyError> { let path = Path::new(&args.path).to_path_buf(); let mode = args.mode.unwrap_or(0o777) & 0o777; - state.borrow_mut::().write.check(&path)?; + state + .borrow_mut::() + .write + .check(&path, Some("Deno.mkdirSync()"))?; debug!("op_mkdir {} {:o} {}", path.display(), mode, args.recursive); let mut builder = std::fs::DirBuilder::new(); builder.recursive(args.recursive); @@ -561,7 +576,10 @@ async fn op_mkdir_async( { let mut state = state.borrow_mut(); - state.borrow_mut::().write.check(&path)?; + state + .borrow_mut::() + .write + .check(&path, Some("Deno.mkdir()"))?; } tokio::task::spawn_blocking(move || { @@ -591,7 +609,10 @@ fn op_chmod_sync( let path = Path::new(&path); let mode = mode & 0o777; - state.borrow_mut::().write.check(path)?; + state + .borrow_mut::() + .write + .check(path, Some("Deno.chmodSync()"))?; raw_chmod(path, mode) } @@ -606,7 +627,10 @@ async fn op_chmod_async( { let mut state = state.borrow_mut(); - state.borrow_mut::().write.check(&path)?; + state + .borrow_mut::() + .write + .check(&path, Some("Deno.chmod()"))?; } tokio::task::spawn_blocking(move || raw_chmod(&path, mode)) @@ -642,7 +666,10 @@ fn op_chown_sync( #[cfg_attr(windows, allow(unused_variables))] gid: Option, ) -> Result<(), AnyError> { let path = Path::new(&path).to_path_buf(); - state.borrow_mut::().write.check(&path)?; + state + .borrow_mut::() + .write + .check(&path, Some("Deno.chownSync()"))?; #[cfg(unix)] { use crate::errors::get_nix_error_class; @@ -675,7 +702,10 @@ async fn op_chown_async( { let mut state = state.borrow_mut(); - state.borrow_mut::().write.check(&path)?; + state + .borrow_mut::() + .write + .check(&path, Some("Deno.chown()"))?; } tokio::task::spawn_blocking(move || { @@ -709,7 +739,10 @@ fn op_remove_sync( ) -> Result<(), AnyError> { let path = PathBuf::from(&path); - state.borrow_mut::().write.check(&path)?; + state + .borrow_mut::() + .write + .check(&path, Some("Deno.removeSync()"))?; #[cfg(not(unix))] use std::os::windows::prelude::MetadataExt; @@ -755,7 +788,10 @@ async fn op_remove_async( { let mut state = state.borrow_mut(); - state.borrow_mut::().write.check(&path)?; + state + .borrow_mut::() + .write + .check(&path, Some("Deno.remove()"))?; } tokio::task::spawn_blocking(move || { @@ -806,8 +842,12 @@ fn op_copy_file_sync( let to_path = PathBuf::from(&to); let permissions = state.borrow_mut::(); - permissions.read.check(&from_path)?; - permissions.write.check(&to_path)?; + permissions + .read + .check(&from_path, Some("Deno.copyFileSync()"))?; + permissions + .write + .check(&to_path, Some("Deno.copyFileSync()"))?; // On *nix, Rust reports non-existent `from` as ErrorKind::InvalidInput // See https://github.com/rust-lang/rust/issues/54800 @@ -903,8 +943,8 @@ async fn op_copy_file_async( { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::(); - permissions.read.check(&from)?; - permissions.write.check(&to)?; + permissions.read.check(&from, Some("Deno.copyFile()"))?; + permissions.write.check(&to, Some("Deno.copyFile()"))?; } tokio::task::spawn_blocking(move || { @@ -1062,7 +1102,10 @@ fn op_stat_sync( out_buf: &mut [u32], ) -> Result<(), AnyError> { let path = PathBuf::from(&path); - state.borrow_mut::().read.check(&path)?; + state + .borrow_mut::() + .read + .check(&path, Some("Deno.statSync()"))?; let err_mapper = |err: Error| { Error::new(err.kind(), format!("{}, stat '{}'", err, path.display())) }; @@ -1088,7 +1131,10 @@ async fn op_stat_async( { let mut state = state.borrow_mut(); - state.borrow_mut::().read.check(&path)?; + state + .borrow_mut::() + .read + .check(&path, Some("Deno.stat()"))?; } tokio::task::spawn_blocking(move || { @@ -1115,9 +1161,13 @@ fn op_realpath_sync( let path = PathBuf::from(&path); let permissions = state.borrow_mut::(); - permissions.read.check(&path)?; + permissions.read.check(&path, Some("Deno.realPathSync()"))?; if path.is_relative() { - permissions.read.check_blind(¤t_dir()?, "CWD")?; + permissions.read.check_blind( + ¤t_dir()?, + "CWD", + "Deno.realPathSync()", + )?; } debug!("op_realpath_sync {}", path.display()); @@ -1138,9 +1188,13 @@ async fn op_realpath_async( { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::(); - permissions.read.check(&path)?; + permissions.read.check(&path, Some("Deno.realPath()"))?; if path.is_relative() { - permissions.read.check_blind(¤t_dir()?, "CWD")?; + permissions.read.check_blind( + ¤t_dir()?, + "CWD", + "Deno.realPath()", + )?; } } @@ -1172,7 +1226,10 @@ fn op_read_dir_sync( ) -> Result, AnyError> { let path = PathBuf::from(&path); - state.borrow_mut::().read.check(&path)?; + state + .borrow_mut::() + .read + .check(&path, Some("Deno.readDirSync()"))?; debug!("op_read_dir_sync {}", path.display()); let err_mapper = |err: Error| { @@ -1213,7 +1270,10 @@ async fn op_read_dir_async( let path = PathBuf::from(&path); { let mut state = state.borrow_mut(); - state.borrow_mut::().read.check(&path)?; + state + .borrow_mut::() + .read + .check(&path, Some("Deno.readDir()"))?; } tokio::task::spawn_blocking(move || { debug!("op_read_dir_async {}", path.display()); @@ -1260,9 +1320,15 @@ fn op_rename_sync( let newpath = PathBuf::from(&newpath); let permissions = state.borrow_mut::(); - permissions.read.check(&oldpath)?; - permissions.write.check(&oldpath)?; - permissions.write.check(&newpath)?; + permissions + .read + .check(&oldpath, Some("Deno.renameSync()"))?; + permissions + .write + .check(&oldpath, Some("Deno.renameSync()"))?; + permissions + .write + .check(&newpath, Some("Deno.renameSync()"))?; let err_mapper = |err: Error| { Error::new( @@ -1290,9 +1356,9 @@ async fn op_rename_async( { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::(); - permissions.read.check(&oldpath)?; - permissions.write.check(&oldpath)?; - permissions.write.check(&newpath)?; + permissions.read.check(&oldpath, Some("Deno.rename()"))?; + permissions.write.check(&oldpath, Some("Deno.rename()"))?; + permissions.write.check(&newpath, Some("Deno.rename()"))?; } tokio::task::spawn_blocking(move || { let err_mapper = |err: Error| { @@ -1323,10 +1389,10 @@ fn op_link_sync( let newpath = PathBuf::from(&newpath); let permissions = state.borrow_mut::(); - permissions.read.check(&oldpath)?; - permissions.write.check(&oldpath)?; - permissions.read.check(&newpath)?; - permissions.write.check(&newpath)?; + permissions.read.check(&oldpath, Some("Deno.linkSync()"))?; + permissions.write.check(&oldpath, Some("Deno.linkSync()"))?; + permissions.read.check(&newpath, Some("Deno.linkSync()"))?; + permissions.write.check(&newpath, Some("Deno.linkSync()"))?; let err_mapper = |err: Error| { Error::new( @@ -1355,10 +1421,10 @@ async fn op_link_async( { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::(); - permissions.read.check(&oldpath)?; - permissions.write.check(&oldpath)?; - permissions.read.check(&newpath)?; - permissions.write.check(&newpath)?; + permissions.read.check(&oldpath, Some("Deno.link()"))?; + permissions.write.check(&oldpath, Some("Deno.link()"))?; + permissions.read.check(&newpath, Some("Deno.link()"))?; + permissions.write.check(&newpath, Some("Deno.link()"))?; } tokio::task::spawn_blocking(move || { @@ -1390,8 +1456,14 @@ fn op_symlink_sync( let oldpath = PathBuf::from(&oldpath); let newpath = PathBuf::from(&newpath); - state.borrow_mut::().write.check_all()?; - state.borrow_mut::().read.check_all()?; + state + .borrow_mut::() + .write + .check_all(Some("Deno.symlinkSync()"))?; + state + .borrow_mut::() + .read + .check_all(Some("Deno.symlinkSync()"))?; let err_mapper = |err: Error| { Error::new( @@ -1450,8 +1522,14 @@ async fn op_symlink_async( { let mut state = state.borrow_mut(); - state.borrow_mut::().write.check_all()?; - state.borrow_mut::().read.check_all()?; + state + .borrow_mut::() + .write + .check_all(Some("Deno.symlink()"))?; + state + .borrow_mut::() + .read + .check_all(Some("Deno.symlink()"))?; } tokio::task::spawn_blocking(move || { @@ -1510,7 +1588,10 @@ fn op_read_link_sync( ) -> Result { let path = PathBuf::from(&path); - state.borrow_mut::().read.check(&path)?; + state + .borrow_mut::() + .read + .check(&path, Some("Deno.readLink()"))?; debug!("op_read_link_value {}", path.display()); let err_mapper = |err: Error| { @@ -1534,7 +1615,10 @@ async fn op_read_link_async( let path = PathBuf::from(&path); { let mut state = state.borrow_mut(); - state.borrow_mut::().read.check(&path)?; + state + .borrow_mut::() + .read + .check(&path, Some("Deno.readLink()"))?; } tokio::task::spawn_blocking(move || { debug!("op_read_link_async {}", path.display()); @@ -1590,7 +1674,10 @@ fn op_truncate_sync( ) -> Result<(), AnyError> { let path = PathBuf::from(&path); - state.borrow_mut::().write.check(&path)?; + state + .borrow_mut::() + .write + .check(&path, Some("Deno.truncateSync()"))?; debug!("op_truncate_sync {} {}", path.display(), len); let err_mapper = |err: Error| { @@ -1617,7 +1704,10 @@ async fn op_truncate_async( { let mut state = state.borrow_mut(); - state.borrow_mut::().write.check(&path)?; + state + .borrow_mut::() + .write + .check(&path, Some("Deno.truncate()"))?; } tokio::task::spawn_blocking(move || { debug!("op_truncate_async {} {}", path.display(), len); @@ -1700,10 +1790,10 @@ fn op_make_temp_dir_sync( let prefix = args.prefix.map(String::from); let suffix = args.suffix.map(String::from); - state - .borrow_mut::() - .write - .check(dir.clone().unwrap_or_else(temp_dir).as_path())?; + state.borrow_mut::().write.check( + dir.clone().unwrap_or_else(temp_dir).as_path(), + Some("Deno.makeTempDirSync()"), + )?; // TODO(piscisaureus): use byte vector for paths, not a string. // See https://github.com/denoland/deno/issues/627. @@ -1730,10 +1820,10 @@ async fn op_make_temp_dir_async( let suffix = args.suffix.map(String::from); { let mut state = state.borrow_mut(); - state - .borrow_mut::() - .write - .check(dir.clone().unwrap_or_else(temp_dir).as_path())?; + state.borrow_mut::().write.check( + dir.clone().unwrap_or_else(temp_dir).as_path(), + Some("Deno.makeTempDir()"), + )?; } tokio::task::spawn_blocking(move || { // TODO(piscisaureus): use byte vector for paths, not a string. @@ -1763,10 +1853,10 @@ fn op_make_temp_file_sync( let prefix = args.prefix.map(String::from); let suffix = args.suffix.map(String::from); - state - .borrow_mut::() - .write - .check(dir.clone().unwrap_or_else(temp_dir).as_path())?; + state.borrow_mut::().write.check( + dir.clone().unwrap_or_else(temp_dir).as_path(), + Some("Deno.makeTempFileSync()"), + )?; // TODO(piscisaureus): use byte vector for paths, not a string. // See https://github.com/denoland/deno/issues/627. @@ -1793,10 +1883,10 @@ async fn op_make_temp_file_async( let suffix = args.suffix.map(String::from); { let mut state = state.borrow_mut(); - state - .borrow_mut::() - .write - .check(dir.clone().unwrap_or_else(temp_dir).as_path())?; + state.borrow_mut::().write.check( + dir.clone().unwrap_or_else(temp_dir).as_path(), + Some("Deno.makeTempFile()"), + )?; } tokio::task::spawn_blocking(move || { // TODO(piscisaureus): use byte vector for paths, not a string. @@ -1873,7 +1963,10 @@ fn op_utime_sync( let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos); let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos); - state.borrow_mut::().write.check(&path)?; + state + .borrow_mut::() + .write + .check(&path, Some("Deno.utime()"))?; filetime::set_file_times(&path, atime, mtime).map_err(|err| { Error::new(err.kind(), format!("{}, utime '{}'", err, path.display())) })?; @@ -1899,7 +1992,7 @@ async fn op_utime_async( .borrow_mut() .borrow_mut::() .write - .check(&path)?; + .check(&path, Some("Deno.utime()"))?; tokio::task::spawn_blocking(move || { filetime::set_file_times(&path, atime, mtime).map_err(|err| { @@ -1914,10 +2007,11 @@ async fn op_utime_async( #[op] fn op_cwd(state: &mut OpState) -> Result { let path = current_dir()?; - state - .borrow_mut::() - .read - .check_blind(&path, "CWD")?; + state.borrow_mut::().read.check_blind( + &path, + "CWD", + "Deno.cwd()", + )?; let path_str = into_string(path.into_os_string())?; Ok(path_str) } @@ -1929,7 +2023,7 @@ fn op_readfile_sync( ) -> Result { let permissions = state.borrow_mut::(); let path = Path::new(&path); - permissions.read.check(path)?; + permissions.read.check(path, Some("Deno.readFileSync()"))?; Ok(std::fs::read(path)?.into()) } @@ -1940,7 +2034,9 @@ fn op_readfile_text_sync( ) -> Result { let permissions = state.borrow_mut::(); let path = Path::new(&path); - permissions.read.check(path)?; + permissions + .read + .check(path, Some("Deno.readTextFileSync()"))?; Ok(string_from_utf8_lossy(std::fs::read(path)?)) } @@ -1953,7 +2049,10 @@ async fn op_readfile_async( { let path = Path::new(&path); let mut state = state.borrow_mut(); - state.borrow_mut::().read.check(path)?; + state + .borrow_mut::() + .read + .check(path, Some("Deno.readFile()"))?; } let fut = tokio::task::spawn_blocking(move || { let path = Path::new(&path); @@ -1980,7 +2079,10 @@ async fn op_readfile_text_async( { let path = Path::new(&path); let mut state = state.borrow_mut(); - state.borrow_mut::().read.check(path)?; + state + .borrow_mut::() + .read + .check(path, Some("Deno.readTextFile()"))?; } let fut = tokio::task::spawn_blocking(move || { let path = Path::new(&path); diff --git a/runtime/ops/fs_events.rs b/runtime/ops/fs_events.rs index 5e185c4bcf..6c631bb599 100644 --- a/runtime/ops/fs_events.rs +++ b/runtime/ops/fs_events.rs @@ -118,7 +118,10 @@ fn op_fs_events_open( }; for path in &args.paths { let path = PathBuf::from(path); - state.borrow_mut::().read.check(&path)?; + state + .borrow_mut::() + .read + .check(&path, Some("Deno.watchFs()"))?; watcher.watch(&path, recursive_mode)?; } let resource = FsEventsResource { diff --git a/runtime/ops/os.rs b/runtime/ops/os.rs index 70d4298397..14c4229a17 100644 --- a/runtime/ops/os.rs +++ b/runtime/ops/os.rs @@ -61,10 +61,11 @@ fn noop_op() -> Result<(), AnyError> { #[op] fn op_exec_path(state: &mut OpState) -> Result { let current_exe = env::current_exe().unwrap(); - state - .borrow_mut::() - .read - .check_blind(¤t_exe, "exec_path")?; + state.borrow_mut::().read.check_blind( + ¤t_exe, + "exec_path", + "Deno.execPath()", + )?; // Now apply URL parser to current exe to get fully resolved path, otherwise // we might get `./` and `../` bits in `exec_path` let exe_url = Url::from_file_path(current_exe).unwrap(); diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs index e4b6140300..88c130e61a 100644 --- a/runtime/ops/process.rs +++ b/runtime/ops/process.rs @@ -144,7 +144,10 @@ struct RunInfo { #[op] fn op_run(state: &mut OpState, run_args: RunArgs) -> Result { let args = run_args.cmd; - state.borrow_mut::().run.check(&args[0])?; + state + .borrow_mut::() + .run + .check(&args[0], Some("Deno.run()"))?; let env = run_args.env; let cwd = run_args.cwd; @@ -348,8 +351,12 @@ fn op_kill( state: &mut OpState, pid: i32, signal: String, + api_name: String, ) -> Result<(), AnyError> { - state.borrow_mut::().run.check_all()?; + state + .borrow_mut::() + .run + .check_all(Some(&api_name))?; kill(pid, &signal)?; Ok(()) } diff --git a/runtime/ops/runtime.rs b/runtime/ops/runtime.rs index d12dfab96e..f6ae0146ce 100644 --- a/runtime/ops/runtime.rs +++ b/runtime/ops/runtime.rs @@ -26,10 +26,11 @@ fn op_main_module(state: &mut OpState) -> Result { let main_path = std::env::current_dir() .context("Failed to get current working directory")? .join(main_url.to_string()); - state - .borrow_mut::() - .read - .check_blind(&main_path, "main_module")?; + state.borrow_mut::().read.check_blind( + &main_path, + "main_module", + "Deno.mainModule", + )?; } Ok(main) } diff --git a/runtime/ops/spawn.rs b/runtime/ops/spawn.rs index b337f24884..9286e6d0c0 100644 --- a/runtime/ops/spawn.rs +++ b/runtime/ops/spawn.rs @@ -122,9 +122,13 @@ pub struct SpawnOutput { fn create_command( state: &mut OpState, args: SpawnArgs, + api_name: &str, ) -> Result { super::check_unstable(state, "Deno.spawn"); - state.borrow_mut::().run.check(&args.cmd)?; + state + .borrow_mut::() + .run + .check(&args.cmd, Some(api_name))?; let mut command = std::process::Command::new(args.cmd); command.args(args.args); @@ -185,8 +189,10 @@ struct Child { fn op_spawn_child( state: &mut OpState, args: SpawnArgs, + api_name: String, ) -> Result { - let mut command = tokio::process::Command::from(create_command(state, args)?); + let mut command = + tokio::process::Command::from(create_command(state, args, &api_name)?); // TODO(@crowlkats): allow detaching processes. // currently deno will orphan a process when exiting with an error or Deno.exit() // We want to kill child when it's closed @@ -246,7 +252,7 @@ fn op_spawn_sync( ) -> Result { let stdout = matches!(args.stdio.stdout, Stdio::Piped); let stderr = matches!(args.stdio.stderr, Stdio::Piped); - let output = create_command(state, args)?.output()?; + let output = create_command(state, args, "Deno.spawnSync()")?.output()?; Ok(SpawnOutput { status: output.status.try_into()?, diff --git a/runtime/permissions.rs b/runtime/permissions.rs index fbe9eb2f58..a502959102 100644 --- a/runtime/permissions.rs +++ b/runtime/permissions.rs @@ -83,16 +83,18 @@ impl PermissionState { fn check( self, name: &str, + api_name: Option<&str>, info: Option<&str>, prompt: bool, ) -> (Result<(), AnyError>, bool) { - self.check2(name, || info.map(|s| s.to_string()), prompt) + self.check2(name, api_name, || info.map(|s| s.to_string()), prompt) } #[inline] fn check2( self, name: &str, + api_name: Option<&str>, info: impl Fn() -> Option, prompt: bool, ) -> (Result<(), AnyError>, bool) { @@ -107,7 +109,7 @@ impl PermissionState { name, info().map_or(String::new(), |info| { format!(" to {}", info) }), ); - if permission_prompt(&msg, name) { + if permission_prompt(&msg, name, api_name) { Self::log_perm_access(name, info); (Ok(()), true) } else { @@ -153,6 +155,7 @@ impl UnitPermission { if permission_prompt( &format!("access to {}", self.description), self.name, + Some("Deno.permissions.query()"), ) { self.state = PermissionState::Granted; } else { @@ -170,7 +173,8 @@ impl UnitPermission { } pub fn check(&mut self) -> Result<(), AnyError> { - let (result, prompted) = self.state.check(self.name, None, self.prompt); + let (result, prompted) = + self.state.check(self.name, None, None, self.prompt); if prompted { if result.is_ok() { self.state = PermissionState::Granted; @@ -339,6 +343,7 @@ impl UnaryPermission { if permission_prompt( &format!("read access to \"{}\"", display_path.display()), self.name, + Some("Deno.permissions.query()"), ) { self.granted_list.insert(ReadDescriptor(resolved_path)); PermissionState::Granted @@ -356,7 +361,11 @@ impl UnaryPermission { } else { let state = self.query(None); if state == PermissionState::Prompt { - if permission_prompt("read access", self.name) { + if permission_prompt( + "read access", + self.name, + Some("Deno.permissions.query()"), + ) { self.granted_list.clear(); self.global_state = PermissionState::Granted; PermissionState::Granted @@ -386,9 +395,14 @@ impl UnaryPermission { } #[inline] - pub fn check(&mut self, path: &Path) -> Result<(), AnyError> { + pub fn check( + &mut self, + path: &Path, + api_name: Option<&str>, + ) -> Result<(), AnyError> { let (result, prompted) = self.query(Some(path)).check2( self.name, + api_name, || Some(format!("\"{}\"", path.to_path_buf().display())), self.prompt, ); @@ -410,10 +424,12 @@ impl UnaryPermission { &mut self, path: &Path, display: &str, + api_name: &str, ) -> Result<(), AnyError> { let resolved_path = resolve_from_cwd(path).unwrap(); let (result, prompted) = self.query(Some(&resolved_path)).check( self.name, + Some(api_name), Some(&format!("<{}>", display)), self.prompt, ); @@ -428,9 +444,11 @@ impl UnaryPermission { result } - pub fn check_all(&mut self) -> Result<(), AnyError> { + pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> { let (result, prompted) = - self.query(None).check(self.name, Some("all"), self.prompt); + self + .query(None) + .check(self.name, api_name, Some("all"), self.prompt); if prompted { if result.is_ok() { self.global_state = PermissionState::Granted; @@ -494,6 +512,7 @@ impl UnaryPermission { if permission_prompt( &format!("write access to \"{}\"", display_path.display()), self.name, + Some("Deno.permissions.query()"), ) { self.granted_list.insert(WriteDescriptor(resolved_path)); PermissionState::Granted @@ -511,7 +530,11 @@ impl UnaryPermission { } else { let state = self.query(None); if state == PermissionState::Prompt { - if permission_prompt("write access", self.name) { + if permission_prompt( + "write access", + self.name, + Some("Deno.permissions.query()"), + ) { self.granted_list.clear(); self.global_state = PermissionState::Granted; PermissionState::Granted @@ -541,9 +564,14 @@ impl UnaryPermission { } #[inline] - pub fn check(&mut self, path: &Path) -> Result<(), AnyError> { + pub fn check( + &mut self, + path: &Path, + api_name: Option<&str>, + ) -> Result<(), AnyError> { let (result, prompted) = self.query(Some(path)).check2( self.name, + api_name, || Some(format!("\"{}\"", path.to_path_buf().display())), self.prompt, ); @@ -559,9 +587,11 @@ impl UnaryPermission { result } - pub fn check_all(&mut self) -> Result<(), AnyError> { + pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> { let (result, prompted) = - self.query(None).check(self.name, Some("all"), self.prompt); + self + .query(None) + .check(self.name, api_name, Some("all"), self.prompt); if prompted { if result.is_ok() { self.global_state = PermissionState::Granted; @@ -633,6 +663,7 @@ impl UnaryPermission { if permission_prompt( &format!("network access to \"{}\"", host), self.name, + Some("Deno.permissions.query()"), ) { self.granted_list.insert(host); PermissionState::Granted @@ -650,7 +681,11 @@ impl UnaryPermission { } else { let state = self.query::<&str>(None); if state == PermissionState::Prompt { - if permission_prompt("network access", self.name) { + if permission_prompt( + "network access", + self.name, + Some("Deno.permissions.query()"), + ) { self.granted_list.clear(); self.global_state = PermissionState::Granted; PermissionState::Granted @@ -689,10 +724,12 @@ impl UnaryPermission { pub fn check>( &mut self, host: &(T, Option), + api_name: Option<&str>, ) -> Result<(), AnyError> { let new_host = NetDescriptor::new(&host); let (result, prompted) = self.query(Some(host)).check( self.name, + api_name, Some(&format!("\"{}\"", new_host)), self.prompt, ); @@ -707,7 +744,11 @@ impl UnaryPermission { result } - pub fn check_url(&mut self, url: &url::Url) -> Result<(), AnyError> { + pub fn check_url( + &mut self, + url: &url::Url, + api_name: Option<&str>, + ) -> Result<(), AnyError> { let hostname = url .host_str() .ok_or_else(|| uri_error("Missing host"))? @@ -719,6 +760,7 @@ impl UnaryPermission { let host = &(&hostname, url.port_or_known_default()); let (result, prompted) = self.query(Some(host)).check( self.name, + api_name, Some(&format!("\"{}\"", display_host)), self.prompt, ); @@ -737,7 +779,7 @@ impl UnaryPermission { let (result, prompted) = self .query::<&str>(None) - .check(self.name, Some("all"), self.prompt); + .check(self.name, None, Some("all"), self.prompt); if prompted { if result.is_ok() { self.global_state = PermissionState::Granted; @@ -788,7 +830,11 @@ impl UnaryPermission { if let Some(env) = env { let state = self.query(Some(env)); if state == PermissionState::Prompt { - if permission_prompt(&format!("env access to \"{}\"", env), self.name) { + if permission_prompt( + &format!("env access to \"{}\"", env), + self.name, + Some("Deno.permissions.query()"), + ) { self.granted_list.insert(EnvDescriptor::new(env)); PermissionState::Granted } else { @@ -805,7 +851,11 @@ impl UnaryPermission { } else { let state = self.query(None); if state == PermissionState::Prompt { - if permission_prompt("env access", self.name) { + if permission_prompt( + "env access", + self.name, + Some("Deno.permissions.query()"), + ) { self.granted_list.clear(); self.global_state = PermissionState::Granted; PermissionState::Granted @@ -834,6 +884,7 @@ impl UnaryPermission { pub fn check(&mut self, env: &str) -> Result<(), AnyError> { let (result, prompted) = self.query(Some(env)).check( self.name, + None, Some(&format!("\"{}\"", env)), self.prompt, ); @@ -850,7 +901,9 @@ impl UnaryPermission { pub fn check_all(&mut self) -> Result<(), AnyError> { let (result, prompted) = - self.query(None).check(self.name, Some("all"), self.prompt); + self + .query(None) + .check(self.name, None, Some("all"), self.prompt); if prompted { if result.is_ok() { self.global_state = PermissionState::Granted; @@ -904,7 +957,11 @@ impl UnaryPermission { if let Some(cmd) = cmd { let state = self.query(Some(cmd)); if state == PermissionState::Prompt { - if permission_prompt(&format!("run access to \"{}\"", cmd), self.name) { + if permission_prompt( + &format!("run access to \"{}\"", cmd), + self.name, + Some("Deno.permissions.query()"), + ) { self .granted_list .insert(RunDescriptor::from_str(cmd).unwrap()); @@ -927,7 +984,11 @@ impl UnaryPermission { } else { let state = self.query(None); if state == PermissionState::Prompt { - if permission_prompt("run access", self.name) { + if permission_prompt( + "run access", + self.name, + Some("Deno.permissions.query()"), + ) { self.granted_list.clear(); self.global_state = PermissionState::Granted; PermissionState::Granted @@ -955,9 +1016,14 @@ impl UnaryPermission { self.query(cmd) } - pub fn check(&mut self, cmd: &str) -> Result<(), AnyError> { + pub fn check( + &mut self, + cmd: &str, + api_name: Option<&str>, + ) -> Result<(), AnyError> { let (result, prompted) = self.query(Some(cmd)).check( self.name, + api_name, Some(&format!("\"{}\"", cmd)), self.prompt, ); @@ -976,9 +1042,11 @@ impl UnaryPermission { result } - pub fn check_all(&mut self) -> Result<(), AnyError> { + pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> { let (result, prompted) = - self.query(None).check(self.name, Some("all"), self.prompt); + self + .query(None) + .check(self.name, api_name, Some("all"), self.prompt); if prompted { if result.is_ok() { self.global_state = PermissionState::Granted; @@ -1033,6 +1101,7 @@ impl UnaryPermission { if permission_prompt( &format!("ffi access to \"{}\"", display_path.display()), self.name, + Some("Deno.permissions.query()"), ) { self.granted_list.insert(FfiDescriptor(resolved_path)); PermissionState::Granted @@ -1050,7 +1119,11 @@ impl UnaryPermission { } else { let state = self.query(None); if state == PermissionState::Prompt { - if permission_prompt("ffi access", self.name) { + if permission_prompt( + "ffi access", + self.name, + Some("Deno.permissions.query()"), + ) { self.granted_list.clear(); self.global_state = PermissionState::Granted; PermissionState::Granted @@ -1082,6 +1155,7 @@ impl UnaryPermission { let (resolved_path, display_path) = resolved_and_display_path(path); let (result, prompted) = self.query(Some(&resolved_path)).check( self.name, + None, Some(&format!("\"{}\"", display_path.display())), self.prompt, ); @@ -1098,7 +1172,7 @@ impl UnaryPermission { result } else { let (result, prompted) = - self.query(None).check(self.name, None, self.prompt); + self.query(None).check(self.name, None, None, self.prompt); if prompted { if result.is_ok() { @@ -1114,7 +1188,9 @@ impl UnaryPermission { pub fn check_all(&mut self) -> Result<(), AnyError> { let (result, prompted) = - self.query(None).check(self.name, Some("all"), self.prompt); + self + .query(None) + .check(self.name, None, Some("all"), self.prompt); if prompted { if result.is_ok() { self.global_state = PermissionState::Granted; @@ -1323,7 +1399,7 @@ impl Permissions { ) -> Result<(), AnyError> { match specifier.scheme() { "file" => match specifier.to_file_path() { - Ok(path) => self.read.check(&path), + Ok(path) => self.read.check(&path, None), Err(_) => Err(uri_error(format!( "Invalid file path.\n Specifier: {}", specifier @@ -1331,7 +1407,7 @@ impl Permissions { }, "data" => Ok(()), "blob" => Ok(()), - _ => self.net.check_url(specifier), + _ => self.net.check_url(specifier, None), } } } @@ -1340,14 +1416,15 @@ impl deno_flash::FlashPermissions for Permissions { fn check_net>( &mut self, host: &(T, Option), + api_name: &str, ) -> Result<(), AnyError> { - self.net.check(host) + self.net.check(host, Some(api_name)) } } impl deno_node::NodePermissions for Permissions { fn check_read(&mut self, path: &Path) -> Result<(), AnyError> { - self.read.check(path) + self.read.check(path, None) } } @@ -1355,26 +1432,43 @@ impl deno_net::NetPermissions for Permissions { fn check_net>( &mut self, host: &(T, Option), + api_name: &str, ) -> Result<(), AnyError> { - self.net.check(host) + self.net.check(host, Some(api_name)) } - fn check_read(&mut self, path: &Path) -> Result<(), AnyError> { - self.read.check(path) + fn check_read( + &mut self, + path: &Path, + api_name: &str, + ) -> Result<(), AnyError> { + self.read.check(path, Some(api_name)) } - fn check_write(&mut self, path: &Path) -> Result<(), AnyError> { - self.write.check(path) + fn check_write( + &mut self, + path: &Path, + api_name: &str, + ) -> Result<(), AnyError> { + self.write.check(path, Some(api_name)) } } impl deno_fetch::FetchPermissions for Permissions { - fn check_net_url(&mut self, url: &url::Url) -> Result<(), AnyError> { - self.net.check_url(url) + fn check_net_url( + &mut self, + url: &url::Url, + api_name: &str, + ) -> Result<(), AnyError> { + self.net.check_url(url, Some(api_name)) } - fn check_read(&mut self, path: &Path) -> Result<(), AnyError> { - self.read.check(path) + fn check_read( + &mut self, + path: &Path, + api_name: &str, + ) -> Result<(), AnyError> { + self.read.check(path, Some(api_name)) } } @@ -1389,8 +1483,12 @@ impl deno_web::TimersPermission for Permissions { } impl deno_websocket::WebSocketPermissions for Permissions { - fn check_net_url(&mut self, url: &url::Url) -> Result<(), AnyError> { - self.net.check_url(url) + fn check_net_url( + &mut self, + url: &url::Url, + api_name: &str, + ) -> Result<(), AnyError> { + self.net.check_url(url, Some(api_name)) } } @@ -1808,7 +1906,7 @@ pub fn create_child_permissions( .net .granted_list .iter() - .all(|desc| main_perms.net.check(&(&desc.0, desc.1)).is_ok()) + .all(|desc| main_perms.net.check(&(&desc.0, desc.1), None).is_ok()) { return Err(escalation_error()); } @@ -1856,7 +1954,7 @@ pub fn create_child_permissions( worker_perms.read = main_perms.read.clone(); } ChildUnaryPermissionArg::Granted => { - if main_perms.read.check_all().is_err() { + if main_perms.read.check_all(None).is_err() { return Err(escalation_error()); } worker_perms.read.global_state = PermissionState::Granted; @@ -1872,7 +1970,7 @@ pub fn create_child_permissions( .read .granted_list .iter() - .all(|desc| main_perms.read.check(&desc.0).is_ok()) + .all(|desc| main_perms.read.check(&desc.0, None).is_ok()) { return Err(escalation_error()); } @@ -1888,7 +1986,7 @@ pub fn create_child_permissions( worker_perms.run = main_perms.run.clone(); } ChildUnaryPermissionArg::Granted => { - if main_perms.run.check_all().is_err() { + if main_perms.run.check_all(None).is_err() { return Err(escalation_error()); } worker_perms.run.global_state = PermissionState::Granted; @@ -1901,7 +1999,7 @@ pub fn create_child_permissions( .run .granted_list .iter() - .all(|desc| main_perms.run.check(&desc.to_string()).is_ok()) + .all(|desc| main_perms.run.check(&desc.to_string(), None).is_ok()) { return Err(escalation_error()); } @@ -1917,7 +2015,7 @@ pub fn create_child_permissions( worker_perms.write = main_perms.write.clone(); } ChildUnaryPermissionArg::Granted => { - if main_perms.write.check_all().is_err() { + if main_perms.write.check_all(None).is_err() { return Err(escalation_error()); } worker_perms.write.global_state = PermissionState::Granted; @@ -1933,7 +2031,7 @@ pub fn create_child_permissions( .write .granted_list .iter() - .all(|desc| main_perms.write.check(&desc.0).is_ok()) + .all(|desc| main_perms.write.check(&desc.0, None).is_ok()) { return Err(escalation_error()); } @@ -1950,7 +2048,11 @@ pub fn create_child_permissions( /// 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, name: &str) -> bool { +fn permission_prompt( + message: &str, + name: &str, + api_name: Option<&str>, +) -> bool { if !atty::is(atty::Stream::Stdin) || !atty::is(atty::Stream::Stderr) { return false; }; @@ -2084,6 +2186,9 @@ fn permission_prompt(message: &str, name: &str) -> bool { eprint!("{}", colors::bold("Deno requests ")); eprint!("{}", colors::bold(message)); eprintln!("{}", colors::bold(".")); + if let Some(api_name) = api_name { + eprintln!(" ├ Requested by `{}` API", api_name); + } let msg = format!( " ├ Run again with --allow-{} to bypass this prompt.", name @@ -2104,13 +2209,13 @@ fn permission_prompt(message: &str, name: &str) -> bool { }; match ch.to_ascii_lowercase() { 'y' => { - clear_n_lines(3); + clear_n_lines(if api_name.is_some() { 4 } else { 3 }); let msg = format!("Granted {}.", message); eprintln!("✅ {}", colors::bold(&msg)); return true; } 'n' => { - clear_n_lines(3); + clear_n_lines(if api_name.is_some() { 4 } else { 3 }); let msg = format!("Denied {}.", message); eprintln!("❌ {}", colors::bold(&msg)); return false; @@ -2128,7 +2233,11 @@ fn permission_prompt(message: &str, name: &str) -> bool { // 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, _flag: &str) -> bool { +fn permission_prompt( + _message: &str, + _flag: &str, + _api_name: Option<&str>, +) -> bool { STUB_PROMPT_VALUE.load(Ordering::SeqCst) } @@ -2177,55 +2286,67 @@ mod tests { .unwrap(); // Inside of /a/specific and /a/specific/dir/name - assert!(perms.read.check(Path::new("/a/specific/dir/name")).is_ok()); - assert!(perms.write.check(Path::new("/a/specific/dir/name")).is_ok()); + assert!(perms + .read + .check(Path::new("/a/specific/dir/name"), None) + .is_ok()); + assert!(perms + .write + .check(Path::new("/a/specific/dir/name"), None) + .is_ok()); // Inside of /a/specific but outside of /a/specific/dir/name - assert!(perms.read.check(Path::new("/a/specific/dir")).is_ok()); - assert!(perms.write.check(Path::new("/a/specific/dir")).is_ok()); + assert!(perms.read.check(Path::new("/a/specific/dir"), None).is_ok()); + assert!(perms + .write + .check(Path::new("/a/specific/dir"), None) + .is_ok()); // Inside of /a/specific and /a/specific/dir/name assert!(perms .read - .check(Path::new("/a/specific/dir/name/inner")) + .check(Path::new("/a/specific/dir/name/inner"), None) .is_ok()); assert!(perms .write - .check(Path::new("/a/specific/dir/name/inner")) + .check(Path::new("/a/specific/dir/name/inner"), None) .is_ok()); // Inside of /a/specific but outside of /a/specific/dir/name - assert!(perms.read.check(Path::new("/a/specific/other/dir")).is_ok()); + assert!(perms + .read + .check(Path::new("/a/specific/other/dir"), None) + .is_ok()); assert!(perms .write - .check(Path::new("/a/specific/other/dir")) + .check(Path::new("/a/specific/other/dir"), None) .is_ok()); // Exact match with /b/c - assert!(perms.read.check(Path::new("/b/c")).is_ok()); - assert!(perms.write.check(Path::new("/b/c")).is_ok()); + assert!(perms.read.check(Path::new("/b/c"), None).is_ok()); + assert!(perms.write.check(Path::new("/b/c"), None).is_ok()); // Sub path within /b/c - assert!(perms.read.check(Path::new("/b/c/sub/path")).is_ok()); - assert!(perms.write.check(Path::new("/b/c/sub/path")).is_ok()); + assert!(perms.read.check(Path::new("/b/c/sub/path"), None).is_ok()); + assert!(perms.write.check(Path::new("/b/c/sub/path"), None).is_ok()); // Sub path within /b/c, needs normalizing assert!(perms .read - .check(Path::new("/b/c/sub/path/../path/.")) + .check(Path::new("/b/c/sub/path/../path/."), None) .is_ok()); assert!(perms .write - .check(Path::new("/b/c/sub/path/../path/.")) + .check(Path::new("/b/c/sub/path/../path/."), None) .is_ok()); // Inside of /b but outside of /b/c - assert!(perms.read.check(Path::new("/b/e")).is_err()); - assert!(perms.write.check(Path::new("/b/e")).is_err()); + assert!(perms.read.check(Path::new("/b/e"), None).is_err()); + assert!(perms.write.check(Path::new("/b/e"), None).is_err()); // Inside of /a but outside of /a/specific - assert!(perms.read.check(Path::new("/a/b")).is_err()); - assert!(perms.write.check(Path::new("/a/b")).is_err()); + assert!(perms.read.check(Path::new("/a/b"), None).is_err()); + assert!(perms.write.check(Path::new("/a/b"), None).is_err()); } #[test] @@ -2267,7 +2388,7 @@ mod tests { ]; for (host, port, is_ok) in domain_tests { - assert_eq!(is_ok, perms.net.check(&(host, Some(port))).is_ok()); + assert_eq!(is_ok, perms.net.check(&(host, Some(port)), None).is_ok()); } } @@ -2302,7 +2423,7 @@ mod tests { ]; for (host, port) in domain_tests { - assert!(perms.net.check(&(host, Some(port))).is_ok()); + assert!(perms.net.check(&(host, Some(port)), None).is_ok()); } } @@ -2337,7 +2458,7 @@ mod tests { ]; for (host, port) in domain_tests { - assert!(perms.net.check(&(host, Some(port))).is_err()); + assert!(perms.net.check(&(host, Some(port)), None).is_err()); } } @@ -2397,7 +2518,7 @@ mod tests { for (url_str, is_ok) in url_tests { let u = url::Url::parse(url_str).unwrap(); - assert_eq!(is_ok, perms.net.check_url(&u).is_ok()); + assert_eq!(is_ok, perms.net.check_url(&u, None).is_ok()); } } @@ -2654,31 +2775,31 @@ mod tests { let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock(); prompt_value.set(true); - assert!(perms.read.check(Path::new("/foo")).is_ok()); + assert!(perms.read.check(Path::new("/foo"), None).is_ok()); prompt_value.set(false); - assert!(perms.read.check(Path::new("/foo")).is_ok()); - assert!(perms.read.check(Path::new("/bar")).is_err()); + assert!(perms.read.check(Path::new("/foo"), None).is_ok()); + assert!(perms.read.check(Path::new("/bar"), None).is_err()); prompt_value.set(true); - assert!(perms.write.check(Path::new("/foo")).is_ok()); + assert!(perms.write.check(Path::new("/foo"), None).is_ok()); prompt_value.set(false); - assert!(perms.write.check(Path::new("/foo")).is_ok()); - assert!(perms.write.check(Path::new("/bar")).is_err()); + assert!(perms.write.check(Path::new("/foo"), None).is_ok()); + assert!(perms.write.check(Path::new("/bar"), None).is_err()); prompt_value.set(true); - assert!(perms.net.check(&("127.0.0.1", Some(8000))).is_ok()); + assert!(perms.net.check(&("127.0.0.1", Some(8000)), None).is_ok()); prompt_value.set(false); - assert!(perms.net.check(&("127.0.0.1", Some(8000))).is_ok()); - assert!(perms.net.check(&("127.0.0.1", Some(8001))).is_err()); - assert!(perms.net.check(&("127.0.0.1", None)).is_err()); - assert!(perms.net.check(&("deno.land", Some(8000))).is_err()); - assert!(perms.net.check(&("deno.land", None)).is_err()); + assert!(perms.net.check(&("127.0.0.1", Some(8000)), None).is_ok()); + assert!(perms.net.check(&("127.0.0.1", Some(8001)), None).is_err()); + assert!(perms.net.check(&("127.0.0.1", None), None).is_err()); + assert!(perms.net.check(&("deno.land", Some(8000)), None).is_err()); + assert!(perms.net.check(&("deno.land", None), None).is_err()); prompt_value.set(true); - assert!(perms.run.check("cat").is_ok()); + assert!(perms.run.check("cat", None).is_ok()); prompt_value.set(false); - assert!(perms.run.check("cat").is_ok()); - assert!(perms.run.check("ls").is_err()); + assert!(perms.run.check("cat", None).is_ok()); + assert!(perms.run.check("ls", None).is_err()); prompt_value.set(true); assert!(perms.env.check("HOME").is_ok()); @@ -2704,38 +2825,38 @@ mod tests { let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock(); prompt_value.set(false); - assert!(perms.read.check(Path::new("/foo")).is_err()); + assert!(perms.read.check(Path::new("/foo"), None).is_err()); prompt_value.set(true); - assert!(perms.read.check(Path::new("/foo")).is_err()); - assert!(perms.read.check(Path::new("/bar")).is_ok()); + assert!(perms.read.check(Path::new("/foo"), None).is_err()); + assert!(perms.read.check(Path::new("/bar"), None).is_ok()); prompt_value.set(false); - assert!(perms.read.check(Path::new("/bar")).is_ok()); + assert!(perms.read.check(Path::new("/bar"), None).is_ok()); prompt_value.set(false); - assert!(perms.write.check(Path::new("/foo")).is_err()); + assert!(perms.write.check(Path::new("/foo"), None).is_err()); prompt_value.set(true); - assert!(perms.write.check(Path::new("/foo")).is_err()); - assert!(perms.write.check(Path::new("/bar")).is_ok()); + assert!(perms.write.check(Path::new("/foo"), None).is_err()); + assert!(perms.write.check(Path::new("/bar"), None).is_ok()); prompt_value.set(false); - assert!(perms.write.check(Path::new("/bar")).is_ok()); + assert!(perms.write.check(Path::new("/bar"), None).is_ok()); prompt_value.set(false); - assert!(perms.net.check(&("127.0.0.1", Some(8000))).is_err()); + assert!(perms.net.check(&("127.0.0.1", Some(8000)), None).is_err()); prompt_value.set(true); - assert!(perms.net.check(&("127.0.0.1", Some(8000))).is_err()); - assert!(perms.net.check(&("127.0.0.1", Some(8001))).is_ok()); - assert!(perms.net.check(&("deno.land", Some(8000))).is_ok()); + assert!(perms.net.check(&("127.0.0.1", Some(8000)), None).is_err()); + assert!(perms.net.check(&("127.0.0.1", Some(8001)), None).is_ok()); + assert!(perms.net.check(&("deno.land", Some(8000)), None).is_ok()); prompt_value.set(false); - assert!(perms.net.check(&("127.0.0.1", Some(8001))).is_ok()); - assert!(perms.net.check(&("deno.land", Some(8000))).is_ok()); + assert!(perms.net.check(&("127.0.0.1", Some(8001)), None).is_ok()); + assert!(perms.net.check(&("deno.land", Some(8000)), None).is_ok()); prompt_value.set(false); - assert!(perms.run.check("cat").is_err()); + assert!(perms.run.check("cat", None).is_err()); prompt_value.set(true); - assert!(perms.run.check("cat").is_err()); - assert!(perms.run.check("ls").is_ok()); + assert!(perms.run.check("cat", None).is_err()); + assert!(perms.run.check("ls", None).is_ok()); prompt_value.set(false); - assert!(perms.run.check("ls").is_ok()); + assert!(perms.run.check("ls", None).is_ok()); prompt_value.set(false); assert!(perms.env.check("HOME").is_err()); @@ -2995,7 +3116,7 @@ mod tests { }) .unwrap(); prompt_value.set(false); - assert!(main_perms.write.check(&PathBuf::from("foo")).is_err()); + assert!(main_perms.write.check(&PathBuf::from("foo"), None).is_err()); let worker_perms = create_child_permissions( &mut main_perms.clone(), ChildPermissionsArg::none(),