Commit 4490c23f64ede6e5e2268b7e5d86e7f69600ff3e

Authored by m-holger
1 parent 2b88c11b

Add `value_validator` to `NNTree` implementations for enhanced validation.

- Introduced `std::function<bool(QPDFObjectHandle const&)>` to validate tree values.
- Updated constructors and methods across `QPDFNameTreeObjectHelper` and `QPDFNumberTreeObjectHelper` to support value validation.
- Refactored `NNTreeImpl` to check for invalid values during insertion, iteration, and retrieval.
- Improved error handling and reporting for invalid values.
include/qpdf/QPDFNameTreeObjectHelper.hh
@@ -45,6 +45,13 @@ class QPDF_DLL_CLASS QPDFNameTreeObjectHelper: public QPDFObjectHelper @@ -45,6 +45,13 @@ class QPDF_DLL_CLASS QPDFNameTreeObjectHelper: public QPDFObjectHelper
45 QPDF_DLL 45 QPDF_DLL
46 QPDFNameTreeObjectHelper(QPDFObjectHandle, QPDF&, bool auto_repair = true); 46 QPDFNameTreeObjectHelper(QPDFObjectHandle, QPDF&, bool auto_repair = true);
47 47
  48 + QPDF_DLL
  49 + QPDFNameTreeObjectHelper(
  50 + QPDFObjectHandle,
  51 + QPDF&,
  52 + std::function<bool(QPDFObjectHandle const&)> value_validator,
  53 + bool auto_repair = true);
  54 +
48 // Create an empty name tree 55 // Create an empty name tree
49 QPDF_DLL 56 QPDF_DLL
50 static QPDFNameTreeObjectHelper newEmpty(QPDF&, bool auto_repair = true); 57 static QPDFNameTreeObjectHelper newEmpty(QPDF&, bool auto_repair = true);
@@ -171,7 +178,11 @@ class QPDF_DLL_CLASS QPDFNameTreeObjectHelper: public QPDFObjectHelper @@ -171,7 +178,11 @@ class QPDF_DLL_CLASS QPDFNameTreeObjectHelper: public QPDFObjectHelper
171 ~Members() = default; 178 ~Members() = default;
172 179
173 private: 180 private:
174 - Members(QPDFObjectHandle& oh, QPDF&, bool auto_repair); 181 + Members(
  182 + QPDFObjectHandle& oh,
  183 + QPDF&,
  184 + std::function<bool(QPDFObjectHandle const&)> value_validator,
  185 + bool auto_repair);
175 Members(Members const&) = delete; 186 Members(Members const&) = delete;
176 187
177 std::shared_ptr<NNTreeImpl> impl; 188 std::shared_ptr<NNTreeImpl> impl;
include/qpdf/QPDFNumberTreeObjectHelper.hh
@@ -44,6 +44,13 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper @@ -44,6 +44,13 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper
44 QPDFNumberTreeObjectHelper(QPDFObjectHandle, QPDF&, bool auto_repair = true); 44 QPDFNumberTreeObjectHelper(QPDFObjectHandle, QPDF&, bool auto_repair = true);
45 45
46 QPDF_DLL 46 QPDF_DLL
  47 + QPDFNumberTreeObjectHelper(
  48 + QPDFObjectHandle,
  49 + QPDF&,
  50 + std::function<bool(QPDFObjectHandle const&)> value_validator,
  51 + bool auto_repair = true);
  52 +
  53 + QPDF_DLL
47 ~QPDFNumberTreeObjectHelper() override; 54 ~QPDFNumberTreeObjectHelper() override;
48 55
49 // Create an empty number tree 56 // Create an empty number tree
@@ -188,7 +195,11 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper @@ -188,7 +195,11 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper
188 ~Members() = default; 195 ~Members() = default;
189 196
190 private: 197 private:
191 - Members(QPDFObjectHandle& oh, QPDF&, bool auto_repair); 198 + Members(
  199 + QPDFObjectHandle& oh,
  200 + QPDF&,
  201 + std::function<bool(QPDFObjectHandle const&)> value_validator,
  202 + bool auto_repair);
