mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-08-05 18:18:37 +02:00
* fix(persisted-scope): Unescape paths before saving to disk. * fix cfg flags * dedupe code * unescape when reading to try to fix existing files * add more patterns. only fix pattern on app start. don't check if pattern is allowed already. * remove dbg log * typo * remove unused imports * clippy * fix compilation with asset-protocol feature flag enabled * update patterns * manually re-save state once * add changefile * remove dbg print
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
persisted-scope: patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Recursively unescape saved patterns before allowing/forbidding them. This effectively prevents `.persisted-scope` files from blowing up, which caused Out-Of-Memory issues, while automatically fixing existing broken files seamlessly.
|
||||||
Generated
+1
@@ -4368,6 +4368,7 @@ dependencies = [
|
|||||||
name = "tauri-plugin-persisted-scope"
|
name = "tauri-plugin-persisted-scope"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
"bincode",
|
"bincode",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ serde_json.workspace = true
|
|||||||
tauri.workspace = true
|
tauri.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
|
aho-corasick = "0.7"
|
||||||
bincode = "1"
|
bincode = "1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@@ -2,19 +2,34 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use aho_corasick::AhoCorasick;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tauri::{
|
use tauri::{
|
||||||
plugin::{Builder, TauriPlugin},
|
plugin::{Builder, TauriPlugin},
|
||||||
FsScopeEvent, Manager, Runtime,
|
AppHandle, FsScopeEvent, Manager, Runtime,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::{create_dir_all, File},
|
fs::{create_dir_all, File},
|
||||||
io::Write,
|
io::Write,
|
||||||
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SCOPE_STATE_FILENAME: &str = ".persisted-scope";
|
const SCOPE_STATE_FILENAME: &str = ".persisted-scope";
|
||||||
|
|
||||||
|
// Most of these patterns are just added to try to fix broken files in the wild.
|
||||||
|
// After a while we can hopefully reduce it to something like [r"[?]", r"[*]", r"\\?\\\?\"]
|
||||||
|
const PATTERNS: &[&str] = &[
|
||||||
|
r"[[]",
|
||||||
|
r"[]]",
|
||||||
|
r"[?]",
|
||||||
|
r"[*]",
|
||||||
|
r"\?\?",
|
||||||
|
r"\\?\\?\",
|
||||||
|
r"\\?\\\?\",
|
||||||
|
];
|
||||||
|
const REPLACE_WITH: &[&str] = &[r"[", r"]", r"?", r"*", r"\?", r"\\?\", r"\\?\"];
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
enum Error {
|
enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
@@ -33,6 +48,41 @@ struct Scope {
|
|||||||
forbidden_patterns: Vec<String>,
|
forbidden_patterns: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fix_pattern(ac: &AhoCorasick, s: &str) -> String {
|
||||||
|
let s = ac.replace_all(s, REPLACE_WITH);
|
||||||
|
|
||||||
|
if ac.find(&s).is_some() {
|
||||||
|
return fix_pattern(ac, &s);
|
||||||
|
}
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_scopes<R: Runtime>(app: &AppHandle<R>, app_dir: &Path, scope_state_path: &Path) {
|
||||||
|
let fs_scope = app.fs_scope();
|
||||||
|
|
||||||
|
let scope = Scope {
|
||||||
|
allowed_paths: fs_scope
|
||||||
|
.allowed_patterns()
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| p.to_string())
|
||||||
|
.collect(),
|
||||||
|
forbidden_patterns: fs_scope
|
||||||
|
.forbidden_patterns()
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| p.to_string())
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = create_dir_all(app_dir)
|
||||||
|
.and_then(|_| File::create(scope_state_path))
|
||||||
|
.map_err(Error::Io)
|
||||||
|
.and_then(|mut f| {
|
||||||
|
f.write_all(&bincode::serialize(&scope).map_err(Error::from)?)
|
||||||
|
.map_err(Into::into)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||||
Builder::new("persisted-scope")
|
Builder::new("persisted-scope")
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
@@ -49,49 +99,38 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
|||||||
#[cfg(feature = "protocol-asset")]
|
#[cfg(feature = "protocol-asset")]
|
||||||
let _ = asset_protocol_scope.forbid_file(&scope_state_path);
|
let _ = asset_protocol_scope.forbid_file(&scope_state_path);
|
||||||
|
|
||||||
|
// We're trying to fix broken .persisted-scope files seamlessly, so we'll be running this on the values read on the saved file.
|
||||||
|
// We will still save some semi-broken values because the scope events are quite spammy and we don't want to reduce runtime performance any further.
|
||||||
|
let ac = AhoCorasick::new_auto_configured(PATTERNS);
|
||||||
|
|
||||||
if scope_state_path.exists() {
|
if scope_state_path.exists() {
|
||||||
let scope: Scope = tauri::api::file::read_binary(&scope_state_path)
|
let scope: Scope = tauri::api::file::read_binary(&scope_state_path)
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
.and_then(|scope| bincode::deserialize(&scope).map_err(Into::into))
|
.and_then(|scope| bincode::deserialize(&scope).map_err(Into::into))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
for allowed in &scope.allowed_paths {
|
for allowed in &scope.allowed_paths {
|
||||||
// allows the path as is
|
let allowed = fix_pattern(&ac, allowed);
|
||||||
let _ = fs_scope.allow_file(allowed);
|
|
||||||
|
let _ = fs_scope.allow_file(&allowed);
|
||||||
#[cfg(feature = "protocol-asset")]
|
#[cfg(feature = "protocol-asset")]
|
||||||
let _ = asset_protocol_scope.allow_file(allowed);
|
let _ = asset_protocol_scope.allow_file(&allowed);
|
||||||
}
|
}
|
||||||
for forbidden in &scope.forbidden_patterns {
|
for forbidden in &scope.forbidden_patterns {
|
||||||
// forbid the path as is
|
let forbidden = fix_pattern(&ac, forbidden);
|
||||||
let _ = fs_scope.forbid_file(forbidden);
|
|
||||||
|
let _ = fs_scope.forbid_file(&forbidden);
|
||||||
#[cfg(feature = "protocol-asset")]
|
#[cfg(feature = "protocol-asset")]
|
||||||
let _ = asset_protocol_scope.forbid_file(forbidden);
|
let _ = asset_protocol_scope.forbid_file(&forbidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Manually save the fixed scopes to disk once.
|
||||||
|
// This is needed to fix broken .peristed-scope files in case the app doesn't update the scope itself.
|
||||||
|
save_scopes(&app, &app_dir, &scope_state_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
fs_scope.listen(move |event| {
|
fs_scope.listen(move |event| {
|
||||||
let fs_scope = app.fs_scope();
|
|
||||||
if let FsScopeEvent::PathAllowed(_) = event {
|
if let FsScopeEvent::PathAllowed(_) = event {
|
||||||
let scope = Scope {
|
save_scopes(&app, &app_dir, &scope_state_path);
|
||||||
allowed_paths: fs_scope
|
|
||||||
.allowed_patterns()
|
|
||||||
.into_iter()
|
|
||||||
.map(|p| p.to_string())
|
|
||||||
.collect(),
|
|
||||||
forbidden_patterns: fs_scope
|
|
||||||
.forbidden_patterns()
|
|
||||||
.into_iter()
|
|
||||||
.map(|p| p.to_string())
|
|
||||||
.collect(),
|
|
||||||
};
|
|
||||||
let scope_state_path = scope_state_path.clone();
|
|
||||||
|
|
||||||
let _ = create_dir_all(&app_dir)
|
|
||||||
.and_then(|_| File::create(scope_state_path))
|
|
||||||
.map_err(Error::Io)
|
|
||||||
.and_then(|mut f| {
|
|
||||||
f.write_all(&bincode::serialize(&scope).map_err(Error::from)?)
|
|
||||||
.map_err(Into::into)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user