mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-11-24 04:03:52 +03:00
fix(windows): Handle root paths that cannot be canonicalized (#10838)
This commit is contained in:
parent
62b52f60a2
commit
40a45b564d
@ -77,10 +77,23 @@ fn push_pattern<P: AsRef<Path>, F: Fn(&str) -> Result<Pattern, glob::PatternErro
|
|||||||
pattern: P,
|
pattern: P,
|
||||||
f: F,
|
f: F,
|
||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
let path: PathBuf = pattern.as_ref().components().collect();
|
let mut path: PathBuf = dunce::simplified(pattern.as_ref()).components().collect();
|
||||||
|
|
||||||
|
if cfg!(windows) {
|
||||||
|
// Canonicalize disk-relative paths before inserting into the list
|
||||||
|
use std::path::{Component, Prefix};
|
||||||
|
let mut components = path.components();
|
||||||
|
if let Some(Component::Prefix(prefix)) = components.next() {
|
||||||
|
if matches!(prefix.kind(), Prefix::Disk(_) | Prefix::VerbatimDisk(_))
|
||||||
|
&& !matches!(components.next(), Some(Component::RootDir))
|
||||||
|
{
|
||||||
|
path = dunce::simplified(&path.canonicalize()?).to_path_buf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
list.insert(f(&path.to_string_lossy())?);
|
list.insert(f(&path.to_string_lossy())?);
|
||||||
|
|
||||||
let mut path = path;
|
|
||||||
let mut checked_path = None;
|
let mut checked_path = None;
|
||||||
|
|
||||||
// attempt to canonicalize parents in case we have a path like `/data/user/0/appid/**`
|
// attempt to canonicalize parents in case we have a path like `/data/user/0/appid/**`
|
||||||
@ -109,7 +122,9 @@ fn push_pattern<P: AsRef<Path>, F: Fn(&str) -> Result<Pattern, glob::PatternErro
|
|||||||
if let Some(mut p) = last {
|
if let Some(mut p) = last {
|
||||||
// remove the last component of the path
|
// remove the last component of the path
|
||||||
// so the next iteration checks its parent
|
// so the next iteration checks its parent
|
||||||
path.pop();
|
if !path.pop() {
|
||||||
|
break None;
|
||||||
|
}
|
||||||
// append the already checked path to the last component
|
// append the already checked path to the last component
|
||||||
if let Some(checked_path) = &checked_path {
|
if let Some(checked_path) = &checked_path {
|
||||||
p.push(checked_path);
|
p.push(checked_path);
|
||||||
@ -123,7 +138,7 @@ fn push_pattern<P: AsRef<Path>, F: Fn(&str) -> Result<Pattern, glob::PatternErro
|
|||||||
|
|
||||||
if let Some(p) = canonicalized {
|
if let Some(p) = canonicalized {
|
||||||
list.insert(f(&p.to_string_lossy())?);
|
list.insert(f(&p.to_string_lossy())?);
|
||||||
} else if cfg!(windows) {
|
} else if cfg!(windows) && !path.to_string_lossy().starts_with("\\\\") {
|
||||||
list.insert(f(&format!("\\\\?\\{}", path.display()))?);
|
list.insert(f(&format!("\\\\?\\{}", path.display()))?);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,11 +358,15 @@ fn escaped_pattern(p: &str) -> Result<Pattern, glob::PatternError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn escaped_pattern_with(p: &str, append: &str) -> Result<Pattern, glob::PatternError> {
|
fn escaped_pattern_with(p: &str, append: &str) -> Result<Pattern, glob::PatternError> {
|
||||||
Pattern::new(&format!(
|
if p.ends_with(MAIN_SEPARATOR) {
|
||||||
"{}{}{append}",
|
Pattern::new(&format!("{}{append}", glob::Pattern::escape(p)))
|
||||||
glob::Pattern::escape(p),
|
} else {
|
||||||
MAIN_SEPARATOR
|
Pattern::new(&format!(
|
||||||
))
|
"{}{}{append}",
|
||||||
|
glob::Pattern::escape(p),
|
||||||
|
MAIN_SEPARATOR
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -470,4 +489,104 @@ mod tests {
|
|||||||
assert!(scope.is_allowed("C:\\home\\tauri\\anyfile"));
|
assert!(scope.is_allowed("C:\\home\\tauri\\anyfile"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[test]
|
||||||
|
fn windows_root_paths() {
|
||||||
|
let scope = new_scope();
|
||||||
|
{
|
||||||
|
// UNC network path
|
||||||
|
scope.allow_directory("\\\\localhost\\c$", true).unwrap();
|
||||||
|
assert!(scope.is_allowed("\\\\localhost\\c$"));
|
||||||
|
assert!(scope.is_allowed("\\\\localhost\\c$\\Windows"));
|
||||||
|
assert!(scope.is_allowed("\\\\localhost\\c$\\NonExistentFile"));
|
||||||
|
assert!(!scope.is_allowed("\\\\localhost\\d$"));
|
||||||
|
assert!(!scope.is_allowed("\\\\OtherServer\\Share"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let scope = new_scope();
|
||||||
|
{
|
||||||
|
// Verbatim UNC network path
|
||||||
|
scope
|
||||||
|
.allow_directory("\\\\?\\UNC\\localhost\\c$", true)
|
||||||
|
.unwrap();
|
||||||
|
assert!(scope.is_allowed("\\\\localhost\\c$"));
|
||||||
|
assert!(scope.is_allowed("\\\\localhost\\c$\\Windows"));
|
||||||
|
assert!(scope.is_allowed("\\\\?\\UNC\\localhost\\c$\\Windows\\NonExistentFile"));
|
||||||
|
// A non-existent file cannot be canonicalized to a verbatim UNC path, so this will fail to match
|
||||||
|
assert!(!scope.is_allowed("\\\\localhost\\c$\\Windows\\NonExistentFile"));
|
||||||
|
assert!(!scope.is_allowed("\\\\localhost\\d$"));
|
||||||
|
assert!(!scope.is_allowed("\\\\OtherServer\\Share"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let scope = new_scope();
|
||||||
|
{
|
||||||
|
// Device namespace
|
||||||
|
scope.allow_file("\\\\.\\COM1").unwrap();
|
||||||
|
assert!(scope.is_allowed("\\\\.\\COM1"));
|
||||||
|
assert!(!scope.is_allowed("\\\\.\\COM2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let scope = new_scope();
|
||||||
|
{
|
||||||
|
// Disk root
|
||||||
|
scope.allow_directory("C:\\", true).unwrap();
|
||||||
|
assert!(scope.is_allowed("C:\\Windows"));
|
||||||
|
assert!(scope.is_allowed("C:\\Windows\\system.ini"));
|
||||||
|
assert!(scope.is_allowed("C:\\NonExistentFile"));
|
||||||
|
assert!(!scope.is_allowed("D:\\home"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let scope = new_scope();
|
||||||
|
{
|
||||||
|
// Verbatim disk root
|
||||||
|
scope.allow_directory("\\\\?\\C:\\", true).unwrap();
|
||||||
|
assert!(scope.is_allowed("C:\\Windows"));
|
||||||
|
assert!(scope.is_allowed("C:\\Windows\\system.ini"));
|
||||||
|
assert!(scope.is_allowed("C:\\NonExistentFile"));
|
||||||
|
assert!(!scope.is_allowed("D:\\home"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let scope = new_scope();
|
||||||
|
{
|
||||||
|
// Verbatim path
|
||||||
|
scope.allow_file("\\\\?\\anyfile").unwrap();
|
||||||
|
assert!(scope.is_allowed("\\\\?\\anyfile"));
|
||||||
|
assert!(!scope.is_allowed("\\\\?\\otherfile"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cwd = std::env::current_dir().unwrap();
|
||||||
|
let disk = {
|
||||||
|
let std::path::Component::Prefix(prefix) = cwd.components().next().unwrap() else {
|
||||||
|
panic!("Expected current dir to start with a prefix");
|
||||||
|
};
|
||||||
|
assert!(
|
||||||
|
matches!(prefix.kind(), std::path::Prefix::Disk(_)),
|
||||||
|
"Expected current dir to be on a disk drive"
|
||||||
|
);
|
||||||
|
prefix.as_os_str().to_string_lossy()
|
||||||
|
};
|
||||||
|
|
||||||
|
let scope = new_scope();
|
||||||
|
{
|
||||||
|
// Disk
|
||||||
|
scope.allow_directory(&*disk, true).unwrap();
|
||||||
|
assert!(scope.is_allowed(format!("{}Cargo.toml", disk)));
|
||||||
|
assert!(scope.is_allowed(cwd.join("Cargo.toml")));
|
||||||
|
assert!(!scope.is_allowed("C:\\Windows"));
|
||||||
|
assert!(!scope.is_allowed("Q:Cargo.toml"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let scope = new_scope();
|
||||||
|
{
|
||||||
|
// Verbatim disk
|
||||||
|
scope
|
||||||
|
.allow_directory(format!("\\\\?\\{}", disk), true)
|
||||||
|
.unwrap();
|
||||||
|
assert!(scope.is_allowed(format!("{}Cargo.toml", disk)));
|
||||||
|
assert!(scope.is_allowed(cwd.join("Cargo.toml")));
|
||||||
|
assert!(!scope.is_allowed("C:\\Windows"));
|
||||||
|
assert!(!scope.is_allowed("Q:Cargo.toml"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user