192 Members(Members const&) = delete; 203 Members(Members const&) = delete;
193 204
194 std::shared_ptr<NNTreeImpl> impl; 205 std::shared_ptr<NNTreeImpl> impl;
libqpdf/NNTree.cc
@@ -141,8 +141,8 @@ NNTreeIterator::increment(bool backward) @@ -141,8 +141,8 @@ NNTreeIterator::increment(bool backward)
141 impl.warn(node, "items array doesn't have enough elements"); 141 impl.warn(node, "items array doesn't have enough elements");
142 } else if (!impl.details.keyValid(items[item_number])) { 142 } else if (!impl.details.keyValid(items[item_number])) {
143 impl.warn(node, ("item " + std::to_string(item_number) + " has the wrong type")); 143 impl.warn(node, ("item " + std::to_string(item_number) + " has the wrong type"));
144 - } else if (!items[item_number + 1]) {  
145 - impl.warn(node, "item " + std::to_string(item_number) + " is null"); 144 + } else if (!impl.value_valid(items[item_number + 1])) {
  145 + impl.warn(node, "item " + std::to_string(item_number + 1) + " is invalid");
146 } else { 146 } else {
147 return; 147 return;
148 } 148 }
@@ -386,6 +386,9 @@ NNTreeIterator::insertAfter(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; @@ -386,6 +386,9 @@ NNTreeIterator::insertAfter(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp;
386 if (!(key && value)) { 386 if (!(key && value)) {
387 impl.error(node, "insert: key or value is null"); 387 impl.error(node, "insert: key or value is null");
388 } 388 }
  389 + if (!impl.value_valid(value)) {
  390 + impl.error(node, "insert: value is invalid");
  391 + }
389 items.insert(item_number + 2, key); 392 items.insert(item_number + 2, key);
390 items.insert(item_number + 3, value); 393 items.insert(item_number + 3, value);
391 resetLimits(node, lastPathElement()); 394 resetLimits(node, lastPathElement());
@@ -621,10 +624,15 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty) @@ -621,10 +624,15 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty)
621 } 624 }
622 625
623 NNTreeImpl::NNTreeImpl( 626 NNTreeImpl::NNTreeImpl(
624 - NNTreeDetails const& details, QPDF& qpdf, QPDFObjectHandle& oh, bool auto_repair) : 627 + NNTreeDetails const& details,
  628 + QPDF& qpdf,
  629 + QPDFObjectHandle& oh,
  630 + std::function<bool(QPDFObjectHandle const&)> value_validator,
  631 + bool auto_repair) :
625 details(details), 632 details(details),
626 qpdf(qpdf), 633 qpdf(qpdf),
627 oh(oh), 634 oh(oh),
  635 + value_valid(value_validator),
