Commit d8acccd5c8fff308a0010f787eedc594fa3e5ee9

Authored by m-holger
1 parent 1e53da74

Warn if catalog type entry is invalid (fixes #810)

include/qpdf/QPDF.hh
@@ -910,8 +910,7 @@ class QPDF @@ -910,8 +910,7 @@ class QPDF
910 } 910 }
911 }; 911 };
912 912
913 - // The ParseGuard class allows QPDFObjectHandle to detect  
914 - // re-entrant parsing. 913 + // The ParseGuard class allows QPDFParser to detect re-entrant parsing.
915 class ParseGuard 914 class ParseGuard
916 { 915 {
917 friend class QPDFParser; 916 friend class QPDFParser;
@@ -933,7 +932,7 @@ class QPDF @@ -933,7 +932,7 @@ class QPDF
933 QPDF* qpdf; 932 QPDF* qpdf;
934 }; 933 };
935 934
936 - // Pipe class is restricted to QPDF_Stream 935 + // Pipe class is restricted to QPDF_Stream.
937 class Pipe 936 class Pipe
938 { 937 {
939 friend class QPDF_Stream; 938 friend class QPDF_Stream;
@@ -961,6 +960,20 @@ class QPDF @@ -961,6 +960,20 @@ class QPDF
961 } 960 }
962 }; 961 };
963 962
  963 + // JobSetter class is restricted to QPDFJob.
  964 + class JobSetter
  965 + {
  966 + friend class QPDFJob;
  967 +
  968 + private:
  969 + // Enable enhanced warnings for pdf file checking.
  970 + static void
  971 + setCheckMode(QPDF& qpdf, bool val)
  972 + {
  973 + qpdf.m->check_mode = val;
  974 + }
  975 + };
  976 +
964 // For testing only -- do not add to DLL 977 // For testing only -- do not add to DLL
965 static bool test_json_validators(); 978 static bool test_json_validators();
966 979
@@ -1698,6 +1711,7 @@ class QPDF @@ -1698,6 +1711,7 @@ class QPDF
1698 bool ignore_xref_streams{false}; 1711 bool ignore_xref_streams{false};
1699 bool suppress_warnings{false}; 1712 bool suppress_warnings{false};
1700 bool attempt_recovery{true}; 1713 bool attempt_recovery{true};
  1714 + bool check_mode{false};
1701 std::shared_ptr<EncryptionParameters> encp; 1715 std::shared_ptr<EncryptionParameters> encp;
1702 std::string pdf_version; 1716 std::string pdf_version;
1703 std::map<QPDFObjGen, QPDFXRefEntry> xref_table; 1717 std::map<QPDFObjGen, QPDFXRefEntry> xref_table;
libqpdf/QPDF.cc
@@ -2461,6 +2461,13 @@ QPDF::getRoot() @@ -2461,6 +2461,13 @@ QPDF::getRoot()
2461 QPDFObjectHandle root = this->m->trailer.getKey("/Root"); 2461 QPDFObjectHandle root = this->m->trailer.getKey("/Root");
2462 if (!root.isDictionary()) { 2462 if (!root.isDictionary()) {
2463 throw damagedPDF("", 0, "unable to find /Root dictionary"); 2463 throw damagedPDF("", 0, "unable to find /Root dictionary");
  2464 + } else if (
  2465 + // Check_mode is an interim solution to request #810 pending a more
  2466 + // comprehensive review of the approach to more extensive checks and
  2467 + // warning levels.
  2468 + m->check_mode && !root.getKey("/Type").isNameAndEquals("/Catalog")) {
  2469 + warn(damagedPDF("", 0, "catalog /Type entry missing or invalid"));
  2470 + root.replaceKey("/Type", "/Catalog"_qpdf);
2464 } 2471 }
2465 return root; 2472 return root;
2466 } 2473 }
libqpdf/QPDFJob.cc
@@ -798,6 +798,7 @@ QPDFJob::doCheck(QPDF&amp; pdf) @@ -798,6 +798,7 @@ QPDFJob::doCheck(QPDF&amp; pdf)
798 bool okay = true; 798 bool okay = true;
799 auto& cout = *this->m->log->getInfo(); 799 auto& cout = *this->m->log->getInfo();
800 cout << "checking " << m->infilename.get() << "\n"; 800 cout << "checking " << m->infilename.get() << "\n";
  801 + QPDF::JobSetter::setCheckMode(pdf, true);
