fix(security): resolve all HIGH/MEDIUM/LOW audit findings
Rust fixes (HIGH): - symbols.rs: path validation (reject near-root, 50K file limit, symlink filter) - memory.rs: FTS5 query quoting (prevent operator injection), 1000 fragment cap, content length limit, transaction wrapping - budget.rs: atomic check-and-reserve via transaction, input validation, index on budget_log - export.rs: safe UTF-8 truncation via chars().take() - git_context.rs: reject paths starting with '-' (flag injection) - branch_policy.rs: action validation (block|warn only), path validation Rust fixes (MEDIUM): - export.rs: named column access (positional→named) - budget.rs: named column access, negative value guards Svelte fixes: - AccountSwitcher: 2-step confirmation before account switch - ProjectMemory: expand/collapse content, 2-step delete confirm, tags split fix - CodeIntelligence: min 2-char symbol query, CodeSymbol rename, aria-labels - BudgetManager: 10M upper bound, aria-label on input, named constants - SessionExporter: timeout cleanup on destroy, aria-live feedback - AnalyticsDashboard: SVG aria-label, removed unused import, named constant
This commit is contained in:
parent
0324f813e2
commit
738574b9f0
13 changed files with 280 additions and 91 deletions
|
|
@ -56,14 +56,32 @@ fn should_skip(name: &str) -> bool {
|
|||
SKIP_DIRS.contains(&name)
|
||||
}
|
||||
|
||||
const MAX_FILES: usize = 50_000;
|
||||
const MAX_DEPTH: usize = 20;
|
||||
|
||||
fn walk_files(dir: &Path, files: &mut Vec<PathBuf>) {
|
||||
walk_files_bounded(dir, files, 0);
|
||||
}
|
||||
|
||||
fn walk_files_bounded(dir: &Path, files: &mut Vec<PathBuf>, depth: usize) {
|
||||
if depth >= MAX_DEPTH || files.len() >= MAX_FILES {
|
||||
return;
|
||||
}
|
||||
let Ok(entries) = std::fs::read_dir(dir) else { return };
|
||||
for entry in entries.flatten() {
|
||||
if files.len() >= MAX_FILES {
|
||||
return;
|
||||
}
|
||||
let ft = entry.file_type();
|
||||
// Skip symlinks
|
||||
if ft.as_ref().map_or(false, |ft| ft.is_symlink()) {
|
||||
continue;
|
||||
}
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
if ft.as_ref().map_or(false, |ft| ft.is_dir()) {
|
||||
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
|
||||
if !should_skip(name) {
|
||||
walk_files(&path, files);
|
||||
walk_files_bounded(&path, files, depth + 1);
|
||||
}
|
||||
}
|
||||
} else if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
|
||||
|
|
@ -164,6 +182,9 @@ fn extract_ts_const_fn(line: &str) -> Option<String> {
|
|||
pub fn pro_symbols_scan(project_path: String) -> Result<ScanResult, String> {
|
||||
let start = std::time::Instant::now();
|
||||
let root = PathBuf::from(&project_path);
|
||||
if !root.is_absolute() || root.components().count() < 3 {
|
||||
return Err("Invalid project path: must be an absolute path at least 3 levels deep".into());
|
||||
}
|
||||
if !root.is_dir() {
|
||||
return Err(format!("Not a directory: {project_path}"));
|
||||
}
|
||||
|
|
@ -205,6 +226,9 @@ pub fn pro_symbols_search(project_path: String, query: String) -> Result<Vec<Sym
|
|||
#[tauri::command]
|
||||
pub fn pro_symbols_find_callers(project_path: String, symbol_name: String) -> Result<Vec<CallerRef>, String> {
|
||||
let root = PathBuf::from(&project_path);
|
||||
if !root.is_absolute() || root.components().count() < 3 {
|
||||
return Err("Invalid project path: must be an absolute path at least 3 levels deep".into());
|
||||
}
|
||||
if !root.is_dir() {
|
||||
return Err(format!("Not a directory: {project_path}"));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue