Skip to content

Commit

Permalink
Allow setting CSP origin (#2708)
Browse files Browse the repository at this point in the history
  • Loading branch information
rot13maxi authored Nov 22, 2023
1 parent a564dc9 commit bd6a1a7
Showing 3 changed files with 76 additions and 16 deletions.
3 changes: 2 additions & 1 deletion src/page_config.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use super::*;

#[derive(Clone)]
#[derive(Clone, Default)]
pub(crate) struct PageConfig {
pub(crate) chain: Chain,
pub(crate) csp_origin: Option<String>,
pub(crate) domain: Option<String>,
pub(crate) index_sats: bool,
}
79 changes: 67 additions & 12 deletions src/subcommand/server.rs
Original file line number Diff line number Diff line change
@@ -132,6 +132,11 @@ pub(crate) struct Server {
help = "Request ACME TLS certificate for <ACME_DOMAIN>. This ord instance must be reachable at <ACME_DOMAIN>:443 to respond to Let's Encrypt ACME challenges."
)]
acme_domain: Vec<String>,
#[arg(
long,
help = "Use <CSP_ORIGIN> in Content-Security-Policy header. Set this to the public-facing URL of your ord instance."
)]
csp_origin: Option<String>,
#[arg(
long,
help = "Listen on <HTTP_PORT> for incoming HTTP requests. [default: 80]."
@@ -182,6 +187,7 @@ impl Server {

let page_config = Arc::new(PageConfig {
chain: options.chain(),
csp_origin: self.csp_origin.clone(),
domain: acme_domains.first().cloned(),
index_sats: index.has_sat_index(),
});
@@ -985,6 +991,7 @@ impl Server {
async fn content(
Extension(index): Extension<Arc<Index>>,
Extension(config): Extension<Arc<Config>>,
Extension(page_config): Extension<Arc<PageConfig>>,
Path(inscription_id): Path<InscriptionId>,
accept_encoding: AcceptEncoding,
) -> ServerResult<Response> {
@@ -997,7 +1004,7 @@ impl Server {
.ok_or_not_found(|| format!("inscription {inscription_id}"))?;

Ok(
Self::content_response(inscription, accept_encoding)?
Self::content_response(inscription, accept_encoding, &page_config)?
.ok_or_not_found(|| format!("inscription {inscription_id} content"))?
.into_response(),
)
@@ -1006,6 +1013,7 @@ impl Server {
fn content_response(
inscription: Inscription,
accept_encoding: AcceptEncoding,
page_config: &PageConfig,
) -> ServerResult<Option<(HeaderMap, Vec<u8>)>> {
let mut headers = HeaderMap::new();

@@ -1027,15 +1035,25 @@ impl Server {
}
}

headers.insert(
header::CONTENT_SECURITY_POLICY,
HeaderValue::from_static("default-src 'self' 'unsafe-eval' 'unsafe-inline' data: blob:"),
);

headers.append(
header::CONTENT_SECURITY_POLICY,
HeaderValue::from_static("default-src *:*/content/ *:*/blockheight *:*/blockhash *:*/blockhash/ *:*/blocktime *:*/r/ 'unsafe-eval' 'unsafe-inline' data: blob:"),
);
match &page_config.csp_origin {
None => {
headers.insert(
header::CONTENT_SECURITY_POLICY,
HeaderValue::from_static("default-src 'self' 'unsafe-eval' 'unsafe-inline' data: blob:"),
);
headers.append(
header::CONTENT_SECURITY_POLICY,
HeaderValue::from_static("default-src *:*/content/ *:*/blockheight *:*/blockhash *:*/blockhash/ *:*/blocktime *:*/r/ 'unsafe-eval' 'unsafe-inline' data: blob:"),
);
}
Some(origin) => {
let csp = format!("default-src {origin}/content/ {origin}/blockheight {origin}/blockhash {origin}/blockhash/ {origin}/blocktime {origin}/r/ 'unsafe-eval' 'unsafe-inline' data: blob:");
headers.insert(
header::CONTENT_SECURITY_POLICY,
HeaderValue::from_str(&csp).map_err(|err| ServerError::Internal(Error::from(err)))?,
);
}
}

headers.insert(
header::CACHE_CONTROL,
@@ -1052,6 +1070,7 @@ impl Server {
async fn preview(
Extension(index): Extension<Arc<Index>>,
Extension(config): Extension<Arc<Config>>,
Extension(page_config): Extension<Arc<PageConfig>>,
Path(inscription_id): Path<InscriptionId>,
accept_encoding: AcceptEncoding,
) -> ServerResult<Response> {
@@ -1089,7 +1108,7 @@ impl Server {
.into_response(),
),
Media::Iframe => Ok(
Self::content_response(inscription, accept_encoding)?
Self::content_response(inscription, accept_encoding, &page_config)?
.ok_or_not_found(|| format!("inscription {inscription_id} content"))?
.into_response(),
),
@@ -3131,7 +3150,8 @@ mod tests {
assert_eq!(
Server::content_response(
Inscription::new(Some("text/plain".as_bytes().to_vec()), None),
AcceptEncoding::default()
AcceptEncoding::default(),
&PageConfig::default(),
)
.unwrap(),
None
@@ -3143,6 +3163,7 @@ mod tests {
let (headers, body) = Server::content_response(
Inscription::new(Some("text/plain".as_bytes().to_vec()), Some(vec![1, 2, 3])),
AcceptEncoding::default(),
&PageConfig::default(),
)
.unwrap()
.unwrap();
@@ -3151,6 +3172,38 @@ mod tests {
assert_eq!(body, vec![1, 2, 3]);
}

#[test]
fn content_security_policy_no_origin() {
let (headers, _) = Server::content_response(
Inscription::new(Some("text/plain".as_bytes().to_vec()), Some(vec![1, 2, 3])),
AcceptEncoding::default(),
&PageConfig::default(),
)
.unwrap()
.unwrap();

assert_eq!(
headers["content-security-policy"],
HeaderValue::from_static("default-src 'self' 'unsafe-eval' 'unsafe-inline' data: blob:")
);
}

#[test]
fn content_security_policy_with_origin() {
let (headers, _) = Server::content_response(
Inscription::new(Some("text/plain".as_bytes().to_vec()), Some(vec![1, 2, 3])),
AcceptEncoding::default(),
&PageConfig {
csp_origin: Some("https://ordinals.com".into()),
..Default::default()
},
)
.unwrap()
.unwrap();

assert_eq!(headers["content-security-policy"], HeaderValue::from_static("default-src https://ordinals.com/content/ https://ordinals.com/blockheight https://ordinals.com/blockhash https://ordinals.com/blockhash/ https://ordinals.com/blocktime https://ordinals.com/r/ 'unsafe-eval' 'unsafe-inline' data: blob:"));
}

#[test]
fn code_preview() {
let server = TestServer::new_with_regtest();
@@ -3181,6 +3234,7 @@ mod tests {
let (headers, body) = Server::content_response(
Inscription::new(None, Some(Vec::new())),
AcceptEncoding::default(),
&PageConfig::default(),
)
.unwrap()
.unwrap();
@@ -3194,6 +3248,7 @@ mod tests {
let (headers, body) = Server::content_response(
Inscription::new(Some("\n".as_bytes().to_vec()), Some(Vec::new())),
AcceptEncoding::default(),
&PageConfig::default(),
)
.unwrap()
.unwrap();
10 changes: 7 additions & 3 deletions src/templates.rs
Original file line number Diff line number Diff line change
@@ -117,6 +117,7 @@ mod tests {
assert_regex_match!(
Foo.page(Arc::new(PageConfig {
chain: Chain::Mainnet,
csp_origin: Some("https://signet.ordinals.com".into()),
domain: Some("signet.ordinals.com".into()),
index_sats: true,
}),),
@@ -162,9 +163,10 @@ mod tests {
assert_regex_match!(
Foo.page(Arc::new(PageConfig {
chain: Chain::Mainnet,
csp_origin: None,
domain: None,
index_sats: true,
}),),
})),
r".*<nav class=links>\s*<a href=/>Ordinals<sup>alpha</sup></a>.*"
);
}
@@ -174,9 +176,10 @@ mod tests {
assert_regex_match!(
Foo.page(Arc::new(PageConfig {
chain: Chain::Mainnet,
csp_origin: None,
domain: None,
index_sats: false,
}),),
})),
r".*<nav class=links>\s*<a href=/>Ordinals<sup>alpha</sup></a>.*<a href=/clock>Clock</a>\s*<form action=/search.*",
);
}
@@ -186,9 +189,10 @@ mod tests {
assert_regex_match!(
Foo.page(Arc::new(PageConfig {
chain: Chain::Signet,
csp_origin: None,
domain: None,
index_sats: true,
}),),
})),
r".*<nav class=links>\s*<a href=/>Ordinals<sup>signet</sup></a>.*"
);
}

0 comments on commit bd6a1a7

Please sign in to comment.