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 45 QPDF_DLL
46 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 55 // Create an empty name tree
49 56 QPDF_DLL
50 57 static QPDFNameTreeObjectHelper newEmpty(QPDF&, bool auto_repair = true);
... ... @@ -171,7 +178,11 @@ class QPDF_DLL_CLASS QPDFNameTreeObjectHelper: public QPDFObjectHelper
171 178 ~Members() = default;
172 179  
173 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 186 Members(Members const&) = delete;
176 187  
177 188 std::shared_ptr<NNTreeImpl> impl;
... ...
include/qpdf/QPDFNumberTreeObjectHelper.hh
... ... @@ -44,6 +44,13 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper
44 44 QPDFNumberTreeObjectHelper(QPDFObjectHandle, QPDF&, bool auto_repair = true);
45 45  
46 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 54 ~QPDFNumberTreeObjectHelper() override;
48 55  
49 56 // Create an empty number tree
... ... @@ -188,7 +195,11 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper
188 195 ~Members() = default;
189 196  
190 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 203 Members(Members const&) = delete;
193 204  
194 205 std::shared_ptr<NNTreeImpl> impl;
... ...
libqpdf/NNTree.cc
... ... @@ -141,8 +141,8 @@ NNTreeIterator::increment(bool backward)
141 141 impl.warn(node, "items array doesn't have enough elements");
142 142 } else if (!impl.details.keyValid(items[item_number])) {
143 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 146 } else {
147 147 return;
148 148 }
... ... @@ -386,6 +386,9 @@ NNTreeIterator::insertAfter(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp;
386 386 if (!(key && value)) {
387 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 392 items.insert(item_number + 2, key);
390 393 items.insert(item_number + 3, value);
391 394 resetLimits(node, lastPathElement());
... ... @@ -621,10 +624,15 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty)
621 624 }
622 625  
623 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 632 details(details),
626 633 qpdf(qpdf),
627 634 oh(oh),
  635 + value_valid(value_validator),
628 636 auto_repair(auto_repair)
629 637 {
630 638 }
... ... @@ -740,7 +748,7 @@ NNTreeImpl::repair()
740 748 {
741 749 auto new_node = QPDFObjectHandle::newDictionary();
742 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 752 for (auto const& [key, value]: *this) {
745 753 if (key && value) {
746 754 repl.insert(key, value);
... ... @@ -774,10 +782,17 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo
774 782 if (first_item == end()) {
775 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 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 812 key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem);
798 813 if (idx >= 0) {
799 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 822 return result;
802 823 }
... ... @@ -830,6 +851,9 @@ NNTreeImpl::insertFirst(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; val
830 851 if (!(key && value)) {
831 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 857 items.insert(0, key);
834 858 items.insert(1, value);
835 859 iter.setItemNumber(iter.node, 0);
... ...
libqpdf/QPDFNameTreeObjectHelper.cc
... ... @@ -40,14 +40,29 @@ QPDFNameTreeObjectHelper::~QPDFNameTreeObjectHelper() // NOLINT (modernize-use-e
40 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 52 QPDFNameTreeObjectHelper::QPDFNameTreeObjectHelper(QPDFObjectHandle oh, QPDF& q, bool auto_repair) :
49 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 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 53 QPDFNumberTreeObjectHelper::QPDFNumberTreeObjectHelper(
50 54 QPDFObjectHandle oh, QPDF& q, bool auto_repair) :
51 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 95 public:
96 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 104 iterator begin();
100 105 iterator end();
101 106 iterator last();
... ... @@ -127,6 +132,7 @@ class NNTreeImpl
127 132 QPDF& qpdf;
128 133 int split_threshold{32};
129 134 QPDFObjectHandle oh;
  135 + const std::function<bool(QPDFObjectHandle const&)> value_valid;
130 136 bool auto_repair{true};
131 137 size_t error_count{0};
132 138 };
... ...