From 0ff3a3682ea8b2e2df68fab3156c2c99d4b8c442 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Sun, 8 Nov 2026 19:22:04 +0000 Subject: [PATCH 1/2] Reject negative and fractional index in file.openRecent Found while testing crates/engine/src/cmd/file/open.rs:33 (open_recent, `.nth(i.max(0.0) as usize)`) Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/engine/src/cmd/file/open.rs | 5 ++++- crates/engine/src/menu_gaps_tests.rs | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/cmd/file/open.rs b/crates/engine/src/cmd/file/open.rs index e4c3e0fb7..b6e154c35 100644 --- a/crates/engine/src/cmd/file/open.rs +++ b/crates/engine/src/cmd/file/open.rs @@ -31,7 +31,10 @@ pub(super) fn open_recent(s: &mut Session, p: &Value) -> Result { Some(path) } (None, Some(i)) => { - Some(s.prefs.recent_files.iter().filter(|r| listed(r)).nth(i.max(0.0) as usize).cloned().ok_or_else(|| bad(CMD, "no such recent file"))?) + if !(i >= 0.0 && i.fract() == 0.0) { + return Err(bad(CMD, "no such recent file")); + } + Some(s.prefs.recent_files.iter().filter(|r| listed(r)).nth(i as usize).cloned().ok_or_else(|| bad(CMD, "no such recent file"))?) } _ => None, }; diff --git a/crates/engine/src/menu_gaps_tests.rs b/crates/engine/src/menu_gaps_tests.rs index 057c5119a..fa32f5d8f 100644 --- a/crates/engine/src/menu_gaps_tests.rs +++ b/crates/engine/src/menu_gaps_tests.rs @@ -84,3 +84,20 @@ fn outlined_layers_draw_as_outlines_and_the_rest_normally() { assert_eq!(outlined.pixel(20, 20).unwrap(), empty, "only its outline"); assert_eq!(outlined.pixel(80, 20).unwrap(), plain.pixel(80, 20).unwrap(), "the other layer draws normally"); } + +#[test] +fn open_recent_rejects_negative_and_fractional_index() { + let dir = std::env::temp_dir().join(format!("figcraft-recent-bad-{}", std::process::id())); + std::fs::create_dir_all(&dir).unwrap(); + let a = dir.join("a.figcraft").to_string_lossy().to_string(); + let b = dir.join("b.figcraft").to_string_lossy().to_string(); + let mut s = Session::new(); + s.execute("file.save", &json!({"path": a})).unwrap(); + s.execute("file.saveAs", &json!({"path": b})).unwrap(); + let recent = s.prefs.recent_files.clone(); + for i in [json!(-1), json!(1.5), json!(-0.5)] { + assert!(s.execute("file.openRecent", &json!({"index": i})).is_err(), "index {i} must be an error"); + } + assert_eq!(s.prefs.recent_files, recent); + let _ = std::fs::remove_dir_all(&dir); +} From 82da8f513acdcb49f75cec25685c7e74874178fd Mon Sep 17 00:00:00 2001 From: bflatastic Date: Thu, 12 Nov 2026 09:19:05 +0700 Subject: [PATCH 2/2] file.openRecent: check the index with the shared req_index --- crates/engine/src/cmd/file/open.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/engine/src/cmd/file/open.rs b/crates/engine/src/cmd/file/open.rs index b6e154c35..ea9a0d90a 100644 --- a/crates/engine/src/cmd/file/open.rs +++ b/crates/engine/src/cmd/file/open.rs @@ -30,11 +30,9 @@ pub(super) fn open_recent(s: &mut Session, p: &Value) -> Result { allow(&path, Access::Read)?; Some(path) } - (None, Some(i)) => { - if !(i >= 0.0 && i.fract() == 0.0) { - return Err(bad(CMD, "no such recent file")); - } - Some(s.prefs.recent_files.iter().filter(|r| listed(r)).nth(i as usize).cloned().ok_or_else(|| bad(CMD, "no such recent file"))?) + (None, Some(_)) => { + let i = req_index(CMD, p, "index")?; + Some(s.prefs.recent_files.iter().filter(|r| listed(r)).nth(i).cloned().ok_or_else(|| bad(CMD, "no such recent file"))?) } _ => None, };