diff --git a/.changes/fix-readir-symlink-scope.md b/.changes/fix-readir-symlink-scope.md new file mode 100644 index 000000000000..ba5939047e55 --- /dev/null +++ b/.changes/fix-readir-symlink-scope.md @@ -0,0 +1,5 @@ +--- +"tauri": patch +--- + +Fix `fs.readDir` recursive option reading symlinked directories that are not allowed by the scope. diff --git a/core/tauri/src/api/dir.rs b/core/tauri/src/api/dir.rs index aa4deb27841b..e63532a57c98 100644 --- a/core/tauri/src/api/dir.rs +++ b/core/tauri/src/api/dir.rs @@ -6,7 +6,7 @@ use serde::Serialize; use std::{ - fs::{self, metadata}, + fs::{self, metadata, symlink_metadata}, path::{Path, PathBuf}, }; use tempfile::{self, tempdir}; @@ -31,8 +31,36 @@ pub fn is_dir>(path: P) -> crate::api::Result { metadata(path).map(|md| md.is_dir()).map_err(Into::into) } +fn is_symlink>(path: P) -> crate::api::Result { + // TODO: remove the different implementation once we raise tauri's MSRV to at least 1.58 + #[cfg(windows)] + let ret = symlink_metadata(path) + .map(|md| md.is_symlink()) + .map_err(Into::into); + + #[cfg(not(windows))] + let ret = symlink_metadata(path) + .map(|md| md.file_type().is_symlink()) + .map_err(Into::into); + + ret +} + /// Reads a directory. Can perform recursive operations. pub fn read_dir>(path: P, recursive: bool) -> crate::api::Result> { + read_dir_with_options(path, recursive, ReadDirOptions { scope: None }) +} + +#[derive(Clone, Copy)] +pub(crate) struct ReadDirOptions<'a> { + pub scope: Option<&'a crate::FsScope>, +} + +pub(crate) fn read_dir_with_options>( + path: P, + recursive: bool, + options: ReadDirOptions<'_>, +) -> crate::api::Result> { let mut files_and_dirs: Vec = vec![]; for entry in fs::read_dir(path)? { let path = entry?.path(); @@ -42,11 +70,16 @@ pub fn read_dir>(path: P, recursive: bool) -> crate::api::Result< files_and_dirs.push(DiskEntry { path: path.clone(), children: if flag { - Some(if recursive { - read_dir(&path_as_string, true)? - } else { - vec![] - }) + Some( + if recursive + && (!is_symlink(&path_as_string)? + || options.scope.map(|s| s.is_allowed(&path)).unwrap_or(true)) + { + read_dir_with_options(&path_as_string, true, options)? + } else { + vec![] + }, + ) } else { None }, diff --git a/core/tauri/src/api/file.rs b/core/tauri/src/api/file.rs index 0b41888856ba..46701e1ab180 100644 --- a/core/tauri/src/api/file.rs +++ b/core/tauri/src/api/file.rs @@ -74,6 +74,7 @@ pub fn read_binary>(file: P) -> crate::api::Result> { #[cfg(test)] mod test { use super::*; + #[cfg(not(windows))] use crate::api::Error; use quickcheck::{Arbitrary, Gen}; diff --git a/core/tauri/src/endpoints/file_system.rs b/core/tauri/src/endpoints/file_system.rs index c54c3cfc9494..dc6fc9881c5b 100644 --- a/core/tauri/src/endpoints/file_system.rs +++ b/core/tauri/src/endpoints/file_system.rs @@ -191,9 +191,15 @@ impl Cmd { path, dir, )?; - dir::read_dir(&resolved_path, recursive) - .with_context(|| format!("path: {}", resolved_path.display())) - .map_err(Into::into) + dir::read_dir_with_options( + &resolved_path, + recursive, + dir::ReadDirOptions { + scope: Some(&context.window.state::().fs), + }, + ) + .with_context(|| format!("path: {}", resolved_path.display())) + .map_err(Into::into) } #[module_command_handler(fs_copy_file)]