Merge pull request #814 from m-holger/check

Warn if catalog type entry is invalid (fixes #810)
This commit is contained in:
Jay Berkenbilt 2023-03-20 06:59:47 -04:00 committed by GitHub
commit f89196b6c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 116 additions and 4 deletions

View File

@ -910,8 +910,7 @@ class QPDF
}
};
// The ParseGuard class allows QPDFObjectHandle to detect
// re-entrant parsing.
// The ParseGuard class allows QPDFParser to detect re-entrant parsing.
class ParseGuard
{
friend class QPDFParser;
@ -933,7 +932,7 @@ class QPDF
QPDF* qpdf;
};
// Pipe class is restricted to QPDF_Stream
// Pipe class is restricted to QPDF_Stream.
class Pipe
{
friend class QPDF_Stream;
@ -961,6 +960,20 @@ class QPDF
}
};
// JobSetter class is restricted to QPDFJob.
class JobSetter
{
friend class QPDFJob;
private:
// Enable enhanced warnings for pdf file checking.
static void
setCheckMode(QPDF& qpdf, bool val)
{
qpdf.m->check_mode = val;
}
};
// For testing only -- do not add to DLL
static bool test_json_validators();
@ -1698,6 +1711,7 @@ class QPDF
bool ignore_xref_streams{false};
bool suppress_warnings{false};
bool attempt_recovery{true};
bool check_mode{false};
std::shared_ptr<EncryptionParameters> encp;
std::string pdf_version;
std::map<QPDFObjGen, QPDFXRefEntry> xref_table;

View File

@ -2461,6 +2461,13 @@ QPDF::getRoot()
QPDFObjectHandle root = this->m->trailer.getKey("/Root");
if (!root.isDictionary()) {
throw damagedPDF("", 0, "unable to find /Root dictionary");
} else if (
// Check_mode is an interim solution to request #810 pending a more
// comprehensive review of the approach to more extensive checks and
// warning levels.
m->check_mode && !root.getKey("/Type").isNameAndEquals("/Catalog")) {
warn(damagedPDF("", 0, "catalog /Type entry missing or invalid"));
root.replaceKey("/Type", "/Catalog"_qpdf);
}
return root;
}

View File

@ -798,6 +798,7 @@ QPDFJob::doCheck(QPDF& pdf)
bool okay = true;
auto& cout = *this->m->log->getInfo();
cout << "checking " << m->infilename.get() << "\n";
QPDF::JobSetter::setCheckMode(pdf, true);
try {
int extension_level = pdf.getExtensionLevel();
cout << "PDF Version: " << pdf.getPDFVersion();

View File

@ -14,7 +14,7 @@ cleanup();
my $td = new TestDriver('invalid-objects');
my $n_tests = 3;
my $n_tests = 4;
$td->runtest("closed input source",
{$td->COMMAND => "test_driver 73 minimal.pdf"},
@ -33,5 +33,10 @@ $td->runtest("object with zero offset",
{$td->FILE => "zero-offset.out", $td->EXIT_STATUS => 3},
$td->NORMALIZE_NEWLINES);
$td->runtest("catalog with invalid type entry",
{$td->COMMAND => "qpdf --check catalgg.pdf"},
{$td->FILE => "catalgg.out", $td->EXIT_STATUS => 3},
$td->NORMALIZE_NEWLINES);
cleanup();
$td->report($n_tests);

View File

@ -0,0 +1,6 @@
checking catalgg.pdf
WARNING: catalgg.pdf: catalog /Type entry missing or invalid
PDF Version: 1.3
File is not encrypted
File is not linearized
qpdf: operation succeeded with warnings

View File

@ -0,0 +1,79 @@
%PDF-1.3
1 0 obj
<<
/Type /Catalgg
/Pages 2 0 R
>>
endobj
2 0 obj
<<
/Type /Pages
/Kids [
3 0 R
]
/Count 1
>>
endobj
3 0 obj
<<
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 612 792]
/Contents 4 0 R
/Resources <<
/ProcSet 5 0 R
/Font <<
/F1 6 0 R
>>
>>
>>
endobj
4 0 obj
<<
/Length 44
>>
stream
BT
/F1 24 Tf
72 720 Td
(Potato) Tj
ET
endstream
endobj
5 0 obj
[
/PDF
/Text
]
endobj
6 0 obj
<<
/Type /Font
/Subtype /Type1
/Name /F1
/BaseFont /Helvetica
/Encoding /WinAnsiEncoding
>>
endobj
xref
0 7
0000000000 65535 f
0000000009 00000 n
0000000063 00000 n
0000000135 00000 n
0000000307 00000 n
0000000403 00000 n
0000000438 00000 n
trailer <<
/Size 7
/Root 1 0 R
>>
startxref
556
%%EOF