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:
@@ -222,7 +222,7 @@ async fn analyze_case_happy_path_writes_input_and_redirects() {
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
format!("/web/cases/{case_id}")
|
||||
format!("/cases/{case_id}")
|
||||
);
|
||||
|
||||
let input_path = case_dir.join(ANALYSIS_INPUT_FILE);
|
||||
@@ -713,7 +713,7 @@ async fn close_writes_marker_and_hides_case() {
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"/web/cases"
|
||||
"/cases"
|
||||
);
|
||||
let marker = case_dir.join(CLOSE_MARKER);
|
||||
assert!(marker.exists(), ".closed marker missing");
|
||||
@@ -833,12 +833,12 @@ async fn show_closed_query_includes_closed_cases() {
|
||||
.await;
|
||||
assert!(
|
||||
!body.contains(case_id),
|
||||
"default /web/cases must hide closed cases"
|
||||
"default /cases must hide closed cases"
|
||||
);
|
||||
|
||||
// With show_closed=1 it must appear, with the closed badge.
|
||||
let body = common::body_string(
|
||||
app.oneshot(get_with_cookie("/web/cases?show_closed=1", &cookie))
|
||||
app.oneshot(get_with_cookie("/cases?show_closed=1", &cookie))
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
@@ -912,7 +912,7 @@ async fn close_redirects_back_to_referer_query() {
|
||||
|
||||
let mut req = csrf_form_post(paths::case_close(case_id), &cookie, &csrf, "");
|
||||
req.headers_mut()
|
||||
.insert(header::REFERER, "/web/cases?show_closed=1".parse().unwrap());
|
||||
.insert(header::REFERER, "/cases?show_closed=1".parse().unwrap());
|
||||
|
||||
let resp = app.oneshot(req).await.unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::SEE_OTHER);
|
||||
@@ -922,13 +922,13 @@ async fn close_redirects_back_to_referer_query() {
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"/web/cases?show_closed=1"
|
||||
"/cases?show_closed=1"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn close_from_detail_redirects_to_list_preserving_query() {
|
||||
// Close from /web/cases/{uuid}?show_closed=1 must NOT redirect back
|
||||
// Close from /cases/{uuid}?show_closed=1 must NOT redirect back
|
||||
// to the detail page (it 404s after the close). The uuid segment
|
||||
// is stripped; the query survives so the user lands in show-closed.
|
||||
let (cfg, settings) = cfg_llm("close-ref-det");
|
||||
@@ -954,7 +954,7 @@ async fn close_from_detail_redirects_to_list_preserving_query() {
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"/web/cases?show_closed=1",
|
||||
"/cases?show_closed=1",
|
||||
"close from detail must redirect to the LIST with the query intact"
|
||||
);
|
||||
}
|
||||
@@ -985,7 +985,7 @@ async fn reopen_redirects_back_to_referer() {
|
||||
|
||||
let mut req = csrf_form_post(paths::case_reopen(case_id), &cookie, &csrf, "");
|
||||
req.headers_mut()
|
||||
.insert(header::REFERER, "/web/cases?show_closed=1".parse().unwrap());
|
||||
.insert(header::REFERER, "/cases?show_closed=1".parse().unwrap());
|
||||
let resp = app.oneshot(req).await.unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::SEE_OTHER);
|
||||
assert_eq!(
|
||||
@@ -994,7 +994,7 @@ async fn reopen_redirects_back_to_referer() {
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"/web/cases?show_closed=1",
|
||||
"/cases?show_closed=1",
|
||||
"reopen must redirect back to the referer path (preserving the query)"
|
||||
);
|
||||
}
|
||||
@@ -1290,7 +1290,7 @@ async fn reset_case_clears_derived_and_unfails_audio() {
|
||||
assert_eq!(resp.status(), StatusCode::SEE_OTHER);
|
||||
assert_eq!(
|
||||
resp.headers().get(header::LOCATION).unwrap(),
|
||||
&format!("/web/cases/{case_id}")
|
||||
&format!("/cases/{case_id}")
|
||||
);
|
||||
|
||||
// Audio preserved, .failed renamed to .m4a.
|
||||
|
||||
Reference in New Issue
Block a user