801 try { 802 try {
802 int extension_level = pdf.getExtensionLevel(); 803 int extension_level = pdf.getExtensionLevel();
803 cout << "PDF Version: " << pdf.getPDFVersion(); 804 cout << "PDF Version: " << pdf.getPDFVersion();
qpdf/qtest/invalid-objects.test
@@ -14,7 +14,7 @@ cleanup(); @@ -14,7 +14,7 @@ cleanup();
14 14
15 my $td = new TestDriver('invalid-objects'); 15 my $td = new TestDriver('invalid-objects');
16 16
17 -my $n_tests = 3; 17 +my $n_tests = 4;
18 18
19 $td->runtest("closed input source", 19 $td->runtest("closed input source",
20 {$td->COMMAND => "test_driver 73 minimal.pdf"}, 20 {$td->COMMAND => "test_driver 73 minimal.pdf"},
@@ -33,5 +33,10 @@ $td-&gt;runtest(&quot;object with zero offset&quot;, @@ -33,5 +33,10 @@ $td-&gt;runtest(&quot;object with zero offset&quot;,
33 {$td->FILE => "zero-offset.out", $td->EXIT_STATUS => 3}, 33 {$td->FILE => "zero-offset.out", $td->EXIT_STATUS => 3},
34 $td->NORMALIZE_NEWLINES); 34 $td->NORMALIZE_NEWLINES);
35 35
  36 +$td->runtest("catalog with invalid type entry",
  37 + {$td->COMMAND => "qpdf --check catalgg.pdf"},
  38 + {$td->FILE => "catalgg.out", $td->EXIT_STATUS => 3},
  39 + $td->NORMALIZE_NEWLINES);
  40 +
36 cleanup(); 41 cleanup();
37 $td->report($n_tests); 42 $td->report($n_tests);
qpdf/qtest/qpdf/catalgg.out 0 → 100644
  1 +checking catalgg.pdf
  2 +WARNING: catalgg.pdf: catalog /Type entry missing or invalid
  3 +PDF Version: 1.3
  4 +File is not encrypted
  5 +File is not linearized
  6 +qpdf: operation succeeded with warnings
qpdf/qtest/qpdf/catalgg.pdf 0 → 100644
  1 +%PDF-1.3
  2 +1 0 obj
  3 +<<
  4 + /Type /Catalgg
  5 + /Pages 2 0 R
  6 +>>
  7 +endobj
  8 +
  9 +2 0 obj
  10 +<<
  11 + /Type /Pages
  12 + /Kids [
  13 + 3 0 R
  14 + ]
  15 + /Count 1
  16 +>>
  17 +endobj
  18 +
  19 +3 0 obj
  20 +<<
  21 + /Type /Page
  22 + /Parent 2 0 R
  23 + /MediaBox [0 0 612 792]
  24 + /Contents 4 0 R
  25 + /Resources <<
  26 + /ProcSet 5 0 R
  27 + /Font <<
  28 + /F1 6 0 R
  29 + >>
  30 + >>
  31 +>>
  32 +endobj
  33 +
  34 +4 0 obj
  35 +<<
  36 + /Length 44
  37 +>>
  38 +stream
  39 +BT
  40 + /F1 24 Tf
  41 + 72 720 Td
  42 + (Potato) Tj
  43 +ET
  44 +endstream
  45 +endobj
  46 +
  47 +5 0 obj
  48 +[
  49 + /PDF
  50 + /Text
  51 +]
  52 +endobj
  53 +
  54 +6 0 obj
  55 +<<
  56 + /Type /Font
  57 + /Subtype /Type1
  58 + /Name /F1
  59 + /BaseFont /Helvetica
  60 + /Encoding /WinAnsiEncoding
  61 +>>
  62 +endobj
  63 +
  64 +xref
  65 +0 7
  66 +0000000000 65535 f
  67 +0000000009 00000 n
  68 +0000000063 00000 n
  69 +0000000135 00000 n
  70 +0000000307 00000 n
  71 +0000000403 00000 n
  72 +0000000438 00000 n
  73 +trailer <<
  74 + /Size 7
  75 + /Root 1 0 R
  76 +>>
  77 +startxref
  78 +556
  79 +%%EOF