M12: sign_document reads every argument before the preSign scripts run

This commit is contained in:
Eric Kaden
2026-10-24 14:04:42 -04:00
parent a756832dd5
commit 9a81dd0716
2 changed files with 22 additions and 13 deletions
+7 -13
View File
@@ -253,6 +253,11 @@ impl Automation {
// A dynamic form's signing scripts run only when asked: what preSign changes is signed,
// and the caller hasn't seen it. What they changed is listed.
let run_scripts = a.opt_bool("run_form_scripts")?.unwrap_or(false);
// Every argument is read before the preSign scripts change anything, so a bad one is
// refused with the document as it was.
let reason = a.opt_str("reason")?.map(str::to_string);
let location = a.opt_str("location")?.map(str::to_string);
let contact = a.opt_str("contact")?.map(str::to_string);
let signing = field.clone();
let scripted = signing.as_deref().is_some_and(|f| {
self.session.sign_event_scripted(doc_id, f, SignEvent::Before) || self.session.sign_event_scripted(doc_id, f, SignEvent::After)
@@ -278,19 +283,8 @@ impl Automation {
} else {
Vec::new()
};
let opts = SignOptions {
field,
page,
rect,
reason: a.opt_str("reason")?.map(str::to_string),
location: a.opt_str("location")?.map(str::to_string),
contact: a.opt_str("contact")?.map(str::to_string),
certify,
appearance,
appearance_labels,
graphic,
..SignOptions::default()
};
let opts =
SignOptions { field, page, rect, reason, location, contact, certify, appearance, appearance_labels, graphic, ..SignOptions::default() };
let signed = self.session.sign(doc_id, &id, opts).map_err(|e| match e {
// A graphic the signer refuses is the caller's to fix.
pdfcraft_engine::EditError::Sign(m) if m.starts_with("the signature graphic") => bad(m),
+15
View File
@@ -5804,6 +5804,21 @@ fn signing_an_xfa_box_runs_its_signing_scripts_through_tools() {
"{unasked}"
);
let doc = ok(&mut a, "doc_open", json!({ "path": "form.pdf" }))["doc"].as_u64().unwrap();
// A bad argument is refused before the preSign scripts run.
let bad = a.call(
"sign_document",
&json!({ "doc": doc, "id": "ada.p12", "password": "secret12", "field": name("sign"), "out": "bad.pdf", "run_form_scripts": true, "reason": 5 }),
);
assert!(matches!(bad, Err(ToolError::InvalidArgs(_))), "{bad:?}");
let now = ok(&mut a, "form_fields", json!({ "doc": doc }));
assert!(
now["fields"]
.as_array()
.unwrap()
.iter()
.any(|f| f["name"].as_str().is_some_and(|n| n == "when" || n.ends_with(".when[0]")) && f["value"].is_null()),
"{now}"
);
// A signed file that can't be written (`out` is a folder): not signed, and what preSign did
// is taken back.
std::fs::create_dir_all(dir.join("taken")).unwrap();