From 7d94fdeb489ae8ac18b8c38f011abf961b5fb45c Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Fri, 6 Jan 2023 10:27:20 +0100 Subject: [PATCH] Support parsing encrypted documents in `XRef.indexObjects` (issue 15893) *Please note:* The reduced test-case is *not* a perfect reproduction of the original PDF document, since this one fails to open in e.g. Adobe Reader, but I do believe that it captures the most important points here. For corrupt *and* encrypted PDF documents, it's possible that only some trailer dictionaries actually contain an /Encrypt-entry. Previously we'd could easily miss that, since we generally pick the first not obviously corrupt trailer dictionary, and the solution implemented here is to simply pre-parse all trailer dictionaries to see if there's any /Encrypt-entries. --- src/core/xref.js | 38 ++++++++++++++++++++++--------- test/pdfs/.gitignore | 1 + test/pdfs/issue15893_reduced.pdf | Bin 0 -> 1610 bytes test/test_manifest.json | 7 ++++++ 4 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 test/pdfs/issue15893_reduced.pdf diff --git a/src/core/xref.js b/src/core/xref.js index c55715463..b378e9476 100644 --- a/src/core/xref.js +++ b/src/core/xref.js @@ -581,16 +581,11 @@ class XRef { this.startXRefQueue.push(xrefStm); this.readXRef(/* recoveryMode */ true); } - // finding main trailer - let trailerDict, trailerError; - for (const trailer of [...trailers, "generationFallback", ...trailers]) { - if (trailer === "generationFallback") { - if (!trailerError) { - break; // No need to fallback if there were no validation errors. - } - this._generationFallback = true; - continue; - } + + const trailerDicts = []; + // Pre-parsing the trailers to check if the document is possibly encrypted. + let isEncrypted = false; + for (const trailer of trailers) { stream.pos = trailer; const parser = new Parser({ lexer: new Lexer(stream), @@ -607,6 +602,23 @@ class XRef { if (!(dict instanceof Dict)) { continue; } + trailerDicts.push(dict); + + if (dict.has("Encrypt")) { + isEncrypted = true; + } + } + + // finding main trailer + let trailerDict, trailerError; + for (const dict of [...trailerDicts, "genFallback", ...trailerDicts]) { + if (dict === "genFallback") { + if (!trailerError) { + break; // No need to fallback if there were no validation errors. + } + this._generationFallback = true; + continue; + } // Do some basic validation of the trailer/root dictionary candidate. let validPagesDict = false; try { @@ -628,7 +640,11 @@ class XRef { continue; } // taking the first one with 'ID' - if (validPagesDict && dict.has("ID")) { + if ( + validPagesDict && + (!isEncrypted || dict.has("Encrypt")) && + dict.has("ID") + ) { return dict; } // The current dictionary is a candidate, but continue searching. diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 4439ffd3d..bb699a606 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -413,6 +413,7 @@ !issue2128r.pdf !bug1703683_page2_reduced.pdf !issue5540.pdf +!issue15893_reduced.pdf !issue5549.pdf !visibility_expressions.pdf !issue5475.pdf diff --git a/test/pdfs/issue15893_reduced.pdf b/test/pdfs/issue15893_reduced.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1a4e66fbeeb88e5329ca3dd475e412861431275f GIT binary patch literal 1610 zcmcgsJ!lj`6t=LMMub@Vnj<+RnVJ1RIWKs*9K=Ku?oKU^Xy$I`&JDTUxVwjmRSH2( zp^aEtSlDS}qsBte!kC1fNhhz}p;rp5Bk z0#u0zON4+*Hb4i+wl9wMK#d9`?!iehfjskgh=VIui+h75r^Rf9%k7#U_8v@tMok5Y z2#?xz$F%Gw;NzBk(Dtk-knA!*jX@^3-Skg^VgwnU?{d~c`3j-|b*K~&F4FL?fdk_Zk8wyi6(`$`PjS_<>b*3qd z8dQ~XSuW{1H5*JZSw=DqT{1YYNe!NzO zeX@4<(8bH2o~=~XTUO!bnI&WBk^TMSjr|Xvmi6%qm#k~U=g$rA>yx&SGZLy?cS{Lm z@eIMBoFNLrlQ3}a^@|T?-n!DiyOoK+&B+vY-z7+n|s0u+F{_y7O^ literal 0 HcmV?d00001 diff --git a/test/test_manifest.json b/test/test_manifest.json index 70f277edd..d126594d3 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -386,6 +386,13 @@ "lastPage": 4, "type": "eq" }, + { "id": "issue15893_reduced", + "file": "pdfs/issue15893_reduced.pdf", + "md5": "cf889b927f9f53d164622a99378bf39d", + "rounds": 1, + "type": "eq", + "password": "test" + }, { "id": "bug1727053", "file": "pdfs/bug1727053.pdf", "md5": "8ed1e52da64000f9fdcb8b732f5a58f8",