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.
This commit is contained in:
Dylan M. Taylor
2026-11-03 16:03:38 +07:00
committed by bflatastic
parent 47bc3dd58c
commit 06139d0011
+45 -1
View File
@@ -698,6 +698,8 @@ fn preset_settings(s: &Session, cmd: &str, given: &Map<String, Value>) -> 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<String, Value>) {
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<String, Value>) -> Result<()> {
pub(crate) fn store_settings(s: &mut Session, mut settings: Map<String, Value>) -> 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);
}
}