From 7f2f8ee09af8be0f65cc85d7b4096659d122768d Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Thu, 15 Oct 2026 08:02:06 +0200 Subject: [PATCH] Refuse negative or fractional indexes in page.select and doc.switch Found while testing crates/engine/src/cmd/page.rs:50 Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/cmd/file.rs | 2 +- crates/engine/src/cmd/page.rs | 2 +- crates/engine/src/panels_behaviour_tests.rs | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/crates/engine/src/cmd/file.rs b/crates/engine/src/cmd/file.rs index 781ed6b0..15086d4c 100644 --- a/crates/engine/src/cmd/file.rs +++ b/crates/engine/src/cmd/file.rs @@ -219,7 +219,7 @@ pub fn specs() -> Vec { } ), spec!("doc.switch", "Switch document", "{index}", false, |s, p| { - let i = req_num("doc.switch", p, "index")? as usize; + let i = req_index("doc.switch", p, "index")?; if i >= s.docs.len() { return Err(bad("doc.switch", "no such document")); } diff --git a/crates/engine/src/cmd/page.rs b/crates/engine/src/cmd/page.rs index b2d46988..dc760d77 100644 --- a/crates/engine/src/cmd/page.rs +++ b/crates/engine/src/cmd/page.rs @@ -47,7 +47,7 @@ pub fn specs() -> Vec { let id = match id(p, "id") { Some(i) => i, None => { - let k = req_num("page.select", p, "index")? as usize; + let k = req_index("page.select", p, "index")?; *doc.pages.get(k).ok_or_else(|| bad("page.select", "no such page"))? } }; diff --git a/crates/engine/src/panels_behaviour_tests.rs b/crates/engine/src/panels_behaviour_tests.rs index f2802461..a5a15a1f 100644 --- a/crates/engine/src/panels_behaviour_tests.rs +++ b/crates/engine/src/panels_behaviour_tests.rs @@ -337,6 +337,23 @@ fn pages_by_the_documented_rules() { assert_ne!(s.page().unwrap(), div, "deleting the open page opens a page, never a divider"); } +#[test] +fn page_select_and_doc_switch_refuse_negative_or_fractional_indexes() { + let mut s = Session::new(); + let p2 = id(&ex(&mut s, "page.add", json!({}))); + for bad_index in [json!(-1), json!(0.5)] { + assert!(s.execute("page.select", &json!({"index": bad_index})).is_err(), "page.select index {bad_index} must fail"); + assert_eq!(s.page().unwrap(), p2, "page.select index {bad_index} leaves the open page alone"); + } + ex(&mut s, "file.new", json!({})); + let active = s.active; + assert_eq!(active, 1); + for bad_index in [json!(-1), json!(0.5)] { + assert!(s.execute("doc.switch", &json!({"index": bad_index})).is_err(), "doc.switch index {bad_index} must fail"); + assert_eq!(s.active, active, "doc.switch index {bad_index} leaves the active tab alone"); + } +} + #[test] fn assets_panel_lists_files_and_drops_by_the_documented_rules() { let mut s = Session::new();