Commit 4490c23f64ede6e5e2268b7e5d86e7f69600ff3e
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.
Showing
6 changed files
with
99 additions
and
17 deletions
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& key, QPDFObjectHandle const& |
| 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& 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& 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& key, QPDFObjectHandle const& 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 | }; | ... | ... |