diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2024-09-16 21:39:37 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-16 21:39:37 +0100 |
commit | 62e952559f600e72d7498c9b12f906cb0b1ba150 (patch) | |
tree | 6dbcce6592973358ef4bf6341888b0bbbdb98cc5 /ext/fs/ops.rs | |
parent | e0b9c745c15720914f14996bf357d5b375e2dbd8 (diff) |
refactor(permissions): split up Descriptor into Allow, Deny, and Query (#25508)
This makes the permission system more versatile.
Diffstat (limited to 'ext/fs/ops.rs')
-rw-r--r-- | ext/fs/ops.rs | 302 |
1 files changed, 144 insertions, 158 deletions
diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs index f25cd944d..150d3b955 100644 --- a/ext/fs/ops.rs +++ b/ext/fs/ops.rs @@ -5,6 +5,7 @@ use std::io; use std::io::SeekFrom; use std::path::Path; use std::path::PathBuf; +use std::path::StripPrefixError; use std::rc::Rc; use deno_core::anyhow::bail; @@ -105,8 +106,9 @@ pub fn op_fs_chdir<P>( where P: FsPermissions + 'static, { - let d = PathBuf::from(&directory); - state.borrow_mut::<P>().check_read(&d, "Deno.chdir()")?; + let d = state + .borrow_mut::<P>() + .check_read(directory, "Deno.chdir()")?; state .borrow::<FileSystemRc>() .chdir(&d) @@ -188,11 +190,9 @@ pub fn op_fs_mkdir_sync<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - let mode = mode.unwrap_or(0o777) & 0o777; - state + let path = state .borrow_mut::<P>() .check_write(&path, "Deno.mkdirSync()")?; @@ -213,14 +213,12 @@ pub async fn op_fs_mkdir_async<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - let mode = mode.unwrap_or(0o777) & 0o777; - let fs = { + let (fs, path) = { let mut state = state.borrow_mut(); - state.borrow_mut::<P>().check_write(&path, "Deno.mkdir()")?; - state.borrow::<FileSystemRc>().clone() + let path = state.borrow_mut::<P>().check_write(&path, "Deno.mkdir()")?; + (state.borrow::<FileSystemRc>().clone(), path) }; fs.mkdir_async(path.clone(), recursive, mode) @@ -239,8 +237,7 @@ pub fn op_fs_chmod_sync<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - state + let path = state .borrow_mut::<P>() .check_write(&path, "Deno.chmodSync()")?; let fs = state.borrow::<FileSystemRc>(); @@ -257,11 +254,10 @@ pub async fn op_fs_chmod_async<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - let fs = { + let (fs, path) = { let mut state = state.borrow_mut(); - state.borrow_mut::<P>().check_write(&path, "Deno.chmod()")?; - state.borrow::<FileSystemRc>().clone() + let path = state.borrow_mut::<P>().check_write(&path, "Deno.chmod()")?; + (state.borrow::<FileSystemRc>().clone(), path) }; fs.chmod_async(path.clone(), mode) .await @@ -279,8 +275,7 @@ pub fn op_fs_chown_sync<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - state + let path = state .borrow_mut::<P>() .check_write(&path, "Deno.chownSync()")?; let fs = state.borrow::<FileSystemRc>(); @@ -299,11 +294,10 @@ pub async fn op_fs_chown_async<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - let fs = { + let (fs, path) = { let mut state = state.borrow_mut(); - state.borrow_mut::<P>().check_write(&path, "Deno.chown()")?; - state.borrow::<FileSystemRc>().clone() + let path = state.borrow_mut::<P>().check_write(&path, "Deno.chown()")?; + (state.borrow::<FileSystemRc>().clone(), path) }; fs.chown_async(path.clone(), uid, gid) .await @@ -320,11 +314,9 @@ pub fn op_fs_remove_sync<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - - state + let path = state .borrow_mut::<P>() - .check_write(&path, "Deno.removeSync()")?; + .check_write(path, "Deno.removeSync()")?; let fs = state.borrow::<FileSystemRc>(); fs.remove_sync(&path, recursive) @@ -342,21 +334,19 @@ pub async fn op_fs_remove_async<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - - let fs = { + let (fs, path) = { let mut state = state.borrow_mut(); - if recursive { + let path = if recursive { state .borrow_mut::<P>() - .check_write(&path, "Deno.remove()")?; + .check_write(&path, "Deno.remove()")? } else { state .borrow_mut::<P>() - .check_write_partial(&path, "Deno.remove()")?; - } + .check_write_partial(&path, "Deno.remove()")? + }; - state.borrow::<FileSystemRc>().clone() + (state.borrow::<FileSystemRc>().clone(), path) }; fs.remove_async(path.clone(), recursive) @@ -375,12 +365,9 @@ pub fn op_fs_copy_file_sync<P>( where P: FsPermissions + 'static, { - let from = PathBuf::from(from); - let to = PathBuf::from(to); - let permissions = state.borrow_mut::<P>(); - permissions.check_read(&from, "Deno.copyFileSync()")?; - permissions.check_write(&to, "Deno.copyFileSync()")?; + let from = permissions.check_read(from, "Deno.copyFileSync()")?; + let to = permissions.check_write(to, "Deno.copyFileSync()")?; let fs = state.borrow::<FileSystemRc>(); fs.copy_file_sync(&from, &to) @@ -398,15 +385,12 @@ pub async fn op_fs_copy_file_async<P>( where P: FsPermissions + 'static, { - let from = PathBuf::from(from); - let to = PathBuf::from(to); - - let fs = { + let (fs, from, to) = { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::<P>(); - permissions.check_read(&from, "Deno.copyFile()")?; - permissions.check_write(&to, "Deno.copyFile()")?; - state.borrow::<FileSystemRc>().clone() + let from = permissions.check_read(&from, "Deno.copyFile()")?; + let to = permissions.check_write(&to, "Deno.copyFile()")?; + (state.borrow::<FileSystemRc>().clone(), from, to) }; fs.copy_file_async(from.clone(), to.clone()) @@ -425,8 +409,7 @@ pub fn op_fs_stat_sync<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - state + let path = state .borrow_mut::<P>() .check_read(&path, "Deno.statSync()")?; let fs = state.borrow::<FileSystemRc>(); @@ -445,12 +428,11 @@ pub async fn op_fs_stat_async<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - let fs = { + let (fs, path) = { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::<P>(); - permissions.check_read(&path, "Deno.stat()")?; - state.borrow::<FileSystemRc>().clone() + let path = permissions.check_read(&path, "Deno.stat()")?; + (state.borrow::<FileSystemRc>().clone(), path) }; let stat = fs .stat_async(path.clone()) @@ -468,8 +450,7 @@ pub fn op_fs_lstat_sync<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - state + let path = state .borrow_mut::<P>() .check_read(&path, "Deno.lstatSync()")?; let fs = state.borrow::<FileSystemRc>(); @@ -488,12 +469,11 @@ pub async fn op_fs_lstat_async<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - let fs = { + let (fs, path) = { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::<P>(); - permissions.check_read(&path, "Deno.lstat()")?; - state.borrow::<FileSystemRc>().clone() + let path = permissions.check_read(&path, "Deno.lstat()")?; + (state.borrow::<FileSystemRc>().clone(), path) }; let stat = fs .lstat_async(path.clone()) @@ -511,11 +491,9 @@ pub fn op_fs_realpath_sync<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - let fs = state.borrow::<FileSystemRc>().clone(); let permissions = state.borrow_mut::<P>(); - permissions.check_read(&path, "Deno.realPathSync()")?; + let path = permissions.check_read(&path, "Deno.realPathSync()")?; if path.is_relative() { permissions.check_read_blind(&fs.cwd()?, "CWD", "Deno.realPathSync()")?; } @@ -536,18 +514,16 @@ pub async fn op_fs_realpath_async<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - - let fs; - { + let (fs, path) = { let mut state = state.borrow_mut(); - fs = state.borrow::<FileSystemRc>().clone(); + let fs = state.borrow::<FileSystemRc>().clone(); let permissions = state.borrow_mut::<P>(); - permissions.check_read(&path, "Deno.realPath()")?; + let path = permissions.check_read(&path, "Deno.realPath()")?; if path.is_relative() { permissions.check_read_blind(&fs.cwd()?, "CWD", "Deno.realPath()")?; } - } + (fs, path) + }; let resolved_path = fs .realpath_async(path.clone()) .await @@ -566,9 +542,7 @@ pub fn op_fs_read_dir_sync<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - - state + let path = state .borrow_mut::<P>() .check_read(&path, "Deno.readDirSync()")?; @@ -587,14 +561,12 @@ pub async fn op_fs_read_dir_async<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - - let fs = { + let (fs, path) = { let mut state = state.borrow_mut(); - state + let path = state .borrow_mut::<P>() .check_read(&path, "Deno.readDir()")?; - state.borrow::<FileSystemRc>().clone() + (state.borrow::<FileSystemRc>().clone(), path) }; let entries = fs @@ -614,13 +586,10 @@ pub fn op_fs_rename_sync<P>( where P: FsPermissions + 'static, { - let oldpath = PathBuf::from(oldpath); - let newpath = PathBuf::from(newpath); - let permissions = state.borrow_mut::<P>(); - permissions.check_read(&oldpath, "Deno.renameSync()")?; - permissions.check_write(&oldpath, "Deno.renameSync()")?; - permissions.check_write(&newpath, "Deno.renameSync()")?; + let _ = permissions.check_read(&oldpath, "Deno.renameSync()")?; + let oldpath = permissions.check_write(&oldpath, "Deno.renameSync()")?; + let newpath = permissions.check_write(&newpath, "Deno.renameSync()")?; let fs = state.borrow::<FileSystemRc>(); fs.rename_sync(&oldpath, &newpath) @@ -638,16 +607,13 @@ pub async fn op_fs_rename_async<P>( where P: FsPermissions + 'static, { - let oldpath = PathBuf::from(oldpath); - let newpath = PathBuf::from(newpath); - - let fs = { + let (fs, oldpath, newpath) = { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::<P>(); - permissions.check_read(&oldpath, "Deno.rename()")?; - permissions.check_write(&oldpath, "Deno.rename()")?; - permissions.check_write(&newpath, "Deno.rename()")?; - state.borrow::<FileSystemRc>().clone() + _ = permissions.check_read(&oldpath, "Deno.rename()")?; + let oldpath = permissions.check_write(&oldpath, "Deno.rename()")?; + let newpath = permissions.check_write(&newpath, "Deno.rename()")?; + (state.borrow::<FileSystemRc>().clone(), oldpath, newpath) }; fs.rename_async(oldpath.clone(), newpath.clone()) @@ -666,14 +632,11 @@ pub fn op_fs_link_sync<P>( where P: FsPermissions + 'static, { - let oldpath = PathBuf::from(oldpath); - let newpath = PathBuf::from(newpath); - let permissions = state.borrow_mut::<P>(); - permissions.check_read(&oldpath, "Deno.linkSync()")?; - permissions.check_write(&oldpath, "Deno.linkSync()")?; - permissions.check_read(&newpath, "Deno.linkSync()")?; - permissions.check_write(&newpath, "Deno.linkSync()")?; + _ = permissions.check_read(oldpath, "Deno.linkSync()")?; + let oldpath = permissions.check_write(oldpath, "Deno.linkSync()")?; + _ = permissions.check_read(newpath, "Deno.linkSync()")?; + let newpath = permissions.check_write(newpath, "Deno.linkSync()")?; let fs = state.borrow::<FileSystemRc>(); fs.link_sync(&oldpath, &newpath) @@ -691,17 +654,14 @@ pub async fn op_fs_link_async<P>( where P: FsPermissions + 'static, { - let oldpath = PathBuf::from(&oldpath); - let newpath = PathBuf::from(&newpath); - - let fs = { + let (fs, oldpath, newpath) = { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::<P>(); - permissions.check_read(&oldpath, "Deno.link()")?; - permissions.check_write(&oldpath, "Deno.link()")?; - permissions.check_read(&newpath, "Deno.link()")?; - permissions.check_write(&newpath, "Deno.link()")?; - state.borrow::<FileSystemRc>().clone() + _ = permissions.check_read(&oldpath, "Deno.link()")?; + let oldpath = permissions.check_write(&oldpath, "Deno.link()")?; + _ = permissions.check_read(&newpath, "Deno.link()")?; + let newpath = permissions.check_write(&newpath, "Deno.link()")?; + (state.borrow::<FileSystemRc>().clone(), oldpath, newpath) }; fs.link_async(oldpath.clone(), newpath.clone()) @@ -772,9 +732,7 @@ pub fn op_fs_read_link_sync<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - - state + let path = state .borrow_mut::<P>() .check_read(&path, "Deno.readLink()")?; @@ -794,14 +752,12 @@ pub async fn op_fs_read_link_async<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - - let fs = { + let (fs, path) = { let mut state = state.borrow_mut(); - state + let path = state .borrow_mut::<P>() .check_read(&path, "Deno.readLink()")?; - state.borrow::<FileSystemRc>().clone() + (state.borrow::<FileSystemRc>().clone(), path) }; let target = fs @@ -821,11 +777,9 @@ pub fn op_fs_truncate_sync<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - - state + let path = state .borrow_mut::<P>() - .check_write(&path, "Deno.truncateSync()")?; + .check_write(path, "Deno.truncateSync()")?; let fs = state.borrow::<FileSystemRc>(); fs.truncate_sync(&path, len) @@ -843,14 +797,12 @@ pub async fn op_fs_truncate_async<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - - let fs = { + let (fs, path) = { let mut state = state.borrow_mut(); - state + let path = state .borrow_mut::<P>() .check_write(&path, "Deno.truncate()")?; - state.borrow::<FileSystemRc>().clone() + (state.borrow::<FileSystemRc>().clone(), path) }; fs.truncate_async(path.clone(), len) @@ -872,9 +824,7 @@ pub fn op_fs_utime_sync<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - - state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?; + let path = state.borrow_mut::<P>().check_write(path, "Deno.utime()")?; let fs = state.borrow::<FileSystemRc>(); fs.utime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos) @@ -895,12 +845,10 @@ pub async fn op_fs_utime_async<P>( where P: FsPermissions + 'static, { - let path = PathBuf::from(path); - - let fs = { + let (fs, path) = { let mut state = state.borrow_mut(); - state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?; - state.borrow::<FileSystemRc>().clone() + let path = state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?; + (state.borrow::<FileSystemRc>().clone(), path) }; fs.utime_async( @@ -920,15 +868,18 @@ where #[string] pub fn op_fs_make_temp_dir_sync<P>( state: &mut OpState, - #[string] dir: Option<String>, + #[string] dir_arg: Option<String>, #[string] prefix: Option<String>, #[string] suffix: Option<String>, ) -> Result<String, AnyError> where P: FsPermissions + 'static, { - let (dir, fs) = - make_temp_check_sync::<P>(state, dir, "Deno.makeTempDirSync()")?; + let (dir, fs) = make_temp_check_sync::<P>( + state, + dir_arg.as_deref(), + "Deno.makeTempDirSync()", + )?; let mut rng = thread_rng(); @@ -936,7 +887,11 @@ where for _ in 0..MAX_TRIES { let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?; match fs.mkdir_sync(&path, false, 0o700) { - Ok(_) => return path_into_string(path.into_os_string()), + Ok(_) => { + // PERMISSIONS: ensure the absolute path is not leaked + let path = strip_dir_prefix(&dir, dir_arg.as_deref(), path)?; + return path_into_string(path.into_os_string()); + } Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => { continue; } @@ -955,14 +910,18 @@ where #[string] pub async fn op_fs_make_temp_dir_async<P>( state: Rc<RefCell<OpState>>, - #[string] dir: Option<String>, + #[string] dir_arg: Option<String>, #[string] prefix: Option<String>, #[string] suffix: Option<String>, ) -> Result<String, AnyError> where P: FsPermissions + 'static, { - let (dir, fs) = make_temp_check_async::<P>(state, dir, "Deno.makeTempDir()")?; + let (dir, fs) = make_temp_check_async::<P>( + state, + dir_arg.as_deref(), + "Deno.makeTempDir()", + )?; let mut rng = thread_rng(); @@ -970,7 +929,11 @@ where for _ in 0..MAX_TRIES { let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?; match fs.clone().mkdir_async(path.clone(), false, 0o700).await { - Ok(_) => return path_into_string(path.into_os_string()), + Ok(_) => { + // PERMISSIONS: ensure the absolute path is not leaked + let path = strip_dir_prefix(&dir, dir_arg.as_deref(), path)?; + return path_into_string(path.into_os_string()); + } Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => { continue; } @@ -989,15 +952,18 @@ where #[string] pub fn op_fs_make_temp_file_sync<P>( state: &mut OpState, - #[string] dir: Option<String>, + #[string] dir_arg: Option<String>, #[string] prefix: Option<String>, #[string] suffix: Option<String>, ) -> Result<String, AnyError> where P: FsPermissions + 'static, { - let (dir, fs) = - make_temp_check_sync::<P>(state, dir, "Deno.makeTempFileSync()")?; + let (dir, fs) = make_temp_check_sync::<P>( + state, + dir_arg.as_deref(), + "Deno.makeTempFileSync()", + )?; let open_opts = OpenOptions { write: true, @@ -1011,7 +977,11 @@ where for _ in 0..MAX_TRIES { let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?; match fs.open_sync(&path, open_opts, None) { - Ok(_) => return path_into_string(path.into_os_string()), + Ok(_) => { + // PERMISSIONS: ensure the absolute path is not leaked + let path = strip_dir_prefix(&dir, dir_arg.as_deref(), path)?; + return path_into_string(path.into_os_string()); + } Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => { continue; } @@ -1030,15 +1000,18 @@ where #[string] pub async fn op_fs_make_temp_file_async<P>( state: Rc<RefCell<OpState>>, - #[string] dir: Option<String>, + #[string] dir_arg: Option<String>, #[string] prefix: Option<String>, #[string] suffix: Option<String>, ) -> Result<String, AnyError> where P: FsPermissions + 'static, { - let (dir, fs) = - make_temp_check_async::<P>(state, dir, "Deno.makeTempFile()")?; + let (dir, fs) = make_temp_check_async::<P>( + state, + dir_arg.as_deref(), + "Deno.makeTempFile()", + )?; let open_opts = OpenOptions { write: true, @@ -1053,7 +1026,11 @@ where for _ in 0..MAX_TRIES { let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?; match fs.clone().open_async(path.clone(), open_opts, None).await { - Ok(_) => return path_into_string(path.into_os_string()), + Ok(_) => { + // PERMISSIONS: ensure the absolute path is not leaked + let path = strip_dir_prefix(&dir, dir_arg.as_deref(), path)?; + return path_into_string(path.into_os_string()); + } Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => { continue; } @@ -1067,9 +1044,26 @@ where .context("tmpfile") } +fn strip_dir_prefix( + resolved_dir: &Path, + dir_arg: Option<&str>, + result_path: PathBuf, +) -> Result<PathBuf, StripPrefixError> { + if resolved_dir.is_absolute() { + match &dir_arg { + Some(dir_arg) => { + Ok(Path::new(dir_arg).join(result_path.strip_prefix(resolved_dir)?)) + } + None => Ok(result_path), + } + } else { + Ok(result_path) + } +} + fn make_temp_check_sync<P>( state: &mut OpState, - dir: Option<String>, + dir: Option<&str>, api_name: &str, ) -> Result<(PathBuf, FileSystemRc), AnyError> where @@ -1077,11 +1071,7 @@ where { let fs = state.borrow::<FileSystemRc>().clone(); let dir = match dir { - Some(dir) => { - let dir = PathBuf::from(dir); - state.borrow_mut::<P>().check_write(&dir, api_name)?; - dir - } + Some(dir) => state.borrow_mut::<P>().check_write(dir, api_name)?, None => { let dir = fs.tmp_dir().context("tmpdir")?; state @@ -1095,7 +1085,7 @@ where fn make_temp_check_async<P>( state: Rc<RefCell<OpState>>, - dir: Option<String>, + dir: Option<&str>, api_name: &str, ) -> Result<(PathBuf, FileSystemRc), AnyError> where @@ -1104,11 +1094,7 @@ where let mut state = state.borrow_mut(); let fs = state.borrow::<FileSystemRc>().clone(); let dir = match dir { - Some(dir) => { - let dir = PathBuf::from(dir); - state.borrow_mut::<P>().check_write(&dir, api_name)?; - dir - } + Some(dir) => state.borrow_mut::<P>().check_write(dir, api_name)?, None => { let dir = fs.tmp_dir().context("tmpdir")?; state |