just breaking stuff, don't mind me

pull/2070/head
FabianLars 8 months ago
parent f323eb8f56
commit 0f2e4089fa
No known key found for this signature in database

@ -1,5 +1,6 @@
--- ---
fs: patch fs: minor
persisted-scope: minor
--- ---
Paths given to the `Scope` methods are now correctly escaped, preventing issues with paths containing `[]`. **Breaking Change:** Replaced the custom `tauri_plugin_fs::Scope` struct with `tauri::fs::Scope`.

@ -143,7 +143,7 @@ pub(crate) async fn open<R: Runtime>(
for folder in folders { for folder in folders {
if let Ok(path) = folder.clone().into_path() { if let Ok(path) = folder.clone().into_path() {
if let Some(s) = window.try_fs_scope() { if let Some(s) = window.try_fs_scope() {
s.allow_directory(&path, options.recursive); let _ = s.allow_directory(&path, options.recursive);
} }
tauri_scope.allow_directory(&path, options.directory)?; tauri_scope.allow_directory(&path, options.directory)?;
} }
@ -157,7 +157,7 @@ pub(crate) async fn open<R: Runtime>(
if let Some(folder) = &folder { if let Some(folder) = &folder {
if let Ok(path) = folder.clone().into_path() { if let Ok(path) = folder.clone().into_path() {
if let Some(s) = window.try_fs_scope() { if let Some(s) = window.try_fs_scope() {
s.allow_directory(&path, options.recursive); let _ = s.allow_directory(&path, options.recursive);
} }
tauri_scope.allow_directory(&path, options.directory)?; tauri_scope.allow_directory(&path, options.directory)?;
} }
@ -175,7 +175,7 @@ pub(crate) async fn open<R: Runtime>(
for file in files { for file in files {
if let Ok(path) = file.clone().into_path() { if let Ok(path) = file.clone().into_path() {
if let Some(s) = window.try_fs_scope() { if let Some(s) = window.try_fs_scope() {
s.allow_file(&path); let _ = s.allow_file(&path);
} }
tauri_scope.allow_file(&path)?; tauri_scope.allow_file(&path)?;
@ -190,7 +190,7 @@ pub(crate) async fn open<R: Runtime>(
if let Some(file) = &file { if let Some(file) = &file {
if let Ok(path) = file.clone().into_path() { if let Ok(path) = file.clone().into_path() {
if let Some(s) = window.try_fs_scope() { if let Some(s) = window.try_fs_scope() {
s.allow_file(&path); let _ = s.allow_file(&path);
} }
tauri_scope.allow_file(&path)?; tauri_scope.allow_file(&path)?;
} }
@ -232,7 +232,7 @@ pub(crate) async fn save<R: Runtime>(
if let Some(p) = &path { if let Some(p) = &path {
if let Ok(path) = p.clone().into_path() { if let Ok(path) = p.clone().into_path() {
if let Some(s) = window.try_fs_scope() { if let Some(s) = window.try_fs_scope() {
s.allow_file(&path); let _ = s.allow_file(&path);
} }
tauri_scope.allow_file(&path)?; tauri_scope.allow_file(&path)?;
} }

@ -7,7 +7,7 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
#[path = "src/entryraw.rs"] #[path = "src/scope.rs"]
#[allow(dead_code)] #[allow(dead_code)]
mod scope; mod scope;

@ -22,7 +22,7 @@ use std::{
time::{SystemTime, UNIX_EPOCH}, time::{SystemTime, UNIX_EPOCH},
}; };
use crate::{scope::Entry, Error, FsExt, SafeFilePath}; use crate::{scope::Entry, Error, SafeFilePath};
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum CommandError { pub enum CommandError {
@ -999,6 +999,8 @@ pub fn resolve_path<R: Runtime>(
path path
}; };
let fs_scope = webview.state::<crate::Scope>();
let scope = tauri::scope::fs::Scope::new( let scope = tauri::scope::fs::Scope::new(
webview, webview,
&FsScope::Scope { &FsScope::Scope {
@ -1014,31 +1016,19 @@ pub fn resolve_path<R: Runtime>(
.filter_map(|e| e.path.clone()) .filter_map(|e| e.path.clone())
.chain(command_scope.denies().iter().filter_map(|e| e.path.clone())) .chain(command_scope.denies().iter().filter_map(|e| e.path.clone()))
.collect(), .collect(),
require_literal_leading_dot: webview.fs_scope().require_literal_leading_dot, require_literal_leading_dot: fs_scope.require_literal_leading_dot,
}, },
)?; )?;
let fs_scope = webview.fs_scope();
let require_literal_leading_dot = fs_scope.require_literal_leading_dot.unwrap_or(cfg!(unix)); let require_literal_leading_dot = fs_scope.require_literal_leading_dot.unwrap_or(cfg!(unix));
if fs_scope if is_forbidden(&fs_scope.scope, &path, require_literal_leading_dot)
.scope
.as_ref()
.map(|s| is_forbidden(s, &path, require_literal_leading_dot))
.unwrap_or(false)
|| is_forbidden(&scope, &path, require_literal_leading_dot) || is_forbidden(&scope, &path, require_literal_leading_dot)
{ {
return Err(CommandError::Plugin(Error::PathForbidden(path))); return Err(CommandError::Plugin(Error::PathForbidden(path)));
} }
if fs_scope if fs_scope.scope.is_allowed(&path) || scope.is_allowed(&path) {
.scope
.as_ref()
.map(|s| s.is_allowed(&path))
.unwrap_or(false)
|| scope.is_allowed(&path)
{
Ok(path) Ok(path)
} else { } else {
Err(CommandError::Plugin(Error::PathForbidden(path))) Err(CommandError::Plugin(Error::PathForbidden(path)))

@ -1,14 +0,0 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::path::PathBuf;
use serde::Deserialize;
#[derive(Deserialize)]
#[serde(untagged)]
pub(crate) enum EntryRaw {
Value(PathBuf),
Object { path: PathBuf },
}

@ -23,7 +23,6 @@ mod commands;
mod config; mod config;
#[cfg(not(target_os = "android"))] #[cfg(not(target_os = "android"))]
mod desktop; mod desktop;
mod entryraw;
mod error; mod error;
mod file_path; mod file_path;
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
@ -40,7 +39,6 @@ pub use desktop::Fs;
pub use mobile::Fs; pub use mobile::Fs;
pub use error::Error; pub use error::Error;
pub use scope::{Event as ScopeEvent, Scope};
pub use file_path::FilePath; pub use file_path::FilePath;
pub use file_path::SafeFilePath; pub use file_path::SafeFilePath;
@ -353,8 +351,8 @@ impl ScopeObject for scope::Entry {
raw: Value, raw: Value,
) -> std::result::Result<Self, Self::Error> { ) -> std::result::Result<Self, Self::Error> {
let path = serde_json::from_value(raw.into()).map(|raw| match raw { let path = serde_json::from_value(raw.into()).map(|raw| match raw {
entryraw::EntryRaw::Value(path) => path, scope::EntryRaw::Value(path) => path,
entryraw::EntryRaw::Object { path } => path, scope::EntryRaw::Object { path } => path,
})?; })?;
match app.path().parse(path) { match app.path().parse(path) {
@ -366,21 +364,26 @@ impl ScopeObject for scope::Entry {
} }
} }
pub(crate) struct Scope {
pub(crate) scope: tauri::fs::Scope,
pub(crate) require_literal_leading_dot: Option<bool>,
}
pub trait FsExt<R: Runtime> { pub trait FsExt<R: Runtime> {
fn fs_scope(&self) -> &Scope; fn fs_scope(&self) -> tauri::fs::Scope;
fn try_fs_scope(&self) -> Option<&Scope>; fn try_fs_scope(&self) -> Option<tauri::fs::Scope>;
/// Cross platform file system APIs that also support manipulating Android files. /// Cross platform file system APIs that also support manipulating Android files.
fn fs(&self) -> &Fs<R>; fn fs(&self) -> &Fs<R>;
} }
impl<R: Runtime, T: Manager<R>> FsExt<R> for T { impl<R: Runtime, T: Manager<R>> FsExt<R> for T {
fn fs_scope(&self) -> &Scope { fn fs_scope(&self) -> tauri::fs::Scope {
self.state::<Scope>().inner() self.state::<Scope>().scope.clone()
} }
fn try_fs_scope(&self) -> Option<&Scope> { fn try_fs_scope(&self) -> Option<tauri::fs::Scope> {
self.try_state::<Scope>().map(|s| s.inner()) self.try_state::<Scope>().map(|s| s.scope.clone())
} }
fn fs(&self) -> &Fs<R> { fn fs(&self) -> &Fs<R> {
@ -425,7 +428,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R, Option<config::Config>> {
.config() .config()
.as_ref() .as_ref()
.and_then(|c| c.require_literal_leading_dot), .and_then(|c| c.require_literal_leading_dot),
scope: Some(tauri::fs::Scope::new(app, &FsScope::default())?), scope: tauri::fs::Scope::new(app, &FsScope::default())?,
}; };
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
@ -449,9 +452,9 @@ pub fn init<R: Runtime>() -> TauriPlugin<R, Option<config::Config>> {
let scope = app.fs_scope(); let scope = app.fs_scope();
for path in paths { for path in paths {
if path.is_file() { if path.is_file() {
scope.allow_file(path); let _ = scope.allow_file(path);
} else { } else {
scope.allow_directory(path, true); let _ = scope.allow_directory(path, true);
} }
} }
} }

@ -2,126 +2,18 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::{ use std::path::PathBuf;
collections::HashSet,
path::{Path, PathBuf}, use serde::Deserialize;
};
#[derive(Debug)] #[derive(Debug)]
pub struct Entry { pub struct Entry {
pub path: Option<PathBuf>, pub path: Option<PathBuf>,
} }
pub type EventId = u32; #[derive(Deserialize)]
#[serde(untagged)]
/// Scope change event. pub(crate) enum EntryRaw {
#[derive(Debug, Clone)] Value(PathBuf),
pub enum Event { Object { path: PathBuf },
/// A path has been allowed.
PathAllowed(PathBuf),
/// A path has been forbidden.
PathForbidden(PathBuf),
}
#[derive(Default)]
pub struct Scope {
// TODO: Remove Option in v2, just used to keep Default
pub(crate) scope: Option<tauri::fs::Scope>,
pub(crate) require_literal_leading_dot: Option<bool>,
}
impl Scope {
/// Extend the allowed patterns with the given directory.
///
/// 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. If `recursive` is `true`, subdirectories will be accessible too.
// TODO: Return Result
pub fn allow_directory<P: AsRef<Path>>(&self, path: P, recursive: bool) {
if let Some(scope) = &self.scope {
let _ = scope.allow_directory(path, recursive);
}
}
/// Extend the allowed patterns with the given file path.
///
/// After this function has been called, the frontend will be able to use the Tauri API to read the contents of this file.
// TODO: Return Result
pub fn allow_file<P: AsRef<Path>>(&self, path: P) {
if let Some(scope) = &self.scope {
let _ = scope.allow_file(path);
}
}
/// Set the given directory path to be forbidden by this scope.
///
/// **Note:** this takes precedence over allowed paths, so its access gets denied **always**.
// TODO: Return Result
pub fn forbid_directory<P: AsRef<Path>>(&self, path: P, recursive: bool) {
if let Some(scope) = &self.scope {
let _ = scope.forbid_directory(path, recursive);
}
}
/// Set the given file path to be forbidden by this scope.
///
/// **Note:** this takes precedence over allowed paths, so its access gets denied **always**.
// TODO: Return Result
pub fn forbid_file<P: AsRef<Path>>(&self, path: P) {
if let Some(scope) = &self.scope {
let _ = scope.forbid_file(path);
}
}
/// List of allowed paths.
#[deprecated(since = "2.1.0", note = "use `allowed_patterns` instead")]
pub fn allowed(&self) -> Vec<PathBuf> {
self.scope
.as_ref()
.map(|s| s.allowed_patterns().clone())
.unwrap_or_default()
.iter()
.map(|p| PathBuf::from(p.as_str()))
.collect()
}
/// List of allowed patterns. Note that this does not include paths defined in capabilites.
pub fn allowed_patterns(&self) -> HashSet<tauri::fs::Pattern> {
self.scope
.as_ref()
.map(|s| s.allowed_patterns().clone())
.unwrap_or_default()
}
/// List of forbidden paths.
#[deprecated(since = "2.1.0", note = "use `forbidden_patterns` instead")]
pub fn forbidden(&self) -> Vec<PathBuf> {
self.scope
.as_ref()
.map(|s| s.forbidden_patterns().clone())
.unwrap_or_default()
.iter()
.map(|p| PathBuf::from(p.as_str()))
.collect()
}
/// List of forbidden patterns. Note that this does not include paths defined in capabilites.
pub fn forbidden_patterns(&self) -> HashSet<tauri::fs::Pattern> {
self.scope
.as_ref()
.map(|s| s.forbidden_patterns())
.unwrap_or_default()
}
/// Listen to an event on this scope.
/// Silently fails and returns `0` until v3 if `Scope` was constructed manually instead of getting it via `app.fs_scope()`.
pub fn listen<F: Fn(&Event) + Send + 'static>(&self, f: F) -> EventId {
if let Some(scope) = &self.scope {
scope.listen(move |e| match e {
tauri::fs::Event::PathAllowed(p) => f(&Event::PathAllowed(p.to_owned())),
tauri::fs::Event::PathForbidden(p) => f(&Event::PathForbidden(p.to_owned())),
})
} else {
0
}
}
} }

@ -14,13 +14,11 @@ use serde::{Deserialize, Serialize};
use tauri::{ use tauri::{
plugin::{Builder, TauriPlugin}, plugin::{Builder, TauriPlugin},
scope::fs::Pattern as GlobPattern,
Manager, Runtime, Manager, Runtime,
}; };
use tauri_plugin_fs::FsExt; use tauri_plugin_fs::FsExt;
use std::{ use std::{
collections::HashSet,
fs::{create_dir_all, File}, fs::{create_dir_all, File},
io::Write, io::Write,
path::Path, path::Path,
@ -44,81 +42,6 @@ const PATTERNS: &[&str] = &[
]; ];
const REPLACE_WITH: &[&str] = &[r"[", r"]", r"?", r"*", r"\?", r"\\?\", r"\\?\"]; const REPLACE_WITH: &[&str] = &[r"[", r"]", r"?", r"*", r"\?", r"\\?\", r"\\?\"];
trait ScopeExt {
type Pattern: ToString;
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<Self::Pattern>;
fn forbidden_patterns(&self) -> HashSet<Self::Pattern>;
}
impl ScopeExt for tauri::scope::fs::Scope {
type Pattern = GlobPattern;
fn allow_file(&self, path: &Path) {
let _ = tauri::scope::fs::Scope::allow_file(self, path);
}
fn allow_directory(&self, path: &Path, recursive: bool) {
let _ = tauri::scope::fs::Scope::allow_directory(self, path, recursive);
}
fn forbid_file(&self, path: &Path) {
let _ = tauri::scope::fs::Scope::forbid_file(self, path);
}
fn forbid_directory(&self, path: &Path, recursive: bool) {
let _ = tauri::scope::fs::Scope::forbid_directory(self, path, recursive);
}
fn allowed_patterns(&self) -> HashSet<Self::Pattern> {
tauri::scope::fs::Scope::allowed_patterns(self)
}
fn forbidden_patterns(&self) -> HashSet<Self::Pattern> {
tauri::scope::fs::Scope::forbidden_patterns(self)
}
}
impl ScopeExt for tauri_plugin_fs::Scope {
type Pattern = String;
fn allow_file(&self, path: &Path) {
tauri_plugin_fs::Scope::allow_file(self, path);
}
fn allow_directory(&self, path: &Path, recursive: bool) {
tauri_plugin_fs::Scope::allow_directory(self, path, recursive);
}
fn forbid_file(&self, path: &Path) {
tauri_plugin_fs::Scope::forbid_file(self, path);
}
fn forbid_directory(&self, path: &Path, recursive: bool) {
tauri_plugin_fs::Scope::forbid_directory(self, path, recursive);
}
fn allowed_patterns(&self) -> HashSet<Self::Pattern> {
self.allowed()
.into_iter()
.map(|p| p.to_string_lossy().to_string())
.collect()
}
fn forbidden_patterns(&self) -> HashSet<Self::Pattern> {
self.forbidden()
.into_iter()
.map(|p| p.to_string_lossy().to_string())
.collect()
}
}
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
enum Error { enum Error {
#[error(transparent)] #[error(transparent)]
@ -179,41 +102,41 @@ fn fix_directory(path_str: &str) -> &Path {
path path
} }
fn allow_path(scope: &impl ScopeExt, path: &str) { fn allow_path(scope: &tauri::fs::Scope, path: &str) {
let target_type = detect_scope_type(path); let target_type = detect_scope_type(path);
match target_type { match target_type {
TargetType::File => { TargetType::File => {
scope.allow_file(Path::new(path)); let _ = scope.allow_file(Path::new(path));
} }
TargetType::Directory => { TargetType::Directory => {
// We remove the '*' at the end of it, else it will be escaped by the pattern. // We remove the '*' at the end of it, else it will be escaped by the pattern.
scope.allow_directory(fix_directory(path), false); let _ = scope.allow_directory(fix_directory(path), false);
} }
TargetType::RecursiveDirectory => { TargetType::RecursiveDirectory => {
// We remove the '**' at the end of it, else it will be escaped by the pattern. // We remove the '**' at the end of it, else it will be escaped by the pattern.
scope.allow_directory(fix_directory(path), true); let _ = scope.allow_directory(fix_directory(path), true);
} }
} }
} }
fn forbid_path(scope: &impl ScopeExt, path: &str) { fn forbid_path(scope: &tauri::fs::Scope, path: &str) {
let target_type = detect_scope_type(path); let target_type = detect_scope_type(path);
match target_type { match target_type {
TargetType::File => { TargetType::File => {
scope.forbid_file(Path::new(path)); let _ = scope.forbid_file(Path::new(path));
} }
TargetType::Directory => { TargetType::Directory => {
scope.forbid_directory(fix_directory(path), false); let _ = scope.forbid_directory(fix_directory(path), false);
} }
TargetType::RecursiveDirectory => { TargetType::RecursiveDirectory => {
scope.forbid_directory(fix_directory(path), true); let _ = scope.forbid_directory(fix_directory(path), true);
} }
} }
} }
fn save_scopes(scope: &impl ScopeExt, app_dir: &Path, scope_state_path: &Path) { fn save_scopes(scope: &tauri::fs::Scope, app_dir: &Path, scope_state_path: &Path) {
let scope = Scope { let scope = Scope {
allowed_paths: scope allowed_paths: scope
.allowed_patterns() .allowed_patterns()
@ -250,8 +173,11 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
#[cfg(feature = "protocol-asset")] #[cfg(feature = "protocol-asset")]
let asset_scope_state_path = app_dir.join(ASSET_SCOPE_STATE_FILENAME); let asset_scope_state_path = app_dir.join(ASSET_SCOPE_STATE_FILENAME);
if let Some(fs_scope) = fs_scope { if let Some(fs_scope) = &fs_scope {
fs_scope.forbid_file(&fs_scope_state_path); let _ = fs_scope.forbid_file(&fs_scope_state_path);
} else {
#[cfg(debug_assertions)]
eprintln!("Please make sure to register the `fs` plugin before the `persisted-scope` plugin!");
} }
#[cfg(feature = "protocol-asset")] #[cfg(feature = "protocol-asset")]
let _ = asset_protocol_scope.forbid_file(&asset_scope_state_path); let _ = asset_protocol_scope.forbid_file(&asset_scope_state_path);
@ -260,7 +186,7 @@ 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. // 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 */); let ac = AhoCorasick::new(PATTERNS).unwrap(/* This should be impossible to fail since we're using a small static input */);
if let Some(fs_scope) = fs_scope { if let Some(fs_scope) = &fs_scope {
if fs_scope_state_path.exists() { if fs_scope_state_path.exists() {
let scope: Scope = std::fs::read(&fs_scope_state_path) let scope: Scope = std::fs::read(&fs_scope_state_path)
.map_err(Error::from) .map_err(Error::from)
@ -269,7 +195,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
for allowed in &scope.allowed_paths { for allowed in &scope.allowed_paths {
let allowed = fix_pattern(&ac, allowed); let allowed = fix_pattern(&ac, allowed);
allow_path(fs_scope, &allowed); allow_path(fs_scope, &allowed);
} }
for forbidden in &scope.forbidden_patterns { for forbidden in &scope.forbidden_patterns {
let forbidden = fix_pattern(&ac, forbidden); let forbidden = fix_pattern(&ac, forbidden);
@ -305,11 +231,11 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
#[cfg(feature = "protocol-asset")] #[cfg(feature = "protocol-asset")]
let app_dir_ = app_dir.clone(); let app_dir_ = app_dir.clone();
if let Some(fs_scope) = fs_scope { if let Some(fs_scope) = &fs_scope {
let app_ = app.clone(); let app_ = app.clone();
fs_scope.listen(move |event| { fs_scope.listen(move |event| {
if let tauri_plugin_fs::ScopeEvent::PathAllowed(_) = event { if let tauri::fs::Event::PathAllowed(_) = event {
save_scopes(app_.fs_scope(), &app_dir, &fs_scope_state_path); save_scopes(&app_.fs_scope(), &app_dir, &fs_scope_state_path);
} }
}); });
} }

Loading…
Cancel
Save