# 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
```rust
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
```rust
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
```rust
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:
| `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:
| 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
```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:
- [Fixing Unix/Linux Filenames](https://dwheeler.com/essays/fixing-unix-linux-filenames.html)
- [LWN: Restricting pathnames](https://lwn.net/Articles/686021/)
## License
Copyright 2025 Adam Mill
Licensed under the Apache License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details.