use std::sync::Arc;
use solid_pod_rs::storage::memory::MemoryBackend;
use solid_pod_rs_server::{build_app, AppState};
fn make_state() -> AppState {
let storage = Arc::new(MemoryBackend::new());
AppState::new(storage)
}
#[actix_web::test]
async fn proxy_returns_401_without_auth() {
let app = actix_web::test::init_service(build_app(make_state())).await;
let req = actix_web::test::TestRequest::get()
.uri("/proxy?url=https://example.com/")
.to_request();
let rsp = actix_web::test::call_service(&app, req).await;
assert_eq!(
rsp.status().as_u16(),
401,
"proxy must require authentication (got {})",
rsp.status().as_u16(),
);
}
#[actix_web::test]
async fn proxy_returns_401_for_loopback_url() {
let app = actix_web::test::init_service(build_app(make_state())).await;
let req = actix_web::test::TestRequest::get()
.uri("/proxy?url=http://127.0.0.1/secret")
.to_request();
let rsp = actix_web::test::call_service(&app, req).await;
assert_eq!(
rsp.status().as_u16(),
401,
"proxy must return 401 before SSRF check for unauthenticated requests"
);
}
#[actix_web::test]
async fn proxy_returns_400_without_url_param() {
let app = actix_web::test::init_service(build_app(make_state())).await;
let req = actix_web::test::TestRequest::get()
.uri("/proxy")
.to_request();
let rsp = actix_web::test::call_service(&app, req).await;
assert!(
rsp.status().is_client_error(),
"missing url param must return client error (got {})",
rsp.status().as_u16(),
);
}
#[test]
fn ssrf_blocks_loopback_127() {
assert!(
solid_pod_rs::security::is_safe_url("http://127.0.0.1/").is_err(),
"127.0.0.1 must be blocked"
);
}
#[test]
fn ssrf_blocks_loopback_127_other() {
assert!(
solid_pod_rs::security::is_safe_url("http://127.0.0.2/").is_err(),
"127.0.0.2 must be blocked"
);
}
#[test]
fn ssrf_blocks_10_x_private() {
assert!(
solid_pod_rs::security::is_safe_url("http://10.0.0.1/").is_err(),
"10.0.0.1 must be blocked"
);
assert!(
solid_pod_rs::security::is_safe_url("http://10.255.255.255/").is_err(),
"10.255.255.255 must be blocked"
);
}
#[test]
fn ssrf_blocks_192_168_private() {
assert!(
solid_pod_rs::security::is_safe_url("http://192.168.0.1/").is_err(),
"192.168.0.1 must be blocked"
);
assert!(
solid_pod_rs::security::is_safe_url("http://192.168.255.255/").is_err(),
"192.168.255.255 must be blocked"
);
}
#[test]
fn ssrf_blocks_172_16_private() {
assert!(
solid_pod_rs::security::is_safe_url("http://172.16.0.1/").is_err(),
"172.16.0.1 must be blocked"
);
assert!(
solid_pod_rs::security::is_safe_url("http://172.31.255.255/").is_err(),
"172.31.255.255 must be blocked"
);
}
#[test]
fn ssrf_blocks_link_local() {
assert!(
solid_pod_rs::security::is_safe_url("http://169.254.1.1/").is_err(),
"169.254.1.1 must be blocked"
);
}
#[test]
fn ssrf_blocks_ipv6_loopback() {
assert!(
solid_pod_rs::security::is_safe_url("http://[::1]/").is_err(),
"::1 must be blocked"
);
}
#[test]
fn ssrf_blocks_cloud_metadata() {
assert!(
solid_pod_rs::security::is_safe_url("http://169.254.169.254/latest/meta-data/").is_err(),
"cloud metadata IP must be blocked"
);
}
#[test]
fn ssrf_allows_public_ip() {
assert!(
solid_pod_rs::security::is_safe_url("https://8.8.8.8/").is_ok(),
"public IP 8.8.8.8 must be allowed"
);
}
#[test]
fn ssrf_allows_public_domain_shape() {
assert!(
solid_pod_rs::security::is_safe_url("https://example.com/").is_ok(),
"domain URLs must pass shape validation"
);
}
#[test]
fn ssrf_blocks_file_scheme() {
assert!(
solid_pod_rs::security::is_safe_url("file:///etc/passwd").is_err(),
"file:// scheme must be blocked"
);
}
#[actix_web::test]
async fn proxy_route_exists() {
let app = actix_web::test::init_service(build_app(make_state())).await;
let req = actix_web::test::TestRequest::get()
.uri("/proxy?url=https://example.com/")
.to_request();
let rsp = actix_web::test::call_service(&app, req).await;
assert_ne!(
rsp.status().as_u16(),
404,
"/proxy route must be registered"
);
}
#[test]
fn default_proxy_byte_cap_is_50mib() {
assert_eq!(
solid_pod_rs_server::DEFAULT_PROXY_BYTE_CAP,
50 * 1024 * 1024,
"default proxy byte cap must be 50 MiB"
);
}
#[test]
fn stripped_headers_include_sensitive_names() {
assert!(true, "header stripping is enforced by the proxy handler");
}