Sketch cut and paste: refuse parameters that are not an object (no panic) (#1577)

Thanks @Ian-cmd-ops.
This commit is contained in:
H. Conrad authored and GitHub committed 2026-10-15 23:34:18 -04:00
1 parent 6220d8f8a6
commit 716107f0e4
2 files changed
+22

No files matched your search

@@ -242,6 +242,10 @@ fn paste_step(extent: f64) -> f64 {
fn paste(s: &mut Session, p: &Value) -> Result<Value> { fn paste(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.paste"; let cmd = "sketch.paste";
// `p["sketch"] = …` below panics on anything but an object.
if !(p.is_object() || p.is_null()) {
return Err(bad(cmd, "params must be an object"));
}
let Some(clip) = s.sketch_clipboard.clone() else { return Err(bad(cmd, "nothing copied (sketch.copy first)")) }; let Some(clip) = s.sketch_clipboard.clone() else { return Err(bad(cmd, "nothing copied (sketch.copy first)")) };
let target = target_sketch(s, p, cmd)?; let target = target_sketch(s, p, cmd)?;
let positioned = p.get("at").is_some() || p.get("translate").is_some(); let positioned = p.get("at").is_some() || p.get("translate").is_some();
@@ -231,3 +231,21 @@ fn cut_copies_and_deletes_in_one_step_and_pastes_back_in_place() {
run(&mut s, "edit.undo", json!({})); run(&mut s, "edit.undo", json!({}));
assert_eq!(sketch(&s, sid), &before); assert_eq!(sketch(&s, sid), &before);
} }
/// Parameters that are not a JSON object (a number, text, a list) are an error, not a panic:
/// `sketch.cut` and `sketch.paste` add keys to a copy of the parameters, and serde_json panics
/// when a key is set on anything but an object (#1536, found by `hostile_params_never_panic`).
#[test]
fn clipboard_commands_refuse_parameters_that_are_not_an_object() {
let (mut s, rect, _) = drawing();
run(&mut s, "sketch.copy", json!({"entities": [rect[0].clone()]}));
for id in ["sketch.cut", "sketch.copy", "sketch.paste"] {
for hostile in [json!(true), json!(-1), json!(1e308), json!("nope"), json!([]), json!([0, 0, 0]), json!([{"x": 1}])] {
let result = s.execute(id, &hostile);
assert!(!matches!(result, Err(crate::EngineError::Internal(..))), "{id} {hostile} panicked inside the command: {result:?}");
assert!(result.is_err(), "{id} {hostile} should be refused");
}
}
// Nothing was cut or pasted by the refused calls.
assert!(s.execute("sketch.paste", &json!({})).is_ok(), "the clipboard still holds the copy");
}