diff --git a/crates/engine/src/cmd/select.rs b/crates/engine/src/cmd/select.rs index 6c2a3437..319a7b33 100644 --- a/crates/engine/src/cmd/select.rs +++ b/crates/engine/src/cmd/select.rs @@ -294,15 +294,9 @@ fn key(s: &mut Session, p: &Value) -> Result { ok() } -/// `[[subpath, anchor]…]` anchor references (malformed entries are left out). -pub(crate) fn parse_refs(v: Option<&Value>) -> Vec<(usize, usize)> { - v.and_then(Value::as_array) - .map(|a| a.iter().filter_map(|x| Some((x.get(0)?.as_u64()? as usize, x.get(1)?.as_u64()? as usize))).collect()) - .unwrap_or_default() -} - -/// Parse an explicitly supplied anchor list without dropping invalid entries. -fn checked_refs(v: Option<&Value>, cmd: &str) -> Result> { +/// `[[subpath, anchor]…]` anchor references of `cmd`: a malformed entry fails rather than being +/// left out, which would act on other anchors than the ones given. +pub(crate) fn checked_refs(v: Option<&Value>, cmd: &str) -> Result> { let refs = v.and_then(Value::as_array).ok_or_else(|| bad(cmd, "`anchors` must be an array of [subpath, anchor] pairs"))?; refs.iter() .map(|v| { diff --git a/crates/engine/src/cmd/typecmd.rs b/crates/engine/src/cmd/typecmd.rs index 2395b3f5..5692b6ad 100644 --- a/crates/engine/src/cmd/typecmd.rs +++ b/crates/engine/src/cmd/typecmd.rs @@ -628,7 +628,7 @@ fn area_options(s: &mut Session, p: &Value) -> Result { fn reshape_area(s: &mut Session, p: &Value) -> Result { const C: &str = "text.reshapeArea"; let id = id_param(p, "id").ok_or_else(|| bad(C, "missing id"))?; - let mut refs = super::select::parse_refs(p.get("anchors")); + let mut refs = super::select::checked_refs(p.get("anchors"), C)?; refs.sort_unstable(); refs.dedup(); if refs.is_empty() { diff --git a/crates/engine/src/tests_typearea.rs b/crates/engine/src/tests_typearea.rs index 946fcd88..9c59eac5 100644 --- a/crates/engine/src/tests_typearea.rs +++ b/crates/engine/src/tests_typearea.rs @@ -236,6 +236,9 @@ fn reshape_area_takes_untrusted_params() { json!({"id": id.0, "dx": 1, "dy": 1}), json!({"id": id.0, "anchors": [], "dx": 1, "dy": 1}), json!({"id": id.0, "anchors": [["a", 1], [0], "x"], "dx": 1, "dy": 1}), + // A malformed reference among good ones fails rather than moving only the good ones. + json!({"id": id.0, "anchors": [[0, 2], [0, "3"]], "dx": 1, "dy": 1}), + json!({"id": id.0, "anchors": [[0, 2], [0, 3, 1]], "dx": 1, "dy": 1}), json!({"id": id.0, "anchors": [[0, 9]], "dx": 1, "dy": 1}), json!({"id": id.0, "anchors": [[0, 1], [7, 0]], "dx": 1, "dy": 1}), json!({"id": id.0, "anchors": [[0, 2]], "dy": 1}),