628 auto_repair(auto_repair) 636 auto_repair(auto_repair)
629 { 637 {
630 } 638 }
@@ -740,7 +748,7 @@ NNTreeImpl::repair() @@ -740,7 +748,7 @@ NNTreeImpl::repair()
740 { 748 {
741 auto new_node = QPDFObjectHandle::newDictionary(); 749 auto new_node = QPDFObjectHandle::newDictionary();
742 new_node.replaceKey(details.itemsKey(), Array()); 750 new_node.replaceKey(details.itemsKey(), Array());
743 - NNTreeImpl repl(details, qpdf, new_node, false); 751 + NNTreeImpl repl(details, qpdf, new_node, value_valid, false);
744 for (auto const& [key, value]: *this) { 752 for (auto const& [key, value]: *this) {
745 if (key && value) { 753 if (key && value) {
746 repl.insert(key, value); 754 repl.insert(key, value);
@@ -774,10 +782,17 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo @@ -774,10 +782,17 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo
774 if (first_item == end()) { 782 if (first_item == end()) {
775 return end(); 783 return end();
776 } 784 }
777 - if (first_item.valid() && details.keyValid(first_item->first) &&  
778 - details.compareKeys(key, first_item->first) < 0) {  
779 - // Before the first key  
780 - return end(); 785 + if (first_item.valid()) {
  786 + if (!details.keyValid(first_item->first)) {
  787 + error(oh, "encountered invalid key in find");
  788 + }
  789 + if (!value_valid(first_item->second)) {
  790 + error(oh, "encountered invalid value in find");
  791 + }
  792 + if (details.compareKeys(key, first_item->first) < 0) {
  793 + // Before the first key
  794 + return end();
  795 + }
781 } 796 }
782 qpdf_assert_debug(!last_item.valid()); 797 qpdf_assert_debug(!last_item.valid());
783 798
@@ -797,6 +812,12 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo @@ -797,6 +812,12 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo
797 key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem); 812 key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem);
798 if (idx >= 0) { 813 if (idx >= 0) {
799 result.setItemNumber(node, 2 * idx); 814 result.setItemNumber(node, 2 * idx);
  815 + if (!result.impl.details.keyValid(result.ivalue.first)) {
  816 + error(node, "encountered invalid key in find");
  817 + }
  818 + if (!result.impl.value_valid(result.ivalue.second)) {
  819 + error(oh, "encountered invalid value in find");
  820 + }
800 } 821 }
801 return result; 822 return result;
802 } 823 }
@@ -830,6 +851,9 @@ NNTreeImpl::insertFirst(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; val @@ -830,6 +851,9 @@ NNTreeImpl::insertFirst(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; val
830 if (!(key && value)) { 851 if (!(key && value)) {
831 error(oh, "unable to insert null key or value"); 852 error(oh, "unable to insert null key or value");
832 } 853 }
  854 + if (!value_valid(value)) {
  855 + error(oh, "attempting to insert an invalid value");
  856 + }
833 items.insert(0, key); 857 items.insert(0, key);
834 items.insert(1, value); 858 items.insert(1, value);
835 iter.setItemNumber(iter.node, 0); 859 iter.setItemNumber(iter.node, 0);
libqpdf/QPDFNameTreeObjectHelper.cc
@@ -40,14 +40,29 @@ QPDFNameTreeObjectHelper::~QPDFNameTreeObjectHelper() // NOLINT (modernize-use-e @@ -40,14 +40,29 @@ QPDFNameTreeObjectHelper::~QPDFNameTreeObjectHelper() // NOLINT (modernize-use-e
40 // class, see github issue #745. 40 // class, see github issue #745.
41 } 41 }
42 42
43 -QPDFNameTreeObjectHelper::Members::Members(QPDFObjectHandle& oh, QPDF& q, bool auto_repair) :  
44 - impl(std::make_shared<NNTreeImpl>(name_tree_details, q, oh, auto_repair)) 43 +QPDFNameTreeObjectHelper::Members::Members(
  44 + QPDFObjectHandle& oh,
  45 + QPDF& q,
  46 + std::function<bool(QPDFObjectHandle const&)> value_validator,
  47 + bool auto_repair) :
  48 + impl(std::make_shared<NNTreeImpl>(name_tree_details, q, oh, value_validator, auto_repair))
45 { 49 {
46 } 50 }
47 51
48 QPDFNameTreeObjectHelper::QPDFNameTreeObjectHelper(QPDFObjectHandle oh, QPDF& q, bool auto_repair) : 52 QPDFNameTreeObjectHelper::QPDFNameTreeObjectHelper(QPDFObjectHandle oh, QPDF& q, bool auto_repair) :
49 QPDFObjectHelper(oh), 53 QPDFObjectHelper(oh),
50 - m(new Members(oh, q, auto_repair)) 54 + m(new Members(
  55 + oh, q, [](QPDFObjectHandle const& o) -> bool { return static_cast<bool>(o); }, auto_repair))
  56 +{
  57 +}
  58 +
  59 +QPDFNameTreeObjectHelper::QPDFNameTreeObjectHelper(
  60 + QPDFObjectHandle oh,
  61 + QPDF& q,
  62 + std::function<bool(QPDFObjectHandle const&)> value_validator,
  63 + bool auto_repair) :
  64 + QPDFObjectHelper(oh),
  65 + m(new Members(oh, q, value_validator, auto_repair))
51 { 66 {
52 } 67 }
53 68
libqpdf/QPDFNumberTreeObjectHelper.cc
@@ -41,15 +41,30 @@ QPDFNumberTreeObjectHelper::~QPDFNumberTreeObjectHelper() // NOLINT (modernize-u @@ -41,15 +41,30 @@ QPDFNumberTreeObjectHelper::~QPDFNumberTreeObjectHelper() // NOLINT (modernize-u
41 // class, see github issue #745. 41 // class, see github issue #745.
42 } 42 }
43 43
44 -QPDFNumberTreeObjectHelper::Members::Members(QPDFObjectHandle& oh, QPDF& q, bool auto_repair) :  
45 - impl(std::make_shared<NNTreeImpl>(number_tree_details, q, oh, auto_repair)) 44 +QPDFNumberTreeObjectHelper::Members::Members(
  45 + QPDFObjectHandle& oh,
  46 + QPDF& q,
  47 + std::function<bool(QPDFObjectHandle const&)> value_validator,
  48 + bool auto_repair) :
  49 + impl(std::make_shared<NNTreeImpl>(number_tree_details, q, oh, value_validator, auto_repair))
