mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-10-12 22:47:08 +02:00
fix(fs): do not parse relative paths with a colon as URLs (#3632)
In plugins/fs/src/file_path.rs SafeFilePath::from_str, any string that parsed as a URL with a scheme of 2+ characters was a URL, so a relative path such as `notes:2024.txt` became a cannot-be-a-base URL and failed with InvalidPathUrl. Cannot-be-a-base URLs are now parsed as paths. The scheme-agnostic url.to_file_path() stays: its result is scope checked (not a bypass). Unit test for the parser; e2e spec writes and reads such a file (not on Windows, where `:` is not allowed in file names).
This commit is contained in:
1 parent
b6f39cc51c
commit
3b19ca096c
3 files changed
+65
-1
No files matched your search
@@ -0,0 +1,6 @@
|
||||
---
|
||||
fs: patch
|
||||
fs-js: patch
|
||||
---
|
||||
|
||||
Fixed relative paths whose first component contains a colon, such as `notes:2024.txt`, being parsed as URLs and rejected with "URL is not a valid path". Only strings with a hierarchical URL form (`scheme://...` or `scheme:/...`) are treated as URLs now.
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
tauriError,
|
||||
describePlugin,
|
||||
itDesktop,
|
||||
itOn,
|
||||
scratchDir
|
||||
} from '../helpers/index.js'
|
||||
|
||||
@@ -394,6 +395,25 @@ describePlugin('fs', () => {
|
||||
expect(message).toMatch(/resource id \d+ is invalid/)
|
||||
})
|
||||
|
||||
// `:` is not allowed in Windows file names
|
||||
itOn(
|
||||
['linux', 'darwin', 'android', 'ios'],
|
||||
'relative paths that look like a URL scheme are paths',
|
||||
async () => {
|
||||
const result = await tauri(async (api) => {
|
||||
const baseDir = api.fs.BaseDirectory.AppData
|
||||
const path = 'e2e-notes:2024.txt'
|
||||
await api.fs.writeTextFile(path, 'notes', { baseDir })
|
||||
try {
|
||||
return await api.fs.readTextFile(path, { baseDir })
|
||||
} finally {
|
||||
await api.fs.remove(path, { baseDir })
|
||||
}
|
||||
})
|
||||
expect(result).toBe('notes')
|
||||
}
|
||||
)
|
||||
|
||||
it('rejects paths outside the configured scope', async () => {
|
||||
// `$HOME` itself is not in the example's fs scope (only the app dirs,
|
||||
// `$DOWNLOAD` and `$RESOURCE` are).
|
||||
|
||||
@@ -203,7 +203,9 @@ impl FromStr for SafeFilePath {
|
||||
type Err = Error;
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
if let Ok(url) = url::Url::from_str(s)
|
||||
&& url.scheme().len() != 1
|
||||
// single letter schemes are Windows drive letters, and URLs that cannot be a base
|
||||
// (no `//` nor `/` after the scheme) are relative paths such as `notes:2024.txt`
|
||||
&& url.scheme().len() != 1 && !url.cannot_be_a_base()
|
||||
{
|
||||
return Ok(Self::Url(url));
|
||||
}
|
||||
@@ -306,3 +308,39 @@ impl TryFrom<FilePath> for SafeFilePath {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::SafeFilePath;
|
||||
|
||||
#[test]
|
||||
fn safe_file_path_from_str() {
|
||||
for url in [
|
||||
"file:///C:/Users",
|
||||
"file:///home/user/file.txt",
|
||||
"content://com.android.providers/document/1",
|
||||
"asset://localhost/file.txt",
|
||||
] {
|
||||
assert!(
|
||||
matches!(SafeFilePath::from_str(url), Ok(SafeFilePath::Url(_))),
|
||||
"{url}"
|
||||
);
|
||||
}
|
||||
|
||||
for path in [
|
||||
"C:/Users",
|
||||
"C:\\Users",
|
||||
"notes:2024.txt",
|
||||
"ab:c",
|
||||
"dir/file.txt",
|
||||
"/home/user/file.txt",
|
||||
] {
|
||||
assert!(
|
||||
matches!(SafeFilePath::from_str(path), Ok(SafeFilePath::Path(_))),
|
||||
"{path}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user