Merge 995f6f2dde88dce365cdae8f7657505a3c41841e into 5d4f0659bcd9b4fd3bda4804ce09c7e0b5dcec04

This commit is contained in:
Aditi 2025-08-25 14:05:04 +02:00 committed by GitHub
commit 5bd6bbd5f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 61 additions and 16 deletions

View File

@ -31,6 +31,7 @@ import {
} from "./core_utils.js";
import { BaseStream } from "./base_stream.js";
import { CipherTransformFactory } from "./crypto.js";
import { NameTree } from "./name_number_tree.js";
class XRef {
#firstXRefStmPos = null;
@ -118,22 +119,6 @@ class XRef {
}
warn(`XRef.parse - Invalid "Encrypt" reference: "${ex}".`);
}
if (encrypt instanceof Dict) {
const ids = trailerDict.get("ID");
const fileId = ids?.length ? ids[0] : "";
// The 'Encrypt' dictionary itself should not be encrypted, and by
// setting `suppressEncryption` we can prevent an infinite loop inside
// of `XRef_fetchUncompressed` if the dictionary contains indirect
// objects (fixes issue7665.pdf).
encrypt.suppressEncryption = true;
this.encrypt = new CipherTransformFactory(
encrypt,
fileId,
this.pdfManager.password
);
}
// Get the root dictionary (catalog) object, and do some basic validation.
let root;
try {
root = trailerDict.get("Root");
@ -143,6 +128,43 @@ class XRef {
}
warn(`XRef.parse - Invalid "Root" reference: "${ex}".`);
}
if (encrypt instanceof Dict) {
// Check if only the file attachments are encrypted.
if (
encrypt.get("CF")?.get("StdCF")?.get("AuthEvent")?.name === "EFOpen"
) {
let hasEncryptedAttachments = false;
if (root instanceof Dict) {
const names = root.get("Names");
if (names instanceof Dict && names.has("EmbeddedFiles")) {
const nameTree = new NameTree(names.getRaw("EmbeddedFiles"), this);
const attachments = nameTree.getAll();
if (attachments.size > 0) {
hasEncryptedAttachments = true;
}
}
}
if (!hasEncryptedAttachments) {
// If there are no encrypted attachments, encrypt dictionary is
// not needed.
encrypt = null;
}
} else {
const ids = trailerDict.get("ID");
const fileId = ids?.length ? ids[0] : "";
// The 'Encrypt' dictionary itself should not be encrypted, and by
// setting `suppressEncryption` we can prevent an infinite loop inside
// of `XRef_fetchUncompressed` if the dictionary contains indirect
// objects (fixes issue7665.pdf).
encrypt.suppressEncryption = true;
this.encrypt = new CipherTransformFactory(
encrypt,
fileId,
this.pdfManager.password
);
}
}
if (root instanceof Dict) {
try {
const pages = root.get("Pages");

View File

@ -385,6 +385,7 @@
!bug1020226.pdf
!issue9534_reduced.pdf
!attachment.pdf
!issue20049.pdf
!basicapi.pdf
!issue15590.pdf
!issue15594_reduced.pdf

BIN
test/pdfs/issue20049.pdf Normal file

Binary file not shown.

View File

@ -5508,6 +5508,13 @@
"rounds": 1,
"type": "eq"
},
{
"id": "issue20049",
"file": "pdfs/issue20049.pdf",
"md5": "1cdfde56be6b070e0c18aafc487d92ff",
"rounds": 1,
"type": "eq"
},
{
"id": "issue8117",
"file": "pdfs/issue8117.pdf",

View File

@ -878,6 +878,21 @@ describe("api", function () {
await loadingTask.destroy();
});
it("should not prompt for password if only attachments are encrypted and there are none", async function () {
const loadingTask = getDocument(buildGetDocumentParams("issue20049.pdf"));
expect(loadingTask instanceof PDFDocumentLoadingTask).toEqual(true);
loadingTask.onPassword = function (callback, reason) {
if (reason === PasswordResponses.NEED_PASSWORD) {
expect(false).toEqual(true);
throw new Error("Should not prompt for password.");
}
};
const pdfDocument = await loadingTask.promise;
expect(pdfDocument.numPages).toBeGreaterThan(0);
});
});
describe("PDFWorker", function () {