| Crates.io | safename |
| lib.rs | safename |
| version | 0.1.0 |
| created_at | 2026-01-07 20:21:27.179492+00 |
| updated_at | 2026-01-07 20:21:27.179492+00 |
| description | Filename and path validation for security hardening |
| homepage | https://github.com/theroyalwhee0/safename |
| repository | https://github.com/theroyalwhee0/safename |
| max_upload_size | |
| id | 2028958 |
| size | 95,013 |
Filename and path validation for security hardening, inspired by David A. Wheeler's proposed Linux safename LSM.
Unix/Linux filesystems allow almost any bytes in filenames, which creates security vulnerabilities:
-rf or --help are interpreted
as flags~user, $HOME, *.txt expand unexpectedlyThis library validates and sanitizes filenames to prevent these attacks.
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"
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
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());
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 |
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-asciiAlternative 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).
mediumShell safety without breaking common filenames. Includes low plus:
block-quotes - Blocks " and 'block-chaining - Blocks &, ;, | (for &&, ;, ||)block-redirection - Blocks |, >, <block-expansion - Blocks $, %, *, ?, `highMaximum 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.
[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
Inspired by David A. Wheeler's work on safe filenames:
Copyright 2025 Adam Mill
Licensed under the Apache License, Version 2.0. See LICENSE.txt for details.