Merge pull request #5305 from storytold/fix/sec-export-settings-no-passwords

Export for Screens: keep PDF passwords out of remembered settings
This commit is contained in:
BFlat
2026-11-03 16:30:42 +07:00
committed by GitHub
+44 -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;
@@ -1334,4 +1360,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);
}
}