78 lines
2.5 KiB
Rust
78 lines
2.5 KiB
Rust
use anyhow::{bail, Result};
|
|
|
|
/// Prüft, ob ein Name Path-Traversal-Muster oder Pfadtrennzeichen enthält (S-02).
|
|
pub fn is_path_traversal(name: &str) -> bool {
|
|
name.is_empty()
|
|
|| name == "."
|
|
|| name == ".."
|
|
|| name.contains('/')
|
|
|| name.contains('\\')
|
|
|| name.contains('\0')
|
|
}
|
|
|
|
/// Validiert einen Datei- oder Verzeichnisnamen gegen Path-Traversal, Null-Bytes,
|
|
/// unzulässige Steuerzeichen und Windows-reservierte Gerätenamen (S-08, R-03).
|
|
pub fn validate_node_name(name: &str) -> Result<()> {
|
|
if name.is_empty() {
|
|
bail!("Dateiname darf nicht leer sein.");
|
|
}
|
|
if name == "." || name == ".." {
|
|
bail!(
|
|
"Ungültiger Dateiname (Verzeichnisreferenz verboten): '{}'",
|
|
name
|
|
);
|
|
}
|
|
if name.contains('/') || name.contains('\\') || name.contains('\0') {
|
|
bail!(
|
|
"Dateiname enthält unzulässige Trennzeichen oder Null-Bytes: '{}'",
|
|
name
|
|
);
|
|
}
|
|
for c in name.chars() {
|
|
if (c as u32) < 0x20 {
|
|
bail!("Dateiname enthält Steuerzeichen: '{}'", name);
|
|
}
|
|
}
|
|
|
|
// Windows reservierte Gerätenamen (CON, PRN, AUX, NUL, COM1..9, LPT1..9)
|
|
let base_name = if let Some(dot_idx) = name.find('.') {
|
|
&name[..dot_idx]
|
|
} else {
|
|
name
|
|
};
|
|
let base_upper = base_name.to_ascii_uppercase();
|
|
let reserved = [
|
|
"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8",
|
|
"COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
|
|
];
|
|
if reserved.contains(&base_upper.as_str()) {
|
|
bail!(
|
|
"Dateiname '{}' kollidiert mit einem reservierten Windows-Gerätenamen.",
|
|
name
|
|
);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_validate_node_name() {
|
|
assert!(validate_node_name("test.txt").is_ok());
|
|
assert!(validate_node_name("").is_err());
|
|
assert!(validate_node_name(".").is_err());
|
|
assert!(validate_node_name("..").is_err());
|
|
assert!(validate_node_name("foo/bar").is_err());
|
|
assert!(validate_node_name("foo\\bar").is_err());
|
|
assert!(validate_node_name("CON").is_err());
|
|
assert!(validate_node_name("aux.txt").is_err());
|
|
assert!(validate_node_name("nul").is_err());
|
|
assert!(validate_node_name("com1.log").is_err());
|
|
assert!(validate_node_name("line\nbreak").is_err());
|
|
assert!(validate_node_name("null\0byte").is_err());
|
|
}
|
|
}
|