Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix(core): escape glob characters in drop/dialogs , closes #5234 (#5237)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
  • Loading branch information
lucasfernog committed Nov 8, 2022
1 parent e7af22c commit bcd9dc7
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changes/escape-pattern.md
@@ -0,0 +1,5 @@
---
"tauri": "patch"
---

Escape glob special characters in files/directories when dropping files or using the open/save dialogs.
108 changes: 91 additions & 17 deletions core/tauri/src/scope/fs.rs
Expand Up @@ -5,7 +5,7 @@
use std::{
collections::{HashMap, HashSet},
fmt,
path::{Path, PathBuf},
path::{Path, PathBuf, MAIN_SEPARATOR},
sync::{Arc, Mutex},
};

Expand Down Expand Up @@ -64,15 +64,19 @@ impl fmt::Debug for Scope {
}
}

fn push_pattern<P: AsRef<Path>>(list: &mut HashSet<Pattern>, pattern: P) -> crate::Result<()> {
fn push_pattern<P: AsRef<Path>, F: Fn(&str) -> Result<Pattern, glob::PatternError>>(
list: &mut HashSet<Pattern>,
pattern: P,
f: F,
) -> crate::Result<()> {
let path: PathBuf = pattern.as_ref().components().collect();
list.insert(Pattern::new(&path.to_string_lossy())?);
list.insert(f(&path.to_string_lossy())?);
#[cfg(windows)]
{
if let Ok(p) = std::fs::canonicalize(&path) {
list.insert(Pattern::new(&p.to_string_lossy())?);
list.insert(f(&p.to_string_lossy())?);
} else {
list.insert(Pattern::new(&format!("\\\\?\\{}", path.display()))?);
list.insert(f(&format!("\\\\?\\{}", path.display()))?);
}
}
Ok(())
Expand All @@ -89,15 +93,15 @@ impl Scope {
let mut alllowed_patterns = HashSet::new();
for path in scope.allowed_paths() {
if let Ok(path) = parse_path(config, package_info, env, path) {
push_pattern(&mut alllowed_patterns, path)?;
push_pattern(&mut alllowed_patterns, path, Pattern::new)?;
}
}

let mut forbidden_patterns = HashSet::new();
if let Some(forbidden_paths) = scope.forbidden_paths() {
for path in forbidden_paths {
if let Ok(path) = parse_path(config, package_info, env, path) {
push_pattern(&mut forbidden_patterns, path)?;
push_pattern(&mut forbidden_patterns, path, Pattern::new)?;
}
}
}
Expand Down Expand Up @@ -139,16 +143,18 @@ impl Scope {
/// After this function has been called, the frontend will be able to use the Tauri API to read
/// the directory and all of its files and subdirectories.
pub fn allow_directory<P: AsRef<Path>>(&self, path: P, recursive: bool) -> crate::Result<()> {
let path = path.as_ref().to_path_buf();
let path = path.as_ref();
{
let mut list = self.alllowed_patterns.lock().unwrap();

// allow the directory to be read
push_pattern(&mut list, &path)?;
push_pattern(&mut list, &path, escaped_pattern)?;
// allow its files and subdirectories to be read
push_pattern(&mut list, path.join(if recursive { "**" } else { "*" }))?;
push_pattern(&mut list, &path, |p| {
escaped_pattern_with(p, if recursive { "**" } else { "*" })
})?;
}
self.trigger(Event::PathAllowed(path));
self.trigger(Event::PathAllowed(path.to_path_buf()));
Ok(())
}

Expand All @@ -157,7 +163,11 @@ impl Scope {
/// After this function has been called, the frontend will be able to use the Tauri API to read the contents of this file.
pub fn allow_file<P: AsRef<Path>>(&self, path: P) -> crate::Result<()> {
let path = path.as_ref();
push_pattern(&mut self.alllowed_patterns.lock().unwrap(), &path)?;
push_pattern(
&mut self.alllowed_patterns.lock().unwrap(),
&path,
escaped_pattern,
)?;
self.trigger(Event::PathAllowed(path.to_path_buf()));
Ok(())
}
Expand All @@ -166,16 +176,18 @@ impl Scope {
///
/// **Note:** this takes precedence over allowed paths, so its access gets denied **always**.
pub fn forbid_directory<P: AsRef<Path>>(&self, path: P, recursive: bool) -> crate::Result<()> {
let path = path.as_ref().to_path_buf();
let path = path.as_ref();
{
let mut list = self.forbidden_patterns.lock().unwrap();

// allow the directory to be read
push_pattern(&mut list, &path)?;
push_pattern(&mut list, &path, escaped_pattern)?;
// allow its files and subdirectories to be read
push_pattern(&mut list, path.join(if recursive { "**" } else { "*" }))?;
push_pattern(&mut list, &path, |p| {
escaped_pattern_with(p, if recursive { "**" } else { "*" })
})?;
}
self.trigger(Event::PathForbidden(path));
self.trigger(Event::PathForbidden(path.to_path_buf()));
Ok(())
}

Expand All @@ -184,7 +196,11 @@ impl Scope {
/// **Note:** this takes precedence over allowed paths, so its access gets denied **always**.
pub fn forbid_file<P: AsRef<Path>>(&self, path: P) -> crate::Result<()> {
let path = path.as_ref();
push_pattern(&mut self.forbidden_patterns.lock().unwrap(), &path)?;
push_pattern(
&mut self.forbidden_patterns.lock().unwrap(),
&path,
escaped_pattern,
)?;
self.trigger(Event::PathForbidden(path.to_path_buf()));
Ok(())
}
Expand Down Expand Up @@ -224,3 +240,61 @@ impl Scope {
}
}
}

fn escaped_pattern(p: &str) -> Result<Pattern, glob::PatternError> {
Pattern::new(&glob::Pattern::escape(p))
}

fn escaped_pattern_with(p: &str, append: &str) -> Result<Pattern, glob::PatternError> {
Pattern::new(&format!(
"{}{}{}",
glob::Pattern::escape(p),
MAIN_SEPARATOR,
append
))
}

#[cfg(test)]
mod tests {
use super::Scope;

fn new_scope() -> Scope {
Scope {
allowed_patterns: Default::default(),
forbidden_patterns: Default::default(),
event_listeners: Default::default(),
}
}

#[test]
fn path_is_escaped() {
let scope = new_scope();
scope.allow_directory("/home/tauri/**", false).unwrap();
assert!(scope.is_allowed("/home/tauri/**"));
assert!(scope.is_allowed("/home/tauri/**/file"));
assert!(!scope.is_allowed("/home/tauri/anyfile"));

let scope = new_scope();
scope.allow_file("/home/tauri/**").unwrap();
assert!(scope.is_allowed("/home/tauri/**"));
assert!(!scope.is_allowed("/home/tauri/**/file"));
assert!(!scope.is_allowed("/home/tauri/anyfile"));

let scope = new_scope();
scope.allow_directory("/home/tauri", true).unwrap();
scope.forbid_directory("/home/tauri/**", false).unwrap();
assert!(!scope.is_allowed("/home/tauri/**"));
assert!(!scope.is_allowed("/home/tauri/**/file"));
assert!(!scope.is_allowed("/home/tauri/**/inner/file"));
assert!(scope.is_allowed("/home/tauri/inner/folder/anyfile"));
assert!(scope.is_allowed("/home/tauri/anyfile"));

let scope = new_scope();
scope.allow_directory("/home/tauri", true).unwrap();
scope.forbid_file("/home/tauri/**").unwrap();
assert!(!scope.is_allowed("/home/tauri/**"));
assert!(scope.is_allowed("/home/tauri/**/file"));
assert!(scope.is_allowed("/home/tauri/**/inner/file"));
assert!(scope.is_allowed("/home/tauri/anyfile"));
}
}

0 comments on commit bcd9dc7

Please sign in to comment.