mirror of
https://github.com/denoland/deno.git
synced 2025-09-26 12:19:12 +00:00
feat(runtime): Allow embedders to perform additional access checks on file open (#23208)
Embedders may have special requirements around file opening, so we add a new `check_open` permission check that is called as part of the file open process.
This commit is contained in:
parent
365e1f48f7
commit
472a370640
17 changed files with 476 additions and 121 deletions
|
@ -21,6 +21,7 @@ use fqdn::FQDN;
|
|||
use once_cell::sync::Lazy;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
|
@ -1641,6 +1642,91 @@ impl PermissionsContainer {
|
|||
self.0.lock().env.check_all()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn check_sys_all(&mut self) -> Result<(), AnyError> {
|
||||
self.0.lock().sys.check_all()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn check_ffi_all(&mut self) -> Result<(), AnyError> {
|
||||
self.0.lock().ffi.check_all()
|
||||
}
|
||||
|
||||
/// This checks to see if the allow-all flag was passed, not whether all
|
||||
/// permissions are enabled!
|
||||
#[inline(always)]
|
||||
pub fn check_was_allow_all_flag_passed(&mut self) -> Result<(), AnyError> {
|
||||
self.0.lock().all.check()
|
||||
}
|
||||
|
||||
/// Checks special file access, returning the failed permission type if
|
||||
/// not successful.
|
||||
pub fn check_special_file(
|
||||
&mut self,
|
||||
path: &Path,
|
||||
_api_name: &str,
|
||||
) -> Result<(), &'static str> {
|
||||
let error_all = |_| "all";
|
||||
|
||||
// Safe files with no major additional side-effects. While there's a small risk of someone
|
||||
// draining system entropy by just reading one of these files constantly, that's not really
|
||||
// something we worry about as they already have --allow-read to /dev.
|
||||
if cfg!(unix)
|
||||
&& (path == OsStr::new("/dev/random")
|
||||
|| path == OsStr::new("/dev/urandom")
|
||||
|| path == OsStr::new("/dev/zero")
|
||||
|| path == OsStr::new("/dev/null"))
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if cfg!(target_os = "linux") {
|
||||
if path.starts_with("/dev")
|
||||
|| path.starts_with("/proc")
|
||||
|| path.starts_with("/sys")
|
||||
{
|
||||
if path.ends_with("/environ") {
|
||||
self.check_env_all().map_err(|_| "env")?;
|
||||
} else {
|
||||
self.check_was_allow_all_flag_passed().map_err(error_all)?;
|
||||
}
|
||||
}
|
||||
if path.starts_with("/etc") {
|
||||
self.check_was_allow_all_flag_passed().map_err(error_all)?;
|
||||
}
|
||||
} else if cfg!(unix) {
|
||||
if path.starts_with("/dev") {
|
||||
self.check_was_allow_all_flag_passed().map_err(error_all)?;
|
||||
}
|
||||
if path.starts_with("/etc") {
|
||||
self.check_was_allow_all_flag_passed().map_err(error_all)?;
|
||||
}
|
||||
if path.starts_with("/private/etc") {
|
||||
self.check_was_allow_all_flag_passed().map_err(error_all)?;
|
||||
}
|
||||
} else if cfg!(target_os = "windows") {
|
||||
fn is_normalized_windows_drive_path(path: &Path) -> bool {
|
||||
let s = path.as_os_str().as_encoded_bytes();
|
||||
// \\?\X:\
|
||||
if s.len() < 7 {
|
||||
false
|
||||
} else if s.starts_with(br#"\\?\"#) {
|
||||
s[4].is_ascii_alphabetic() && s[5] == b':' && s[6] == b'\\'
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a normalized drive path, accept it
|
||||
if !is_normalized_windows_drive_path(path) {
|
||||
self.check_was_allow_all_flag_passed().map_err(error_all)?;
|
||||
}
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn check_net_url(
|
||||
&mut self,
|
||||
|
@ -2795,7 +2881,6 @@ mod tests {
|
|||
fn test_check_fail() {
|
||||
set_prompter(Box::new(TestPrompter));
|
||||
let mut perms = Permissions::none_with_prompt();
|
||||
|
||||
let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
|
||||
|
||||
prompt_value.set(false);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue