datasynth_server/rest/
security_headers.rs1use axum::{
6 body::Body,
7 http::{header::HeaderValue, Request, Response},
8 middleware::Next,
9};
10
11pub async fn security_headers_middleware(request: Request<Body>, next: Next) -> Response<Body> {
21 let mut response = next.run(request).await;
22 let headers = response.headers_mut();
23
24 headers.insert(
25 "x-content-type-options",
26 HeaderValue::from_static("nosniff"),
27 );
28 headers.insert("x-frame-options", HeaderValue::from_static("DENY"));
29 headers.insert("x-xss-protection", HeaderValue::from_static("0"));
30 headers.insert(
31 "referrer-policy",
32 HeaderValue::from_static("strict-origin-when-cross-origin"),
33 );
34 headers.insert(
35 "content-security-policy",
36 HeaderValue::from_static("default-src 'none'; frame-ancestors 'none'"),
37 );
38 headers.insert("cache-control", HeaderValue::from_static("no-store"));
39
40 response
41}
42
43#[cfg(test)]
44#[allow(clippy::unwrap_used)]
45mod tests {
46 use super::*;
47 use axum::{routing::get, Router};
48 use tower::ServiceExt;
49
50 async fn ok_handler() -> &'static str {
51 "ok"
52 }
53
54 fn test_router() -> Router {
55 Router::new()
56 .route("/test", get(ok_handler))
57 .layer(axum::middleware::from_fn(security_headers_middleware))
58 }
59
60 #[tokio::test]
61 async fn test_security_headers_present() {
62 let router = test_router();
63 let request = Request::builder().uri("/test").body(Body::empty()).unwrap();
64
65 let response = router.oneshot(request).await.unwrap();
66 let headers = response.headers();
67
68 assert_eq!(headers.get("x-content-type-options").unwrap(), "nosniff");
69 assert_eq!(headers.get("x-frame-options").unwrap(), "DENY");
70 assert_eq!(headers.get("x-xss-protection").unwrap(), "0");
71 assert_eq!(
72 headers.get("referrer-policy").unwrap(),
73 "strict-origin-when-cross-origin"
74 );
75 assert_eq!(
76 headers.get("content-security-policy").unwrap(),
77 "default-src 'none'; frame-ancestors 'none'"
78 );
79 assert_eq!(headers.get("cache-control").unwrap(), "no-store");
80 }
81}