lemon/handlers/
security_wrappers.rs

1use async_trait::async_trait;
2use hyper::http::header;
3use std::sync::Arc;
4
5use crate::{
6    common,
7    config::SecurityConfig,
8    handlers::{BoxedBody, Handler},
9};
10use hyper::body::Incoming as IncomingBody;
11use hyper::{Request, Response};
12
13pub struct SecHeaders<H> {
14    inner: H,
15    cfg: Arc<SecurityConfig>,
16    hsts_value: Option<header::HeaderValue>,
17}
18
19impl<H> SecHeaders<H> {
20    pub fn new(inner: H, cfg: Arc<SecurityConfig>, is_https: bool) -> Self {
21        let hsts_value = if is_https && cfg.add_default_headers != Some(false) {
22            Some(common::build_hsts(&cfg))
23        } else {
24            None
25        };
26        Self {
27            inner,
28            cfg,
29            hsts_value,
30        }
31    }
32}
33
34#[async_trait]
35impl<H: Handler> Handler for SecHeaders<H> {
36    async fn handle(&self, req: Request<IncomingBody>) -> anyhow::Result<Response<BoxedBody>> {
37        let mut resp = self.inner.handle(req).await?;
38
39        let hdrs = resp.headers_mut();
40
41        // --- X-Content-Type-Options ---
42        hdrs.entry(common::H_XCTO)
43            .or_insert(common::V_NOSNIFF.clone());
44
45        // --- X-Frame-Options / CSP frame-ancestors ---
46        match self.cfg.frame_options.as_deref() {
47            Some("SAMEORIGIN") => {
48                hdrs.entry(common::H_XFO)
49                    .or_insert(common::V_SAMEORIGIN.clone());
50            }
51            Some("NONE") => { /* caller disabled XFO */ }
52            _ => {
53                hdrs.entry(common::H_XFO).or_insert(common::V_DENY.clone());
54            }
55        }
56
57        // --- Strict-Transport-Security ---
58        if let Some(hsts_header_value) = &self.hsts_value {
59            hdrs.entry(header::STRICT_TRANSPORT_SECURITY)
60                .or_insert_with(|| hsts_header_value.clone());
61        }
62
63        Ok(resp)
64    }
65}