From 06139d00117a1994022a1ff28ef8121c92101f99 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Mon, 2 Nov 2026 20:36:46 -0500 Subject: [PATCH 1/2] Export for Screens: keep PDF passwords out of remembered settings A successful Export for Screens stored its params in the document's export settings, which native saves write, PDF security passwords included, although the PDF settings themselves are saved without them. Saved export presets kept them in the preferences the same way. Both now drop openPassword and permissionsPassword wherever they appear in the settings, keeping the other security choices. --- crates/engine/src/cmd/fileio/screens.rs | 46 ++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/cmd/fileio/screens.rs b/crates/engine/src/cmd/fileio/screens.rs index 19381709a..b6fe4d85a 100644 --- a/crates/engine/src/cmd/fileio/screens.rs +++ b/crates/engine/src/cmd/fileio/screens.rs @@ -698,6 +698,8 @@ fn preset_settings(s: &Session, cmd: &str, given: &Map) -> Result // Refuse what an export would refuse, now rather than when the preset is used. check_settings(s, &Value::Object(settings.clone())).map_err(|e| bad(cmd, e.to_string()))?; Folders::of(&ScreenParams::of(&Value::Object(settings.clone()))?).map_err(|e| bad(cmd, e.to_string()))?; + let mut settings = settings; + remove_passwords(&mut settings); Ok(settings) } @@ -1275,9 +1277,33 @@ fn remember(s: &mut Session, p: &Value) { let _ = store_settings(s, settings); } +/// The PDF passwords an export may be given (`security` of a PDF row or of `settings.pdf`). +const PASSWORD_KEYS: [&str; 2] = ["openPassword", "permissionsPassword"]; + +/// `settings` without the PDF passwords anywhere in them: settings kept in the document (which +/// native saves write) or in the preferences hold the export's choices, never its secrets, as +/// the PDF settings themselves are saved without them. +fn remove_passwords(settings: &mut Map) { + for key in PASSWORD_KEYS { + settings.remove(key); + } + for v in settings.values_mut() { + remove_passwords_in(v); + } +} + +fn remove_passwords_in(v: &mut Value) { + match v { + Value::Object(m) => remove_passwords(m), + Value::Array(items) => items.iter_mut().for_each(remove_passwords_in), + _ => {} + } +} + /// Make `settings` the document's export settings. Not an undo step (like the settings of any /// export); the document has changed only if they differ. -pub(crate) fn store_settings(s: &mut Session, settings: Map) -> Result<()> { +pub(crate) fn store_settings(s: &mut Session, mut settings: Map) -> Result<()> { + remove_passwords(&mut settings); let st = s.doc_mut()?; if st.doc.export_settings != settings { Arc::make_mut(&mut st.doc).export_settings = settings; @@ -1314,6 +1340,7 @@ mod tests { assert_eq!(out.claim("same.png", "last").as_deref(), Some("same-20001.png")); } + #[test] fn size_parse_refuses_signs_and_exponents() { for s in ["+2x", "+2", "+100w", "+100h", "+72ppi", "-2x", "1e2", "2e0w"] { @@ -1334,4 +1361,21 @@ mod tests { assert_eq!(safe_file_name("lpt9 .png"), "lpt9-.png"); assert_eq!(safe_file_name("My CON .png"), "My CON .png"); } + + /// The settings an export leaves in the document keep its PDF security choices but not its + /// passwords, in a row or in `settings.pdf`. + #[test] + fn stored_settings_leave_out_pdf_passwords() { + let mut s = crate::Session::new(); + s.execute("file.new", &serde_json::json!({"width": 100, "height": 100})).unwrap(); + let given = serde_json::json!({ + "formats": [{"format": "pdf", "security": {"openPassword": "open sesame", "permissionsPassword": "owner", "printing": "low"}}], + "settings": {"pdf": {"security": {"openPassword": "open sesame", "copy": false}}}, + }); + super::store_settings(&mut s, given.as_object().unwrap().clone()).unwrap(); + let kept = serde_json::Value::Object(s.doc().unwrap().doc.export_settings.clone()); + assert!(!kept.to_string().contains("Password"), "{kept}"); + assert_eq!(kept["formats"][0]["security"]["printing"], "low"); + assert_eq!(kept["settings"]["pdf"]["security"]["copy"], false); + } } From c59cd281dbde39213b71e31c5aea58a0a2f0e8c7 Mon Sep 17 00:00:00 2001 From: bflatastic Date: Tue, 3 Nov 2026 15:58:02 +0700 Subject: [PATCH 2/2] Format with rustfmt --- crates/engine/src/cmd/fileio/screens.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/engine/src/cmd/fileio/screens.rs b/crates/engine/src/cmd/fileio/screens.rs index b6fe4d85a..dcc8833a8 100644 --- a/crates/engine/src/cmd/fileio/screens.rs +++ b/crates/engine/src/cmd/fileio/screens.rs @@ -1340,7 +1340,6 @@ mod tests { assert_eq!(out.claim("same.png", "last").as_deref(), Some("same-20001.png")); } - #[test] fn size_parse_refuses_signs_and_exponents() { for s in ["+2x", "+2", "+100w", "+100h", "+72ppi", "-2x", "1e2", "2e0w"] {