Skip to main content

atrg_core/
security.rs

1//! Security headers middleware for production deployments.
2//!
3//! In non-development environments, adds standard security headers to all responses.
4
5use axum::extract::Request;
6use axum::http::{HeaderName, HeaderValue};
7use axum::middleware::Next;
8
9/// Security headers applied in non-development mode.
10const SECURITY_HEADERS: &[(&str, &str)] = &[
11    ("x-content-type-options", "nosniff"),
12    ("x-frame-options", "DENY"),
13    ("referrer-policy", "strict-origin-when-cross-origin"),
14    (
15        "content-security-policy",
16        "default-src 'none'; frame-ancestors 'none'",
17    ),
18];
19
20/// Axum middleware that adds security headers in production.
21pub async fn security_headers_middleware(req: Request, next: Next) -> axum::response::Response {
22    let mut response = next.run(req).await;
23    let headers = response.headers_mut();
24    for (name, value) in SECURITY_HEADERS {
25        if let (Ok(n), Ok(v)) = (
26            HeaderName::from_bytes(name.as_bytes()),
27            HeaderValue::from_str(value),
28        ) {
29            headers.insert(n, v);
30        }
31    }
32    response
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    #[test]
40    fn security_headers_are_valid() {
41        for (name, value) in SECURITY_HEADERS {
42            assert!(
43                HeaderName::from_bytes(name.as_bytes()).is_ok(),
44                "invalid header name: {name}"
45            );
46            assert!(
47                HeaderValue::from_str(value).is_ok(),
48                "invalid header value: {value}"
49            );
50        }
51    }
52}