Skip to content

Commit 082197b

Browse files
committed
gateway: split up the csp lines
Signed-off-by: Younes Khoudli <[email protected]>
1 parent 65476a9 commit 082197b

File tree

3 files changed

+89
-8
lines changed

3 files changed

+89
-8
lines changed

front/public/netzgrafik-frontend/index.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
<!DOCTYPE html>
1+
<!doctype html>
22
<html class="sbb-lean sbb-light">
33
<head>
4-
<base href="/netzgrafik-frontend/">
5-
<link rel="stylesheet" href="/netzgrafik-frontend/assets/styles.css">
4+
<base href="/netzgrafik-frontend/" />
5+
<link rel="stylesheet" href="/netzgrafik-frontend/assets/styles.css" />
66
<script type="module" src="/netzgrafik-frontend/assets/runtime.js"></script>
77
<script type="module" src="/netzgrafik-frontend/assets/polyfills.js"></script>
88
<script type="module" src="/netzgrafik-frontend/assets/vendor.js"></script>

gateway/src/csp.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use std::{
2+
future::{Future, Ready, ready},
3+
pin::Pin,
4+
};
5+
6+
use actix_web::{
7+
Error,
8+
dev::{Service, ServiceRequest, ServiceResponse, Transform, forward_ready},
9+
};
10+
11+
use actix_web::http::header::CONTENT_SECURITY_POLICY;
12+
13+
pub struct Csp;
14+
15+
// `S` - type of the next service
16+
// `B` - type of response's body
17+
impl<S, B> Transform<S, ServiceRequest> for Csp
18+
where
19+
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
20+
S::Future: 'static,
21+
B: 'static,
22+
{
23+
type Response = ServiceResponse<B>;
24+
type Error = Error;
25+
type InitError = ();
26+
type Transform = CSPMiddleware<S>;
27+
type Future = Ready<Result<Self::Transform, Self::InitError>>;
28+
29+
fn new_transform(&self, service: S) -> Self::Future {
30+
ready(Ok(CSPMiddleware { service }))
31+
}
32+
}
33+
34+
pub struct CSPMiddleware<S> {
35+
/// The next service to call
36+
service: S,
37+
}
38+
39+
// This future doesn't have the requirement of being `Send`.
40+
// See: futures_util::future::LocalBoxFuture
41+
type LocalBoxFuture<T> = Pin<Box<dyn Future<Output = T> + 'static>>;
42+
43+
// `S`: type of the wrapped service
44+
// `B`: type of the body - try to be generic over the body where possible
45+
impl<S, B> Service<ServiceRequest> for CSPMiddleware<S>
46+
where
47+
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
48+
S::Future: 'static,
49+
B: 'static,
50+
{
51+
type Response = ServiceResponse<B>;
52+
type Error = Error;
53+
type Future = LocalBoxFuture<Result<Self::Response, Self::Error>>;
54+
55+
// This service is ready when its next service is ready
56+
forward_ready!(service);
57+
58+
fn call(&self, req: ServiceRequest) -> Self::Future {
59+
let csp_string = if req.path().starts_with("/netzgrafik-frontend") {
60+
// FIXME: add unsafe-inline for scripts if we don't keep the next commit
61+
// or, ideally, fix NGE to not require it.
62+
concat!(
63+
"default-src 'self'; form-action 'self'; frame-ancestors 'self'; ",
64+
"connect-src 'self' https://icons.app.sbb.ch; img-src 'self' data:; ",
65+
"font-src 'self' https://cdn.app.sbb.ch/fonts/ data:; style-src 'self' 'unsafe-inline';"
66+
)
67+
} else {
68+
"default-src 'self'; frame-ancestors 'self'; form-action 'self'; img-src 'self' data:; font-src 'self' data:; style-src 'self';"
69+
};
70+
let fut = self.service.call(req);
71+
72+
Box::pin(async move {
73+
let mut res = fut.await?;
74+
if !res.headers().contains_key(CONTENT_SECURITY_POLICY) {
75+
res.headers_mut().insert(
76+
CONTENT_SECURITY_POLICY,
77+
actix_web::http::header::HeaderValue::from_static(csp_string),
78+
);
79+
}
80+
Ok(res)
81+
})
82+
}
83+
}

gateway/src/main.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use actix_auth::AuthMiddleware;
2222

2323
mod config;
2424
mod config_parser;
25+
mod csp;
2526
mod request_modifier;
2627

2728
#[actix_web::main]
@@ -86,13 +87,10 @@ async fn main() -> std::io::Result<()> {
8687
actix_cors::Cors::default()
8788
};
8889

89-
let default_headers = actix_web::middleware::DefaultHeaders::new().add((
90-
"Content-Security-Policy",
91-
"default-src 'self'; connect-src 'self' https://icons.app.sbb.ch; frame-ancestors 'self'; form-action 'self'; img-src 'self' data:; font-src 'self' https://cdn.app.sbb.ch/fonts/ data:; style-src 'self' 'unsafe-inline';",
92-
));
90+
let csp = csp::Csp;
9391

9492
let mut app = App::new()
95-
.wrap(default_headers)
93+
.wrap(csp)
9694
.wrap(cors)
9795
.wrap(RequestTracing::new())
9896
.wrap(Compress::default()) // enable compress

0 commit comments

Comments
 (0)