From 089be151574cae62e2f0215e11675f7a0f498fd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Mis=CC=81kiewicz?= Date: Tue, 28 Jul 2026 20:00:26 +0200 Subject: [PATCH] fix: Don't crash on PEP items without a payload getTag() returns None when the payload element is missing, which some clients send instead of retracting the item. Tune, Mood, Activity and Location all dereferenced the result directly, so the handler chain aborted with an AttributeError. --- nbxmpp/modules/activity.py | 2 +- nbxmpp/modules/location.py | 2 +- nbxmpp/modules/mood.py | 2 +- nbxmpp/modules/tune.py | 2 +- test/unit/test_activity.py | 15 +++++++++++++++ test/unit/test_location.py | 15 +++++++++++++++ test/unit/test_mood.py | 15 +++++++++++++++ test/unit/test_tune.py | 16 ++++++++++++++++ 8 files changed, 65 insertions(+), 4 deletions(-) diff --git a/nbxmpp/modules/activity.py b/nbxmpp/modules/activity.py index d6318be..25b0207 100644 --- a/nbxmpp/modules/activity.py +++ b/nbxmpp/modules/activity.py @@ -56,7 +56,7 @@ def _process_pubsub_activity( return activity_node = item.getTag("activity", namespace=Namespace.ACTIVITY) - if not activity_node.getChildren(): + if activity_node is None or not activity_node.getChildren(): self._log.info("Received activity: %s - no activity set", properties.jid) return diff --git a/nbxmpp/modules/location.py b/nbxmpp/modules/location.py index 8eaa676..fda501f 100644 --- a/nbxmpp/modules/location.py +++ b/nbxmpp/modules/location.py @@ -55,7 +55,7 @@ def _process_pubsub_location( return location_node = item.getTag("geoloc", namespace=Namespace.LOCATION) - if not location_node.getChildren(): + if location_node is None or not location_node.getChildren(): self._log.info("Received location: %s - no location set", properties.jid) return diff --git a/nbxmpp/modules/mood.py b/nbxmpp/modules/mood.py index bec3264..da98605 100644 --- a/nbxmpp/modules/mood.py +++ b/nbxmpp/modules/mood.py @@ -56,7 +56,7 @@ def _process_pubsub_mood( return mood_node = item.getTag("mood", namespace=Namespace.MOOD) - if not mood_node.getChildren(): + if mood_node is None or not mood_node.getChildren(): self._log.info("Received mood: %s - removed mood", properties.jid) return diff --git a/nbxmpp/modules/tune.py b/nbxmpp/modules/tune.py index f056f8b..e2150ad 100644 --- a/nbxmpp/modules/tune.py +++ b/nbxmpp/modules/tune.py @@ -55,7 +55,7 @@ def _process_pubsub_tune( return tune_node = item.getTag("tune", namespace=Namespace.TUNE) - if not tune_node.getChildren(): + if tune_node is None or not tune_node.getChildren(): self._log.info("Received tune: %s - no tune set", properties.jid) return diff --git a/test/unit/test_activity.py b/test/unit/test_activity.py index 7c0f645..a1f32c4 100644 --- a/test/unit/test_activity.py +++ b/test/unit/test_activity.py @@ -53,3 +53,18 @@ def _on_message(_con, _stanza, properties): ) self.dispatcher.process_data(event) + + def test_activity_item_without_payload(self): + # An item published without an payload must not crash the handler. + event = """ + + + + + + + + """ + + with self.assertNoLogs("nbxmpp", level="ERROR"): + self.dispatcher.process_data(event) diff --git a/test/unit/test_location.py b/test/unit/test_location.py index 362a69b..2b072b4 100644 --- a/test/unit/test_location.py +++ b/test/unit/test_location.py @@ -96,3 +96,18 @@ def _on_message(_con, _stanza, properties): ) self.dispatcher.process_data(event) + + def test_location_item_without_payload(self): + # An item published without a payload must not crash the handler. + event = """ + + + + + + + + """ + + with self.assertNoLogs("nbxmpp", level="ERROR"): + self.dispatcher.process_data(event) diff --git a/test/unit/test_mood.py b/test/unit/test_mood.py index 8db0124..cd40b86 100644 --- a/test/unit/test_mood.py +++ b/test/unit/test_mood.py @@ -49,3 +49,18 @@ def _on_message(_con, _stanza, properties): ) self.dispatcher.process_data(event) + + def test_mood_item_without_payload(self): + # An item published without a payload must not crash the handler. + event = """ + + + + + + + + """ + + with self.assertNoLogs("nbxmpp", level="ERROR"): + self.dispatcher.process_data(event) diff --git a/test/unit/test_tune.py b/test/unit/test_tune.py index 1ea4e86..4055f2b 100644 --- a/test/unit/test_tune.py +++ b/test/unit/test_tune.py @@ -62,3 +62,19 @@ def _on_message(_con, _stanza, properties): ) self.dispatcher.process_data(event) + + def test_tune_item_without_payload(self): + # Some clients signal "stopped listening" with an empty instead + # of retracting it, so the payload can be absent. + event = """ + + + + + + + + """ + + with self.assertNoLogs("nbxmpp", level="ERROR"): + self.dispatcher.process_data(event)