mirror of
https://github.com/bybrooklyn/alchemist.git
synced 2026-04-18 09:53:33 -04:00
feat: comprehensive codebase improvements from audit
Architecture: - Split server.rs (4,727 LOC) into 11 focused modules - Add typed EventChannels (jobs/config/system) with appropriate capacities - Add database query timeouts (5s on critical queries) - Add graceful shutdown with signal handling API: - Add API versioning (/api/v1/) with backwards-compatible aliases - Add X-Request-Id header for request tracing - Create OpenAPI spec (docs/openapi.yaml) Security: - Add security headers middleware (CSP, X-Frame-Options, etc.) - Add HSTS header (config-gated via https_only setting) - Add config file permission check on Unix - Fix path traversal vulnerability in file browser - Add symlink detection in file browser Frontend: - Handle SSE lagged events with toast notification - Clean up banned CSS patterns in components - Add warning toast variant Testing & Docs: - Add FFmpeg integration tests with fixtures - Expand documentation site (9 new pages) - Pin MSRV to 1.85 in Cargo.toml Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -106,6 +106,15 @@ pub async fn preview(request: FsPreviewRequest) -> Result<FsPreviewResponse> {
|
||||
|
||||
fn browse_blocking(path: &Path) -> Result<FsBrowseResponse> {
|
||||
let path = canonical_or_original(path)?;
|
||||
|
||||
// Check if the resolved path is now in a sensitive location
|
||||
// (handles symlinks pointing to sensitive directories)
|
||||
if is_sensitive_path(&path) {
|
||||
return Err(AlchemistError::Watch(
|
||||
"Access to this directory is restricted".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let readable = path.is_dir();
|
||||
let mut warnings = directory_warnings(&path, readable);
|
||||
if !readable {
|
||||
@@ -121,17 +130,31 @@ fn browse_blocking(path: &Path) -> Result<FsBrowseResponse> {
|
||||
if !entry_path.is_dir() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Check for symlinks and warn about them
|
||||
let is_symlink = entry_path
|
||||
.symlink_metadata()
|
||||
.map(|m| m.file_type().is_symlink())
|
||||
.unwrap_or(false);
|
||||
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
let hidden = is_hidden(&name, &entry_path);
|
||||
let readable = std::fs::read_dir(&entry_path).is_ok();
|
||||
let media_hint = classify_media_hint(&entry_path);
|
||||
|
||||
let warning = if is_symlink {
|
||||
Some("This is a symbolic link".to_string())
|
||||
} else {
|
||||
entry_warning(&entry_path, readable)
|
||||
};
|
||||
|
||||
Some(FsDirEntry {
|
||||
name,
|
||||
path: entry_path.to_string_lossy().to_string(),
|
||||
readable,
|
||||
hidden,
|
||||
media_hint,
|
||||
warning: entry_warning(&entry_path, readable),
|
||||
warning,
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
@@ -282,11 +305,76 @@ fn preview_blocking(request: FsPreviewRequest) -> Result<FsPreviewResponse> {
|
||||
|
||||
fn resolve_browse_path(path: Option<&str>) -> Result<PathBuf> {
|
||||
match path.map(str::trim).filter(|value| !value.is_empty()) {
|
||||
Some(value) => Ok(PathBuf::from(value)),
|
||||
Some(value) => {
|
||||
let path = PathBuf::from(value);
|
||||
|
||||
// Normalize and resolve the path
|
||||
let resolved = if path.exists() {
|
||||
std::fs::canonicalize(&path).map_err(AlchemistError::Io)?
|
||||
} else {
|
||||
path
|
||||
};
|
||||
|
||||
// Block sensitive system directories
|
||||
if is_sensitive_path(&resolved) {
|
||||
return Err(AlchemistError::Watch(
|
||||
"Access to this directory is restricted".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(resolved)
|
||||
}
|
||||
None => default_browse_root(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a path is a sensitive system directory that shouldn't be browsed.
|
||||
fn is_sensitive_path(path: &Path) -> bool {
|
||||
let path_str = path.to_string_lossy().to_lowercase();
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
// Block sensitive Unix system directories
|
||||
let sensitive_prefixes = [
|
||||
"/etc",
|
||||
"/var/log",
|
||||
"/var/run",
|
||||
"/proc",
|
||||
"/sys",
|
||||
"/dev",
|
||||
"/boot",
|
||||
"/root",
|
||||
"/private/etc", // macOS
|
||||
"/private/var/log",
|
||||
];
|
||||
|
||||
for prefix in sensitive_prefixes {
|
||||
if path_str == prefix || path_str.starts_with(&format!("{}/", prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
// Block sensitive Windows system directories
|
||||
let sensitive_patterns = [
|
||||
"\\windows\\system32",
|
||||
"\\windows\\syswow64",
|
||||
"\\windows\\winsxs",
|
||||
"\\programdata\\microsoft",
|
||||
];
|
||||
|
||||
for pattern in sensitive_patterns {
|
||||
if path_str.contains(pattern) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn default_browse_root() -> Result<PathBuf> {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user