use anyhow::{bail, Result};
use crate::constants::*;
pub fn detect_windows_attacks(path: &str) -> Result<()> {
if path.contains(':') {
let starts_with_drive = path.len() >= 2 &&
path.chars().nth(0).map_or(false, |c| c.is_ascii_alphabetic()) &&
path.chars().nth(1) == Some(':');
if !starts_with_drive {
bail!("Colon detected in path (possible NTFS stream or device): {}", path);
} else if path.len() > 2 {
if path[2..].contains(':') {
bail!("NTFS alternate data stream syntax detected: {}", path);
}
}
}
if path.starts_with("\\\\") || path.starts_with("//") {
bail!("UNC path detected: {}", path);
}
if path.starts_with("\\\\?\\") || path.starts_with("\\\\.\\") {
bail!("Windows extended-length path prefix detected: {}", path);
}
let path_upper = path.to_uppercase();
if path_upper.starts_with("\\\\.\\") || path_upper.contains("\\DEVICE\\") {
bail!("Windows device path detected");
}
if let Some(last_component) = path.split(&['/', '\\'][..]).last() {
if last_component.ends_with('.') && last_component != "." && last_component != ".." {
bail!("Trailing dot detected in path component (Windows exploit): {}", last_component);
}
if last_component.ends_with(' ') {
bail!("Trailing space detected in path component (Windows exploit)");
}
}
Ok(())
}
pub fn detect_separator_manipulation(path: &str) -> Result<()> {
if path.contains("//") || path.contains("\\\\") {
if !path.starts_with("//") && !path.starts_with("\\\\") {
bail!("Multiple consecutive path separators detected");
}
}
if path.contains('/') && path.contains('\\') {
bail!("Mixed path separators detected (possible evasion)");
}
for sep in DANGEROUS_SEPARATORS.iter() {
if path.contains(*sep) {
bail!("Unusual separator character detected in path");
}
}
Ok(())
}
pub fn detect_advanced_traversal(path: &str) -> Result<()> {
for pattern in TRAVERSAL_PATTERNS.iter() {
if path.contains(pattern) {
bail!("Directory traversal pattern detected: {}", pattern);
}
}
for pattern in NESTED_TRAVERSAL_PATTERNS.iter() {
if path.contains(pattern) {
bail!("Nested traversal pattern detected: {}", pattern);
}
}
if path.contains("\\x2e") || path.contains("\\x2f") || path.contains("\\x5c") {
bail!("Hex-encoded path characters detected");
}
Ok(())
}
pub fn validate_special_paths(path: &str) -> Result<()> {
let path_lower = path.to_lowercase();
for dangerous in SYSTEM_PATHS.iter() {
if path_lower.starts_with(&dangerous.to_lowercase()) {
bail!("Access to sensitive system path denied: {}", dangerous);
}
}
Ok(())
}
pub fn detect_protocol_schemes(path: &str) -> Result<()> {
let path_lower = path.to_lowercase();
if path_lower.starts_with("file://") || path_lower.starts_with("file:/") {
bail!("File protocol scheme detected in path");
}
if path_lower.starts_with("http://") || path_lower.starts_with("https://") {
bail!("HTTP protocol scheme detected in path (possible SSRF)");
}
for protocol in DANGEROUS_PROTOCOLS.iter() {
if path_lower.starts_with(protocol) {
bail!("Dangerous protocol scheme detected: {}", protocol);
}
}
Ok(())
}
pub fn detect_suspicious_patterns(path: &str) -> Result<()> {
for pattern in SUSPICIOUS_PATTERNS.iter() {
if path.contains(pattern) {
bail!("Suspicious pattern '{}' detected in path: {}", pattern, path);
}
}
Ok(())
}