|
|
|
@ -15,11 +15,12 @@ use aho_corasick::AhoCorasick;
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
use tauri::{
|
|
|
|
|
plugin::{Builder, TauriPlugin},
|
|
|
|
|
FsScope, FsScopeEvent, Manager, Runtime,
|
|
|
|
|
FsScope, FsScopeEvent, GlobPattern, Manager, Runtime,
|
|
|
|
|
};
|
|
|
|
|
use tauri_plugin_fs::{FsExt, ScopeEvent as FsScopeEvent};
|
|
|
|
|
use tauri_plugin_fs::{FsExt, Scope as FsPluginScope, ScopeEvent as FsPluginScopeEvent};
|
|
|
|
|
|
|
|
|
|
use std::{
|
|
|
|
|
collections::HashSet,
|
|
|
|
|
fs::{create_dir_all, File},
|
|
|
|
|
io::Write,
|
|
|
|
|
path::Path,
|
|
|
|
@ -43,6 +44,70 @@ const PATTERNS: &[&str] = &[
|
|
|
|
|
];
|
|
|
|
|
const REPLACE_WITH: &[&str] = &[r"[", r"]", r"?", r"*", r"\?", r"\\?\", r"\\?\"];
|
|
|
|
|
|
|
|
|
|
trait ScopeExt {
|
|
|
|
|
fn allow_file_(&self, path: &Path);
|
|
|
|
|
fn allow_directory_(&self, path: &Path, recursive: bool);
|
|
|
|
|
|
|
|
|
|
fn forbid_file_(&self, path: &Path);
|
|
|
|
|
fn forbid_directory_(&self, path: &Path, recursive: bool);
|
|
|
|
|
|
|
|
|
|
fn allowed_patterns_(&self) -> HashSet<GlobPattern>;
|
|
|
|
|
fn forbidden_patterns_(&self) -> HashSet<GlobPattern>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ScopeExt for &FsPluginScope {
|
|
|
|
|
fn allow_file_(&self, path: &Path) {
|
|
|
|
|
let _ = self.allow_file(path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn allow_directory_(&self, path: &Path, recursive: bool) {
|
|
|
|
|
let _ = self.allow_directory(path, recursive);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn forbid_file_(&self, path: &Path) {
|
|
|
|
|
let _ = self.forbid_file(path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn forbid_directory_(&self, path: &Path, recursive: bool) {
|
|
|
|
|
let _ = self.forbid_directory(path, recursive);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn allowed_patterns_(&self) -> HashSet<GlobPattern> {
|
|
|
|
|
self.allowed_patterns()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn forbidden_patterns_(&self) -> HashSet<GlobPattern> {
|
|
|
|
|
self.forbidden_patterns()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "protocol-asset")]
|
|
|
|
|
impl ScopeExt for &FsScope {
|
|
|
|
|
fn allow_file_(&self, path: &Path) {
|
|
|
|
|
let _ = self.allow_file(path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn allow_directory_(&self, path: &Path, recursive: bool) {
|
|
|
|
|
let _ = self.allow_directory(path, recursive);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn forbid_file_(&self, path: &Path) {
|
|
|
|
|
let _ = self.forbid_file(path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn forbid_directory_(&self, path: &Path, recursive: bool) {
|
|
|
|
|
let _ = self.forbid_directory(path, recursive);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn allowed_patterns_(&self) -> HashSet<GlobPattern> {
|
|
|
|
|
self.allowed_patterns()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn forbidden_patterns_(&self) -> HashSet<GlobPattern> {
|
|
|
|
|
self.forbidden_patterns()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
|
enum Error {
|
|
|
|
|
#[error(transparent)]
|
|
|
|
@ -105,49 +170,49 @@ fn fix_directory(path_str: &str) -> &Path {
|
|
|
|
|
path
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn allow_path(scope: &FsScope, path: &str) {
|
|
|
|
|
fn allow_path(scope: impl ScopeExt, path: &str) {
|
|
|
|
|
let target_type = detect_scope_type(path);
|
|
|
|
|
|
|
|
|
|
match target_type {
|
|
|
|
|
TargetType::File => {
|
|
|
|
|
let _ = scope.allow_file(path);
|
|
|
|
|
scope.allow_file_(Path::new(path));
|
|
|
|
|
}
|
|
|
|
|
TargetType::Directory => {
|
|
|
|
|
// We remove the '*' at the end of it, else it will be escaped by the pattern.
|
|
|
|
|
let _ = scope.allow_directory(fix_directory(path), false);
|
|
|
|
|
scope.allow_directory_(fix_directory(path), false);
|
|
|
|
|
}
|
|
|
|
|
TargetType::RecursiveDirectory => {
|
|
|
|
|
// We remove the '**' at the end of it, else it will be escaped by the pattern.
|
|
|
|
|
let _ = scope.allow_directory(fix_directory(path), true);
|
|
|
|
|
scope.allow_directory_(fix_directory(path), true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn forbid_path(scope: &FsScope, path: &str) {
|
|
|
|
|
fn forbid_path(scope: impl ScopeExt, path: &str) {
|
|
|
|
|
let target_type = detect_scope_type(path);
|
|
|
|
|
|
|
|
|
|
match target_type {
|
|
|
|
|
TargetType::File => {
|
|
|
|
|
let _ = scope.forbid_file(path);
|
|
|
|
|
scope.forbid_file_(Path::new(path));
|
|
|
|
|
}
|
|
|
|
|
TargetType::Directory => {
|
|
|
|
|
let _ = scope.forbid_directory(fix_directory(path), false);
|
|
|
|
|
scope.forbid_directory_(fix_directory(path), false);
|
|
|
|
|
}
|
|
|
|
|
TargetType::RecursiveDirectory => {
|
|
|
|
|
let _ = scope.forbid_directory(fix_directory(path), true);
|
|
|
|
|
scope.forbid_directory_(fix_directory(path), true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn save_scopes(scope: &FsScope, app_dir: &Path, scope_state_path: &Path) {
|
|
|
|
|
fn save_scopes(scope: impl ScopeExt, app_dir: &Path, scope_state_path: &Path) {
|
|
|
|
|
let scope = Scope {
|
|
|
|
|
allowed_paths: scope
|
|
|
|
|
.allowed_patterns()
|
|
|
|
|
.allowed_patterns_()
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|p| p.to_string())
|
|
|
|
|
.collect(),
|
|
|
|
|
forbidden_patterns: scope
|
|
|
|
|
.forbidden_patterns()
|
|
|
|
|
.forbidden_patterns_()
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|p| p.to_string())
|
|
|
|
|
.collect(),
|
|
|
|
@ -166,16 +231,17 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
|
|
|
|
Builder::new("persisted-scope")
|
|
|
|
|
.setup(|app, _api| {
|
|
|
|
|
let fs_scope = app.try_fs_scope();
|
|
|
|
|
let core_scopes = app.state::<tauri::scope::Scopes>();
|
|
|
|
|
let asset_protocol_scope = app.asset_protocol_scope();
|
|
|
|
|
let app = app.clone();
|
|
|
|
|
let app_dir = app.path().app_data_dir();
|
|
|
|
|
|
|
|
|
|
if let Some(app_dir) = app_dir {
|
|
|
|
|
if let Ok(app_dir) = app_dir {
|
|
|
|
|
let fs_scope_state_path = app_dir.join(SCOPE_STATE_FILENAME);
|
|
|
|
|
#[cfg(feature = "protocol-asset")]
|
|
|
|
|
let asset_scope_state_path = app_dir.join(ASSET_SCOPE_STATE_FILENAME);
|
|
|
|
|
|
|
|
|
|
let _ = fs_scope.forbid_file(&fs_scope_state_path);
|
|
|
|
|
if let Some(fs_scope) = fs_scope {
|
|
|
|
|
let _ = fs_scope.forbid_file(&fs_scope_state_path);}
|
|
|
|
|
#[cfg(feature = "protocol-asset")]
|
|
|
|
|
let _ = asset_protocol_scope.forbid_file(&asset_scope_state_path);
|
|
|
|
|
|
|
|
|
@ -183,7 +249,8 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
|
|
|
|
// 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(PATTERNS).unwrap(/* This should be impossible to fail since we're using a small static input */);
|
|
|
|
|
|
|
|
|
|
if fs_scope_state_path.exists() {
|
|
|
|
|
if let Some(fs_scope) = fs_scope {
|
|
|
|
|
if fs_scope_state_path.exists() {
|
|
|
|
|
let scope: Scope = tauri::api::file::read_binary(&fs_scope_state_path)
|
|
|
|
|
.map_err(Error::from)
|
|
|
|
|
.and_then(|scope| bincode::deserialize(&scope).map_err(Into::into))
|
|
|
|
@ -191,16 +258,17 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
|
|
|
|
|
|
|
|
|
for allowed in &scope.allowed_paths {
|
|
|
|
|
let allowed = fix_pattern(&ac, allowed);
|
|
|
|
|
allow_path(&fs_scope, &allowed);
|
|
|
|
|
allow_path(fs_scope, &allowed);
|
|
|
|
|
}
|
|
|
|
|
for forbidden in &scope.forbidden_patterns {
|
|
|
|
|
let forbidden = fix_pattern(&ac, forbidden);
|
|
|
|
|
forbid_path(&fs_scope, &forbidden);
|
|
|
|
|
forbid_path(fs_scope, &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(&fs_scope, &app_dir, &fs_scope_state_path);
|
|
|
|
|
save_scopes(fs_scope, &app_dir, &fs_scope_state_path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "protocol-asset")]
|
|
|
|
@ -225,12 +293,15 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "protocol-asset")]
|
|
|
|
|
let app_dir_ = app_dir.clone();
|
|
|
|
|
let fs_scope_ = fs_scope.clone();
|
|
|
|
|
fs_scope.listen(move |event| {
|
|
|
|
|
if let FsScopeEvent::PathAllowed(_) = event {
|
|
|
|
|
save_scopes(&fs_scope_, &app_dir, &fs_scope_state_path);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
if let Some(fs_scope) = fs_scope {
|
|
|
|
|
let fs_scope_ = fs_scope.clone();
|
|
|
|
|
fs_scope.listen(move |event| {
|
|
|
|
|
if let FsPluginScopeEvent::PathAllowed(_) = event {
|
|
|
|
|
save_scopes(&fs_scope_, &app_dir, &fs_scope_state_path);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "protocol-asset")]
|
|
|
|
|
{
|
|
|
|
|
let asset_protocol_scope_ = asset_protocol_scope.clone();
|
|
|
|
|