From bd2f6d052fe5941e85e37082c2a43453d48d1295 Mon Sep 17 00:00:00 2001 From: Stefan <96178532+stefan6419846@users.noreply.github.com> Date: Tue, 17 Feb 2026 17:51:28 +0100 Subject: [PATCH] SEC: Detect cyclic references when accessing TreeObject.children (#3645) --- PyPDF2/generic/_data_structures.py | 11 ++++++++++- tests/generic/test_data_structures.py | 18 ++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) Index: PyPDF2-2.11.1/PyPDF2/generic/_data_structures.py =================================================================== --- PyPDF2-2.11.1.orig/PyPDF2/generic/_data_structures.py +++ PyPDF2-2.11.1/PyPDF2/generic/_data_structures.py @@ -372,10 +372,19 @@ class TreeObject(DictionaryObject): return child_ref = self[NameObject("/First")] + last = self[NameObject("/Last")] child = child_ref.get_object() + visited: set[int] = set() while True: + child_id = id(child) + if child_id in visited: + logger_warning(f"Detected cycle in outline structure for {child}", __name__) + return + visited.add(child_id) + yield child - if child == self[NameObject("/Last")]: + + if child == last: return child_ref = child.get(NameObject("/Next")) # type: ignore if child_ref is None: Index: PyPDF2-2.11.1/tests/test_generic.py =================================================================== --- PyPDF2-2.11.1.orig/tests/test_generic.py +++ PyPDF2-2.11.1/tests/test_generic.py @@ -918,3 +918,17 @@ def test_create_string_object_force(): ) def test_float_object_decimal_to_string(value, expected): assert repr(FloatObject(value)) == expected + + +def test_tree_object__cyclic_reference(caplog): + writer = PdfWriter() + child1 = writer._add_object(DictionaryObject()) + child2 = writer._add_object(DictionaryObject({NameObject("/Next"): child1})) + child3 = writer._add_object(DictionaryObject({NameObject("/Next"): child2})) + child1.get_object()[NameObject("/Next")] = child3 + tree = TreeObject() + tree[NameObject("/First")] = child2 + tree[NameObject("/Last")] = writer._add_object(DictionaryObject()) + + assert list(tree.children()) == [child2.get_object(), child1.get_object(), child3.get_object()] + assert "Detected cycle in outline structure for " in caplog.text