diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 46054c1445..a3bc548515 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -15,7 +15,7 @@ use { axum::{ body, extract::{DefaultBodyLimit, Extension, Json, Path, Query}, - http::{header, HeaderValue, StatusCode, Uri}, + http::{header, HeaderName, HeaderValue, StatusCode, Uri}, response::{IntoResponse, Redirect, Response}, routing::{get, post}, Router, @@ -84,6 +84,8 @@ pub struct Server { help = "Decompress encoded content. Currently only supports brotli. Be careful using this on production instances. A decompressed inscription may be arbitrarily large, making decompression a DoS vector." )] pub(crate) decompress: bool, + #[arg(long, help = "Disable cross origin isolation.")] + pub(crate) disable_cross_origin_isolation: bool, #[arg(long, help = "Disable JSON API.")] pub(crate) disable_json_api: bool, #[arg( @@ -153,12 +155,13 @@ impl Server { let server_config = Arc::new(ServerConfig { chain: settings.chain(), - proxy: self.proxy.clone(), + cross_origin_isolation: !self.disable_cross_origin_isolation, csp_origin: self.csp_origin.clone(), decompress: self.decompress, domain: acme_domains.first().cloned(), index_sats: index.has_sat_index(), json_api_enabled: !self.disable_json_api, + proxy: self.proxy.clone(), }); let router = Router::new() @@ -285,6 +288,24 @@ impl Server { .layer(CompressionLayer::new()) .with_state(server_config.clone()); + let router = if server_config.cross_origin_isolation { + router + .layer(SetResponseHeaderLayer::overriding( + HeaderName::from_static("cross-origin-embedder-policy"), + HeaderValue::from_static("require-corp"), + )) + .layer(SetResponseHeaderLayer::overriding( + HeaderName::from_static("cross-origin-opener-policy"), + HeaderValue::from_static("same-origin"), + )) + .layer(SetResponseHeaderLayer::overriding( + HeaderName::from_static("cross-origin-resource-policy"), + HeaderValue::from_static("same-site"), + )) + } else { + router + }; + let router = if server_config.json_api_enabled { router.layer(DefaultBodyLimit::disable()) } else { @@ -4856,6 +4877,31 @@ mod tests { ); } + #[test] + fn cross_origin_isolation_headers() { + const COEP: HeaderName = HeaderName::from_static("cross-origin-embedder-policy"); + const COOP: HeaderName = HeaderName::from_static("cross-origin-opener-policy"); + const CORP: HeaderName = HeaderName::from_static("cross-origin-resource-policy"); + + { + let response = TestServer::new().get("/status"); + assert_eq!(response.headers().get(COEP).unwrap(), "require-corp"); + assert_eq!(response.headers().get(COOP).unwrap(), "same-origin"); + assert_eq!(response.headers().get(CORP).unwrap(), "same-site"); + } + + { + let response = TestServer::builder() + .server_flag("--disable-cross-origin-isolation") + .build() + .get("/status"); + + assert!(response.headers().get(COEP).is_none()); + assert!(response.headers().get(COOP).is_none()); + assert!(response.headers().get(CORP).is_none()); + } + } + #[test] fn feed() { let server = TestServer::builder() diff --git a/src/subcommand/server/server_config.rs b/src/subcommand/server/server_config.rs index ffa9c02ca8..85f5f5d394 100644 --- a/src/subcommand/server/server_config.rs +++ b/src/subcommand/server/server_config.rs @@ -3,12 +3,13 @@ use {super::*, axum::http::HeaderName}; #[derive(Default)] pub(crate) struct ServerConfig { pub(crate) chain: Chain, - pub(crate) proxy: Option, + pub(crate) cross_origin_isolation: bool, pub(crate) csp_origin: Option, pub(crate) decompress: bool, pub(crate) domain: Option, pub(crate) index_sats: bool, pub(crate) json_api_enabled: bool, + pub(crate) proxy: Option, } impl ServerConfig {