refactor: drop /web/ URL prefix from browser routes

The /web/ prefix predated the /api/ split; today it just clutters every URL
without disambiguating anything. All 16 browser routes move to the apex
(/cases, /login, /magic, /events, /audio/...). The 6 /api/* routes are
unchanged. A new /->>/cases redirect closes the apex 404.

The open-redirect guard in magic.rs and case_actions.rs flips from a
positive whitelist (starts_with("/web/")) to a deny-list: same-origin path,
not protocol-relative, not under /api/, no \. The /api/ exclusion is now
load-bearing and covered by tests.

Pre-production: no transition redirects.
This commit is contained in:
2026-05-04 18:36:10 +02:00
parent 3d67cbc1c8
commit 2c6062a53e
43 changed files with 313 additions and 266 deletions
+2 -2
View File
@@ -30,7 +30,7 @@ pub fn extract_session_cookie(resp: &Response) -> Option<String> {
None
}
/// Build the `POST /web/login` request with a URL-encoded form body.
/// Build the `POST /login` request with a URL-encoded form body.
/// Pulled out so tests that need to *inspect* the login response
/// (e.g. session-fixation tests) can reuse the exact wire shape.
pub fn login_request(slug: &str, password: &str) -> Request<Body> {
@@ -62,7 +62,7 @@ pub async fn login(app: &Router, slug: &str, password: &str) -> String {
/// the session store. Pairs with
/// `doctate_server::create_router_and_session_store` — the returned
/// token is what the `CsrfForm<T>` extractor validates on every
/// state-changing POST under `/web/`.
/// state-changing POST under `/`.
///
/// Returns `(cookie_header_value, csrf_token)`.
pub async fn login_with_csrf(