Reject invalid layout index in master.deleteLayout and renameLayout

Found while testing crates/engine/src/cmd/design.rs:1353 (layout_target), used by delete_layout at :1368 and rename_layout at :1356

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
NetterSilver
2026-10-25 09:16:09 +00:00
co-authored by Claude Haiku 5.5
parent a0eeb341d2
commit 6523a49fb7
+23 -4
View File
@@ -1344,17 +1344,17 @@ fn insert_layout(s: &mut Session, p: &Value) -> Result<Value> {
})
}
fn layout_target(s: &Session, p: &Value) -> Result<(usize, usize)> {
fn layout_target(s: &Session, p: &Value, cmd: &str) -> Result<(usize, usize)> {
let st = s.doc()?;
let (m, l) = match st.selection.target {
Target::Layout { master, layout } => (master, layout),
_ => (master_index(&st.doc, &st.selection), 0),
};
Ok((usize_param(p, "master").unwrap_or(m), usize_param(p, "layout").unwrap_or(l)))
Ok((opt_usize_param(p, "master", cmd)?.unwrap_or(m), opt_usize_param(p, "layout", cmd)?.unwrap_or(l)))
}
fn rename_layout(s: &mut Session, p: &Value) -> Result<Value> {
let (mi, li) = layout_target(s, p)?;
let (mi, li) = layout_target(s, p, "master.renameLayout")?;
let name = str_param(p, "name").ok_or_else(|| bad("master.renameLayout", "missing `name`"))?.to_string();
s.edit(|doc, _| {
let l =
@@ -1366,7 +1366,7 @@ fn rename_layout(s: &mut Session, p: &Value) -> Result<Value> {
}
fn delete_layout(s: &mut Session, p: &Value) -> Result<Value> {
let (mi, li) = layout_target(s, p)?;
let (mi, li) = layout_target(s, p, "master.deleteLayout")?;
s.edit(|doc, sel| {
let id = doc.masters.get(mi).and_then(|m| m.layouts.get(li)).map(|l| l.id).ok_or_else(|| bad("master.deleteLayout", "no such layout"))?;
if doc.slides.iter().any(|x| x.layout == id) {
@@ -1619,6 +1619,25 @@ mod tests {
}
}
#[test]
fn layout_commands_reject_invalid_index() {
let mut s = Session::with_new();
s.execute("master.insertLayout", &json!({})).unwrap();
let count = |s: &Session| s.doc().unwrap().doc.masters.iter().map(|m| m.layouts.len()).sum::<usize>();
let names = |s: &Session| s.doc().unwrap().doc.masters.iter().flat_map(|m| m.layouts.iter().map(|l| l.name.clone())).collect::<Vec<_>>();
let n = count(&s);
let before = names(&s);
for bad in [json!(-1), json!(1.5)] {
assert!(s.execute("master.deleteLayout", &json!({"layout": bad})).is_err());
assert!(s.execute("master.deleteLayout", &json!({"master": bad})).is_err());
assert!(s.execute("master.renameLayout", &json!({"layout": bad, "name": "x"})).is_err());
assert_eq!(count(&s), n);
assert_eq!(names(&s), before);
}
s.execute("master.deleteLayout", &json!({})).unwrap();
assert_eq!(count(&s), n - 1);
}
#[test]
fn civil_dates() {
assert_eq!(super::civil(0), (1970, 1, 1));