diff --git a/services/api/src/lib.rs b/services/api/src/lib.rs index ad87602..caa5af3 100644 --- a/services/api/src/lib.rs +++ b/services/api/src/lib.rs @@ -29,7 +29,7 @@ use db::Database; use email::{queue::EmailQueue, service::EmailService, webhook::WebhookHandler}; use metrics::Metrics; use newsletter::IpRateLimiter; -use security::{ApiKeyAuth, IpWhitelist, RateLimiter}; +use security::{ApiKeyAuth, IpWhitelist, RateLimiter, TrustProxy}; use shutdown::ShutdownCoordinator; use tokio::net::TcpListener; use tower_http::{ diff --git a/services/api/src/security.rs b/services/api/src/security.rs index 92f3e40..03f5fbf 100644 --- a/services/api/src/security.rs +++ b/services/api/src/security.rs @@ -464,7 +464,30 @@ mod tests { h } - // ── existing behaviour (trust_proxy = true) ─────────────────────────── + // ── security headers middleware ─────────────────────────────────────── + + #[tokio::test] + async fn security_headers_middleware_sets_required_headers() { + use axum::{body::Body, http::Request, middleware, routing::get, Router}; + use tower::ServiceExt; + + let app = Router::new() + .route("/", get(|| async { "ok" })) + .layer(middleware::from_fn(super::security_headers_middleware)); + + let response = app + .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap()) + .await + .unwrap(); + + let headers = response.headers(); + assert!(headers.contains_key("content-security-policy")); + assert!(headers.contains_key("strict-transport-security")); + assert!(headers.contains_key("x-frame-options")); + assert!(headers.contains_key("referrer-policy")); + assert_eq!(headers["x-frame-options"], "DENY"); + assert_eq!(headers["x-content-type-options"], "nosniff"); + } #[test] fn test_extract_client_ip_precedence() {