diff options
author | Yoshiya Hinosawa <stibium121@gmail.com> | 2022-09-28 21:46:50 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-28 21:46:50 +0900 |
commit | fa9e7aab6d49f241a4eb30cc0e261f8ceb64af2f (patch) | |
tree | 04f3babcb09101e9264f021ecff53f7db266a80c /runtime/permissions.rs | |
parent | b312279e58e51520a38e51cca317a09cdadd7cb4 (diff) |
feat: add --allow-sys permission flag (#16028)
Diffstat (limited to 'runtime/permissions.rs')
-rw-r--r-- | runtime/permissions.rs | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/runtime/permissions.rs b/runtime/permissions.rs index a50295910..84ff286b4 100644 --- a/runtime/permissions.rs +++ b/runtime/permissions.rs @@ -302,6 +302,9 @@ impl ToString for RunDescriptor { } #[derive(Clone, Eq, PartialEq, Hash, Debug)] +pub struct SysDescriptor(pub String); + +#[derive(Clone, Eq, PartialEq, Hash, Debug)] pub struct FfiDescriptor(pub PathBuf); impl UnaryPermission<ReadDescriptor> { @@ -928,6 +931,128 @@ impl Default for UnaryPermission<EnvDescriptor> { } } +impl UnaryPermission<SysDescriptor> { + pub fn query(&self, kind: Option<&str>) -> PermissionState { + if self.global_state == PermissionState::Denied + && match kind { + None => true, + Some(kind) => { + self.denied_list.contains(&SysDescriptor(kind.to_string())) + } + } + { + PermissionState::Denied + } else if self.global_state == PermissionState::Granted + || match kind { + None => false, + Some(kind) => { + self.granted_list.contains(&SysDescriptor(kind.to_string())) + } + } + { + PermissionState::Granted + } else { + PermissionState::Prompt + } + } + + pub fn request(&mut self, kind: Option<&str>) -> PermissionState { + let state = self.query(kind); + if state != PermissionState::Prompt { + return state; + } + if let Some(kind) = kind { + let desc = SysDescriptor(kind.to_string()); + if permission_prompt( + &format!("sys access to \"{}\"", kind), + self.name, + Some("Deno.permissions.query()"), + ) { + self.granted_list.insert(desc); + PermissionState::Granted + } else { + self.denied_list.insert(desc); + self.global_state = PermissionState::Denied; + PermissionState::Denied + } + } else { + if permission_prompt( + "sys access", + self.name, + Some("Deno.permissions.query()"), + ) { + self.global_state = PermissionState::Granted; + } else { + self.granted_list.clear(); + self.global_state = PermissionState::Denied; + } + self.global_state + } + } + + pub fn revoke(&mut self, kind: Option<&str>) -> PermissionState { + if let Some(kind) = kind { + self.granted_list.remove(&SysDescriptor(kind.to_string())); + } else { + self.granted_list.clear(); + } + if self.global_state == PermissionState::Granted { + self.global_state = PermissionState::Prompt; + } + self.query(kind) + } + + pub fn check( + &mut self, + kind: &str, + api_name: Option<&str>, + ) -> Result<(), AnyError> { + let (result, prompted) = self.query(Some(kind)).check( + self.name, + api_name, + Some(&format!("\"{}\"", kind)), + self.prompt, + ); + if prompted { + if result.is_ok() { + self.granted_list.insert(SysDescriptor(kind.to_string())); + } else { + self.denied_list.insert(SysDescriptor(kind.to_string())); + self.global_state = PermissionState::Denied; + } + } + result + } + + pub fn check_all(&mut self) -> Result<(), AnyError> { + let (result, prompted) = + self + .query(None) + .check(self.name, None, Some("all"), self.prompt); + if prompted { + if result.is_ok() { + self.global_state = PermissionState::Granted; + } else { + self.global_state = PermissionState::Denied; + } + } + result + } +} + +impl Default for UnaryPermission<SysDescriptor> { + fn default() -> Self { + UnaryPermission::<SysDescriptor> { + name: "sys", + description: "system information", + global_state: Default::default(), + granted_list: Default::default(), + denied_list: Default::default(), + prompt: false, + } + } +} + impl UnaryPermission<RunDescriptor> { pub fn query(&self, cmd: Option<&str>) -> PermissionState { if self.global_state == PermissionState::Denied @@ -1221,6 +1346,7 @@ pub struct Permissions { pub write: UnaryPermission<WriteDescriptor>, pub net: UnaryPermission<NetDescriptor>, pub env: UnaryPermission<EnvDescriptor>, + pub sys: UnaryPermission<SysDescriptor>, pub run: UnaryPermission<RunDescriptor>, pub ffi: UnaryPermission<FfiDescriptor>, pub hrtime: UnitPermission, @@ -1233,6 +1359,7 @@ impl Default for Permissions { write: Permissions::new_write(&None, false).unwrap(), net: Permissions::new_net(&None, false).unwrap(), env: Permissions::new_env(&None, false).unwrap(), + sys: Permissions::new_sys(&None, false).unwrap(), run: Permissions::new_run(&None, false).unwrap(), ffi: Permissions::new_ffi(&None, false).unwrap(), hrtime: Permissions::new_hrtime(false), @@ -1248,6 +1375,7 @@ pub struct PermissionsOptions { pub allow_ffi: Option<Vec<PathBuf>>, pub allow_read: Option<Vec<PathBuf>>, pub allow_run: Option<Vec<String>>, + pub allow_sys: Option<Vec<String>>, pub allow_write: Option<Vec<PathBuf>>, pub prompt: bool, } @@ -1321,6 +1449,31 @@ impl Permissions { }) } + pub fn new_sys( + state: &Option<Vec<String>>, + prompt: bool, + ) -> Result<UnaryPermission<SysDescriptor>, AnyError> { + Ok(UnaryPermission::<SysDescriptor> { + global_state: global_state_from_option(state), + granted_list: state.as_ref().map_or_else( + || Ok(HashSet::new()), + |v| { + v.iter() + .map(|x| { + if x.is_empty() { + Err(AnyError::msg("emtpy")) + } else { + Ok(SysDescriptor(x.to_string())) + } + }) + .collect() + }, + )?, + prompt, + ..Default::default() + }) + } + pub fn new_run( state: &Option<Vec<String>>, prompt: bool, @@ -1373,6 +1526,7 @@ impl Permissions { write: Permissions::new_write(&opts.allow_write, opts.prompt)?, net: Permissions::new_net(&opts.allow_net, opts.prompt)?, env: Permissions::new_env(&opts.allow_env, opts.prompt)?, + sys: Permissions::new_sys(&opts.allow_sys, opts.prompt)?, run: Permissions::new_run(&opts.allow_run, opts.prompt)?, ffi: Permissions::new_ffi(&opts.allow_ffi, opts.prompt)?, hrtime: Permissions::new_hrtime(opts.allow_hrtime), @@ -1385,6 +1539,7 @@ impl Permissions { write: Permissions::new_write(&Some(vec![]), false).unwrap(), net: Permissions::new_net(&Some(vec![]), false).unwrap(), env: Permissions::new_env(&Some(vec![]), false).unwrap(), + sys: Permissions::new_sys(&Some(vec![]), false).unwrap(), run: Permissions::new_run(&Some(vec![]), false).unwrap(), ffi: Permissions::new_ffi(&Some(vec![]), false).unwrap(), hrtime: Permissions::new_hrtime(true), @@ -1722,6 +1877,7 @@ pub struct ChildPermissionsArg { ffi: ChildUnaryPermissionArg, read: ChildUnaryPermissionArg, run: ChildUnaryPermissionArg, + sys: ChildUnaryPermissionArg, write: ChildUnaryPermissionArg, } @@ -1734,6 +1890,7 @@ impl ChildPermissionsArg { ffi: ChildUnaryPermissionArg::Inherit, read: ChildUnaryPermissionArg::Inherit, run: ChildUnaryPermissionArg::Inherit, + sys: ChildUnaryPermissionArg::Inherit, write: ChildUnaryPermissionArg::Inherit, } } @@ -1746,6 +1903,7 @@ impl ChildPermissionsArg { ffi: ChildUnaryPermissionArg::NotGranted, read: ChildUnaryPermissionArg::NotGranted, run: ChildUnaryPermissionArg::NotGranted, + sys: ChildUnaryPermissionArg::NotGranted, write: ChildUnaryPermissionArg::NotGranted, } } @@ -1822,6 +1980,11 @@ impl<'de> Deserialize<'de> for ChildPermissionsArg { child_permissions_arg.run = arg.map_err(|e| { de::Error::custom(format!("(deno.permissions.run) {}", e)) })?; + } else if key == "sys" { + let arg = serde_json::from_value::<ChildUnaryPermissionArg>(value); + child_permissions_arg.sys = arg.map_err(|e| { + de::Error::custom(format!("(deno.permissions.sys) {}", e)) + })?; } else if key == "write" { let arg = serde_json::from_value::<ChildUnaryPermissionArg>(value); child_permissions_arg.write = arg.map_err(|e| { @@ -1872,6 +2035,35 @@ pub fn create_child_permissions( worker_perms.env.global_state = PermissionState::Denied; } worker_perms.env.prompt = main_perms.env.prompt; + match child_permissions_arg.sys { + ChildUnaryPermissionArg::Inherit => { + worker_perms.sys = main_perms.sys.clone(); + } + ChildUnaryPermissionArg::Granted => { + if main_perms.sys.check_all().is_err() { + return Err(escalation_error()); + } + worker_perms.sys.global_state = PermissionState::Granted; + } + ChildUnaryPermissionArg::NotGranted => {} + ChildUnaryPermissionArg::GrantedList(granted_list) => { + worker_perms.sys.granted_list = + Permissions::new_sys(&Some(granted_list), false)?.granted_list; + if !worker_perms + .sys + .granted_list + .iter() + .all(|desc| main_perms.sys.check(&desc.0, None).is_ok()) + { + return Err(escalation_error()); + } + } + } + worker_perms.sys.denied_list = main_perms.sys.denied_list.clone(); + if main_perms.sys.global_state == PermissionState::Denied { + worker_perms.sys.global_state = PermissionState::Denied; + } + worker_perms.sys.prompt = main_perms.sys.prompt; match child_permissions_arg.hrtime { ChildUnitPermissionArg::Inherit => { worker_perms.hrtime = main_perms.hrtime.clone(); @@ -2608,6 +2800,10 @@ mod tests { global_state: PermissionState::Prompt, ..Permissions::new_env(&Some(svec!["HOME"]), false).unwrap() }, + sys: UnaryPermission { + global_state: PermissionState::Prompt, + ..Permissions::new_sys(&Some(svec!["hostname"]), false).unwrap() + }, run: UnaryPermission { global_state: PermissionState::Prompt, ..Permissions::new_run(&Some(svec!["deno"]), false).unwrap() @@ -2642,6 +2838,10 @@ mod tests { assert_eq!(perms1.env.query(Some("HOME")), PermissionState::Granted); assert_eq!(perms2.env.query(None), PermissionState::Prompt); assert_eq!(perms2.env.query(Some("HOME")), PermissionState::Granted); + assert_eq!(perms1.sys.query(None), PermissionState::Granted); + assert_eq!(perms1.sys.query(Some("HOME")), PermissionState::Granted); + assert_eq!(perms2.env.query(None), PermissionState::Prompt); + assert_eq!(perms2.sys.query(Some("hostname")), PermissionState::Granted); assert_eq!(perms1.run.query(None), PermissionState::Granted); assert_eq!(perms1.run.query(Some("deno")), PermissionState::Granted); assert_eq!(perms2.run.query(None), PermissionState::Prompt); @@ -2681,6 +2881,11 @@ mod tests { prompt_value.set(false); assert_eq!(perms.env.request(Some("HOME")), PermissionState::Granted); prompt_value.set(true); + assert_eq!(perms.sys.request(Some("hostname")), PermissionState::Granted); + assert_eq!(perms.sys.query(None), PermissionState::Prompt); + prompt_value.set(false); + assert_eq!(perms.sys.request(Some("hostname")), PermissionState::Granted); + prompt_value.set(true); assert_eq!(perms.run.request(Some("deno")), PermissionState::Granted); assert_eq!(perms.run.query(None), PermissionState::Prompt); prompt_value.set(false); @@ -2728,6 +2933,10 @@ mod tests { global_state: PermissionState::Prompt, ..Permissions::new_env(&Some(svec!["HOME"]), false).unwrap() }, + sys: UnaryPermission { + global_state: PermissionState::Prompt, + ..Permissions::new_sys(&Some(svec!["hostname"]), false).unwrap() + }, run: UnaryPermission { global_state: PermissionState::Prompt, ..Permissions::new_run(&Some(svec!["deno"]), false).unwrap() @@ -2754,6 +2963,7 @@ mod tests { assert_eq!(perms.net.query(Some(&("127.0.0.1", None))), PermissionState::Prompt); assert_eq!(perms.net.query(Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted); assert_eq!(perms.env.revoke(Some("HOME")), PermissionState::Prompt); + assert_eq!(perms.env.revoke(Some("hostname")), PermissionState::Prompt); assert_eq!(perms.run.revoke(Some("deno")), PermissionState::Prompt); assert_eq!(perms.ffi.revoke(Some(Path::new("deno"))), PermissionState::Prompt); assert_eq!(perms.hrtime.revoke(), PermissionState::Denied); @@ -2767,6 +2977,7 @@ mod tests { write: Permissions::new_write(&None, true).unwrap(), net: Permissions::new_net(&None, true).unwrap(), env: Permissions::new_env(&None, true).unwrap(), + sys: Permissions::new_sys(&None, true).unwrap(), run: Permissions::new_run(&None, true).unwrap(), ffi: Permissions::new_ffi(&None, true).unwrap(), hrtime: Permissions::new_hrtime(false), @@ -2807,6 +3018,12 @@ mod tests { assert!(perms.env.check("HOME").is_ok()); assert!(perms.env.check("PATH").is_err()); + prompt_value.set(true); + assert!(perms.env.check("hostname").is_ok()); + prompt_value.set(false); + assert!(perms.env.check("hostname").is_ok()); + assert!(perms.env.check("osRelease").is_err()); + assert!(perms.hrtime.check().is_err()); } @@ -2817,6 +3034,7 @@ mod tests { write: Permissions::new_write(&None, true).unwrap(), net: Permissions::new_net(&None, true).unwrap(), env: Permissions::new_env(&None, true).unwrap(), + sys: Permissions::new_sys(&None, true).unwrap(), run: Permissions::new_run(&None, true).unwrap(), ffi: Permissions::new_ffi(&None, true).unwrap(), hrtime: Permissions::new_hrtime(false), @@ -2867,6 +3085,14 @@ mod tests { assert!(perms.env.check("PATH").is_ok()); prompt_value.set(false); + assert!(perms.sys.check("hostname", None).is_err()); + prompt_value.set(true); + assert!(perms.sys.check("hostname", None).is_err()); + assert!(perms.sys.check("osRelease", None).is_ok()); + prompt_value.set(false); + assert!(perms.sys.check("osRelease", None).is_ok()); + + prompt_value.set(false); assert!(perms.hrtime.check().is_err()); prompt_value.set(true); assert!(perms.hrtime.check().is_err()); @@ -2902,6 +3128,7 @@ mod tests { ffi: ChildUnaryPermissionArg::Inherit, read: ChildUnaryPermissionArg::Inherit, run: ChildUnaryPermissionArg::Inherit, + sys: ChildUnaryPermissionArg::Inherit, write: ChildUnaryPermissionArg::Inherit, } ); @@ -2914,6 +3141,7 @@ mod tests { ffi: ChildUnaryPermissionArg::NotGranted, read: ChildUnaryPermissionArg::NotGranted, run: ChildUnaryPermissionArg::NotGranted, + sys: ChildUnaryPermissionArg::NotGranted, write: ChildUnaryPermissionArg::NotGranted, } ); @@ -2966,6 +3194,7 @@ mod tests { "ffi": true, "read": true, "run": true, + "sys": true, "write": true, })) .unwrap(), @@ -2975,6 +3204,7 @@ mod tests { ffi: ChildUnaryPermissionArg::Granted, read: ChildUnaryPermissionArg::Granted, run: ChildUnaryPermissionArg::Granted, + sys: ChildUnaryPermissionArg::Granted, write: ChildUnaryPermissionArg::Granted, ..ChildPermissionsArg::none() } @@ -2986,6 +3216,7 @@ mod tests { "ffi": false, "read": false, "run": false, + "sys": false, "write": false, })) .unwrap(), @@ -2995,6 +3226,7 @@ mod tests { ffi: ChildUnaryPermissionArg::NotGranted, read: ChildUnaryPermissionArg::NotGranted, run: ChildUnaryPermissionArg::NotGranted, + sys: ChildUnaryPermissionArg::NotGranted, write: ChildUnaryPermissionArg::NotGranted, ..ChildPermissionsArg::none() } @@ -3006,6 +3238,7 @@ mod tests { "ffi": ["foo", "file:///bar/baz"], "read": ["foo", "file:///bar/baz"], "run": ["foo", "file:///bar/baz", "./qux"], + "sys": ["hostname", "osRelease"], "write": ["foo", "file:///bar/baz"], })) .unwrap(), @@ -3025,6 +3258,10 @@ mod tests { "file:///bar/baz", "./qux" ]), + sys: ChildUnaryPermissionArg::GrantedList(svec![ + "hostname", + "osRelease" + ]), write: ChildUnaryPermissionArg::GrantedList(svec![ "foo", "file:///bar/baz" @@ -3129,6 +3366,7 @@ mod tests { fn test_handle_empty_value() { assert!(Permissions::new_read(&Some(vec![PathBuf::new()]), false).is_err()); assert!(Permissions::new_env(&Some(vec![String::new()]), false).is_err()); + assert!(Permissions::new_sys(&Some(vec![String::new()]), false).is_err()); assert!(Permissions::new_run(&Some(vec![String::new()]), false).is_err()); assert!(Permissions::new_ffi(&Some(vec![PathBuf::new()]), false).is_err()); assert!(Permissions::new_net(&Some(svec![String::new()]), false).is_err()); |