This commit is contained in:
Mateusz Gruszczyński committed 2026-11-08 12:57:19 +01:00
1 parent b6e8ddb9c7
commit 818cae8592
12 files changed
+253 -33

No files matched your search

+38 -2
View File
@@ -34,6 +34,7 @@ pub fn public_router(app: Arc<app::App>) -> Router {
.route("/autodiscover/autodiscover.xml", any(autodiscover::handler))
.route("/healthz", axum::routing::get(healthz))
.route("/", axum::routing::get(index))
.route("/favicon.svg", axum::routing::get(favicon))
.fallback(not_found)
.layer(axum::middleware::map_response(mark))
.with_state(app)
@@ -47,10 +48,45 @@ async fn mark(mut r: axum::response::Response) -> axum::response::Response {
r
}
const LANDING: &str = include_str!("../web/landing.html");
const FAVICON: &str = include_str!("../web/favicon.svg");
/// Landing page for browsers opening the ActiveSync host. Self-contained (inline SVG, one inline
/// style block pinned by a hash in the CSP, no scripts, no external requests).
async fn index() -> impl axum::response::IntoResponse {
static CSP: std::sync::OnceLock<String> = std::sync::OnceLock::new();
let csp = CSP.get_or_init(|| {
use base64::Engine;
use sha2::{Digest, Sha256};
let css = LANDING
.split_once("<style>")
.and_then(|(_, r)| r.split_once("</style>"))
.map(|(c, _)| c)
.unwrap_or("");
let hash = base64::engine::general_purpose::STANDARD.encode(Sha256::digest(css.as_bytes()));
format!(
"default-src 'none'; style-src 'sha256-{hash}'; img-src 'self' data:; base-uri 'none'; form-action 'none'; frame-ancestors 'none'"
)
});
(
[("Content-Type", "text/plain; charset=utf-8")],
"ActivePush - Exchange ActiveSync endpoint.\nDevices connect to /Microsoft-Server-ActiveSync.\n",
[
("Content-Type", "text/html; charset=utf-8"),
("Content-Security-Policy", csp.as_str()),
("X-Content-Type-Options", "nosniff"),
("Referrer-Policy", "no-referrer"),
("Cache-Control", "no-cache"),
],
LANDING,
)
}
async fn favicon() -> impl axum::response::IntoResponse {
(
[
("Content-Type", "image/svg+xml"),
("Cache-Control", "public, max-age=86400"),
],
FAVICON,
)
}