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 libraw_sys::*;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::os::raw::c_int;
|
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
|
/// Photo Editor - A Rust-based photo editor with RAW image processing capabilities using libraw
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
@ -80,11 +129,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
println!("Temperature: {}", args.temperature);
|
println!("Temperature: {}", args.temperature);
|
||||||
println!("Tint: {}", args.tint);
|
println!("Tint: {}", args.tint);
|
||||||
|
|
||||||
// Check if input file exists
|
// Sanitize and resolve input path
|
||||||
if !Path::new(&args.input).exists() {
|
let input_path = sanitize_path(&args.input, true).map_err(|e| {
|
||||||
eprintln!("Error: Input file '{}' does not exist.", args.input);
|
eprintln!("Error: {}", e);
|
||||||
return Err("Input file not found".into());
|
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...");
|
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
|
// 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()) };
|
let ret = unsafe { libraw_open_file(lr, input_cstr.as_ptr()) };
|
||||||
|
|
||||||
if ret != LIBRAW_SUCCESS {
|
if ret != LIBRAW_SUCCESS {
|
||||||
@ -133,24 +189,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
println!("[INFO] Image processed successfully");
|
println!("[INFO] Image processed successfully");
|
||||||
|
|
||||||
// Determine output format from extension
|
// Determine output format from extension
|
||||||
let ext = Path::new(&args.output)
|
let ext = output_path
|
||||||
.extension()
|
.extension()
|
||||||
.and_then(|s| s.to_str())
|
.and_then(|s| s.to_str())
|
||||||
.unwrap_or("jpg")
|
.unwrap_or("jpg")
|
||||||
.to_lowercase();
|
.to_lowercase();
|
||||||
|
|
||||||
let ret = match ext.as_str() {
|
let output_cstr = CString::new(output_path.to_string_lossy().as_bytes())
|
||||||
"jpg" | "jpeg" => {
|
.map_err(|_| "Output path contains null bytes, cannot process".into())?;
|
||||||
// For JPEG output, we'll write directly to file
|
let ret = unsafe { libraw_dcraw_ppm_tiff_writer(lr, output_cstr.as_ptr()) };
|
||||||
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()) }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if ret != LIBRAW_SUCCESS {
|
if ret != LIBRAW_SUCCESS {
|
||||||
eprintln!("Error: Failed to write output file. Error code: {}", ret);
|
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!("\n[SUCCESS] Photo editing completed successfully with libraw integration!");
|
||||||
println!(
|
println!(
|
||||||
"[NOTICE] The edited image has been saved as: {}",
|
"[NOTICE] The edited image has been saved as: {}",
|
||||||
args.output
|
output_path.display()
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user