Merge pull request 'fix(#1): sanitize paths - block traversal, resolve to absolute' (#17) from fix/issue-1 into master
This commit is contained in:
commit
2481ed644b
89
src/main.rs
89
src/main.rs
@ -2,7 +2,56 @@ use clap::Parser;
|
||||
use libraw_sys::*;
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::c_int;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Resolve and sanitize a user-provided path.
|
||||
/// - Resolves to absolute path via canonicalize (or parent canonicalization for output)
|
||||
/// - Rejects paths containing `..` components after resolution
|
||||
/// - Returns human-readable error on failure
|
||||
fn sanitize_path(
|
||||
path_str: &str,
|
||||
must_exist: bool,
|
||||
) -> Result<PathBuf, Box<dyn std::error::Error>> {
|
||||
let path = Path::new(path_str);
|
||||
|
||||
// Block explicit traversal sequences in user input
|
||||
for component in path.components() {
|
||||
if component.as_os_str() == ".." {
|
||||
return Err(format!(
|
||||
"Path traversal ('..') is not allowed in: {}",
|
||||
path_str
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
if must_exist {
|
||||
let resolved = path.canonicalize().map_err(|e| {
|
||||
format!("Failed to resolve input path '{}': {}", path_str, e)
|
||||
})?;
|
||||
Ok(resolved)
|
||||
} else {
|
||||
// For output paths that may not exist yet, resolve the parent directory
|
||||
if let Some(parent) = path.parent() {
|
||||
let resolved_parent = parent.canonicalize().map_err(|e| {
|
||||
format!(
|
||||
"Output directory '{}' does not exist or is not accessible: {}",
|
||||
parent.display(),
|
||||
e
|
||||
)
|
||||
})?;
|
||||
let filename = path
|
||||
.file_name()
|
||||
.unwrap_or(path.as_os_str())
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
Ok(resolved_parent.join(filename))
|
||||
} else {
|
||||
let resolved = path.canonicalize().unwrap_or_else(|_| path.to_path_buf());
|
||||
Ok(resolved)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Photo Editor - A Rust-based photo editor with RAW image processing capabilities using libraw
|
||||
#[derive(Parser, Debug)]
|
||||
@ -80,11 +129,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Temperature: {}", args.temperature);
|
||||
println!("Tint: {}", args.tint);
|
||||
|
||||
// Check if input file exists
|
||||
if !Path::new(&args.input).exists() {
|
||||
eprintln!("Error: Input file '{}' does not exist.", args.input);
|
||||
return Err("Input file not found".into());
|
||||
}
|
||||
// Sanitize and resolve input path
|
||||
let input_path = sanitize_path(&args.input, true).map_err(|e| {
|
||||
eprintln!("Error: {}", e);
|
||||
e
|
||||
})?;
|
||||
|
||||
// Sanitize and resolve output path
|
||||
let output_path = sanitize_path(&args.output, false).map_err(|e| {
|
||||
eprintln!("Error: {}", e);
|
||||
e
|
||||
})?;
|
||||
|
||||
println!("\n[INFO] Attempting RAW image processing with libraw integration...");
|
||||
|
||||
@ -96,7 +151,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
|
||||
// Open the RAW file
|
||||
let input_cstr = CString::new(args.input.clone()).unwrap();
|
||||
let input_cstr = CString::new(input_path.to_string_lossy().as_bytes())
|
||||
.map_err(|_| "Input path contains null bytes, cannot process".into())?;
|
||||
let ret = unsafe { libraw_open_file(lr, input_cstr.as_ptr()) };
|
||||
|
||||
if ret != LIBRAW_SUCCESS {
|
||||
@ -133,24 +189,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("[INFO] Image processed successfully");
|
||||
|
||||
// Determine output format from extension
|
||||
let ext = Path::new(&args.output)
|
||||
let ext = output_path
|
||||
.extension()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("jpg")
|
||||
.to_lowercase();
|
||||
|
||||
let ret = match ext.as_str() {
|
||||
"jpg" | "jpeg" => {
|
||||
// For JPEG output, we'll write directly to file
|
||||
let output_cstr = CString::new(args.output.clone()).unwrap();
|
||||
unsafe { libraw_dcraw_ppm_tiff_writer(lr, output_cstr.as_ptr()) }
|
||||
}
|
||||
_ => {
|
||||
// For other formats, attempt to convert
|
||||
let output_cstr = CString::new(args.output.clone()).unwrap();
|
||||
unsafe { libraw_dcraw_ppm_tiff_writer(lr, output_cstr.as_ptr()) }
|
||||
}
|
||||
};
|
||||
let output_cstr = CString::new(output_path.to_string_lossy().as_bytes())
|
||||
.map_err(|_| "Output path contains null bytes, cannot process".into())?;
|
||||
let ret = unsafe { libraw_dcraw_ppm_tiff_writer(lr, output_cstr.as_ptr()) };
|
||||
|
||||
if ret != LIBRAW_SUCCESS {
|
||||
eprintln!("Error: Failed to write output file. Error code: {}", ret);
|
||||
@ -168,7 +215,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("\n[SUCCESS] Photo editing completed successfully with libraw integration!");
|
||||
println!(
|
||||
"[NOTICE] The edited image has been saved as: {}",
|
||||
args.output
|
||||
output_path.display()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user