Skip to main content

systemprompt_api/services/middleware/
security_headers.rs

1//! Security response headers (CSP, frame options, HSTS).
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use axum::extract::Request;
7use axum::http::HeaderValue;
8use axum::middleware::Next;
9use axum::response::Response;
10use systemprompt_extension::FrameOptionsOverride;
11use systemprompt_models::profile::SecurityHeadersConfig;
12
13pub async fn inject_security_headers(
14    config: SecurityHeadersConfig,
15    request: Request,
16    next: Next,
17) -> Response {
18    let mut response = next.run(request).await;
19    let frame_override = response.extensions().get::<FrameOptionsOverride>().copied();
20    let headers = response.headers_mut();
21
22    if let Ok(value) = HeaderValue::from_str(&config.hsts) {
23        headers.insert("strict-transport-security", value);
24    }
25
26    if let Some(FrameOptionsOverride(frame_options)) = frame_override {
27        match frame_options.header_value() {
28            Some(value) => {
29                headers.insert("x-frame-options", HeaderValue::from_static(value));
30            },
31            None => {
32                headers.remove("x-frame-options");
33            },
34        }
35        if let Ok(value) = HeaderValue::from_str(&format!(
36            "frame-ancestors {}",
37            frame_options.frame_ancestors()
38        )) {
39            headers.insert("content-security-policy", value);
40        }
41    } else if let Ok(value) = HeaderValue::from_str(&config.frame_options) {
42        headers.insert("x-frame-options", value);
43    }
44
45    if let Ok(value) = HeaderValue::from_str(&config.content_type_options) {
46        headers.insert("x-content-type-options", value);
47    }
48
49    if let Ok(value) = HeaderValue::from_str(&config.referrer_policy) {
50        headers.insert("referrer-policy", value);
51    }
52
53    if let Ok(value) = HeaderValue::from_str(&config.permissions_policy) {
54        headers.insert("permissions-policy", value);
55    }
56
57    if let Some(ref csp) = config.content_security_policy
58        && frame_override.is_none()
59        && let Ok(value) = HeaderValue::from_str(csp)
60    {
61        headers.insert("content-security-policy", value);
62    }
63
64    response
65}