Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
**Vulnerability:** The manual SVG sanitizer `apps/web/src/lib/sanitize-svg.ts` used an insufficient allowlist/blocklist. It allowed `<foreignObject>`, `<use>`, `<animate>`, `<set>`, `<image>`, and `<math>` which could lead to mutation XSS (mXSS) and other attack vectors when rendering untrusted Mermaid or Math blocks.
**Learning:** Manual DOM tree sanitization is notoriously hard to get right because attackers can hide active elements (e.g. `<a>` tags with `javascript:` payloads) inside grandchild trees (like `<math><mtext><a>`) or use SVG-specific tricks (like `<animate href... values="javascript:...">`) which naive list-based cleaners overlook.
**Prevention:** Rather than adding heavy third-party dependencies (like DOMPurify) against explicit project instructions, expanding the `BLOCKED` Set to include these structural injection vectors provides a robust, zero-dependency mitigation. Future expansions should verify nested elements carefully.
## 2026-10-14 - [Axum Middleware Security Headers]
**Vulnerability:** Missing fundamental security headers (X-Frame-Options, X-Content-Type-Options, Strict-Transport-Security) across the Axum HTTP routing backend (`apps/server/src/security.rs`).
**Learning:** Axum provides easy layer definitions using `middleware::from_fn`. We can inject these security headers universally into existing router layers without disrupting other caching (`static_cache_headers`) or CORS (`cors_layer`) mechanisms.
**Prevention:** When developing custom backend routing in Rust Axum, ensure a standard middleware layer enforces security headers alongside CORS configurations to provide defense in depth against client-side exploitation such as clickjacking and MIME-sniffing.
2 changes: 2 additions & 0 deletions apps/server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
});
}
let mut app = worker::worker_router(state.clone())
.layer(middleware::from_fn(security::security_headers))
.layer(middleware::from_fn(security::static_cache_headers))
.layer(security::cors_layer())
.layer(TraceLayer::new_for_http());
Expand Down Expand Up @@ -392,6 +393,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
state.clone(),
worker::fence_stale_leader,
))
.layer(middleware::from_fn(security::security_headers))
.layer(middleware::from_fn(security::static_cache_headers))
.layer(security::cors_layer())
.layer(TraceLayer::new_for_http())
Expand Down
22 changes: 22 additions & 0 deletions apps/server/src/security.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,28 @@ pub fn cors_layer() -> CorsLayer {
])
}

/// Ajoute les headers de sécurité HTTP (X-Frame-Options, X-Content-Type-Options, HSTS).
pub async fn security_headers(req: Request<Body>, next: Next) -> Response<Body> {
let mut res = next.run(req).await;

// HSTS : Strict-Transport-Security, max-age=1 year, includeSubDomains
res.headers_mut().insert(
header::STRICT_TRANSPORT_SECURITY,
HeaderValue::from_static("max-age=31536000; includeSubDomains")
);
// Anti-Clickjacking : X-Frame-Options DENY
res.headers_mut().insert(
header::X_FRAME_OPTIONS,
HeaderValue::from_static("DENY")
);
// Anti-MIME-Sniffing : X-Content-Type-Options nosniff
res.headers_mut().insert(
header::X_CONTENT_TYPE_OPTIONS,
HeaderValue::from_static("nosniff")
);
res
}

/// HTML / routes app : pas de cache (évite page blanche après MAJ Flatpak).
/// Assets fingerprinted `/_astro/*` : cache long.
pub async fn static_cache_headers(req: Request<Body>, next: Next) -> Response<Body> {
Expand Down
Loading