safename 0.1.0

Filename and path validation for security hardening
Documentation
//! Lookup tables for blocked bytes.

/// Bytes blocked at all positions: control chars (0x00-0x1F, 0x7F), 0xFF,
/// plus feature-gated colon and backslash.
pub const BLOCKED_ALWAYS: [bool; 256] = {
    let mut bytes = [false; 256];
    // Control characters: 0x00 (NUL) through 0x1F (US)
    bytes[0x00] = true; // NUL
    bytes[0x01] = true; // SOH
    bytes[0x02] = true; // STX
    bytes[0x03] = true; // ETX
    bytes[0x04] = true; // EOT
    bytes[0x05] = true; // ENQ
    bytes[0x06] = true; // ACK
    bytes[0x07] = true; // BEL
    bytes[0x08] = true; // BS
    bytes[0x09] = true; // HT (tab)
    bytes[0x0A] = true; // LF (newline)
    bytes[0x0B] = true; // VT
    bytes[0x0C] = true; // FF
    bytes[0x0D] = true; // CR
    bytes[0x0E] = true; // SO
    bytes[0x0F] = true; // SI
    bytes[0x10] = true; // DLE
    bytes[0x11] = true; // DC1
    bytes[0x12] = true; // DC2
    bytes[0x13] = true; // DC3
    bytes[0x14] = true; // DC4
    bytes[0x15] = true; // NAK
    bytes[0x16] = true; // SYN
    bytes[0x17] = true; // ETB
    bytes[0x18] = true; // CAN
    bytes[0x19] = true; // EM
    bytes[0x1A] = true; // SUB
    bytes[0x1B] = true; // ESC
    bytes[0x1C] = true; // FS
    bytes[0x1D] = true; // GS
    bytes[0x1E] = true; // RS
    bytes[0x1F] = true; // US
    bytes[0x7F] = true; // DEL
    // 0x2F: Forward slash - path separator, cannot appear in filename
    bytes[b'/' as usize] = true;
    // 0xFF: Invalid UTF-8 leading byte
    bytes[0xFF] = true;
    #[cfg(feature = "block-colon")]
    {
        // Colon: prevents injection in PATH, LD_LIBRARY_PATH, /etc/passwd formats
        bytes[b':' as usize] = true;
    }
    #[cfg(feature = "block-backslash")]
    {
        // Backslash: prevents path traversal via cross-platform normalization
        bytes[b'\\' as usize] = true;
    }
    #[cfg(feature = "block-quotes")]
    {
        // 0x22: Double quote - prevents shell injection
        bytes[b'"' as usize] = true;
        // 0x27: Single quote - prevents shell injection
        bytes[b'\'' as usize] = true;
    }
    #[cfg(feature = "block-chaining")]
    {
        // 0x26: Ampersand - prevents shell command chaining (&&, &)
        bytes[b'&' as usize] = true;
        // 0x3B: Semicolon - prevents shell command chaining
        bytes[b';' as usize] = true;
        // 0x7C: Pipe - prevents shell command chaining (||)
        // Note: Also blocked in block-redirection for stdout piping (|).
        // Both features block pipe, but for different shell behaviors.
        bytes[b'|' as usize] = true;
    }
    #[cfg(feature = "block-redirection")]
    {
        // 0x7C: Pipe - redirects stdout to another process (|)
        // Note: Also blocked in block-chaining for logical OR (||).
        // Both features block pipe, but for different shell behaviors.
        bytes[b'|' as usize] = true;
        // 0x3E: Greater-than - redirects stdout to file
        bytes[b'>' as usize] = true;
        // 0x3C: Less-than - redirects file to stdin
        bytes[b'<' as usize] = true;
    }
    #[cfg(feature = "block-expansion")]
    {
        // 0x24: Dollar - shell variable expansion
        bytes[b'$' as usize] = true;
        // 0x25: Percent - variable expansion (Windows cmd, make, etc.)
        bytes[b'%' as usize] = true;
        // 0x2A: Asterisk - glob expansion (matches multiple chars)
        bytes[b'*' as usize] = true;
        // 0x3F: Question mark - glob expansion (matches single char)
        bytes[b'?' as usize] = true;
        // 0x60: Backtick - command substitution
        bytes[b'`' as usize] = true;
    }
    #[cfg(feature = "block-brackets")]
    {
        // 0x28: Open paren - subshell grouping
        bytes[b'(' as usize] = true;
        // 0x29: Close paren - subshell grouping
        bytes[b')' as usize] = true;
        // 0x5B: Open bracket - glob character class
        bytes[b'[' as usize] = true;
        // 0x5D: Close bracket - glob character class
        bytes[b']' as usize] = true;
    }
    #[cfg(feature = "block-space")]
    {
        // 0x20: Space - blocks spaces everywhere, not just leading/trailing
        bytes[b' ' as usize] = true;
    }
    bytes
};

/// Additional bytes blocked at initial position: dash, tilde, space.
pub const BLOCKED_INITIAL: [bool; 256] = {
    let mut bytes = [false; 256];
    // 0x2D: Dash - prevents interpretation as command-line options
    bytes[b'-' as usize] = true;
    // 0x7E: Tilde - prevents shell home directory expansion
    bytes[b'~' as usize] = true;
    // 0x20: Space - prevents quoting bugs and argument splitting
    bytes[b' ' as usize] = true;
    bytes
};

/// Additional bytes blocked at final position: space.
pub const BLOCKED_FINAL: [bool; 256] = {
    let mut bytes = [false; 256];
    // 0x20: Space - prevents quoting bugs and argument splitting
    bytes[b' ' as usize] = true;
    bytes
};