From c9eec2f7efa34ccbfbf9c48f393cbe5da08566e6 Mon Sep 17 00:00:00 2001 From: Shubham Padkonde Date: Fri, 18 Sep 2026 04:13:37 +0530 Subject: [PATCH] fix: reject self-parenting in PanObject child operations Signed-off-by: Shubham Padkonde --- panos/base.py | 22 +++++++++++++++++++--- tests/test_base.py | 27 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/panos/base.py b/panos/base.py index c1ee2def..a8b5e27e 100644 --- a/panos/base.py +++ b/panos/base.py @@ -210,7 +210,7 @@ def uid(self): return "" def add(self, child): - """Add a child node to this node + """Add a child node to this node. Args: child (PanObject): Node to add as a child @@ -218,13 +218,18 @@ def add(self, child): Returns: PanObject: Child node + Raises: + ValueError: If the child is this node itself + """ + if child is self: + raise ValueError("An object cannot be added as its own child") child.parent = self self.children.append(child) return child def insert(self, index, child): - """Insert a child node at a specific index + """Insert a child node at a specific index. This is useful for ordering or reordering security policy rules @@ -235,18 +240,29 @@ def insert(self, index, child): Returns: PanObject: Child node + Raises: + ValueError: If the child is this node itself + """ + if child is self: + raise ValueError("An object cannot be added as its own child") child.parent = self self.children.insert(index, child) return child def extend(self, children): - """Add a list of child nodes to this node + """Add a list of child nodes to this node. Args: children (list): List of PanObject instances + Raises: + ValueError: If the children include this node itself + """ + children = list(children) + if any(child is self for child in children): + raise ValueError("An object cannot be added as its own child") for child in children: child.parent = self self.children.extend(children) diff --git a/tests/test_base.py b/tests/test_base.py index 2ba1437d..3014d4a8 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -20,6 +20,7 @@ import uuid import xml.etree.ElementTree as ET +import pytest import pan.xapi import panos.base as Base import panos.errors as Err @@ -78,6 +79,32 @@ def test_property_uid(self): self.assertEqual(expected, ret_val) + def test_add_self_leaves_tree_unchanged(self): + """Reject self-parenting without modifying the tree.""" + with pytest.raises(ValueError): + self.obj.add(self.obj) + assert self.obj.parent is None + assert self.obj.children == [] + + def test_insert_self_leaves_tree_unchanged(self): + """Reject self-insertion without modifying the tree.""" + with pytest.raises(ValueError): + self.obj.insert(0, self.obj) + assert self.obj.parent is None + assert self.obj.children == [] + + def test_extend_self_leaves_all_parents_unchanged(self): + """Reject self-extension before changing any parent links.""" + existing = self.obj.add(Base.PanObject("existing")) + other_parent = Base.PanObject("other") + child = other_parent.add(Base.PanObject("child")) + with pytest.raises(ValueError): + self.obj.extend([child, self.obj]) + assert self.obj.parent is None + assert self.obj.children == [existing] + assert child.parent is other_parent + assert other_parent.children == [child] + def test_add_without_children(self): CHILD_NAME = "child" child = Base.PanObject(CHILD_NAME)