summaryrefslogtreecommitdiff
path: root/runtime/permissions.rs
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/permissions.rs
parentb3fe85163f303a1592335b23c25554dd0e39a4c4 (diff)
refactor: clean up permission handling (#9367)
Diffstat (limited to 'runtime/permissions.rs')
-rw-r--r--runtime/permissions.rs1188
1 files changed, 581 insertions, 607 deletions
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);
};
}
}