fix(shaper): reject invalid explicit scribble targets

Reapply on current upstream main.
This commit is contained in:
Nigel Ihza Firdausy 2026-10-11 07:07:05 +07:00 committed by bflatastic
parent a031ff6c7b
commit 0d748500d6
2 changed files with 20 additions and 1 deletions

View File

@ -500,7 +500,10 @@ pub(crate) fn refresh(before: &Document, d: &mut Document) -> Result<()> {
fn candidates(s: &Session, p: &Value, points: &[Point], tolerance: f64) -> Result<Vec<NodeId>> {
let st = s.doc()?;
let area = points.iter().fold(Rect::new(f64::MAX, f64::MAX, f64::MIN, f64::MIN), |b, p| b.union_pt(*p)).inflate(tolerance, tolerance);
let supplied = p.get("ids").and_then(Value::as_array).map(|v| v.iter().filter_map(Value::as_u64).map(NodeId).collect::<Vec<_>>());
let supplied = match p.get("ids") {
Some(_) => Some(checked_ids_param(s, p, "ids", "shaper.scribble")?),
None => None,
};
let expand = supplied.is_none() && st.selection.objects.len() < 2;
let ids = supplied.unwrap_or_else(|| if st.selection.objects.len() > 1 { st.selection.objects.clone() } else { st.doc.selectable_art() });
let eligible: Vec<NodeId> = ids

View File

@ -42,6 +42,22 @@ fn stroke(s: &Session, id: NodeId, x: f64, y: f64) -> bool {
})
}
/// An invalid explicit Shaper target must never silently change which shapes get edited.
#[test]
fn shaper_scribble_rejects_malformed_explicit_targets() {
let mut s = session();
let a = rectangle(&mut s, 0.0, "#ff0000");
let untouched = s.doc().unwrap().doc.layers.clone();
let undo = s.doc().unwrap().history.undo.len();
for ids in [json!([a.0, "bad"]), json!([a.0, 999999]), json!({"id": a.0}), json!("bad")] {
let err = s.execute("shaper.scribble", &json!({"points": [[25, 20], [35, 30]], "ids": ids}));
assert!(err.is_err(), "malformed targets were accepted");
assert_eq!(s.doc().unwrap().doc.layers, untouched);
assert_eq!(s.doc().unwrap().history.undo.len(), undo);
}
s.execute("shaper.scribble", &json!({"points": [[25, 20], [35, 30]], "ids": [a.0]})).unwrap();
}
#[test]
fn shaper_merges_with_origin_color_keeps_live_originals_and_undoes_once() {
for reverse in [false, true] {