mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-25 18:47:22 -06:00
Merge PR #2702: Remove security also from files with /Encrypt inline in the trailer
This commit is contained in:
@@ -851,9 +851,10 @@ impl Document {
|
||||
self.repair_log = Arc::new(log);
|
||||
}
|
||||
|
||||
/// `true` when there are unsaved edits.
|
||||
/// `true` when there are unsaved edits. Removing an `/Encrypt` dictionary written inline in
|
||||
/// the trailer (as MuPDF writes it) changes no object, and is an edit all the same.
|
||||
pub fn is_modified(&self) -> bool {
|
||||
!self.overlay.is_empty()
|
||||
!self.overlay.is_empty() || self.encryption_changed
|
||||
}
|
||||
|
||||
/// Object numbers changed since the document was opened (or last saved).
|
||||
|
||||
@@ -336,3 +336,41 @@ fn an_earlier_revision_opens_with_the_documents_own_credentials() {
|
||||
assert_eq!(content(¤t.open_with_same_credentials(Arc::new(fixture())).unwrap()), SECRET, "{alg:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// `protect`, with the `/Encrypt` dictionary written inline in the trailer, as MuPDF writes it,
|
||||
/// instead of as an indirect object.
|
||||
fn protect_inline(alg: Algorithm, user: &str, owner: &str) -> Vec<u8> {
|
||||
let mut doc = Document::open(Arc::new(fixture())).unwrap();
|
||||
doc.set_encryption(&NewEncryption {
|
||||
algorithm: alg,
|
||||
user_password: user,
|
||||
owner_password: owner,
|
||||
permissions: -1,
|
||||
encrypt_metadata: true,
|
||||
seed: [9; 32],
|
||||
})
|
||||
.unwrap();
|
||||
let dict = doc.get(doc.trailer().reference(b"Encrypt").unwrap()).as_dict().cloned().unwrap();
|
||||
doc.trailer_mut().set(b"Encrypt".to_vec(), Object::Dict(dict));
|
||||
write_full(&doc, &SaveOptions::default()).unwrap()
|
||||
}
|
||||
|
||||
/// MuPDF writes `/Encrypt` inline in the trailer, so removing it changes no object. It is still
|
||||
/// an unsaved change, and saving must write a file that opens without a password (#2661).
|
||||
#[test]
|
||||
fn removing_an_inline_encrypt_dictionary_leaves_an_unencrypted_file() {
|
||||
for alg in ALL {
|
||||
let bytes = Arc::new(protect_inline(alg, "user", "owner"));
|
||||
assert!(matches!(Document::open(bytes.clone()), Err(CosError::NeedsPassword)), "{alg:?}");
|
||||
let mut doc = Document::open_with_password(bytes, Some("owner")).unwrap();
|
||||
assert!(matches!(doc.trailer().get(b"Encrypt"), Some(Object::Dict(_))), "{alg:?}: inline");
|
||||
assert!(!doc.is_modified(), "{alg:?}");
|
||||
doc.remove_encryption();
|
||||
assert!(doc.is_modified(), "{alg:?}: removing security is an unsaved change");
|
||||
let plain = write_incremental(&doc, &SaveOptions::default()).unwrap();
|
||||
let again = Document::open(Arc::new(plain.clone())).unwrap_or_else(|e| panic!("{alg:?}: {e:?}"));
|
||||
assert!(again.security().is_none() && !again.trailer().contains(b"Encrypt"), "{alg:?}");
|
||||
assert_eq!(content(&again), SECRET, "{alg:?}");
|
||||
assert!(hayro_syntax::Pdf::new(plain).is_ok(), "{alg:?}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1186,6 +1186,22 @@ fn the_permissions_password_unlocks_an_open_document() {
|
||||
assert!(pdfcraft_cos::Document::open(plain).unwrap().security().is_none(), "security removed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_security_from_a_file_with_its_encrypt_dictionary_in_the_trailer() {
|
||||
// MuPDF writes /Encrypt inline in the trailer rather than as an object (#2661).
|
||||
let mut doc = pdfcraft_cos::Document::open_with_password(protected("user", "owner", -1), Some("owner")).unwrap();
|
||||
let r = doc.trailer().reference(b"Encrypt").unwrap();
|
||||
let dict = doc.get(r).as_dict().cloned().unwrap();
|
||||
doc.trailer_mut().set(b"Encrypt".to_vec(), pdfcraft_cos::Object::Dict(dict));
|
||||
let bytes = Arc::new(pdfcraft_cos::write_full(&doc, &Default::default()).unwrap());
|
||||
let mut s = Session::new();
|
||||
let id = s.open("m.pdf", None, bytes, Some("owner")).unwrap();
|
||||
s.apply(id, Edit::RemoveProtection).unwrap();
|
||||
assert!(!s.get(id).unwrap().info.encrypted);
|
||||
let plain = s.save_bytes(id).unwrap();
|
||||
assert!(pdfcraft_cos::Document::open(plain).unwrap().security().is_none(), "security removed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unlocking_needs_the_permissions_password_not_the_open_password() {
|
||||
let mut s = Session::new();
|
||||
|
||||
Reference in New Issue
Block a user