Test that the server correctly reports missing certificate errors, rejects invalid hostnames, fails gracefully on port conflicts, and starts successfully with multiple virtual hosts configured.
167 lines
4.7 KiB
Rust
167 lines
4.7 KiB
Rust
mod common;
|
|
|
|
#[test]
|
|
fn test_per_host_content_isolation() {
|
|
let temp_dir = common::setup_test_environment();
|
|
|
|
// Create different content for each host
|
|
let site1_root = temp_dir.path().join("site1");
|
|
let site2_root = temp_dir.path().join("site2");
|
|
std::fs::create_dir(&site1_root).unwrap();
|
|
std::fs::create_dir(&site2_root).unwrap();
|
|
|
|
// Create different index.gmi files for each site
|
|
std::fs::write(site1_root.join("index.gmi"), "Welcome to Site 1").unwrap();
|
|
std::fs::write(site2_root.join("index.gmi"), "Welcome to Site 2").unwrap();
|
|
|
|
// Create config with two hosts
|
|
let config_path = temp_dir.path().join("config.toml");
|
|
let port = 1965 + (std::process::id() % 1000) as u16; // Use dynamic port
|
|
let config_content = format!(
|
|
r#"
|
|
bind_host = "127.0.0.1"
|
|
port = {}
|
|
|
|
["site1.com"]
|
|
root = "{}"
|
|
cert = "{}"
|
|
key = "{}"
|
|
|
|
["site2.org"]
|
|
root = "{}"
|
|
cert = "{}"
|
|
key = "{}"
|
|
"#,
|
|
port,
|
|
site1_root.display(),
|
|
temp_dir.path().join("cert.pem").display(),
|
|
temp_dir.path().join("key.pem").display(),
|
|
site2_root.display(),
|
|
temp_dir.path().join("cert.pem").display(),
|
|
temp_dir.path().join("key.pem").display()
|
|
);
|
|
std::fs::write(&config_path, config_content).unwrap();
|
|
|
|
// Start server with TLS
|
|
let mut server_process = std::process::Command::new(env!("CARGO_BIN_EXE_pollux"))
|
|
.arg("--config")
|
|
.arg(&config_path)
|
|
.arg("--quiet")
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
// Wait for server to start
|
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
|
|
|
// Test site1.com serves its content
|
|
let response1 = make_gemini_request("127.0.0.1", port, "gemini://site1.com/");
|
|
assert!(
|
|
response1.starts_with("20"),
|
|
"Expected success for site1.com, got: {}",
|
|
response1
|
|
);
|
|
assert!(
|
|
response1.contains("Welcome to Site 1"),
|
|
"Should serve site1 content, got: {}",
|
|
response1
|
|
);
|
|
|
|
// Test site2.org serves its content
|
|
let response2 = make_gemini_request("127.0.0.1", port, "gemini://site2.org/");
|
|
assert!(
|
|
response2.starts_with("20"),
|
|
"Expected success for site2.org, got: {}",
|
|
response2
|
|
);
|
|
assert!(
|
|
response2.contains("Welcome to Site 2"),
|
|
"Should serve site2 content, got: {}",
|
|
response2
|
|
);
|
|
|
|
server_process.kill().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_per_host_path_security() {
|
|
let temp_dir = common::setup_test_environment();
|
|
|
|
// Create directory structure for site1
|
|
let site1_root = temp_dir.path().join("site1");
|
|
std::fs::create_dir(&site1_root).unwrap();
|
|
std::fs::create_dir(site1_root.join("subdir")).unwrap();
|
|
std::fs::write(
|
|
site1_root.join("subdir").join("secret.gmi"),
|
|
"Secret content",
|
|
)
|
|
.unwrap();
|
|
|
|
// Create config
|
|
let config_path = temp_dir.path().join("config.toml");
|
|
let port = 1968 + (std::process::id() % 1000) as u16;
|
|
let cert_path = temp_dir.path().join("cert.pem");
|
|
let key_path = temp_dir.path().join("key.pem");
|
|
|
|
let config_content = format!(
|
|
r#"
|
|
bind_host = "127.0.0.1"
|
|
port = {}
|
|
|
|
["site1.com"]
|
|
root = "{}"
|
|
cert = "{}"
|
|
key = "{}"
|
|
"#,
|
|
port,
|
|
site1_root.display(),
|
|
cert_path.display(),
|
|
key_path.display()
|
|
);
|
|
std::fs::write(&config_path, config_content).unwrap();
|
|
|
|
let mut server_process = std::process::Command::new(env!("CARGO_BIN_EXE_pollux"))
|
|
.arg("--config")
|
|
.arg(&config_path)
|
|
.arg("--quiet")
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
|
|
|
// Test path traversal attempt should be blocked
|
|
let response = make_gemini_request("127.0.0.1", port, "gemini://site1.com/../../../etc/passwd");
|
|
assert!(
|
|
response.starts_with("51"),
|
|
"Path traversal should be blocked, got: {}",
|
|
response
|
|
);
|
|
|
|
// Test valid subdirectory access should work
|
|
let response = make_gemini_request("127.0.0.1", port, "gemini://site1.com/subdir/secret.gmi");
|
|
assert!(
|
|
response.starts_with("20"),
|
|
"Valid subdirectory access should work, got: {}",
|
|
response
|
|
);
|
|
assert!(
|
|
response.contains("Secret content"),
|
|
"Should serve content from subdirectory, got: {}",
|
|
response
|
|
);
|
|
|
|
server_process.kill().unwrap();
|
|
}
|
|
|
|
fn make_gemini_request(host: &str, port: u16, url: &str) -> String {
|
|
// Use the Python client for TLS requests
|
|
use std::process::Command;
|
|
|
|
let output = Command::new("python3")
|
|
.arg("tests/gemini_test_client.py")
|
|
.arg(url)
|
|
.env("GEMINI_PORT", &port.to_string())
|
|
.env("GEMINI_CONNECT_HOST", host)
|
|
.output()
|
|
.unwrap();
|
|
String::from_utf8(output.stdout).unwrap()
|
|
}
|