From f4f91d0d5cfc1e54b1b755da0869974e8c52d65d Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Sat, 24 Oct 2026 21:03:25 -0400 Subject: [PATCH] fix(xfa): require full save when stripping XFA packets (#4568) --- crates/xfa/src/pdf.rs | 1 + crates/xfa/src/tests.rs | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/crates/xfa/src/pdf.rs b/crates/xfa/src/pdf.rs index 8e930c8f46..fd80949e8f 100644 --- a/crates/xfa/src/pdf.rs +++ b/crates/xfa/src/pdf.rs @@ -1203,6 +1203,7 @@ pub fn strip(doc: &mut Document) -> Result { for r in streams { doc.free(r); } + doc.require_full_save(); Ok(out) } diff --git a/crates/xfa/src/tests.rs b/crates/xfa/src/tests.rs index 3175882f89..f0e6816c2f 100644 --- a/crates/xfa/src/tests.rs +++ b/crates/xfa/src/tests.rs @@ -3350,6 +3350,7 @@ fn stripping_takes_the_xfa_out_and_leaves_an_ordinary_document() { ur.set(b"UR3".to_vec(), Object::Dict(pdfcraft_cos::Dict::new())); doc.update_dict(root, |d| d.set(b"Perms".to_vec(), Object::Dict(ur))).unwrap(); let r = strip(&mut doc).unwrap(); + assert!(doc.full_save_required()); assert_eq!(r.packets, vec!["template".to_string(), "datasets".to_string()]); assert_eq!(r.unbound_data, Some(0)); assert_eq!((r.script_buttons, r.image_fields), (0, 1), "{r:?}"); @@ -3435,6 +3436,16 @@ fn stripping_takes_the_xfa_out_and_leaves_an_ordinary_document() { assert!(started.elapsed() < std::time::Duration::from_secs(5), "{:?}", started.elapsed()); } +#[test] +fn strip_marks_document_as_requiring_full_save() { + use crate::pdf::strip; + let mut doc = Document::open(Arc::new(shell(&fields_template("
JP
")))).unwrap(); + render_into(&mut doc).unwrap(); + assert!(!doc.full_save_required()); + strip(&mut doc).unwrap(); + assert!(doc.full_save_required(), "stripping XFA packets must require a full save to prevent data exposure in incremental saves"); +} + #[test] fn som_indices_count_earlier_same_named_siblings() { let draw = |name: Option<&str>| {