safename

Crates.iosafename
lib.rssafename
version0.1.0
created_at2026-01-07 20:21:27.179492+00
updated_at2026-01-07 20:21:27.179492+00
descriptionFilename and path validation for security hardening
homepagehttps://github.com/theroyalwhee0/safename
repositoryhttps://github.com/theroyalwhee0/safename
max_upload_size
id2028958
size95,013
Adam Mill (theroyalwhee0)

documentation

https://docs.rs/safename

README

safename

Filename and path validation for security hardening, inspired by David A. Wheeler's proposed Linux safename LSM.

Problem

Unix/Linux filesystems allow almost any bytes in filenames, which creates security vulnerabilities:

  • Command injection: Filenames like -rf or --help are interpreted as flags
  • Shell expansion: ~user, $HOME, *.txt expand unexpectedly
  • Terminal attacks: Control characters can inject escape sequences
  • Path traversal: Backslashes normalize to forward slashes on some systems
  • Delimiter injection: Colons in PATH, semicolons in scripts

This library validates and sanitizes filenames to prevent these attacks.

Usage

use safename::{validate_file, is_file_safe, sanitize_file, SafeNameError};

// Check if a filename is safe
assert!(is_file_safe("normal_file.txt"));
assert!(!is_file_safe("-rf"));           // Leading dash
assert!(!is_file_safe("file\x00name"));  // Control character

// Get detailed error information
match validate_file("-rf") {
    Ok(()) => println!("Valid"),
    Err(SafeNameError::InvalidByte { index, byte }) => {
        println!("Invalid byte 0x{:02X} at index {}", byte, index);
        // Prints: Invalid byte 0x2D at index 0
    }
    Err(SafeNameError::InvalidLength { len, max }) => {
        println!("Length {} exceeds max {}", len, max);
    }
}

// Sanitize unsafe filenames (returns Result)
let safe = sanitize_file("-rf").unwrap();  // Returns b"_rf"

Path validation

use safename::{validate_path, is_path_safe, sanitize_path};

assert!(is_path_safe("/home/user/file.txt"));
assert!(!is_path_safe("/home/-rf"));  // Component starts with dash

Custom options

use safename::{validate_file_with_options, FileValidationOptions};

let opts = FileValidationOptions { max_len: 64, ..Default::default() };
assert!(validate_file_with_options(b"short.txt", &opts).is_ok());

Default Rules

Always blocked:

Bytes Description
0x00-0x1F Control characters (NUL, tab, newline, escape, etc.)
/ Path separator (cannot appear in filename)
0x7F DEL control character
0xFF Invalid UTF-8 leading byte

Position-dependent:

Position Blocked Reason
Initial - Interpreted as command-line option
Initial ~ Shell home directory expansion
Initial space Quoting bugs, argument splitting
Final space Quoting bugs, argument splitting

Feature Flags

Features are organized into tiers:

low (default)

Cross-platform safety. Includes:

  • block-colon - Blocks : (PATH injection, /etc/passwd formats)
  • block-backslash - Blocks \ (path traversal via normalization)

require-utf8 (default)

Requires valid UTF-8 encoding. Enabled by default alongside low.

require-ascii

Alternative to require-utf8 for ASCII-only environments. Blocks all bytes >= 0x80.

Note: require-utf8 and require-ascii are mutually exclusive (compile error if both enabled).

medium

Shell safety without breaking common filenames. Includes low plus:

  • block-quotes - Blocks " and '
  • block-chaining - Blocks &, ;, | (for &&, ;, ||)
  • block-redirection - Blocks |, >, <
  • block-expansion - Blocks $, %, *, ?, `

high

Maximum restriction. Includes medium plus:

  • block-brackets - Blocks (, ), [, ]
  • block-space - Blocks spaces everywhere (not just leading/trailing)

Note: These features may break common filenames like file (copy).txt or my document.pdf.

Cargo.toml

[dependencies]
safename = "0.1"                          # low (default)
safename = { version = "0.1", features = ["medium"] }
safename = { version = "0.1", features = ["high"] }
safename = { version = "0.1", default-features = false }  # minimal
safename = { version = "0.1", default-features = false, features = ["require-ascii"] }  # ASCII-only

Background

Inspired by David A. Wheeler's work on safe filenames:

License

Copyright 2025 Adam Mill

Licensed under the Apache License, Version 2.0. See LICENSE.txt for details.

Commit count: 31

cargo fmt