test: document the #3276 regression tests and helpers

This commit is contained in:
skylar taylor-barrick
2026-10-18 19:46:41 +02:00
parent 0bafd8ba08
commit d06641a133
2 changed files with 16 additions and 0 deletions
+5
View File
@@ -378,11 +378,13 @@ fn removing_an_inline_encrypt_dictionary_leaves_an_unencrypted_file() {
// Undo can restore a snapshot whose output security differs from the last saved file (#3276).
// Overlaying such a snapshot onto that file must be refused before anything changes.
/// Edit `/Title` in the document information dictionary: an unsaved change to compare.
fn set_title(doc: &mut Document, title: &str) {
let info = doc.trailer().reference(b"Info").unwrap();
doc.update_dict(info, |d| d.set(b"Title".to_vec(), Object::String(PdfString { bytes: title.as_bytes().to_vec(), hex: false }))).unwrap();
}
/// Protect again with the same passwords and no restrictions; `seed` picks the key and `/ID`.
fn rekey(doc: &mut Document, alg: Algorithm, seed: u8) {
doc.set_encryption(&NewEncryption {
algorithm: alg,
@@ -395,6 +397,7 @@ fn rekey(doc: &mut Document, alg: Algorithm, seed: u8) {
.unwrap();
}
/// Replace the first file identifier, which is part of the key, and keep the second.
fn with_first_id(doc: &mut Document, id: &[u8]) {
let Some(Object::Array(ids)) = doc.trailer().get(b"ID").cloned() else { panic!("no /ID") };
let id = Object::String(PdfString { bytes: id.to_vec(), hex: true });
@@ -407,6 +410,8 @@ fn state(doc: &Document) -> (Dict, Vec<u32>, Vec<u32>, bool, Vec<u8>) {
(doc.trailer().clone(), doc.object_numbers(), doc.modified_objects(), doc.is_modified(), save)
}
/// Every incompatible pairing is refused with `CosError::Security` and leaves the target exactly
/// as it was; the same overlay with matching security does apply.
#[test]
fn an_overlay_with_different_output_security_is_refused_before_any_change() {
let encrypted = Arc::new(protect(Algorithm::Aes256, "user", "owner", -1));
+11
View File
@@ -2,6 +2,8 @@
use super::*;
use pdfcraft_cos::{Algorithm, Document as CosDocument, NewEncryption, Object};
/// A two-page file with a title and XMP metadata, protected with `user`/`owner` and no
/// restrictions. `inline` moves `/Encrypt` into the trailer, as some writers store it.
fn encrypted_fixture(algorithm: Algorithm, inline: bool) -> Arc<Vec<u8>> {
let mut doc = CosDocument::open(Arc::new(fixture(2))).unwrap();
pdfcraft_organize::set_info(&mut doc, "Title", "Security regression title").unwrap();
@@ -59,6 +61,7 @@ fn assert_saved_content(s: &Session, id: DocId, bytes: Arc<Vec<u8>>, password: O
Ok(())
}
/// Remove protection, save, undo, save: the file is encrypted again with its original passwords.
#[test]
fn undo_saved_security_removal_restores_encrypted_content() {
let mut failures = Vec::new();
@@ -94,6 +97,7 @@ fn undo_saved_security_removal_restores_encrypted_content() {
assert!(failures.is_empty(), "{}", failures.join("\n"));
}
/// Protect, save, undo, save: the file is plain again and readable without a password.
#[test]
fn undo_saved_protection_restores_plain_content() {
let mut failures = Vec::new();
@@ -121,6 +125,8 @@ fn undo_saved_protection_restores_plain_content() {
assert!(failures.is_empty(), "{}", failures.join("\n"));
}
/// Undoing a saved change of algorithm, permissions, metadata encryption or key restores the
/// original security dictionary, even though the password strings never changed.
#[test]
fn undo_saved_rekey_with_the_same_passwords_restores_original_security() {
let mut failures = Vec::new();
@@ -155,6 +161,8 @@ fn undo_saved_rekey_with_the_same_passwords_restores_original_security() {
assert!(failures.is_empty(), "{}", failures.join("\n"));
}
/// A full save renumbers `/Encrypt`; undoing past it must not leave the restored dictionary
/// encrypted as an ordinary object.
#[test]
fn undo_encrypted_compaction_restores_the_encrypt_dictionary_exemption() {
let mut cos = CosDocument::open(Arc::new(fixture(3))).unwrap();
@@ -220,6 +228,7 @@ fn undo_saved_removal_after_unlock_keeps_the_user_restrictions() {
}
}
/// Undo across a save that kept the same security still appends to the last saved file.
#[test]
fn undo_ordinary_encrypted_edits_keeps_the_last_saved_revision() {
let mut s = Session::new();
@@ -237,6 +246,8 @@ fn undo_ordinary_encrypted_edits_keeps_the_last_saved_revision() {
assert_saved_content(&s, id, rotated, Some("user"), true).unwrap();
}
/// Undoing a saved password change restores the original passwords; the new ones no longer open
/// the file.
#[test]
fn undo_saved_password_change_restores_original_passwords() {
let mut s = Session::new();