use strict_path::PathBoundary;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Tempfile Integration Examples ===\n");
println!("Pattern: tempfile::tempdir() -> PathBoundary::try_new() -> secure operations\n");
println!("1. Basic Temporary Directory with RAII Cleanup:");
basic_tempdir()?;
println!("\n2. Validating Untrusted Upload Filenames (HTTP multipart / CLI args):");
validate_uploaded_files()?;
println!("\n3. Archive Extraction with Malicious Path Detection:");
archive_extraction_staging()?;
println!("\n4. Test Fixture Pattern:");
test_fixture_pattern()?;
println!("\nAll examples completed successfully!");
println!("\nKey benefit: tempfile handles cleanup, strict-path handles security");
Ok(())
}
fn basic_tempdir() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = tempfile::tempdir()?;
println!(" Created temp dir: {}", temp_dir.path().display());
let work_dir: PathBoundary = PathBoundary::try_new(temp_dir.path())?;
let data_file = work_dir.strict_join("data/file.txt")?;
data_file.create_parent_dir_all()?;
data_file.write(b"temporary content")?;
println!(" Wrote file: {}", data_file.strictpath_display());
println!(" Content: {}", data_file.read_to_string()?);
Ok(())
}
fn validate_uploaded_files() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = tempfile::Builder::new()
.prefix("upload-staging-")
.tempdir()?;
println!(" Staging dir: {}", temp_dir.path().display());
let upload_staging_dir: PathBoundary = PathBoundary::try_new(temp_dir.path())?;
let incoming_uploads: &[&str] = &[
"report.pdf",
"../../etc/passwd", "../outside_staging.txt", "data/user_files/notes.txt", ];
println!(" Validating filenames from HTTP multipart upload:");
for uploaded_filename in incoming_uploads {
match upload_staging_dir.strict_join(uploaded_filename) {
Ok(safe_staged_file) => {
safe_staged_file.create_parent_dir_all()?;
safe_staged_file.write(b"uploaded data")?;
println!(
" OK '{}' -> {}",
uploaded_filename,
safe_staged_file.strictpath_display()
);
}
Err(_) => {
println!(
" BLOCKED '{}' (traversal / escape attempt rejected)",
uploaded_filename
);
}
}
}
Ok(())
}
fn archive_extraction_staging() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = tempfile::Builder::new()
.prefix("archive-extract-")
.tempdir()?;
let extract_dir: PathBoundary = PathBoundary::try_new(temp_dir.path())?;
let archive_entries: &[(&str, &str)] = &[
("README.md", "# Project README"),
("docs/guide.md", "# User Guide"),
("../../etc/passwd", "root:x:0:0"), ("data/config.json", r#"{"setting": true}"#),
("../../../root/.ssh/id_rsa", "PRIVATE KEY"), ("images/logo.png", "PNG binary data"),
];
println!(" Extracting {} archive entries:", archive_entries.len());
let mut safe_count = 0;
let mut blocked_count = 0;
for (entry_path, content) in archive_entries {
match extract_dir.strict_join(entry_path) {
Ok(safe_path) => {
safe_path.create_parent_dir_all()?;
safe_path.write(content.as_bytes())?;
println!(" OK {entry_path}");
safe_count += 1;
}
Err(_) => {
println!(
" BLOCKED '{}' - {}",
entry_path,
if entry_path.contains("..") {
"path traversal attack"
} else {
"escape attempt"
}
);
blocked_count += 1;
}
}
}
println!("\n Results: {safe_count} safe, {blocked_count} blocked");
Ok(())
}
fn test_fixture_pattern() -> Result<(), Box<dyn std::error::Error>> {
let temp = tempfile::tempdir()?;
let fixture_dir: PathBoundary = PathBoundary::try_new(temp.path())?;
println!(" Setting up test fixture...");
let input = fixture_dir.strict_join("input.txt")?;
input.write(b"test data")?;
println!(" Created input.txt");
let output = fixture_dir.strict_join("output.txt")?;
let data = input.read_to_string()?;
output.write(data.to_uppercase().as_bytes())?;
println!(" Processed -> output.txt");
assert_eq!(output.read_to_string()?, "TEST DATA");
println!(" Test passed");
Ok(())
}