46 { 50 {
47 } 51 }
48 52
49 QPDFNumberTreeObjectHelper::QPDFNumberTreeObjectHelper( 53 QPDFNumberTreeObjectHelper::QPDFNumberTreeObjectHelper(
50 QPDFObjectHandle oh, QPDF& q, bool auto_repair) : 54 QPDFObjectHandle oh, QPDF& q, bool auto_repair) :
51 QPDFObjectHelper(oh), 55 QPDFObjectHelper(oh),
52 - m(new Members(oh, q, auto_repair)) 56 + m(new Members(
  57 + oh, q, [](QPDFObjectHandle const& o) -> bool { return static_cast<bool>(o); }, auto_repair))
  58 +{
  59 +}
  60 +
  61 +QPDFNumberTreeObjectHelper::QPDFNumberTreeObjectHelper(
  62 + QPDFObjectHandle oh,
  63 + QPDF& q,
  64 + std::function<bool(QPDFObjectHandle const&)> value_validator,
  65 + bool auto_repair) :
  66 + QPDFObjectHelper(oh),
  67 + m(new Members(oh, q, value_validator, auto_repair))
53 { 68 {
54 } 69 }
55 70
libqpdf/qpdf/NNTree.hh
@@ -95,7 +95,12 @@ class NNTreeImpl @@ -95,7 +95,12 @@ class NNTreeImpl
95 public: 95 public:
96 typedef NNTreeIterator iterator; 96 typedef NNTreeIterator iterator;
97 97
98 - NNTreeImpl(NNTreeDetails const&, QPDF&, QPDFObjectHandle&, bool auto_repair = true); 98 + NNTreeImpl(
  99 + NNTreeDetails const&,
  100 + QPDF&,
  101 + QPDFObjectHandle&,
  102 + std::function<bool(QPDFObjectHandle const&)> value_validator,
  103 + bool auto_repair = true);
99 iterator begin(); 104 iterator begin();
100 iterator end(); 105 iterator end();
101 iterator last(); 106 iterator last();
@@ -127,6 +132,7 @@ class NNTreeImpl @@ -127,6 +132,7 @@ class NNTreeImpl
127 QPDF& qpdf; 132 QPDF& qpdf;
128 int split_threshold{32}; 133 int split_threshold{32};
129 QPDFObjectHandle oh; 134 QPDFObjectHandle oh;
  135 + const std::function<bool(QPDFObjectHandle const&)> value_valid;
130 bool auto_repair{true}; 136 bool auto_repair{true};
131 size_t error_count{0}; 137 size_t error_count{0};
132 }; 138 };