Skip to main content

systemprompt_extension/
frame_options.rs

1//! Per-route framing policy override for extension routers.
2//!
3//! The host's global security-headers middleware sets `X-Frame-Options`
4//! sitewide. An extension that must allow its pages to be framed (embed
5//! widgets, chrome-free tool pages) declares a [`FrameOptions`] on its
6//! router; [`stamp_frame_options`] records the choice as a
7//! [`FrameOptionsOverride`] response extension, which the host middleware
8//! honours instead of the profile default. Setting the raw header without
9//! the marker has no effect — the global middleware overwrites it.
10//!
11//! Copyright (c) systemprompt.io — Business Source License 1.1.
12//! See <https://systemprompt.io> for licensing details.
13
14use axum::extract::Request;
15use axum::middleware::Next;
16use axum::response::Response;
17use serde::{Deserialize, Serialize};
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
20pub enum FrameOptions {
21    #[serde(rename = "DENY")]
22    Deny,
23    #[serde(rename = "SAMEORIGIN")]
24    SameOrigin,
25    #[serde(rename = "ALLOWALL")]
26    AllowAll,
27}
28
29impl FrameOptions {
30    #[must_use]
31    pub const fn header_value(self) -> Option<&'static str> {
32        match self {
33            Self::Deny => Some("DENY"),
34            Self::SameOrigin => Some("SAMEORIGIN"),
35            Self::AllowAll => None,
36        }
37    }
38
39    #[must_use]
40    pub const fn frame_ancestors(self) -> &'static str {
41        match self {
42            Self::Deny => "'none'",
43            Self::SameOrigin => "'self'",
44            Self::AllowAll => "*",
45        }
46    }
47}
48
49/// Response-extension marker read by the host's security-headers middleware.
50#[derive(Debug, Clone, Copy)]
51pub struct FrameOptionsOverride(pub FrameOptions);
52
53pub async fn stamp_frame_options(
54    frame_options: FrameOptions,
55    request: Request,
56    next: Next,
57) -> Response {
58    let mut response = next.run(request).await;
59    response
60        .extensions_mut()
61        .insert(FrameOptionsOverride(frame_options));
62    response
63}