#3692: formatting restriction refuses Copy Styles

Finding 50. check_formatting blocked Update Styles and Attach Template
with update, but not styles.copyFrom, which replaces style definitions
(locked flag included) from another file. Under "limit formatting to a
selection of styles" an editor could import changed formatting and
unlock a locked style without the password. styles.copyFrom is now
refused like Update Styles. copy_styles' callers are all blocked under
the restriction, so its lock handling is left as is.

Test: cmd::protect::tests::formatting_restriction_refuses_attach_template_update (extended).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Martin Ball
2026-10-23 20:19:49 +01:00
co-authored by Claude Opus 5.5
parent 6142567b6a
commit 66e92e3698
+10 -1
View File
@@ -406,6 +406,8 @@ fn check_formatting(s: &Session, id: &str, params: &Value) -> Result<(), CmdErro
| "table.modifyStyle"
| "table.deleteStyle"
| "tools.updateStyles"
// Copies style definitions in from another file, locked flags included (#3692).
| "styles.copyFrom"
)
// Attach Template updates the styles from it when asked (`linkStyles` is only a setting).
|| (id == "tools.attachTemplate" && p::bool(params, "update") == Some(true));
@@ -745,7 +747,8 @@ mod tests {
}
/// Formatting limited to styles: attaching a template refuses to update the styles from it
/// (it would bypass the Update Styles block); attaching without updating still works.
/// (it would bypass the Update Styles block), nor can Copy Styles; attaching without
/// updating still works.
#[test]
fn formatting_restriction_refuses_attach_template_update() {
let mut t = Document::from_text("Template.");
@@ -759,6 +762,12 @@ mod tests {
let e = s.run("tools.attachTemplate", &json!({"path": "Report.dotx", "update": true, "data": data})).unwrap_err().to_string();
assert!(e.contains("formatting is limited"), "{e}");
assert_eq!(s.doc, before);
// Copying styles in replaces their definitions, locked flag included (#3692).
s.doc.styles.styles.iter_mut().filter(|st| st.id == "Heading1").for_each(|st| st.locked = true);
let before = s.doc.clone();
let e = s.run("styles.copyFrom", &json!({"path": "Report.dotx", "styles": ["Heading 1"], "data": data})).unwrap_err().to_string();
assert!(e.contains("formatting is limited"), "{e}");
assert_eq!(s.doc, before);
s.run("tools.attachTemplate", &json!({"path": "Report.dotx"})).unwrap();
assert_eq!(s.doc.settings.attached_template.as_deref(), Some("Report.dotx"));
}