#![cfg(test)]
use context_creator::core::semantic::path_validator::validate_import_path;
use std::fs;
use std::path::Path;
use std::thread;
use std::time::Duration;
use tempfile::TempDir;
#[test]
#[ignore] fn test_toctou_race_condition_vulnerability() {
let temp_dir = TempDir::new().unwrap();
let base_dir = temp_dir.path();
let safe_file = base_dir.join("safe.txt");
fs::write(&safe_file, "safe content").unwrap();
let target_path = base_dir.join("target.txt");
fs::write(&target_path, "initial safe content").unwrap();
let attacker_target = target_path.clone();
let attacker_thread = thread::spawn(move || {
thread::sleep(Duration::from_micros(100));
fs::remove_file(&attacker_target).ok();
#[cfg(unix)]
{
use std::os::unix::fs::symlink;
symlink("/etc/passwd", &attacker_target).ok();
}
});
for _ in 0..100 {
let result = validate_import_path(base_dir, &target_path);
if result.is_ok() {
#[cfg(unix)]
{
if let Ok(link_target) = fs::read_link(&target_path) {
if link_target.to_str() == Some("/etc/passwd") {
panic!("VULNERABILITY: Path validation passed for symlink to /etc/passwd!");
}
}
}
}
}
attacker_thread.join().unwrap();
}
#[test]
fn test_url_encoding_bypass_vulnerability() {
let temp_dir = TempDir::new().unwrap();
let base_dir = temp_dir.path();
fs::create_dir_all(base_dir.join("src")).unwrap();
fs::write(base_dir.join("src/main.rs"), "fn main() {}").unwrap();
let url_encoded_attacks = vec![
"src/%2e%2e/%2e%2e/%2e%2e/etc/passwd",
"src/%2E%2E/%2E%2E/%2E%2E/etc/passwd", "%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd",
"src/%252e%252e/%252e%252e/etc/passwd",
"src/%2e%2e/../etc/passwd",
"src/../%2e%2e/etc/passwd",
"src/%c0%ae%c0%ae/%c0%ae%c0%ae/etc/passwd", "src/../etc/passwd%00.rs",
"src/%252E%252E%252F%252E%252E%252Fetc%252Fpasswd",
"%5c%2e%2e%5c%2e%2e%5cetc%5cpasswd", "src/%u002e%u002e/%u002e%u002e/etc/passwd", "src/..%c0%af..%c0%afetc%c0%afpasswd", "%2e%2e%252f%2e%2e%252fetc%252fpasswd", ];
for attack in url_encoded_attacks {
let attack_path = base_dir.join(attack);
let result = validate_import_path(base_dir, &attack_path);
if result.is_ok() {
panic!("VULNERABILITY: URL-encoded attack passed validation: {attack}");
}
}
}
#[test]
fn test_mixed_slash_bypass_vulnerability() {
let temp_dir = TempDir::new().unwrap();
let base_dir = temp_dir.path();
let attacks = vec![
"src\\..\\..\\etc\\passwd",
"src/..\\../etc/passwd",
"src\\../..\\etc/passwd",
"src%5c..%5c..%5cetc%5cpasswd",
"src%5C..%5C..%5Cetc%5Cpasswd",
];
for attack in attacks {
let attack_path = base_dir.join(attack);
let result = validate_import_path(base_dir, &attack_path);
if result.is_ok() {
if let Ok(canonical) = attack_path.canonicalize() {
if !canonical.starts_with(base_dir) {
panic!("VULNERABILITY: Mixed slash attack escaped base directory: {attack}");
}
}
}
}
}
#[test]
fn test_symlink_escape_vulnerability() {
#[cfg(unix)]
{
let temp_dir = TempDir::new().unwrap();
let base_dir = temp_dir.path();
use std::os::unix::fs::symlink;
let symlink_path = base_dir.join("escape_link");
symlink("/etc/passwd", &symlink_path).unwrap();
let result = validate_import_path(base_dir, &symlink_path);
if result.is_ok() {
panic!("VULNERABILITY: Symlink to /etc/passwd passed validation!");
}
}
}
#[test]
fn test_absolute_path_outside_project() {
let temp_dir = TempDir::new().unwrap();
let base_dir = temp_dir.path();
let attacks = vec![
Path::new("/etc/passwd"),
Path::new("/home/user/.ssh/id_rsa"),
Path::new("/var/log/secure"),
#[cfg(windows)]
Path::new("C:\\Windows\\System32\\config\\SAM"),
];
for attack_path in attacks {
if attack_path.exists() {
let result = validate_import_path(base_dir, attack_path);
if result.is_ok() {
panic!(
"VULNERABILITY: Absolute path outside project passed validation: {attack_path:?}"
);
}
}
}
}
#[test]
fn test_path_normalization_attacks() {
let temp_dir = TempDir::new().unwrap();
let base_dir = temp_dir.path();
let attacks = vec![
"src//..//..//..//etc//passwd",
"src/./././../../../etc/passwd",
"./../../etc/passwd",
"././././../../../etc/passwd",
"src../../../etc/passwd...",
"src.../..../.../etc/passwd",
"...src/../../etc/passwd",
];
for attack in attacks {
let attack_path = base_dir.join(attack);
let result = validate_import_path(base_dir, &attack_path);
let mut current = base_dir.to_path_buf();
for component in Path::new(attack).components() {
match component {
std::path::Component::ParentDir => {
current.pop();
}
std::path::Component::Normal(c) => {
current.push(c);
}
_ => {}
}
}
if result.is_ok() && !current.starts_with(base_dir) {
panic!("VULNERABILITY: Path normalization attack escaped base directory: {attack}");
}
}
}