From 87b9a3a9ce79b73b89226a2263beab80fd078d89 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Thu, 15 Oct 2026 01:08:36 +0000 Subject: [PATCH] Reject negative or fractional index in effect.set, export.update and export.remove Found while testing crates/engine/src/cmd/edit.rs:256 (effect.set), :305 (export.update), :317 (export.remove) Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/cmd/edit.rs | 6 +++--- crates/engine/src/effects_tests.rs | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/crates/engine/src/cmd/edit.rs b/crates/engine/src/cmd/edit.rs index 57ba9fb0..cb194497 100644 --- a/crates/engine/src/cmd/edit.rs +++ b/crates/engine/src/cmd/edit.rs @@ -253,7 +253,7 @@ pub fn specs() -> Vec { ok() }), spec!("effect.set", "Set effect", "{ids?, index, effect {..}}", true, |s, p| { - let i = req_num("effect.set", p, "index")? as usize; + let i = req_index("effect.set", p, "index")?; let e: Effect = parse("effect.set", p, "effect")?.ok_or_else(|| bad("effect.set", "`effect` required"))?; for id in req_targets("effect.set", s, p)? { let n = s.doc_mut()?.get_mut(id)?; @@ -302,7 +302,7 @@ pub fn specs() -> Vec { true, |s, p| { let cmd = "export.update"; - let i = req_num(cmd, p, "index")? as usize; + let i = req_index(cmd, p, "index")?; super::file::export_setting(cmd, ExportSetting::default(), p)?; for id in req_targets(cmd, s, p)? { let n = s.doc_mut()?.get_mut(id)?; @@ -314,7 +314,7 @@ pub fn specs() -> Vec { } ), spec!("export.remove", "Remove export setting", "{ids?, index}", true, |s, p| { - let i = req_num("export.remove", p, "index")? as usize; + let i = req_index("export.remove", p, "index")?; for id in req_targets("export.remove", s, p)? { let n = s.doc_mut()?.get_mut(id)?; if i < n.exports.len() { diff --git a/crates/engine/src/effects_tests.rs b/crates/engine/src/effects_tests.rs index 5142c40d..be69fac2 100644 --- a/crates/engine/src/effects_tests.rs +++ b/crates/engine/src/effects_tests.rs @@ -85,3 +85,24 @@ fn inspect_and_css_show_the_new_effects() { let css = code_for(doc, r, CodeLang::Css); assert!(css.contains("backdrop-filter: blur(2px);"), "{css}"); } + +#[test] +fn negative_or_fractional_index_is_refused_not_item_zero() { + let mut s = Session::new(); + let r = rect(&mut s); + s.execute("effect.add", &json!({"ids": [r], "type": "layerBlur"})).unwrap(); + s.execute("export.add", &json!({"ids": [r]})).unwrap(); + s.execute("export.add", &json!({"ids": [r]})).unwrap(); + let before = s.doc().unwrap().node(r).unwrap().clone(); + assert_eq!(before.exports.len(), 2); + for index in [json!(-1), json!(-0.5), json!(1.5)] { + let set = json!({"ids": [r], "index": index, "effect": {"type": "dropShadow"}}); + assert!(s.execute("effect.set", &set).is_err(), "effect.set index {index}"); + let upd = json!({"ids": [r], "index": index, "format": "svg"}); + assert!(s.execute("export.update", &upd).is_err(), "export.update index {index}"); + assert!(s.execute("export.remove", &json!({"ids": [r], "index": index})).is_err(), "export.remove index {index}"); + let n = s.doc().unwrap().node(r).unwrap(); + assert_eq!(n.effects, before.effects, "index {index}"); + assert_eq!(n.exports, before.exports, "index {index}"); + } +}