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,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& key, QPDFObjectHandle const& | @@ -386,6 +386,9 @@ NNTreeIterator::insertAfter(QPDFObjectHandle const& key, QPDFObjectHandle const& | ||
| 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& key, bool return_prev_if_not_fo | @@ -774,10 +782,17 @@ NNTreeImpl::findInternal(QPDFObjectHandle const& 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& key, bool return_prev_if_not_fo | @@ -797,6 +812,12 @@ NNTreeImpl::findInternal(QPDFObjectHandle const& 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& key, QPDFObjectHandle const& val | @@ -830,6 +851,9 @@ NNTreeImpl::insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& 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 | }; |