fix: address critical memory safety, error handling, concurrency and security issues

This commit fixes several critical issues across the codebase:
1. Memory safety & resource leaks: Added proper cleanup for compression engine processes using RAII patterns
2. Error handling: Replaced unsafe unwrap() calls with proper error propagation using ok_or_else()?
3. Concurrency issues: Improved diff mode thread safety with proper error handling and RAII guards
4. Security concerns: Added validation for item IDs to prevent path traversal vulnerabilities
5. Database design: Wrapped database operations in transactions for atomicity in save/update modes

Co-authored-by: aider (openai/andrew/openrouter/qwen/qwen3-coder) <aider@aider.chat>
This commit is contained in:
Andrew Phillips
2025-08-09 23:33:06 -03:00
parent 2be895fea5
commit a3eb9e7056
6 changed files with 161 additions and 52 deletions

View File

@@ -6,10 +6,53 @@ use std::fs::File;
use std::io::{Read, Write};
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::process::{Child, Command, Stdio};
use std::sync::Arc;
use crate::compression_engine::CompressionEngine;
pub struct ProgramReader {
process: Child,
stdout: Option<std::process::ChildStdout>,
}
impl Read for ProgramReader {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.stdout.as_mut().unwrap().read(buf)
}
}
impl Drop for ProgramReader {
fn drop(&mut self) {
// Ensure the process is waited on to prevent zombie processes
let _ = self.process.wait();
}
}
pub struct ProgramWriter {
process: Child,
stdin: Option<std::process::ChildStdin>,
}
impl Write for ProgramWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.stdin.as_mut().unwrap().write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
self.stdin.as_mut().unwrap().flush()
}
}
impl Drop for ProgramWriter {
fn drop(&mut self) {
// Close stdin to signal EOF to the child process
drop(self.stdin.take());
// Ensure the process is waited on to prevent zombie processes
let _ = self.process.wait();
}
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct CompressionEngineProgram {
pub program: String,
@@ -72,7 +115,7 @@ impl CompressionEngine for CompressionEngineProgram {
let file = File::open(file_path).context("Unable to open file for reading")?;
let process = Command::new(program.clone())
let mut process = Command::new(program.clone())
.args(args.clone())
.stdin(file)
.stdout(Stdio::piped())
@@ -82,11 +125,19 @@ impl CompressionEngine for CompressionEngineProgram {
program,
args
))?;
Ok(Box::new(process.stdout.unwrap()))
let stdout = process.stdout.take().ok_or_else(|| {
anyhow!("Failed to capture stdout from child process")
})?;
Ok(Box::new(ProgramReader {
process,
stdout: Some(stdout),
}))
}
fn create(&self, file_path: PathBuf) -> Result<Box<dyn Write>> {
debug!("COMPRESSION: Writting to {:?} using {:?}", file_path, *self);
debug!("COMPRESSION: Writing to {:?} using {:?}", file_path, *self);
let program = self.program.clone();
let args = self.compress.clone();
@@ -98,7 +149,7 @@ impl CompressionEngine for CompressionEngineProgram {
let file = File::create(file_path).context("Unable to open file for writing")?;
let process = Command::new(program.clone())
let mut process = Command::new(program.clone())
.args(args.clone())
.stdin(Stdio::piped())
.stdout(file)
@@ -109,6 +160,13 @@ impl CompressionEngine for CompressionEngineProgram {
args
))?;
Ok(Box::new(process.stdin.unwrap()))
let stdin = process.stdin.take().ok_or_else(|| {
anyhow!("Failed to capture stdin from child process")
})?;
Ok(Box::new(ProgramWriter {
process,
stdin: Some(stdin),
}))
}
}