Files
haex-hub-mirror/src-tauri/src/extension/filesystem/core.rs
haex 931d51a1e1 Remove unused function parameters
Removed unused parameters:
- allowed_origin from parse_extension_info_from_path in protocol.rs
- app_handle from resolve_path_pattern in filesystem/core.rs
2025-11-02 15:07:44 +01:00

186 lines
5.4 KiB
Rust

use serde::{Deserialize, Serialize};
use crate::extension::error::ExtensionError;
/// Simple filesystem permissions using path patterns with environment-style variables
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct FilesystemPermissions {
/// Read access to files and directories
/// Examples: ["$DOCUMENT/**", "$PICTURE/*.jpg", "$APPDATA/my-extension/*"]
pub read: Option<Vec<String>>,
/// Write access to files and directories (includes create/delete)
/// Examples: ["$APPDATA/my-extension/**", "$DOWNLOAD/*.pdf"]
pub write: Option<Vec<String>>,
}
impl FilesystemPermissions {
/// Helper to create common permission patterns
pub fn new() -> Self {
Self {
read: None,
write: None,
}
}
/// Add read permission for a path pattern
pub fn add_read(&mut self, pattern: &str) {
match &mut self.read {
Some(patterns) => patterns.push(pattern.to_string()),
None => self.read = Some(vec![pattern.to_string()]),
}
}
/// Add write permission for a path pattern
pub fn add_write(&mut self, pattern: &str) {
match &mut self.write {
Some(patterns) => patterns.push(pattern.to_string()),
None => self.write = Some(vec![pattern.to_string()]),
}
}
/// Helper: Add extension's own data directory permissions
pub fn extension_data(extension_id: &str) -> Self {
Self {
read: Some(vec![format!("$APPDATA/extensions/{}/**", extension_id)]),
write: Some(vec![format!("$APPDATA/extensions/{}/**", extension_id)]),
}
}
/// Helper: Add document access permissions
pub fn documents_read_only() -> Self {
Self {
read: Some(vec!["$DOCUMENT/**".to_string()]),
write: None,
}
}
/// Helper: Add picture access permissions
pub fn pictures_read_only() -> Self {
Self {
read: Some(vec!["$PICTURE/**".to_string()]),
write: None,
}
}
/// Validate all path patterns
pub fn validate(&self) -> Result<(), ExtensionError> {
if let Some(read_patterns) = &self.read {
for pattern in read_patterns {
validate_path_pattern(pattern)?;
}
}
if let Some(write_patterns) = &self.write {
for pattern in write_patterns {
validate_path_pattern(pattern)?;
}
}
Ok(())
}
}
/// Validates a filesystem path pattern
fn validate_path_pattern(pattern: &str) -> Result<(), ExtensionError> {
if pattern.is_empty() {
return Err(ExtensionError::ValidationError {
reason: "Path pattern cannot be empty".to_string(),
});
}
// Check if pattern starts with valid base directory variable
let valid_bases = [
"$APPDATA",
"$APPCACHE",
"$APPCONFIG",
"$APPLOCALDATA",
"$APPLOG",
"$AUDIO",
"$CACHE",
"$CONFIG",
"$DATA",
"$LOCALDATA",
"$DESKTOP",
"$DOCUMENT",
"$DOWNLOAD",
"$EXECUTABLE",
"$FONT",
"$HOME",
"$PICTURE",
"$PUBLIC",
"$RUNTIME",
"$TEMPLATE",
"$VIDEO",
"$RESOURCE",
"$TEMP",
];
let starts_with_valid_base = valid_bases.iter().any(|&base| {
pattern.starts_with(base)
&& (pattern.len() == base.len() || pattern.chars().nth(base.len()) == Some('/'))
});
if !starts_with_valid_base {
return Err(ExtensionError::ValidationError {
reason: format!(
"Path pattern '{}' must start with a valid base directory: {}",
pattern,
valid_bases.join(", ")
),
});
}
// Check for path traversal attempts
if pattern.contains("../") || pattern.contains("..\\") {
return Err(ExtensionError::SecurityViolation {
reason: format!("Path traversal detected in pattern: {pattern}"),
});
}
Ok(())
}
/// Resolves a path pattern to actual filesystem paths using Tauri's BaseDirectory
pub fn resolve_path_pattern(
pattern: &str,
) -> Result<(String, String), ExtensionError> {
let (base_var, relative_path) = if let Some(slash_pos) = pattern.find('/') {
(&pattern[..slash_pos], &pattern[slash_pos + 1..])
} else {
(pattern, "")
};
let base_directory = match base_var {
"$APPDATA" => "AppData",
"$APPCACHE" => "AppCache",
"$APPCONFIG" => "AppConfig",
"$APPLOCALDATA" => "AppLocalData",
"$APPLOG" => "AppLog",
"$AUDIO" => "Audio",
"$CACHE" => "Cache",
"$CONFIG" => "Config",
"$DATA" => "Data",
"$LOCALDATA" => "LocalData",
"$DESKTOP" => "Desktop",
"$DOCUMENT" => "Document",
"$DOWNLOAD" => "Download",
"$EXECUTABLE" => "Executable",
"$FONT" => "Font",
"$HOME" => "Home",
"$PICTURE" => "Picture",
"$PUBLIC" => "Public",
"$RUNTIME" => "Runtime",
"$TEMPLATE" => "Template",
"$VIDEO" => "Video",
"$RESOURCE" => "Resource",
"$TEMP" => "Temp",
_ => {
return Err(ExtensionError::ValidationError {
reason: format!("Unknown base directory variable: {base_var}"),
});
}
};
Ok((base_directory.to_string(), relative_path.to_string()))
}