summaryrefslogtreecommitdiff
path: root/runtime
diff options
context:
space:
mode:
authorcrowlKats <13135287+crowlKats@users.noreply.github.com>2021-03-17 22:45:12 +0100
committerGitHub <noreply@github.com>2021-03-17 17:45:12 -0400
commit0e70d9e59bc0e70f1921bb217ee00fc2e6facb69 (patch)
treed501399bd07855148ce817c568e7228bdfd87d87 /runtime
parentb3fe85163f303a1592335b23c25554dd0e39a4c4 (diff)
refactor: clean up permission handling (#9367)
Diffstat (limited to 'runtime')
-rw-r--r--runtime/ops/fs.rs105
-rw-r--r--runtime/ops/fs_events.rs3
-rw-r--r--runtime/ops/net.rs20
-rw-r--r--runtime/ops/os.rs21
-rw-r--r--runtime/ops/permissions.rs48
-rw-r--r--runtime/ops/plugin.rs2
-rw-r--r--runtime/ops/process.rs6
-rw-r--r--runtime/ops/runtime.rs3
-rw-r--r--runtime/ops/timers.rs2
-rw-r--r--runtime/ops/tls.rs14
-rw-r--r--runtime/ops/worker_host.rs239
-rw-r--r--runtime/permissions.rs1188
12 files changed, 863 insertions, 788 deletions
diff --git a/runtime/ops/fs.rs b/runtime/ops/fs.rs
index 8560d9f3e..c9a2f1efe 100644
--- a/runtime/ops/fs.rs
+++ b/runtime/ops/fs.rs
@@ -161,11 +161,11 @@ fn open_helper(
let options = args.options;
if options.read {
- permissions.check_read(&path)?;
+ permissions.read.check(&path)?;
}
if options.write || options.append {
- permissions.check_write(&path)?;
+ permissions.write.check(&path)?;
}
open_options
@@ -463,7 +463,7 @@ fn op_chdir(
) -> Result<Value, AnyError> {
let args: ChdirArgs = serde_json::from_value(args)?;
let d = PathBuf::from(&args.directory);
- state.borrow::<Permissions>().check_read(&d)?;
+ state.borrow::<Permissions>().read.check(&d)?;
set_current_dir(&d)?;
Ok(json!({}))
}
@@ -484,7 +484,7 @@ fn op_mkdir_sync(
let args: MkdirArgs = serde_json::from_value(args)?;
let path = Path::new(&args.path).to_path_buf();
let mode = args.mode.unwrap_or(0o777) & 0o777;
- state.borrow::<Permissions>().check_write(&path)?;
+ state.borrow::<Permissions>().write.check(&path)?;
debug!("op_mkdir {} {:o} {}", path.display(), mode, args.recursive);
let mut builder = std::fs::DirBuilder::new();
builder.recursive(args.recursive);
@@ -508,7 +508,7 @@ async fn op_mkdir_async(
{
let state = state.borrow();
- state.borrow::<Permissions>().check_write(&path)?;
+ state.borrow::<Permissions>().write.check(&path)?;
}
tokio::task::spawn_blocking(move || {
@@ -543,7 +543,7 @@ fn op_chmod_sync(
let path = Path::new(&args.path).to_path_buf();
let mode = args.mode & 0o777;
- state.borrow::<Permissions>().check_write(&path)?;
+ state.borrow::<Permissions>().write.check(&path)?;
debug!("op_chmod_sync {} {:o}", path.display(), mode);
#[cfg(unix)]
{
@@ -572,7 +572,7 @@ async fn op_chmod_async(
{
let state = state.borrow();
- state.borrow::<Permissions>().check_write(&path)?;
+ state.borrow::<Permissions>().write.check(&path)?;
}
tokio::task::spawn_blocking(move || {
@@ -611,7 +611,7 @@ fn op_chown_sync(
) -> Result<Value, AnyError> {
let args: ChownArgs = serde_json::from_value(args)?;
let path = Path::new(&args.path).to_path_buf();
- state.borrow::<Permissions>().check_write(&path)?;
+ state.borrow::<Permissions>().write.check(&path)?;
debug!(
"op_chown_sync {} {:?} {:?}",
path.display(),
@@ -643,7 +643,7 @@ async fn op_chown_async(
{
let state = state.borrow();
- state.borrow::<Permissions>().check_write(&path)?;
+ state.borrow::<Permissions>().write.check(&path)?;
}
tokio::task::spawn_blocking(move || {
@@ -685,7 +685,7 @@ fn op_remove_sync(
let path = PathBuf::from(&args.path);
let recursive = args.recursive;
- state.borrow::<Permissions>().check_write(&path)?;
+ state.borrow::<Permissions>().write.check(&path)?;
#[cfg(not(unix))]
use std::os::windows::prelude::MetadataExt;
@@ -730,7 +730,7 @@ async fn op_remove_async(
{
let state = state.borrow();
- state.borrow::<Permissions>().check_write(&path)?;
+ state.borrow::<Permissions>().write.check(&path)?;
}
tokio::task::spawn_blocking(move || {
@@ -786,8 +786,8 @@ fn op_copy_file_sync(
let to = PathBuf::from(&args.to);
let permissions = state.borrow::<Permissions>();
- permissions.check_read(&from)?;
- permissions.check_write(&to)?;
+ permissions.read.check(&from)?;
+ permissions.write.check(&to)?;
debug!("op_copy_file_sync {} {}", from.display(), to.display());
// On *nix, Rust reports non-existent `from` as ErrorKind::InvalidInput
@@ -814,8 +814,8 @@ async fn op_copy_file_async(
{
let state = state.borrow();
let permissions = state.borrow::<Permissions>();
- permissions.check_read(&from)?;
- permissions.check_write(&to)?;
+ permissions.read.check(&from)?;
+ permissions.write.check(&to)?;
}
debug!("op_copy_file_async {} {}", from.display(), to.display());
@@ -908,7 +908,7 @@ fn op_stat_sync(
let args: StatArgs = serde_json::from_value(args)?;
let path = PathBuf::from(&args.path);
let lstat = args.lstat;
- state.borrow::<Permissions>().check_read(&path)?;
+ state.borrow::<Permissions>().read.check(&path)?;
debug!("op_stat_sync {} {}", path.display(), lstat);
let metadata = if lstat {
std::fs::symlink_metadata(&path)?
@@ -929,7 +929,7 @@ async fn op_stat_async(
{
let state = state.borrow();
- state.borrow::<Permissions>().check_read(&path)?;
+ state.borrow::<Permissions>().read.check(&path)?;
}
tokio::task::spawn_blocking(move || {
@@ -960,9 +960,9 @@ fn op_realpath_sync(
let path = PathBuf::from(&args.path);
let permissions = state.borrow::<Permissions>();
- permissions.check_read(&path)?;
+ permissions.read.check(&path)?;
if path.is_relative() {
- permissions.check_read_blind(&current_dir()?, "CWD")?;
+ permissions.read.check_blind(&current_dir()?, "CWD")?;
}
debug!("op_realpath_sync {}", path.display());
@@ -984,9 +984,9 @@ async fn op_realpath_async(
{
let state = state.borrow();
let permissions = state.borrow::<Permissions>();
- permissions.check_read(&path)?;
+ permissions.read.check(&path)?;
if path.is_relative() {
- permissions.check_read_blind(&current_dir()?, "CWD")?;
+ permissions.read.check_blind(&current_dir()?, "CWD")?;
}
}
@@ -1016,7 +1016,7 @@ fn op_read_dir_sync(
let args: ReadDirArgs = serde_json::from_value(args)?;
let path = PathBuf::from(&args.path);
- state.borrow::<Permissions>().check_read(&path)?;
+ state.borrow::<Permissions>().read.check(&path)?;
debug!("op_read_dir_sync {}", path.display());
let entries: Vec<_> = std::fs::read_dir(path)?
@@ -1048,7 +1048,7 @@ async fn op_read_dir_async(
let path = PathBuf::from(&args.path);
{
let state = state.borrow();
- state.borrow::<Permissions>().check_read(&path)?;
+ state.borrow::<Permissions>().read.check(&path)?;
}
tokio::task::spawn_blocking(move || {
debug!("op_read_dir_async {}", path.display());
@@ -1092,9 +1092,9 @@ fn op_rename_sync(
let newpath = PathBuf::from(&args.newpath);
let permissions = state.borrow::<Permissions>();
- permissions.check_read(&oldpath)?;
- permissions.check_write(&oldpath)?;
- permissions.check_write(&newpath)?;
+ permissions.read.check(&oldpath)?;
+ permissions.write.check(&oldpath)?;
+ permissions.write.check(&newpath)?;
debug!("op_rename_sync {} {}", oldpath.display(), newpath.display());
std::fs::rename(&oldpath, &newpath)?;
Ok(json!({}))
@@ -1111,9 +1111,9 @@ async fn op_rename_async(
{
let state = state.borrow();
let permissions = state.borrow::<Permissions>();
- permissions.check_read(&oldpath)?;
- permissions.check_write(&oldpath)?;
- permissions.check_write(&newpath)?;
+ permissions.read.check(&oldpath)?;
+ permissions.write.check(&oldpath)?;
+ permissions.write.check(&newpath)?;
}
tokio::task::spawn_blocking(move || {
debug!(
@@ -1145,10 +1145,10 @@ fn op_link_sync(
let newpath = PathBuf::from(&args.newpath);
let permissions = state.borrow::<Permissions>();
- permissions.check_read(&oldpath)?;
- permissions.check_write(&oldpath)?;
- permissions.check_read(&newpath)?;
- permissions.check_write(&newpath)?;
+ permissions.read.check(&oldpath)?;
+ permissions.write.check(&oldpath)?;
+ permissions.read.check(&newpath)?;
+ permissions.write.check(&newpath)?;
debug!("op_link_sync {} {}", oldpath.display(), newpath.display());
std::fs::hard_link(&oldpath, &newpath)?;
@@ -1167,10 +1167,10 @@ async fn op_link_async(
{
let state = state.borrow();
let permissions = state.borrow::<Permissions>();
- permissions.check_read(&oldpath)?;
- permissions.check_write(&oldpath)?;
- permissions.check_read(&newpath)?;
- permissions.check_write(&newpath)?;
+ permissions.read.check(&oldpath)?;
+ permissions.write.check(&oldpath)?;
+ permissions.read.check(&newpath)?;
+ permissions.write.check(&newpath)?;
}
tokio::task::spawn_blocking(move || {
@@ -1207,7 +1207,7 @@ fn op_symlink_sync(
let oldpath = PathBuf::from(&args.oldpath);
let newpath = PathBuf::from(&args.newpath);
- state.borrow::<Permissions>().check_write(&newpath)?;
+ state.borrow::<Permissions>().write.check(&newpath)?;
debug!(
"op_symlink_sync {} {}",
@@ -1259,7 +1259,7 @@ async fn op_symlink_async(
{
let state = state.borrow();
- state.borrow::<Permissions>().check_write(&newpath)?;
+ state.borrow::<Permissions>().write.check(&newpath)?;
}
tokio::task::spawn_blocking(move || {
@@ -1315,7 +1315,7 @@ fn op_read_link_sync(
let args: ReadLinkArgs = serde_json::from_value(args)?;
let path = PathBuf::from(&args.path);
- state.borrow::<Permissions>().check_read(&path)?;
+ state.borrow::<Permissions>().read.check(&path)?;
debug!("op_read_link_value {}", path.display());
let target = std::fs::read_link(&path)?.into_os_string();
@@ -1332,7 +1332,7 @@ async fn op_read_link_async(
let path = PathBuf::from(&args.path);
{
let state = state.borrow();
- state.borrow::<Permissions>().check_read(&path)?;
+ state.borrow::<Permissions>().read.check(&path)?;
}
tokio::task::spawn_blocking(move || {
debug!("op_read_link_async {}", path.display());
@@ -1411,7 +1411,7 @@ fn op_truncate_sync(
let path = PathBuf::from(&args.path);
let len = args.len;
- state.borrow::<Permissions>().check_write(&path)?;
+ state.borrow::<Permissions>().write.check(&path)?;
debug!("op_truncate_sync {} {}", path.display(), len);
let f = std::fs::OpenOptions::new().write(true).open(&path)?;
@@ -1429,7 +1429,7 @@ async fn op_truncate_async(
let len = args.len;
{
let state = state.borrow();
- state.borrow::<Permissions>().check_write(&path)?;
+ state.borrow::<Permissions>().write.check(&path)?;
}
tokio::task::spawn_blocking(move || {
debug!("op_truncate_async {} {}", path.display(), len);
@@ -1507,7 +1507,8 @@ fn op_make_temp_dir_sync(
state
.borrow::<Permissions>()
- .check_write(dir.clone().unwrap_or_else(temp_dir).as_path())?;
+ .write
+ .check(dir.clone().unwrap_or_else(temp_dir).as_path())?;
// TODO(piscisaureus): use byte vector for paths, not a string.
// See https://github.com/denoland/deno/issues/627.
@@ -1538,7 +1539,8 @@ async fn op_make_temp_dir_async(
let state = state.borrow();
state
.borrow::<Permissions>()
- .check_write(dir.clone().unwrap_or_else(temp_dir).as_path())?;
+ .write
+ .check(dir.clone().unwrap_or_else(temp_dir).as_path())?;
}
tokio::task::spawn_blocking(move || {
// TODO(piscisaureus): use byte vector for paths, not a string.
@@ -1572,7 +1574,8 @@ fn op_make_temp_file_sync(
state
.borrow::<Permissions>()
- .check_write(dir.clone().unwrap_or_else(temp_dir).as_path())?;
+ .write
+ .check(dir.clone().unwrap_or_else(temp_dir).as_path())?;
// TODO(piscisaureus): use byte vector for paths, not a string.
// See https://github.com/denoland/deno/issues/627.
@@ -1603,7 +1606,8 @@ async fn op_make_temp_file_async(
let state = state.borrow();
state
.borrow::<Permissions>()
- .check_write(dir.clone().unwrap_or_else(temp_dir).as_path())?;
+ .write
+ .check(dir.clone().unwrap_or_else(temp_dir).as_path())?;
}
tokio::task::spawn_blocking(move || {
// TODO(piscisaureus): use byte vector for paths, not a string.
@@ -1718,7 +1722,7 @@ fn op_utime_sync(
let atime = filetime::FileTime::from_unix_time(args.atime.0, args.atime.1);
let mtime = filetime::FileTime::from_unix_time(args.mtime.0, args.mtime.1);
- state.borrow::<Permissions>().check_write(&path)?;
+ state.borrow::<Permissions>().write.check(&path)?;
filetime::set_file_times(path, atime, mtime)?;
Ok(json!({}))
}
@@ -1735,7 +1739,7 @@ async fn op_utime_async(
let atime = filetime::FileTime::from_unix_time(args.atime.0, args.atime.1);
let mtime = filetime::FileTime::from_unix_time(args.mtime.0, args.mtime.1);
- state.borrow().borrow::<Permissions>().check_write(&path)?;
+ state.borrow().borrow::<Permissions>().write.check(&path)?;
tokio::task::spawn_blocking(move || {
filetime::set_file_times(path, atime, mtime)?;
@@ -1753,7 +1757,8 @@ fn op_cwd(
let path = current_dir()?;
state
.borrow::<Permissions>()
- .check_read_blind(&path, "CWD")?;
+ .read
+ .check_blind(&path, "CWD")?;
let path_str = into_string(path.into_os_string())?;
Ok(json!(path_str))
}
diff --git a/runtime/ops/fs_events.rs b/runtime/ops/fs_events.rs
index 6f6ba04bd..aafe91d71 100644
--- a/runtime/ops/fs_events.rs
+++ b/runtime/ops/fs_events.rs
@@ -112,7 +112,8 @@ fn op_fs_events_open(
for path in &args.paths {
state
.borrow::<Permissions>()
- .check_read(&PathBuf::from(path))?;
+ .read
+ .check(&PathBuf::from(path))?;
watcher.watch(path, recursive_mode)?;
}
let resource = FsEventsResource {
diff --git a/runtime/ops/net.rs b/runtime/ops/net.rs
index 7e80bb86b..9084cac0d 100644
--- a/runtime/ops/net.rs
+++ b/runtime/ops/net.rs
@@ -204,7 +204,8 @@ async fn op_datagram_send(
{
let s = state.borrow();
s.borrow::<Permissions>()
- .check_net(&(&args.hostname, Some(args.port)))?;
+ .net
+ .check(&(&args.hostname, Some(args.port)))?;
}
let addr = resolve_addr(&args.hostname, args.port)
.await?
@@ -229,7 +230,7 @@ async fn op_datagram_send(
let address_path = Path::new(&args.path);
{
let s = state.borrow();
- s.borrow::<Permissions>().check_write(&address_path)?;
+ s.borrow::<Permissions>().write.check(&address_path)?;
}
let resource = state
.borrow()
@@ -269,7 +270,8 @@ async fn op_connect(
let state_ = state.borrow();
state_
.borrow::<Permissions>()
- .check_net(&(&args.hostname, Some(args.port)))?;
+ .net
+ .check(&(&args.hostname, Some(args.port)))?;
}
let addr = resolve_addr(&args.hostname, args.port)
.await?
@@ -306,8 +308,8 @@ async fn op_connect(
super::check_unstable2(&state, "Deno.connect");
{
let state_ = state.borrow();
- state_.borrow::<Permissions>().check_read(&address_path)?;
- state_.borrow::<Permissions>().check_write(&address_path)?;
+ state_.borrow::<Permissions>().read.check(&address_path)?;
+ state_.borrow::<Permissions>().write.check(&address_path)?;
}
let path = args.path;
let unix_stream = net_unix::UnixStream::connect(Path::new(&path)).await?;
@@ -433,7 +435,7 @@ fn op_listen(
if transport == "udp" {
super::check_unstable(state, "Deno.listenDatagram");
}
- permissions.check_net(&(&args.hostname, Some(args.port)))?;
+ permissions.net.check(&(&args.hostname, Some(args.port)))?;
}
let addr = resolve_addr_sync(&args.hostname, args.port)?
.next()
@@ -471,8 +473,8 @@ fn op_listen(
if transport == "unixpacket" {
super::check_unstable(state, "Deno.listenDatagram");
}
- permissions.check_read(&address_path)?;
- permissions.check_write(&address_path)?;
+ permissions.read.check(&address_path)?;
+ permissions.write.check(&address_path)?;
}
let (rid, local_addr) = if transport == "unix" {
net_unix::listen_unix(state, &address_path)?
@@ -580,7 +582,7 @@ async fn op_dns_resolve(
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.net.check(&(ip, Some(port)))?;
}
}
diff --git a/runtime/ops/os.rs b/runtime/ops/os.rs
index 10e0c8bc1..addab7894 100644
--- a/runtime/ops/os.rs
+++ b/runtime/ops/os.rs
@@ -34,7 +34,8 @@ fn op_exec_path(
let current_exe = env::current_exe().unwrap();
state
.borrow::<Permissions>()
- .check_read_blind(&current_exe, "exec_path")?;
+ .read
+ .check_blind(&current_exe, "exec_path")?;
// 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();
@@ -54,7 +55,7 @@ fn op_set_env(
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
let args: SetEnv = serde_json::from_value(args)?;
- state.borrow::<Permissions>().check_env()?;
+ state.borrow::<Permissions>().env.check()?;
let invalid_key =
args.key.is_empty() || args.key.contains(&['=', '\0'] as &[char]);
let invalid_value = args.value.contains('\0');
@@ -70,7 +71,7 @@ fn op_env(
_args: Value,
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
- state.borrow::<Permissions>().check_env()?;
+ state.borrow::<Permissions>().env.check()?;
let v = env::vars().collect::<HashMap<String, String>>();
Ok(json!(v))
}
@@ -86,7 +87,7 @@ fn op_get_env(
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
let args: GetEnv = serde_json::from_value(args)?;
- state.borrow::<Permissions>().check_env()?;
+ state.borrow::<Permissions>().env.check()?;
if args.key.is_empty() || args.key.contains(&['=', '\0'] as &[char]) {
return Err(type_error("Key contains invalid characters."));
}
@@ -108,7 +109,7 @@ fn op_delete_env(
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
let args: DeleteEnv = serde_json::from_value(args)?;
- state.borrow::<Permissions>().check_env()?;
+ state.borrow::<Permissions>().env.check()?;
if args.key.is_empty() || args.key.contains(&['=', '\0'] as &[char]) {
return Err(type_error("Key contains invalid characters."));
}
@@ -136,7 +137,7 @@ fn op_loadavg(
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
super::check_unstable(state, "Deno.loadavg");
- state.borrow::<Permissions>().check_env()?;
+ state.borrow::<Permissions>().env.check()?;
match sys_info::loadavg() {
Ok(loadavg) => Ok(json!([loadavg.one, loadavg.five, loadavg.fifteen])),
Err(_) => Ok(json!([0f64, 0f64, 0f64])),
@@ -149,7 +150,7 @@ fn op_hostname(
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
super::check_unstable(state, "Deno.hostname");
- state.borrow::<Permissions>().check_env()?;
+ state.borrow::<Permissions>().env.check()?;
let hostname = sys_info::hostname().unwrap_or_else(|_| "".to_string());
Ok(json!(hostname))
}
@@ -160,7 +161,7 @@ fn op_os_release(
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
super::check_unstable(state, "Deno.osRelease");
- state.borrow::<Permissions>().check_env()?;
+ state.borrow::<Permissions>().env.check()?;
let release = sys_info::os_release().unwrap_or_else(|_| "".to_string());
Ok(json!(release))
}
@@ -171,7 +172,7 @@ fn op_system_memory_info(
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
super::check_unstable(state, "Deno.systemMemoryInfo");
- state.borrow::<Permissions>().check_env()?;
+ state.borrow::<Permissions>().env.check()?;
match sys_info::mem_info() {
Ok(info) => Ok(json!({
"total": info.total,
@@ -192,7 +193,7 @@ fn op_system_cpu_info(
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
super::check_unstable(state, "Deno.systemCpuInfo");
- state.borrow::<Permissions>().check_env()?;
+ state.borrow::<Permissions>().env.check()?;
let cores = sys_info::cpu_num().ok();
let speed = sys_info::cpu_speed().ok();
diff --git a/runtime/ops/permissions.rs b/runtime/ops/permissions.rs
index 86ad2206b..b82efd89c 100644
--- a/runtime/ops/permissions.rs
+++ b/runtime/ops/permissions.rs
@@ -35,19 +35,19 @@ pub fn op_query_permission(
let permissions = state.borrow::<Permissions>();
let path = args.path.as_deref();
let perm = match args.name.as_ref() {
- "read" => permissions.query_read(&path.as_deref().map(Path::new)),
- "write" => permissions.query_write(&path.as_deref().map(Path::new)),
- "net" => permissions.query_net(
- &match args.host.as_deref() {
+ "read" => permissions.read.query(path.as_deref().map(Path::new)),
+ "write" => permissions.write.query(path.as_deref().map(Path::new)),
+ "net" => permissions.net.query(
+ match args.host.as_deref() {
None => None,
Some(h) => Some(parse_host(h)?),
}
.as_ref(),
),
- "env" => permissions.query_env(),
- "run" => permissions.query_run(),
- "plugin" => permissions.query_plugin(),
- "hrtime" => permissions.query_hrtime(),
+ "env" => permissions.env.query(),
+ "run" => permissions.run.query(),
+ "plugin" => permissions.plugin.query(),
+ "hrtime" => permissions.hrtime.query(),
n => {
return Err(custom_error(
"ReferenceError",
@@ -67,19 +67,19 @@ pub fn op_revoke_permission(
let permissions = state.borrow_mut::<Permissions>();
let path = args.path.as_deref();
let perm = match args.name.as_ref() {
- "read" => permissions.revoke_read(&path.as_deref().map(Path::new)),
- "write" => permissions.revoke_write(&path.as_deref().map(Path::new)),
- "net" => permissions.revoke_net(
- &match args.host.as_deref() {
+ "read" => permissions.read.revoke(path.as_deref().map(Path::new)),
+ "write" => permissions.write.revoke(path.as_deref().map(Path::new)),
+ "net" => permissions.net.revoke(
+ match args.host.as_deref() {
None => None,
Some(h) => Some(parse_host(h)?),
}
.as_ref(),
),
- "env" => permissions.revoke_env(),
- "run" => permissions.revoke_run(),
- "plugin" => permissions.revoke_plugin(),
- "hrtime" => permissions.revoke_hrtime(),
+ "env" => permissions.env.revoke(),
+ "run" => permissions.run.revoke(),
+ "plugin" => permissions.plugin.revoke(),
+ "hrtime" => permissions.hrtime.revoke(),
n => {
return Err(custom_error(
"ReferenceError",
@@ -99,19 +99,19 @@ pub fn op_request_permission(
let permissions = state.borrow_mut::<Permissions>();
let path = args.path.as_deref();
let perm = match args.name.as_ref() {
- "read" => permissions.request_read(&path.as_deref().map(Path::new)),
- "write" => permissions.request_write(&path.as_deref().map(Path::new)),
- "net" => permissions.request_net(
- &match args.host.as_deref() {
+ "read" => permissions.read.request(path.as_deref().map(Path::new)),
+ "write" => permissions.write.request(path.as_deref().map(Path::new)),
+ "net" => permissions.net.request(
+ match args.host.as_deref() {
None => None,
Some(h) => Some(parse_host(h)?),
}
.as_ref(),
),
- "env" => permissions.request_env(),
- "run" => permissions.request_run(),
- "plugin" => permissions.request_plugin(),
- "hrtime" => permissions.request_hrtime(),
+ "env" => permissions.env.request(),
+ "run" => permissions.run.request(),
+ "plugin" => permissions.plugin.request(),
+ "hrtime" => permissions.hrtime.request(),
n => {
return Err(custom_error(
"ReferenceError",
diff --git a/runtime/ops/plugin.rs b/runtime/ops/plugin.rs
index 424b1dca0..e02276756 100644
--- a/runtime/ops/plugin.rs
+++ b/runtime/ops/plugin.rs
@@ -46,7 +46,7 @@ pub fn op_open_plugin(
super::check_unstable(state, "Deno.openPlugin");
let permissions = state.borrow::<Permissions>();
- permissions.check_plugin(&filename)?;
+ permissions.plugin.check()?;
debug!("Loading Plugin: {:#?}", filename);
let plugin_lib = Library::open(filename).map(Rc::new)?;
diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs
index 89f323232..a60b451eb 100644
--- a/runtime/ops/process.rs
+++ b/runtime/ops/process.rs
@@ -88,7 +88,7 @@ fn op_run(
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
let run_args: RunArgs = serde_json::from_value(args)?;
- state.borrow::<Permissions>().check_run()?;
+ state.borrow::<Permissions>().run.check()?;
let args = run_args.cmd;
let env = run_args.env;
@@ -193,7 +193,7 @@ async fn op_run_status(
{
let s = state.borrow();
- s.borrow::<Permissions>().check_run()?;
+ s.borrow::<Permissions>().run.check()?;
}
let resource = state
@@ -285,7 +285,7 @@ fn op_kill(
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
super::check_unstable(state, "Deno.kill");
- state.borrow::<Permissions>().check_run()?;
+ state.borrow::<Permissions>().run.check()?;
let args: KillArgs = serde_json::from_value(args)?;
kill(args.pid, args.signo)?;
diff --git a/runtime/ops/runtime.rs b/runtime/ops/runtime.rs
index a2f377bed..420931b0c 100644
--- a/runtime/ops/runtime.rs
+++ b/runtime/ops/runtime.rs
@@ -32,7 +32,8 @@ fn op_main_module(
let main_path = std::env::current_dir().unwrap().join(main_url.to_string());
state
.borrow::<Permissions>()
- .check_read_blind(&main_path, "main_module")?;
+ .read
+ .check_blind(&main_path, "main_module")?;
}
Ok(json!(&main))
}
diff --git a/runtime/ops/timers.rs b/runtime/ops/timers.rs
index 783395cad..34a3eea5f 100644
--- a/runtime/ops/timers.rs
+++ b/runtime/ops/timers.rs
@@ -166,7 +166,7 @@ fn op_now(
// If the permission is not enabled
// Round the nano result on 2 milliseconds
// see: https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#Reduced_time_precision
- if op_state.borrow::<Permissions>().check_hrtime().is_err() {
+ if op_state.borrow::<Permissions>().hrtime.check().is_err() {
subsec_nanos -= subsec_nanos % reduced_time_precision;
}
diff --git a/runtime/ops/tls.rs b/runtime/ops/tls.rs
index 893c068c4..f35425555 100644
--- a/runtime/ops/tls.rs
+++ b/runtime/ops/tls.rs
@@ -110,9 +110,9 @@ async fn op_start_tls(
super::check_unstable2(&state, "Deno.startTls");
let s = state.borrow();
let permissions = s.borrow::<Permissions>();
- permissions.check_net(&(&domain, Some(0)))?;
+ permissions.net.check(&(&domain, Some(0)))?;
if let Some(path) = &args.cert_file {
- permissions.check_read(Path::new(&path))?;
+ permissions.read.check(Path::new(&path))?;
}
}
@@ -174,9 +174,9 @@ async fn op_connect_tls(
{
let s = state.borrow();
let permissions = s.borrow::<Permissions>();
- permissions.check_net(&(&args.hostname, Some(args.port)))?;
+ permissions.net.check(&(&args.hostname, Some(args.port)))?;
if let Some(path) = &args.cert_file {
- permissions.check_read(Path::new(&path))?;
+ permissions.read.check(Path::new(&path))?;
}
}
let mut domain = args.hostname.as_str();
@@ -318,9 +318,9 @@ fn op_listen_tls(
let key_file = args.key_file;
{
let permissions = state.borrow::<Permissions>();
- permissions.check_net(&(&args.hostname, Some(args.port)))?;
- permissions.check_read(Path::new(&cert_file))?;
- permissions.check_read(Path::new(&key_file))?;
+ permissions.net.check(&(&args.hostname, Some(args.port)))?;
+ permissions.read.check(Path::new(&cert_file))?;
+ permissions.read.check(Path::new(&key_file))?;
}
let mut config = ServerConfig::new(NoClientAuth::new());
config
diff --git a/runtime/ops/worker_host.rs b/runtime/ops/worker_host.rs
index 6b375605f..df624cd44 100644
--- a/runtime/ops/worker_host.rs
+++ b/runtime/ops/worker_host.rs
@@ -1,9 +1,14 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-use crate::permissions::resolve_fs_allowlist;
+use crate::permissions::resolve_read_allowlist;
+use crate::permissions::resolve_write_allowlist;
+use crate::permissions::BooleanPermission;
+use crate::permissions::NetPermission;
use crate::permissions::PermissionState;
use crate::permissions::Permissions;
+use crate::permissions::ReadPermission;
use crate::permissions::UnaryPermission;
+use crate::permissions::WritePermission;
use crate::web_worker::run_web_worker;
use crate::web_worker::WebWorker;
use crate::web_worker::WebWorkerHandle;
@@ -105,49 +110,54 @@ pub fn init(
);
}
-fn merge_permission_state(
- target: &PermissionState,
+fn merge_boolean_permission(
+ target: &BooleanPermission,
incoming: Option<PermissionState>,
-) -> Result<PermissionState, AnyError> {
- match target {
+) -> Result<BooleanPermission, AnyError> {
+ let mut perm = target.clone();
+ perm.state = match target.state {
PermissionState::Granted => match incoming {
- Some(x) => Ok(x),
- None => Ok(*target),
+ Some(state) => state,
+ None => perm.state,
},
_ => match incoming {
- Some(x) => match x {
- PermissionState::Denied => Ok(x),
- _ => Err(custom_error(
- "PermissionDenied",
- "Can't escalate parent thread permissions",
- )),
+ Some(state) => match state {
+ PermissionState::Denied => state,
+ _ => {
+ return Err(custom_error(
+ "PermissionDenied",
+ "Can't escalate parent thread permissions",
+ ))
+ }
},
- None => Ok(*target),
+ None => perm.state,
},
- }
+ };
+ Ok(perm)
}
fn check_net_permission_contains(
- a: &HashSet<String>,
- b: &HashSet<String>,
+ a: &HashSet<NetPermission>,
+ b: &HashSet<NetPermission>,
) -> bool {
b.iter().all(|x| a.contains(x))
}
fn merge_net_permissions(
- target: &UnaryPermission<String>,
- incoming: Option<UnaryPermission<String>>,
-) -> Result<UnaryPermission<String>, AnyError> {
+ target: &UnaryPermission<NetPermission>,
+ incoming: Option<UnaryPermission<NetPermission>>,
+) -> Result<UnaryPermission<NetPermission>, AnyError> {
if incoming.is_none() {
return Ok(target.clone());
};
let new_permissions = incoming.unwrap();
match &target.global_state {
- PermissionState::Granted => Ok(UnaryPermission::<String> {
+ PermissionState::Granted => Ok(UnaryPermission::<NetPermission> {
global_state: new_permissions.global_state,
granted_list: new_permissions.granted_list,
denied_list: new_permissions.denied_list,
+ ..Permissions::new_net(&None)
}),
PermissionState::Prompt => match new_permissions.global_state {
//Throw
@@ -161,10 +171,11 @@ fn merge_net_permissions(
&target.granted_list,
&new_permissions.granted_list,
) {
- Ok(UnaryPermission::<String> {
+ Ok(UnaryPermission::<NetPermission> {
global_state: new_permissions.global_state,
granted_list: new_permissions.granted_list,
denied_list: target.denied_list.clone(),
+ ..Permissions::new_net(&None)
})
} else {
Err(custom_error(
@@ -174,17 +185,19 @@ fn merge_net_permissions(
}
}
//Copy
- PermissionState::Denied => Ok(UnaryPermission::<String> {
+ PermissionState::Denied => Ok(UnaryPermission::<NetPermission> {
global_state: new_permissions.global_state,
granted_list: new_permissions.granted_list,
denied_list: new_permissions.denied_list,
+ ..Permissions::new_net(&None)
}),
},
PermissionState::Denied => match new_permissions.global_state {
- PermissionState::Denied => Ok(UnaryPermission::<String> {
+ PermissionState::Denied => Ok(UnaryPermission::<NetPermission> {
global_state: new_permissions.global_state,
granted_list: new_permissions.granted_list,
denied_list: new_permissions.denied_list,
+ ..Permissions::new_net(&None)
}),
_ => Err(custom_error(
"PermissionDenied",
@@ -194,45 +207,105 @@ fn merge_net_permissions(
}
}
-enum WorkerPermissionType {
- READ,
- WRITE,
-}
-
fn check_read_permissions(
- allow_list: &HashSet<PathBuf>,
+ allow_list: &HashSet<ReadPermission>,
current_permissions: &Permissions,
) -> bool {
allow_list
.iter()
- .all(|x| current_permissions.check_read(&x).is_ok())
+ .all(|x| current_permissions.read.check(&x.0).is_ok())
}
fn check_write_permissions(
- allow_list: &HashSet<PathBuf>,
+ allow_list: &HashSet<WritePermission>,
current_permissions: &Permissions,
) -> bool {
allow_list
.iter()
- .all(|x| current_permissions.check_write(&x).is_ok())
+ .all(|x| current_permissions.write.check(&x.0).is_ok())
+}
+
+fn merge_read_permissions(
+ target: &UnaryPermission<ReadPermission>,
+ incoming: Option<UnaryPermission<ReadPermission>>,
+ current_permissions: &Permissions,
+) -> Result<UnaryPermission<ReadPermission>, AnyError> {
+ if incoming.is_none() {
+ return Ok(target.clone());
+ };
+
+ let new_permissions = incoming.unwrap();
+ match &target.global_state {
+ PermissionState::Granted => Ok(UnaryPermission::<ReadPermission> {
+ global_state: new_permissions.global_state,
+ granted_list: new_permissions.granted_list,
+ denied_list: new_permissions.denied_list,
+ ..Permissions::new_read(&None)
+ }),
+ PermissionState::Prompt => match new_permissions.global_state {
+ //Throw
+ PermissionState::Granted => Err(custom_error(
+ "PermissionDenied",
+ "Can't escalate parent thread permissions",
+ )),
+ //Merge
+ PermissionState::Prompt => {
+ if check_read_permissions(
+ &new_permissions.granted_list,
+ current_permissions,
+ ) {
+ Ok(UnaryPermission::<ReadPermission> {
+ global_state: new_permissions.global_state,
+ granted_list: new_permissions.granted_list,
+ denied_list: target.denied_list.clone(),
+ ..Permissions::new_read(&None)
+ })
+ } else {
+ Err(custom_error(
+ "PermissionDenied",
+ "Can't escalate parent thread permissions",
+ ))
+ }
+ }
+ //Copy
+ PermissionState::Denied => Ok(UnaryPermission::<ReadPermission> {
+ global_state: new_permissions.global_state,
+ granted_list: new_permissions.granted_list,
+ denied_list: new_permissions.denied_list,
+ ..Permissions::new_read(&None)
+ }),
+ },
+ PermissionState::Denied => match new_permissions.global_state {
+ PermissionState::Denied => Ok(UnaryPermission::<ReadPermission> {
+ global_state: new_permissions.global_state,
+ granted_list: new_permissions.granted_list,
+ denied_list: new_permissions.denied_list,
+ ..Permissions::new_read(&None)
+ }),
+ _ => Err(custom_error(
+ "PermissionDenied",
+ "Can't escalate parent thread permissions",
+ )),
+ },
+ }
}
-fn merge_read_write_permissions(
- permission_type: WorkerPermissionType,
- target: &UnaryPermission<PathBuf>,
- incoming: Option<UnaryPermission<PathBuf>>,
+fn merge_write_permissions(
+ target: &UnaryPermission<WritePermission>,
+ incoming: Option<UnaryPermission<WritePermission>>,
current_permissions: &Permissions,
-) -> Result<UnaryPermission<PathBuf>, AnyError> {
+) -> Result<UnaryPermission<WritePermission>, AnyError> {
if incoming.is_none() {
return Ok(target.clone());
};
let new_permissions = incoming.unwrap();
match &target.global_state {
- PermissionState::Granted => Ok(UnaryPermission::<PathBuf> {
+ PermissionState::Granted => Ok(UnaryPermission::<WritePermission> {
global_state: new_permissions.global_state,
granted_list: new_permissions.granted_list,
denied_list: new_permissions.denied_list,
+ ..Permissions::new_write(&None)
}),
PermissionState::Prompt => match new_permissions.global_state {
//Throw
@@ -242,20 +315,15 @@ fn merge_read_write_permissions(
)),
//Merge
PermissionState::Prompt => {
- if match permission_type {
- WorkerPermissionType::READ => check_read_permissions(
- &new_permissions.granted_list,
- current_permissions,
- ),
- WorkerPermissionType::WRITE => check_write_permissions(
- &new_permissions.granted_list,
- current_permissions,
- ),
- } {
- Ok(UnaryPermission::<PathBuf> {
+ if check_write_permissions(
+ &new_permissions.granted_list,
+ current_permissions,
+ ) {
+ Ok(UnaryPermission::<WritePermission> {
global_state: new_permissions.global_state,
granted_list: new_permissions.granted_list,
denied_list: target.denied_list.clone(),
+ ..Permissions::new_write(&None)
})
} else {
Err(custom_error(
@@ -265,17 +333,19 @@ fn merge_read_write_permissions(
}
}
//Copy
- PermissionState::Denied => Ok(UnaryPermission::<PathBuf> {
+ PermissionState::Denied => Ok(UnaryPermission::<WritePermission> {
global_state: new_permissions.global_state,
granted_list: new_permissions.granted_list,
denied_list: new_permissions.denied_list,
+ ..Permissions::new_write(&None)
}),
},
PermissionState::Denied => match new_permissions.global_state {
- PermissionState::Denied => Ok(UnaryPermission::<PathBuf> {
+ PermissionState::Denied => Ok(UnaryPermission::<WritePermission> {
global_state: new_permissions.global_state,
granted_list: new_permissions.granted_list,
denied_list: new_permissions.denied_list,
+ ..Permissions::new_write(&None)
}),
_ => Err(custom_error(
"PermissionDenied",
@@ -290,11 +360,11 @@ fn create_worker_permissions(
permission_args: PermissionsArg,
) -> Result<Permissions, AnyError> {
Ok(Permissions {
- env: merge_permission_state(
+ env: merge_boolean_permission(
&main_thread_permissions.env,
permission_args.env,
)?,
- hrtime: merge_permission_state(
+ hrtime: merge_boolean_permission(
&main_thread_permissions.hrtime,
permission_args.hrtime,
)?,
@@ -302,22 +372,20 @@ fn create_worker_permissions(
&main_thread_permissions.net,
permission_args.net,
)?,
- plugin: merge_permission_state(
+ plugin: merge_boolean_permission(
&main_thread_permissions.plugin,
permission_args.plugin,
)?,
- read: merge_read_write_permissions(
- WorkerPermissionType::READ,
+ read: merge_read_permissions(
&main_thread_permissions.read,
permission_args.read,
&main_thread_permissions,
)?,
- run: merge_permission_state(
+ run: merge_boolean_permission(
&main_thread_permissions.run,
permission_args.run,
)?,
- write: merge_read_write_permissions(
- WorkerPermissionType::WRITE,
+ write: merge_write_permissions(
&main_thread_permissions.write,
permission_args.write,
&main_thread_permissions,
@@ -331,16 +399,16 @@ struct PermissionsArg {
env: Option<PermissionState>,
#[serde(default, deserialize_with = "as_permission_state")]
hrtime: Option<PermissionState>,
- #[serde(default, deserialize_with = "as_unary_string_permission")]
- net: Option<UnaryPermission<String>>,
+ #[serde(default, deserialize_with = "as_unary_net_permission")]
+ net: Option<UnaryPermission<NetPermission>>,
#[serde(default, deserialize_with = "as_permission_state")]
plugin: Option<PermissionState>,
- #[serde(default, deserialize_with = "as_unary_path_permission")]
- read: Option<UnaryPermission<PathBuf>>,
+ #[serde(default, deserialize_with = "as_unary_read_permission")]
+ read: Option<UnaryPermission<ReadPermission>>,
#[serde(default, deserialize_with = "as_permission_state")]
run: Option<PermissionState>,
- #[serde(default, deserialize_with = "as_unary_path_permission")]
- write: Option<UnaryPermission<PathBuf>>,
+ #[serde(default, deserialize_with = "as_unary_write_permission")]
+ write: Option<UnaryPermission<WritePermission>>,
}
fn as_permission_state<'de, D>(
@@ -402,27 +470,50 @@ impl<'de> de::Visitor<'de> for ParseBooleanOrStringVec {
}
}
-fn as_unary_string_permission<'de, D>(
+fn as_unary_net_permission<'de, D>(
deserializer: D,
-) -> Result<Option<UnaryPermission<String>>, D::Error>
+) -> Result<Option<UnaryPermission<NetPermission>>, D::Error>
where
D: Deserializer<'de>,
{
let value: UnaryPermissionBase =
deserializer.deserialize_any(ParseBooleanOrStringVec)?;
- let allowed: HashSet<String> = value.paths.into_iter().collect();
+ let allowed: HashSet<NetPermission> = value
+ .paths
+ .into_iter()
+ .map(NetPermission::from_string)
+ .collect();
- Ok(Some(UnaryPermission::<String> {
+ Ok(Some(UnaryPermission::<NetPermission> {
global_state: value.global_state,
granted_list: allowed,
..Default::default()
}))
}
-fn as_unary_path_permission<'de, D>(
+fn as_unary_read_permission<'de, D>(
+ deserializer: D,
+) -> Result<Option<UnaryPermission<ReadPermission>>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ let value: UnaryPermissionBase =
+ deserializer.deserialize_any(ParseBooleanOrStringVec)?;
+
+ let paths: Vec<PathBuf> =
+ value.paths.into_iter().map(PathBuf::from).collect();
+
+ Ok(Some(UnaryPermission::<ReadPermission> {
+ global_state: value.global_state,
+ granted_list: resolve_read_allowlist(&Some(paths)),
+ ..Default::default()
+ }))
+}
+
+fn as_unary_write_permission<'de, D>(
deserializer: D,
-) -> Result<Option<UnaryPermission<PathBuf>>, D::Error>
+) -> Result<Option<UnaryPermission<WritePermission>>, D::Error>
where
D: Deserializer<'de>,
{
@@ -432,9 +523,9 @@ where
let paths: Vec<PathBuf> =
value.paths.into_iter().map(PathBuf::from).collect();
- Ok(Some(UnaryPermission::<PathBuf> {
+ Ok(Some(UnaryPermission::<WritePermission> {
global_state: value.global_state,
- granted_list: resolve_fs_allowlist(&Some(paths)),
+ granted_list: resolve_write_allowlist(&Some(paths)),
..Default::default()
}))
}
diff --git a/runtime/permissions.rs b/runtime/permissions.rs
index 066f32747..5d28a0640 100644
--- a/runtime/permissions.rs
+++ b/runtime/permissions.rs
@@ -10,7 +10,6 @@ use deno_core::serde::Serialize;
use deno_core::url;
use deno_core::ModuleSpecifier;
use std::collections::HashSet;
-use std::env::current_dir;
use std::fmt;
use std::hash::Hash;
#[cfg(not(test))]
@@ -35,12 +34,21 @@ pub enum PermissionState {
impl PermissionState {
/// Check the permission state.
- fn check(self, msg: &str, flag_name: &str) -> Result<(), AnyError> {
+ fn check(self, name: &str, info: Option<&str>) -> Result<(), AnyError> {
if self == PermissionState::Granted {
- log_perm_access(msg);
+ log_perm_access(&format!(
+ "{} access{}",
+ name,
+ info.map_or(Default::default(), |info| { format!(" to {}", info) }),
+ ));
return Ok(());
}
- let message = format!("{}, run again with the {} flag", msg, flag_name);
+ let message = format!(
+ "Requires {} access{}, run again with the --allow-{} flag",
+ name,
+ info.map_or(Default::default(), |info| { format!(" to {}", info) }),
+ name
+ );
Err(custom_error("PermissionDenied", message))
}
}
@@ -63,515 +71,505 @@ impl Default for PermissionState {
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct UnaryPermission<T: Eq + Hash> {
+ #[serde(skip)]
+ pub name: &'static str,
+ #[serde(skip)]
+ pub description: &'static str,
pub global_state: PermissionState,
pub granted_list: HashSet<T>,
pub denied_list: HashSet<T>,
}
-#[derive(Clone, Debug, Default, PartialEq)]
-pub struct Permissions {
- pub read: UnaryPermission<PathBuf>,
- pub write: UnaryPermission<PathBuf>,
- pub net: UnaryPermission<String>,
- pub env: PermissionState,
- pub run: PermissionState,
- pub plugin: PermissionState,
- pub hrtime: PermissionState,
-}
+#[derive(Clone, Eq, PartialEq, Hash, Debug, Default, Deserialize)]
+pub struct ReadPermission(pub PathBuf);
-pub fn resolve_fs_allowlist(allow: &Option<Vec<PathBuf>>) -> HashSet<PathBuf> {
- if let Some(v) = allow {
- v.iter()
- .map(|raw_path| resolve_from_cwd(Path::new(&raw_path)).unwrap())
- .collect()
- } else {
- HashSet::new()
+impl UnaryPermission<ReadPermission> {
+ pub fn query(&self, path: Option<&Path>) -> PermissionState {
+ let path = path.map(|p| resolve_from_cwd(p).unwrap());
+ if self.global_state == PermissionState::Denied
+ && match path.as_ref() {
+ None => true,
+ Some(path) => self
+ .denied_list
+ .iter()
+ .any(|path_| path_.0.starts_with(path)),
+ }
+ {
+ PermissionState::Denied
+ } else if self.global_state == PermissionState::Granted
+ || match path.as_ref() {
+ None => false,
+ Some(path) => self
+ .granted_list
+ .iter()
+ .any(|path_| path.starts_with(&path_.0)),
+ }
+ {
+ PermissionState::Granted
+ } else {
+ PermissionState::Prompt
+ }
}
-}
-#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
-pub struct PermissionsOptions {
- pub allow_env: bool,
- pub allow_hrtime: bool,
- pub allow_net: Option<Vec<String>>,
- pub allow_plugin: bool,
- pub allow_read: Option<Vec<PathBuf>>,
- pub allow_run: bool,
- pub allow_write: Option<Vec<PathBuf>>,
-}
-
-impl Permissions {
- pub fn from_options(opts: &PermissionsOptions) -> Self {
- fn global_state_from_flag_bool(flag: bool) -> PermissionState {
- if flag {
- PermissionState::Granted
+ pub fn request(&mut self, path: Option<&Path>) -> PermissionState {
+ if let Some(path) = path {
+ let (resolved_path, display_path) = resolved_and_display_path(path);
+ let state = self.query(Some(&resolved_path));
+ if state == PermissionState::Prompt {
+ if permission_prompt(&format!(
+ "read access to \"{}\"",
+ display_path.display()
+ )) {
+ self
+ .granted_list
+ .retain(|path| !path.0.starts_with(&resolved_path));
+ self.granted_list.insert(ReadPermission(resolved_path));
+ PermissionState::Granted
+ } else {
+ self
+ .denied_list
+ .retain(|path| !resolved_path.starts_with(&path.0));
+ self.denied_list.insert(ReadPermission(resolved_path));
+ self.global_state = PermissionState::Denied;
+ PermissionState::Denied
+ }
} else {
- PermissionState::Prompt
+ state
}
- }
- fn global_state_from_option<T>(flag: &Option<Vec<T>>) -> PermissionState {
- if matches!(flag, Some(v) if v.is_empty()) {
- PermissionState::Granted
+ } else {
+ let state = self.query(None);
+ if state == PermissionState::Prompt {
+ if permission_prompt("read access") {
+ self.granted_list.clear();
+ self.global_state = PermissionState::Granted;
+ PermissionState::Granted
+ } else {
+ self.global_state = PermissionState::Denied;
+ PermissionState::Denied
+ }
} else {
- PermissionState::Prompt
+ state
}
}
- Self {
- read: UnaryPermission::<PathBuf> {
- global_state: global_state_from_option(&opts.allow_read),
- granted_list: resolve_fs_allowlist(&opts.allow_read),
- ..Default::default()
- },
- write: UnaryPermission::<PathBuf> {
- global_state: global_state_from_option(&opts.allow_write),
- granted_list: resolve_fs_allowlist(&opts.allow_write),
- ..Default::default()
- },
- net: UnaryPermission::<String> {
- global_state: global_state_from_option(&opts.allow_net),
- granted_list: opts
- .allow_net
- .as_ref()
- .map(|v| v.iter().cloned().collect())
- .unwrap_or_else(HashSet::new),
- ..Default::default()
- },
- env: global_state_from_flag_bool(opts.allow_env),
- run: global_state_from_flag_bool(opts.allow_run),
- plugin: global_state_from_flag_bool(opts.allow_plugin),
- hrtime: global_state_from_flag_bool(opts.allow_hrtime),
- }
}
- /// Arbitrary helper. Resolves the path from CWD, and also gets a path that
- /// can be displayed without leaking the CWD when not allowed.
- fn resolved_and_display_path(&self, path: &Path) -> (PathBuf, PathBuf) {
- let resolved_path = resolve_from_cwd(path).unwrap();
- let display_path = if path.is_absolute() {
- path.to_path_buf()
+ pub fn revoke(&mut self, path: Option<&Path>) -> PermissionState {
+ if let Some(path) = path {
+ let path = resolve_from_cwd(path).unwrap();
+ self
+ .granted_list
+ .retain(|path_| !path_.0.starts_with(&path));
} else {
- match self
- .query_read(&Some(&current_dir().unwrap()))
- .check("", "")
- {
- Ok(_) => resolved_path.clone(),
- Err(_) => path.to_path_buf(),
+ self.granted_list.clear();
+ if self.global_state == PermissionState::Granted {
+ self.global_state = PermissionState::Prompt;
}
- };
- (resolved_path, display_path)
+ }
+ self.query(path)
}
- pub fn allow_all() -> Self {
- Self {
- read: UnaryPermission {
- global_state: PermissionState::Granted,
- ..Default::default()
- },
- write: UnaryPermission {
- global_state: PermissionState::Granted,
- ..Default::default()
- },
- net: UnaryPermission {
- global_state: PermissionState::Granted,
- ..Default::default()
- },
- env: PermissionState::Granted,
- run: PermissionState::Granted,
- plugin: PermissionState::Granted,
- hrtime: PermissionState::Granted,
- }
+ pub fn check(&self, path: &Path) -> Result<(), AnyError> {
+ let (resolved_path, display_path) = resolved_and_display_path(path);
+ self
+ .query(Some(&resolved_path))
+ .check(self.name, Some(&format!("\"{}\"", display_path.display())))
}
- pub fn query_read(&self, path: &Option<&Path>) -> PermissionState {
- let path = path.map(|p| resolve_from_cwd(p).unwrap());
- if self.read.global_state == PermissionState::Denied
- && match path.as_ref() {
- None => true,
- Some(path) => check_path_blocklist(path, &self.read.denied_list),
- }
- {
- return PermissionState::Denied;
- }
- if self.read.global_state == PermissionState::Granted
- || match path.as_ref() {
- None => false,
- Some(path) => check_path_allowlist(path, &self.read.granted_list),
- }
- {
- return PermissionState::Granted;
- }
- PermissionState::Prompt
+ /// As `check()`, but permission error messages will anonymize the path
+ /// by replacing it with the given `display`.
+ pub fn check_blind(
+ &self,
+ path: &Path,
+ display: &str,
+ ) -> Result<(), AnyError> {
+ let resolved_path = resolve_from_cwd(path).unwrap();
+ self
+ .query(Some(&resolved_path))
+ .check(self.name, Some(&format!("<{}>", display)))
}
+}
+
+#[derive(Clone, Eq, PartialEq, Hash, Debug, Default, Deserialize)]
+pub struct WritePermission(pub PathBuf);
- pub fn query_write(&self, path: &Option<&Path>) -> PermissionState {
+impl UnaryPermission<WritePermission> {
+ pub fn query(&self, path: Option<&Path>) -> PermissionState {
let path = path.map(|p| resolve_from_cwd(p).unwrap());
- if self.write.global_state == PermissionState::Denied
+ if self.global_state == PermissionState::Denied
&& match path.as_ref() {
None => true,
- Some(path) => check_path_blocklist(path, &self.write.denied_list),
+ Some(path) => self
+ .denied_list
+ .iter()
+ .any(|path_| path_.0.starts_with(path)),
}
{
- return PermissionState::Denied;
- }
- if self.write.global_state == PermissionState::Granted
+ PermissionState::Denied
+ } else if self.global_state == PermissionState::Granted
|| match path.as_ref() {
None => false,
- Some(path) => check_path_allowlist(path, &self.write.granted_list),
- }
- {
- return PermissionState::Granted;
- }
- PermissionState::Prompt
- }
-
- pub fn query_net<T: AsRef<str>>(
- &self,
- host: &Option<&(T, Option<u16>)>,
- ) -> PermissionState {
- if self.net.global_state == PermissionState::Denied
- && match host.as_ref() {
- None => true,
- Some(host) => check_host_blocklist(host, &self.net.denied_list),
- }
- {
- return PermissionState::Denied;
- }
- if self.net.global_state == PermissionState::Granted
- || match host.as_ref() {
- None => false,
- Some(host) => check_host_allowlist(host, &self.net.granted_list),
+ Some(path) => self
+ .granted_list
+ .iter()
+ .any(|path_| path.starts_with(&path_.0)),
}
{
- return PermissionState::Granted;
+ PermissionState::Granted
+ } else {
+ PermissionState::Prompt
}
- PermissionState::Prompt
- }
-
- pub fn query_env(&self) -> PermissionState {
- self.env
- }
-
- pub fn query_run(&self) -> PermissionState {
- self.run
}
- pub fn query_plugin(&self) -> PermissionState {
- self.plugin
- }
-
- pub fn query_hrtime(&self) -> PermissionState {
- self.hrtime
- }
-
- pub fn request_read(&mut self, path: &Option<&Path>) -> PermissionState {
+ pub fn request(&mut self, path: Option<&Path>) -> PermissionState {
if let Some(path) = path {
- let (resolved_path, display_path) = self.resolved_and_display_path(path);
- let state = self.query_read(&Some(&resolved_path));
+ let (resolved_path, display_path) = resolved_and_display_path(path);
+ let state = self.query(Some(&resolved_path));
if state == PermissionState::Prompt {
if permission_prompt(&format!(
- "Deno requests read access to \"{}\"",
+ "write access to \"{}\"",
display_path.display()
)) {
self
- .read
.granted_list
- .retain(|path| !path.starts_with(&resolved_path));
- self.read.granted_list.insert(resolved_path);
- return PermissionState::Granted;
+ .retain(|path| !path.0.starts_with(&resolved_path));
+ self.granted_list.insert(WritePermission(resolved_path));
+ PermissionState::Granted
} else {
self
- .read
.denied_list
- .retain(|path| !resolved_path.starts_with(path));
- self.read.denied_list.insert(resolved_path);
- self.read.global_state = PermissionState::Denied;
- return PermissionState::Denied;
+ .retain(|path| !resolved_path.starts_with(&path.0));
+ self.denied_list.insert(WritePermission(resolved_path));
+ self.global_state = PermissionState::Denied;
+ PermissionState::Denied
}
+ } else {
+ state
}
- state
} else {
- let state = self.query_read(&None);
+ let state = self.query(None);
if state == PermissionState::Prompt {
- if permission_prompt("Deno requests read access") {
- self.read.granted_list.clear();
- self.read.global_state = PermissionState::Granted;
- return PermissionState::Granted;
+ if permission_prompt("write access") {
+ self.granted_list.clear();
+ self.global_state = PermissionState::Granted;
+ PermissionState::Granted
} else {
- self.read.global_state = PermissionState::Denied;
- return PermissionState::Denied;
+ self.global_state = PermissionState::Denied;
+ PermissionState::Denied
}
+ } else {
+ state
}
- state
}
}
- pub fn request_write(&mut self, path: &Option<&Path>) -> PermissionState {
+ pub fn revoke(&mut self, path: Option<&Path>) -> PermissionState {
if let Some(path) = path {
- let (resolved_path, display_path) = self.resolved_and_display_path(path);
- let state = self.query_write(&Some(&resolved_path));
- if state == PermissionState::Prompt {
- if permission_prompt(&format!(
- "Deno requests write access to \"{}\"",
- display_path.display()
- )) {
- self
- .write
- .granted_list
- .retain(|path| !path.starts_with(&resolved_path));
- self.write.granted_list.insert(resolved_path);
- return PermissionState::Granted;
- } else {
- self
- .write
+ let path = resolve_from_cwd(path).unwrap();
+ self
+ .granted_list
+ .retain(|path_| !path_.0.starts_with(&path));
+ } else {
+ self.granted_list.clear();
+ if self.global_state == PermissionState::Granted {
+ self.global_state = PermissionState::Prompt;
+ }
+ }
+ self.query(path)
+ }
+
+ pub fn check(&self, path: &Path) -> Result<(), AnyError> {
+ let (resolved_path, display_path) = resolved_and_display_path(path);
+ self
+ .query(Some(&resolved_path))
+ .check(self.name, Some(&format!("\"{}\"", display_path.display())))
+ }
+}
+
+#[derive(Clone, Eq, PartialEq, Hash, Debug, Default, Deserialize)]
+pub struct NetPermission(pub String, pub Option<u16>);
+
+impl NetPermission {
+ fn new<T: AsRef<str>>(host: &&(T, Option<u16>)) -> Self {
+ NetPermission(host.0.as_ref().to_string(), host.1)
+ }
+
+ pub fn from_string(host: String) -> Self {
+ let url = url::Url::parse(&format!("http://{}", host)).unwrap();
+ let hostname = url.host_str().unwrap().to_string();
+
+ NetPermission(hostname, url.port())
+ }
+}
+
+impl fmt::Display for NetPermission {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(&match self.1 {
+ None => self.0.clone(),
+ Some(port) => format!("{}:{}", self.0, port),
+ })
+ }
+}
+
+impl UnaryPermission<NetPermission> {
+ pub fn query<T: AsRef<str>>(
+ &self,
+ host: Option<&(T, Option<u16>)>,
+ ) -> PermissionState {
+ if self.global_state == PermissionState::Denied
+ && match host.as_ref() {
+ None => true,
+ Some(host) => match host.1 {
+ None => self
.denied_list
- .retain(|path| !resolved_path.starts_with(path));
- self.write.denied_list.insert(resolved_path);
- self.write.global_state = PermissionState::Denied;
- return PermissionState::Denied;
- }
+ .iter()
+ .any(|host_| host.0.as_ref() == host_.0),
+ Some(_) => self.denied_list.contains(&NetPermission::new(host)),
+ },
}
- state
- } else {
- let state = self.query_write(&None);
- if state == PermissionState::Prompt {
- if permission_prompt("Deno requests write access") {
- self.write.granted_list.clear();
- self.write.global_state = PermissionState::Granted;
- return PermissionState::Granted;
- } else {
- self.write.global_state = PermissionState::Denied;
- return PermissionState::Denied;
+ {
+ PermissionState::Denied
+ } else if self.global_state == PermissionState::Granted
+ || match host.as_ref() {
+ None => false,
+ Some(host) => {
+ self.granted_list.contains(&NetPermission::new(&&(
+ host.0.as_ref().to_string(),
+ None,
+ )))
+ || self.granted_list.contains(&NetPermission::new(host))
}
}
- state
+ {
+ PermissionState::Granted
+ } else {
+ PermissionState::Prompt
}
}
- pub fn request_net<T: AsRef<str>>(
+ pub fn request<T: AsRef<str>>(
&mut self,
- host: &Option<&(T, Option<u16>)>,
+ host: Option<&(T, Option<u16>)>,
) -> PermissionState {
if let Some(host) = host {
- let state = self.query_net(&Some(host));
+ let state = self.query(Some(host));
if state == PermissionState::Prompt {
- let host_string = format_host(host);
- if permission_prompt(&format!(
- "Deno requests network access to \"{}\"",
- host_string,
- )) {
+ let host = NetPermission::new(&host);
+ if permission_prompt(&format!("network access to \"{}\"", host)) {
if host.1.is_none() {
- self
- .net
- .granted_list
- .retain(|h| !h.starts_with(&format!("{}:", host.0.as_ref())));
+ self.granted_list.retain(|h| h.0 != host.0);
}
- self.net.granted_list.insert(host_string);
- return PermissionState::Granted;
+ self.granted_list.insert(host);
+ PermissionState::Granted
} else {
if host.1.is_some() {
- self.net.denied_list.remove(host.0.as_ref());
+ self.denied_list.remove(&host);
}
- self.net.denied_list.insert(host_string);
- self.net.global_state = PermissionState::Denied;
- return PermissionState::Denied;
+ self.denied_list.insert(host);
+ self.global_state = PermissionState::Denied;
+ PermissionState::Denied
}
+ } else {
+ state
}
- state
} else {
- let state = self.query_net::<&str>(&None);
+ let state = self.query::<&str>(None);
if state == PermissionState::Prompt {
- if permission_prompt("Deno requests network access") {
- self.net.granted_list.clear();
- self.net.global_state = PermissionState::Granted;
- return PermissionState::Granted;
+ if permission_prompt("network access") {
+ self.granted_list.clear();
+ self.global_state = PermissionState::Granted;
+ PermissionState::Granted
} else {
- self.net.global_state = PermissionState::Denied;
- return PermissionState::Denied;
+ self.global_state = PermissionState::Denied;
+ PermissionState::Denied
}
+ } else {
+ state
}
- state
}
}
- pub fn request_env(&mut self) -> PermissionState {
- if self.env == PermissionState::Prompt {
- if permission_prompt("Deno requests access to environment variables") {
- self.env = PermissionState::Granted;
- } else {
- self.env = PermissionState::Denied;
+ pub fn revoke<T: AsRef<str>>(
+ &mut self,
+ host: Option<&(T, Option<u16>)>,
+ ) -> PermissionState {
+ if let Some(host) = host {
+ self.granted_list.remove(&NetPermission::new(&host));
+ if host.1.is_none() {
+ self.granted_list.retain(|h| h.0 != host.0.as_ref());
+ }
+ } else {
+ self.granted_list.clear();
+ if self.global_state == PermissionState::Granted {
+ self.global_state = PermissionState::Prompt;
}
}
- self.env
+ self.query(host)
}
- pub fn request_run(&mut self) -> PermissionState {
- if self.run == PermissionState::Prompt {
- if permission_prompt("Deno requests to access to run a subprocess") {
- self.run = PermissionState::Granted;
- } else {
- self.run = PermissionState::Denied;
- }
- }
- self.run
+ pub fn check<T: AsRef<str>>(
+ &self,
+ host: &(T, Option<u16>),
+ ) -> Result<(), AnyError> {
+ self.query(Some(host)).check(
+ self.name,
+ Some(&format!("\"{}\"", NetPermission::new(&host))),
+ )
}
- pub fn request_plugin(&mut self) -> PermissionState {
- if self.plugin == PermissionState::Prompt {
- if permission_prompt("Deno requests to open plugins") {
- self.plugin = PermissionState::Granted;
- } else {
- self.plugin = PermissionState::Denied;
- }
- }
- self.plugin
+ pub fn check_url(&self, url: &url::Url) -> Result<(), AnyError> {
+ let hostname = url
+ .host_str()
+ .ok_or_else(|| uri_error("Missing host"))?
+ .to_string();
+ let display_host = match url.port() {
+ None => hostname.clone(),
+ Some(port) => format!("{}:{}", hostname, port),
+ };
+ self
+ .query(Some(&(hostname, url.port_or_known_default())))
+ .check(self.name, Some(&format!("\"{}\"", display_host)))
}
+}
- pub fn request_hrtime(&mut self) -> PermissionState {
- if self.hrtime == PermissionState::Prompt {
- if permission_prompt("Deno requests access to high precision time") {
- self.hrtime = PermissionState::Granted;
- } else {
- self.hrtime = PermissionState::Denied;
- }
- }
- self.hrtime
+#[derive(Clone, Debug, Default, PartialEq)]
+pub struct BooleanPermission {
+ pub name: &'static str,
+ pub description: &'static str,
+ pub state: PermissionState,
+}
+
+impl BooleanPermission {
+ pub fn query(&self) -> PermissionState {
+ self.state
}
- pub fn revoke_read(&mut self, path: &Option<&Path>) -> PermissionState {
- if let Some(path) = path {
- let path = resolve_from_cwd(path).unwrap();
- self
- .read
- .granted_list
- .retain(|path_| !path_.starts_with(&path));
- } else {
- self.read.granted_list.clear();
- if self.read.global_state == PermissionState::Granted {
- self.read.global_state = PermissionState::Prompt;
+ pub fn request(&mut self) -> PermissionState {
+ if self.state == PermissionState::Prompt {
+ if permission_prompt(&format!("access to {}", self.description)) {
+ self.state = PermissionState::Granted;
+ } else {
+ self.state = PermissionState::Denied;
}
}
- self.query_read(path)
+ self.state
}
- pub fn revoke_write(&mut self, path: &Option<&Path>) -> PermissionState {
- if let Some(path) = path {
- let path = resolve_from_cwd(path).unwrap();
- self
- .write
- .granted_list
- .retain(|path_| !path_.starts_with(&path));
- } else {
- self.write.granted_list.clear();
- if self.write.global_state == PermissionState::Granted {
- self.write.global_state = PermissionState::Prompt;
- }
+ pub fn revoke(&mut self) -> PermissionState {
+ if self.state == PermissionState::Granted {
+ self.state = PermissionState::Prompt;
}
- self.query_write(path)
+ self.state
}
- pub fn revoke_net<T: AsRef<str>>(
- &mut self,
- host: &Option<&(T, Option<u16>)>,
- ) -> PermissionState {
- if let Some(host) = host {
- self.net.granted_list.remove(&format_host(host));
- if host.1.is_none() {
- self
- .net
- .granted_list
- .retain(|h| !h.starts_with(&format!("{}:", host.0.as_ref())));
- }
- } else {
- self.net.granted_list.clear();
- if self.net.global_state == PermissionState::Granted {
- self.net.global_state = PermissionState::Prompt;
- }
- }
- self.query_net(host)
+ pub fn check(&self) -> Result<(), AnyError> {
+ self.state.check(self.name, None)
}
+}
- pub fn revoke_env(&mut self) -> PermissionState {
- if self.env == PermissionState::Granted {
- self.env = PermissionState::Prompt;
+#[derive(Clone, Debug, Default, PartialEq)]
+pub struct Permissions {
+ pub read: UnaryPermission<ReadPermission>,
+ pub write: UnaryPermission<WritePermission>,
+ pub net: UnaryPermission<NetPermission>,
+ pub env: BooleanPermission,
+ pub run: BooleanPermission,
+ pub plugin: BooleanPermission,
+ pub hrtime: BooleanPermission,
+}
+
+#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
+pub struct PermissionsOptions {
+ pub allow_env: bool,
+ pub allow_hrtime: bool,
+ pub allow_net: Option<Vec<String>>,
+ pub allow_plugin: bool,
+ pub allow_read: Option<Vec<PathBuf>>,
+ pub allow_run: bool,
+ pub allow_write: Option<Vec<PathBuf>>,
+}
+
+impl Permissions {
+ pub fn new_read(
+ state: &Option<Vec<PathBuf>>,
+ ) -> UnaryPermission<ReadPermission> {
+ UnaryPermission::<ReadPermission> {
+ name: "read",
+ description: "read the file system",
+ global_state: global_state_from_option(state),
+ granted_list: resolve_read_allowlist(&state),
+ denied_list: Default::default(),
}
- self.env
}
- pub fn revoke_run(&mut self) -> PermissionState {
- if self.run == PermissionState::Granted {
- self.run = PermissionState::Prompt;
+ pub fn new_write(
+ state: &Option<Vec<PathBuf>>,
+ ) -> UnaryPermission<WritePermission> {
+ UnaryPermission::<WritePermission> {
+ name: "write",
+ description: "write to the file system",
+ global_state: global_state_from_option(state),
+ granted_list: resolve_write_allowlist(&state),
+ denied_list: Default::default(),
}
- self.run
}
- pub fn revoke_plugin(&mut self) -> PermissionState {
- if self.plugin == PermissionState::Granted {
- self.plugin = PermissionState::Prompt;
+ pub fn new_net(
+ state: &Option<Vec<String>>,
+ ) -> UnaryPermission<NetPermission> {
+ UnaryPermission::<NetPermission> {
+ name: "net",
+ description: "network",
+ global_state: global_state_from_option(state),
+ granted_list: state
+ .as_ref()
+ .map(|v| {
+ v.iter()
+ .map(|x| NetPermission::from_string(x.clone()))
+ .collect()
+ })
+ .unwrap_or_else(HashSet::new),
+ denied_list: Default::default(),
}
- self.plugin
}
- pub fn revoke_hrtime(&mut self) -> PermissionState {
- if self.hrtime == PermissionState::Granted {
- self.hrtime = PermissionState::Prompt;
- }
- self.hrtime
+ pub fn new_env(state: bool) -> BooleanPermission {
+ boolean_permission_from_flag_bool(state, "env", "environment variables")
}
- pub fn check_read(&self, path: &Path) -> Result<(), AnyError> {
- let (resolved_path, display_path) = self.resolved_and_display_path(path);
- self.query_read(&Some(&resolved_path)).check(
- &format!("read access to \"{}\"", display_path.display()),
- "--allow-read",
- )
+ pub fn new_run(state: bool) -> BooleanPermission {
+ boolean_permission_from_flag_bool(state, "run", "run a subprocess")
}
- /// As `check_read()`, but permission error messages will anonymize the path
- /// by replacing it with the given `display`.
- pub fn check_read_blind(
- &self,
- path: &Path,
- display: &str,
- ) -> Result<(), AnyError> {
- let resolved_path = resolve_from_cwd(path).unwrap();
- self
- .query_read(&Some(&resolved_path))
- .check(&format!("read access to <{}>", display), "--allow-read")
+ pub fn new_plugin(state: bool) -> BooleanPermission {
+ boolean_permission_from_flag_bool(state, "plugin", "open a plugin")
}
- pub fn check_write(&self, path: &Path) -> Result<(), AnyError> {
- let (resolved_path, display_path) = self.resolved_and_display_path(path);
- self.query_write(&Some(&resolved_path)).check(
- &format!("write access to \"{}\"", display_path.display()),
- "--allow-write",
- )
+ pub fn new_hrtime(state: bool) -> BooleanPermission {
+ boolean_permission_from_flag_bool(state, "hrtime", "high precision time")
}
- pub fn check_net<T: AsRef<str>>(
- &self,
- host: &(T, Option<u16>),
- ) -> Result<(), AnyError> {
- self.query_net(&Some(host)).check(
- &format!("network access to \"{}\"", format_host(host)),
- "--allow-net",
- )
+ pub fn from_options(opts: &PermissionsOptions) -> Self {
+ Self {
+ read: Permissions::new_read(&opts.allow_read),
+ write: Permissions::new_write(&opts.allow_write),
+ net: Permissions::new_net(&opts.allow_net),
+ env: Permissions::new_env(opts.allow_env),
+ run: Permissions::new_run(opts.allow_run),
+ plugin: Permissions::new_plugin(opts.allow_plugin),
+ hrtime: Permissions::new_hrtime(opts.allow_hrtime),
+ }
}
- pub fn check_net_url(&self, url: &url::Url) -> Result<(), AnyError> {
- let hostname = url
- .host_str()
- .ok_or_else(|| uri_error("Missing host"))?
- .to_string();
- let display_host = match url.port() {
- None => hostname.clone(),
- Some(port) => format!("{}:{}", hostname, port),
- };
- self
- .query_net(&Some(&(hostname, url.port_or_known_default())))
- .check(
- &format!("network access to \"{}\"", display_host),
- "--allow-net",
- )
+ pub fn allow_all() -> Self {
+ Self {
+ read: Permissions::new_read(&Some(vec![])),
+ write: Permissions::new_write(&Some(vec![])),
+ net: Permissions::new_net(&Some(vec![])),
+ env: Permissions::new_env(true),
+ run: Permissions::new_run(true),
+ plugin: Permissions::new_plugin(true),
+ hrtime: Permissions::new_hrtime(true),
+ }
}
/// A helper function that determines if the module specifier is a local or
@@ -582,58 +580,101 @@ impl Permissions {
) -> Result<(), AnyError> {
match specifier.scheme() {
"file" => match specifier.to_file_path() {
- Ok(path) => self.check_read(&path),
+ Ok(path) => self.read.check(&path),
Err(_) => Err(uri_error(format!(
"Invalid file path.\n Specifier: {}",
specifier
))),
},
"data" => Ok(()),
- _ => self.check_net_url(specifier),
+ _ => self.net.check_url(specifier),
}
}
+}
- pub fn check_env(&self) -> Result<(), AnyError> {
- self
- .env
- .check("access to environment variables", "--allow-env")
+impl deno_fetch::FetchPermissions for Permissions {
+ fn check_net_url(&self, url: &url::Url) -> Result<(), AnyError> {
+ self.net.check_url(url)
}
- pub fn check_run(&self) -> Result<(), AnyError> {
- self.run.check("access to run a subprocess", "--allow-run")
+ fn check_read(&self, path: &Path) -> Result<(), AnyError> {
+ self.read.check(path)
}
+}
- pub fn check_plugin(&self, path: &Path) -> Result<(), AnyError> {
- let (_, display_path) = self.resolved_and_display_path(path);
- self.plugin.check(
- &format!("access to open a plugin: {}", display_path.display()),
- "--allow-plugin",
- )
+impl deno_websocket::WebSocketPermissions for Permissions {
+ fn check_net_url(&self, url: &url::Url) -> Result<(), AnyError> {
+ self.net.check_url(url)
}
+}
- pub fn check_hrtime(&self) -> Result<(), AnyError> {
- self
- .hrtime
- .check("access to high precision time", "--allow-hrtime")
+fn log_perm_access(message: &str) {
+ debug!(
+ "{}",
+ colors::bold(&format!("{}️ Granted {}", PERMISSION_EMOJI, message))
+ );
+}
+
+fn boolean_permission_from_flag_bool(
+ flag: bool,
+ name: &'static str,
+ description: &'static str,
+) -> BooleanPermission {
+ BooleanPermission {
+ name,
+ description,
+ state: if flag {
+ PermissionState::Granted
+ } else {
+ PermissionState::Prompt
+ },
}
}
-impl deno_fetch::FetchPermissions for Permissions {
- fn check_net_url(&self, url: &url::Url) -> Result<(), AnyError> {
- Permissions::check_net_url(self, url)
+fn global_state_from_option<T>(flag: &Option<Vec<T>>) -> PermissionState {
+ if matches!(flag, Some(v) if v.is_empty()) {
+ PermissionState::Granted
+ } else {
+ PermissionState::Prompt
}
+}
- fn check_read(&self, p: &Path) -> Result<(), AnyError> {
- Permissions::check_read(self, p)
+pub fn resolve_read_allowlist(
+ allow: &Option<Vec<PathBuf>>,
+) -> HashSet<ReadPermission> {
+ if let Some(v) = allow {
+ v.iter()
+ .map(|raw_path| {
+ ReadPermission(resolve_from_cwd(Path::new(&raw_path)).unwrap())
+ })
+ .collect()
+ } else {
+ HashSet::new()
}
}
-impl deno_websocket::WebSocketPermissions for Permissions {
- fn check_net_url(&self, url: &url::Url) -> Result<(), AnyError> {
- Permissions::check_net_url(self, url)
+pub fn resolve_write_allowlist(
+ allow: &Option<Vec<PathBuf>>,
+) -> HashSet<WritePermission> {
+ if let Some(v) = allow {
+ v.iter()
+ .map(|raw_path| {
+ WritePermission(resolve_from_cwd(Path::new(&raw_path)).unwrap())
+ })
+ .collect()
+ } else {
+ HashSet::new()
}
}
+/// Arbitrary helper. Resolves the path from CWD, and also gets a path that
+/// can be displayed without leaking the CWD when not allowed.
+fn resolved_and_display_path(path: &Path) -> (PathBuf, PathBuf) {
+ let resolved_path = resolve_from_cwd(path).unwrap();
+ let display_path = path.to_path_buf();
+ (resolved_path, display_path)
+}
+
/// 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))]
@@ -642,7 +683,7 @@ fn permission_prompt(message: &str) -> bool {
return false;
};
let msg = format!(
- "️{} {}. Grant? [g/d (g = grant, d = deny)] ",
+ "{} ️Deno requests {}. Grant? [g/d (g = grant, d = deny)] ",
PERMISSION_EMOJI, message
);
// print to stderr so that if deno is > to a file this is still displayed.
@@ -668,6 +709,13 @@ fn permission_prompt(message: &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) -> bool {
+ STUB_PROMPT_VALUE.load(Ordering::SeqCst)
+}
+
#[cfg(test)]
lazy_static! {
/// Lock this when you use `set_prompt_result` in a test case.
@@ -682,69 +730,6 @@ fn set_prompt_result(value: bool) {
STUB_PROMPT_VALUE.store(value, Ordering::SeqCst);
}
-// 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) -> bool {
- STUB_PROMPT_VALUE.load(Ordering::SeqCst)
-}
-
-fn log_perm_access(message: &str) {
- debug!(
- "{}",
- colors::bold(&format!("{}️ Granted {}", PERMISSION_EMOJI, message))
- );
-}
-
-fn check_path_allowlist(path: &Path, allowlist: &HashSet<PathBuf>) -> bool {
- for path_ in allowlist {
- if path.starts_with(path_) {
- return true;
- }
- }
- false
-}
-
-fn check_path_blocklist(path: &Path, blocklist: &HashSet<PathBuf>) -> bool {
- for path_ in blocklist {
- if path_.starts_with(path) {
- return true;
- }
- }
- false
-}
-
-fn check_host_allowlist<T: AsRef<str>>(
- host: &(T, Option<u16>),
- allowlist: &HashSet<String>,
-) -> bool {
- let (hostname, port) = host;
- allowlist.contains(hostname.as_ref())
- || (port.is_some() && allowlist.contains(&format_host(host)))
-}
-
-fn check_host_blocklist<T: AsRef<str>>(
- host: &(T, Option<u16>),
- blocklist: &HashSet<String>,
-) -> bool {
- let (hostname, port) = host;
- match port {
- None => blocklist.iter().any(|host| {
- host == hostname.as_ref()
- || host.starts_with(&format!("{}:", hostname.as_ref()))
- }),
- Some(_) => blocklist.contains(&format_host(host)),
- }
-}
-
-fn format_host<T: AsRef<str>>(host: &(T, Option<u16>)) -> String {
- let (hostname, port) = host;
- match port {
- None => hostname.as_ref().to_string(),
- Some(port) => format!("{}:{}", hostname.as_ref(), port),
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;
@@ -770,50 +755,55 @@ mod tests {
});
// Inside of /a/specific and /a/specific/dir/name
- assert!(perms.check_read(Path::new("/a/specific/dir/name")).is_ok());
- assert!(perms.check_write(Path::new("/a/specific/dir/name")).is_ok());
+ assert!(perms.read.check(Path::new("/a/specific/dir/name")).is_ok());
+ assert!(perms.write.check(Path::new("/a/specific/dir/name")).is_ok());
// Inside of /a/specific but outside of /a/specific/dir/name
- assert!(perms.check_read(Path::new("/a/specific/dir")).is_ok());
- assert!(perms.check_write(Path::new("/a/specific/dir")).is_ok());
+ assert!(perms.read.check(Path::new("/a/specific/dir")).is_ok());
+ assert!(perms.write.check(Path::new("/a/specific/dir")).is_ok());
// Inside of /a/specific and /a/specific/dir/name
assert!(perms
- .check_read(Path::new("/a/specific/dir/name/inner"))
+ .read
+ .check(Path::new("/a/specific/dir/name/inner"))
.is_ok());
assert!(perms
- .check_write(Path::new("/a/specific/dir/name/inner"))
+ .write
+ .check(Path::new("/a/specific/dir/name/inner"))
.is_ok());
// Inside of /a/specific but outside of /a/specific/dir/name
- assert!(perms.check_read(Path::new("/a/specific/other/dir")).is_ok());
+ assert!(perms.read.check(Path::new("/a/specific/other/dir")).is_ok());
assert!(perms
- .check_write(Path::new("/a/specific/other/dir"))
+ .write
+ .check(Path::new("/a/specific/other/dir"))
.is_ok());
// Exact match with /b/c
- assert!(perms.check_read(Path::new("/b/c")).is_ok());
- assert!(perms.check_write(Path::new("/b/c")).is_ok());
+ assert!(perms.read.check(Path::new("/b/c")).is_ok());
+ assert!(perms.write.check(Path::new("/b/c")).is_ok());
// Sub path within /b/c
- assert!(perms.check_read(Path::new("/b/c/sub/path")).is_ok());
- assert!(perms.check_write(Path::new("/b/c/sub/path")).is_ok());
+ assert!(perms.read.check(Path::new("/b/c/sub/path")).is_ok());
+ assert!(perms.write.check(Path::new("/b/c/sub/path")).is_ok());
// Sub path within /b/c, needs normalizing
assert!(perms
- .check_read(Path::new("/b/c/sub/path/../path/."))
+ .read
+ .check(Path::new("/b/c/sub/path/../path/."))
.is_ok());
assert!(perms
- .check_write(Path::new("/b/c/sub/path/../path/."))
+ .write
+ .check(Path::new("/b/c/sub/path/../path/."))
.is_ok());
// Inside of /b but outside of /b/c
- assert!(perms.check_read(Path::new("/b/e")).is_err());
- assert!(perms.check_write(Path::new("/b/e")).is_err());
+ assert!(perms.read.check(Path::new("/b/e")).is_err());
+ assert!(perms.write.check(Path::new("/b/e")).is_err());
// Inside of /a but outside of /a/specific
- assert!(perms.check_read(Path::new("/a/b")).is_err());
- assert!(perms.check_write(Path::new("/a/b")).is_err());
+ assert!(perms.read.check(Path::new("/a/b")).is_err());
+ assert!(perms.write.check(Path::new("/a/b")).is_err());
}
#[test]
@@ -854,7 +844,7 @@ mod tests {
];
for (host, port, is_ok) in domain_tests {
- assert_eq!(is_ok, perms.check_net(&(host, Some(port))).is_ok());
+ assert_eq!(is_ok, perms.net.check(&(host, Some(port))).is_ok());
}
}
@@ -888,7 +878,7 @@ mod tests {
];
for (host, port) in domain_tests {
- assert!(perms.check_net(&(host, Some(port))).is_ok());
+ assert!(perms.net.check(&(host, Some(port))).is_ok());
}
}
@@ -922,7 +912,7 @@ mod tests {
];
for (host, port) in domain_tests {
- assert!(!perms.check_net(&(host, Some(port))).is_ok());
+ assert!(!perms.net.check(&(host, Some(port))).is_ok());
}
}
@@ -981,7 +971,7 @@ mod tests {
for (url_str, is_ok) in url_tests {
let u = url::Url::parse(url_str).unwrap();
- assert_eq!(is_ok, perms.check_net_url(&u).is_ok());
+ assert_eq!(is_ok, perms.net.check_url(&u).is_ok());
}
}
@@ -1050,125 +1040,100 @@ mod tests {
#[test]
fn test_query() {
- let perms1 = Permissions {
+ let perms1 = Permissions::allow_all();
+ let perms2 = Permissions {
read: UnaryPermission {
- global_state: PermissionState::Granted,
- ..Default::default()
+ global_state: PermissionState::Prompt,
+ ..Permissions::new_read(&Some(vec![PathBuf::from("/foo")]))
},
write: UnaryPermission {
- global_state: PermissionState::Granted,
- ..Default::default()
+ global_state: PermissionState::Prompt,
+ ..Permissions::new_write(&Some(vec![PathBuf::from("/foo")]))
},
net: UnaryPermission {
- global_state: PermissionState::Granted,
+ global_state: PermissionState::Prompt,
+ ..Permissions::new_net(&Some(svec!["127.0.0.1:8000"]))
+ },
+ env: BooleanPermission {
+ state: PermissionState::Prompt,
..Default::default()
},
- env: PermissionState::Granted,
- run: PermissionState::Granted,
- plugin: PermissionState::Granted,
- hrtime: PermissionState::Granted,
- };
- let perms2 = Permissions {
- read: UnaryPermission {
- global_state: PermissionState::Prompt,
- granted_list: resolve_fs_allowlist(&Some(vec![PathBuf::from("/foo")])),
+ run: BooleanPermission {
+ state: PermissionState::Prompt,
..Default::default()
},
- write: UnaryPermission {
- global_state: PermissionState::Prompt,
- granted_list: resolve_fs_allowlist(&Some(vec![PathBuf::from("/foo")])),
+ plugin: BooleanPermission {
+ state: PermissionState::Prompt,
..Default::default()
},
- net: UnaryPermission {
- global_state: PermissionState::Prompt,
- granted_list: ["127.0.0.1:8000".to_string()].iter().cloned().collect(),
+ hrtime: BooleanPermission {
+ state: PermissionState::Prompt,
..Default::default()
},
- env: PermissionState::Prompt,
- run: PermissionState::Prompt,
- plugin: PermissionState::Prompt,
- hrtime: PermissionState::Prompt,
};
#[rustfmt::skip]
{
- assert_eq!(perms1.query_read(&None), PermissionState::Granted);
- assert_eq!(perms1.query_read(&Some(&Path::new("/foo"))), PermissionState::Granted);
- assert_eq!(perms2.query_read(&None), PermissionState::Prompt);
- assert_eq!(perms2.query_read(&Some(&Path::new("/foo"))), PermissionState::Granted);
- assert_eq!(perms2.query_read(&Some(&Path::new("/foo/bar"))), PermissionState::Granted);
- assert_eq!(perms1.query_write(&None), PermissionState::Granted);
- assert_eq!(perms1.query_write(&Some(&Path::new("/foo"))), PermissionState::Granted);
- assert_eq!(perms2.query_write(&None), PermissionState::Prompt);
- assert_eq!(perms2.query_write(&Some(&Path::new("/foo"))), PermissionState::Granted);
- assert_eq!(perms2.query_write(&Some(&Path::new("/foo/bar"))), PermissionState::Granted);
- assert_eq!(perms1.query_net::<&str>(&None), PermissionState::Granted);
- assert_eq!(perms1.query_net(&Some(&("127.0.0.1", None))), PermissionState::Granted);
- assert_eq!(perms2.query_net::<&str>(&None), PermissionState::Prompt);
- assert_eq!(perms2.query_net(&Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted);
- assert_eq!(perms1.query_env(), PermissionState::Granted);
- assert_eq!(perms2.query_env(), PermissionState::Prompt);
- assert_eq!(perms1.query_run(), PermissionState::Granted);
- assert_eq!(perms2.query_run(), PermissionState::Prompt);
- assert_eq!(perms1.query_plugin(), PermissionState::Granted);
- assert_eq!(perms2.query_plugin(), PermissionState::Prompt);
- assert_eq!(perms1.query_hrtime(), PermissionState::Granted);
- assert_eq!(perms2.query_hrtime(), PermissionState::Prompt);
+ assert_eq!(perms1.read.query(None), PermissionState::Granted);
+ assert_eq!(perms1.read.query(Some(&Path::new("/foo"))), PermissionState::Granted);
+ assert_eq!(perms2.read.query(None), PermissionState::Prompt);
+ assert_eq!(perms2.read.query(Some(&Path::new("/foo"))), PermissionState::Granted);
+ assert_eq!(perms2.read.query(Some(&Path::new("/foo/bar"))), PermissionState::Granted);
+ assert_eq!(perms1.write.query(None), PermissionState::Granted);
+ assert_eq!(perms1.write.query(Some(&Path::new("/foo"))), PermissionState::Granted);
+ assert_eq!(perms2.write.query(None), PermissionState::Prompt);
+ assert_eq!(perms2.write.query(Some(&Path::new("/foo"))), PermissionState::Granted);
+ assert_eq!(perms2.write.query(Some(&Path::new("/foo/bar"))), PermissionState::Granted);
+ assert_eq!(perms1.net.query::<&str>(None), PermissionState::Granted);
+ assert_eq!(perms1.net.query(Some(&("127.0.0.1", None))), PermissionState::Granted);
+ assert_eq!(perms2.net.query::<&str>(None), PermissionState::Prompt);
+ assert_eq!(perms2.net.query(Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted);
+ assert_eq!(perms1.env.query(), PermissionState::Granted);
+ assert_eq!(perms2.env.query(), PermissionState::Prompt);
+ assert_eq!(perms1.run.query(), PermissionState::Granted);
+ assert_eq!(perms2.run.query(), PermissionState::Prompt);
+ assert_eq!(perms1.plugin.query(), PermissionState::Granted);
+ assert_eq!(perms2.plugin.query(), PermissionState::Prompt);
+ assert_eq!(perms1.hrtime.query(), PermissionState::Granted);
+ assert_eq!(perms2.hrtime.query(), PermissionState::Prompt);
};
}
#[test]
fn test_request() {
- let mut perms = Permissions {
- read: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Default::default()
- },
- write: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Default::default()
- },
- net: UnaryPermission {
- global_state: PermissionState::Prompt,
- ..Default::default()
- },
- env: PermissionState::Prompt,
- run: PermissionState::Prompt,
- plugin: PermissionState::Prompt,
- hrtime: PermissionState::Prompt,
- };
+ let mut perms: Permissions = Default::default();
#[rustfmt::skip]
{
let _guard = PERMISSION_PROMPT_GUARD.lock().unwrap();
set_prompt_result(true);
- assert_eq!(perms.request_read(&Some(&Path::new("/foo"))), PermissionState::Granted);
- assert_eq!(perms.query_read(&None), PermissionState::Prompt);
+ assert_eq!(perms.read.request(Some(&Path::new("/foo"))), PermissionState::Granted);
+ assert_eq!(perms.read.query(None), PermissionState::Prompt);
set_prompt_result(false);
- assert_eq!(perms.request_read(&Some(&Path::new("/foo/bar"))), PermissionState::Granted);
+ assert_eq!(perms.read.request(Some(&Path::new("/foo/bar"))), PermissionState::Granted);
set_prompt_result(false);
- assert_eq!(perms.request_write(&Some(&Path::new("/foo"))), PermissionState::Denied);
- assert_eq!(perms.query_write(&Some(&Path::new("/foo/bar"))), PermissionState::Prompt);
+ assert_eq!(perms.write.request(Some(&Path::new("/foo"))), PermissionState::Denied);
+ assert_eq!(perms.write.query(Some(&Path::new("/foo/bar"))), PermissionState::Prompt);
set_prompt_result(true);
- assert_eq!(perms.request_write(&None), PermissionState::Denied);
+ assert_eq!(perms.write.request(None), PermissionState::Denied);
set_prompt_result(true);
- assert_eq!(perms.request_net(&Some(&("127.0.0.1", None))), PermissionState::Granted);
+ assert_eq!(perms.net.request(Some(&("127.0.0.1", None))), PermissionState::Granted);
set_prompt_result(false);
- assert_eq!(perms.request_net(&Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted);
+ assert_eq!(perms.net.request(Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted);
set_prompt_result(true);
- assert_eq!(perms.request_env(), PermissionState::Granted);
+ assert_eq!(perms.env.request(), PermissionState::Granted);
set_prompt_result(false);
- assert_eq!(perms.request_env(), PermissionState::Granted);
+ assert_eq!(perms.env.request(), PermissionState::Granted);
set_prompt_result(false);
- assert_eq!(perms.request_run(), PermissionState::Denied);
+ assert_eq!(perms.run.request(), PermissionState::Denied);
set_prompt_result(true);
- assert_eq!(perms.request_run(), PermissionState::Denied);
+ assert_eq!(perms.run.request(), PermissionState::Denied);
set_prompt_result(true);
- assert_eq!(perms.request_plugin(), PermissionState::Granted);
+ assert_eq!(perms.plugin.request(), PermissionState::Granted);
set_prompt_result(false);
- assert_eq!(perms.request_plugin(), PermissionState::Granted);
+ assert_eq!(perms.plugin.request(), PermissionState::Granted);
set_prompt_result(false);
- assert_eq!(perms.request_hrtime(), PermissionState::Denied);
+ assert_eq!(perms.hrtime.request(), PermissionState::Denied);
set_prompt_result(true);
- assert_eq!(perms.request_hrtime(), PermissionState::Denied);
+ assert_eq!(perms.hrtime.request(), PermissionState::Denied);
};
}
@@ -1177,38 +1142,47 @@ mod tests {
let mut perms = Permissions {
read: UnaryPermission {
global_state: PermissionState::Prompt,
- granted_list: resolve_fs_allowlist(&Some(vec![PathBuf::from("/foo")])),
- ..Default::default()
+ ..Permissions::new_read(&Some(vec![PathBuf::from("/foo")]))
},
write: UnaryPermission {
global_state: PermissionState::Prompt,
- granted_list: resolve_fs_allowlist(&Some(vec![PathBuf::from("/foo")])),
- ..Default::default()
+ ..Permissions::new_write(&Some(vec![PathBuf::from("/foo")]))
},
net: UnaryPermission {
global_state: PermissionState::Prompt,
- granted_list: svec!["127.0.0.1"].iter().cloned().collect(),
+ ..Permissions::new_net(&Some(svec!["127.0.0.1"]))
+ },
+ env: BooleanPermission {
+ state: PermissionState::Granted,
+ ..Default::default()
+ },
+ run: BooleanPermission {
+ state: PermissionState::Granted,
+ ..Default::default()
+ },
+ plugin: BooleanPermission {
+ state: PermissionState::Prompt,
+ ..Default::default()
+ },
+ hrtime: BooleanPermission {
+ state: PermissionState::Denied,
..Default::default()
},
- env: PermissionState::Granted,
- run: PermissionState::Granted,
- plugin: PermissionState::Prompt,
- hrtime: PermissionState::Denied,
};
#[rustfmt::skip]
{
- assert_eq!(perms.revoke_read(&Some(&Path::new("/foo/bar"))), PermissionState::Granted);
- assert_eq!(perms.revoke_read(&Some(&Path::new("/foo"))), PermissionState::Prompt);
- assert_eq!(perms.query_read(&Some(&Path::new("/foo/bar"))), PermissionState::Prompt);
- assert_eq!(perms.revoke_write(&Some(&Path::new("/foo/bar"))), PermissionState::Granted);
- assert_eq!(perms.revoke_write(&None), PermissionState::Prompt);
- assert_eq!(perms.query_write(&Some(&Path::new("/foo/bar"))), PermissionState::Prompt);
- assert_eq!(perms.revoke_net(&Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted);
- assert_eq!(perms.revoke_net(&Some(&("127.0.0.1", None))), PermissionState::Prompt);
- assert_eq!(perms.revoke_env(), PermissionState::Prompt);
- assert_eq!(perms.revoke_run(), PermissionState::Prompt);
- assert_eq!(perms.revoke_plugin(), PermissionState::Prompt);
- assert_eq!(perms.revoke_hrtime(), PermissionState::Denied);
+ assert_eq!(perms.read.revoke(Some(&Path::new("/foo/bar"))), PermissionState::Granted);
+ assert_eq!(perms.read.revoke(Some(&Path::new("/foo"))), PermissionState::Prompt);
+ assert_eq!(perms.read.query(Some(&Path::new("/foo/bar"))), PermissionState::Prompt);
+ assert_eq!(perms.write.revoke(Some(&Path::new("/foo/bar"))), PermissionState::Granted);
+ assert_eq!(perms.write.revoke(None), PermissionState::Prompt);
+ assert_eq!(perms.write.query(Some(&Path::new("/foo/bar"))), PermissionState::Prompt);
+ assert_eq!(perms.net.revoke(Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted);
+ assert_eq!(perms.net.revoke(Some(&("127.0.0.1", None))), PermissionState::Prompt);
+ assert_eq!(perms.env.revoke(), PermissionState::Prompt);
+ assert_eq!(perms.run.revoke(), PermissionState::Prompt);
+ assert_eq!(perms.plugin.revoke(), PermissionState::Prompt);
+ assert_eq!(perms.hrtime.revoke(), PermissionState::Denied);
};
}
}