From 4f927bc0257e6cb8779f90c3df860a2a30692d9c Mon Sep 17 00:00:00 2001 From: Twirre Meulenbelt <43213592+TwirreM@users.noreply.github.com> Date: Mon, 26 Jan 2026 19:51:14 +0100 Subject: [PATCH] fix: make DOS from other agents impossible There were some missing value checks. Other agents could cause errors in the User Interrupt agent or the Program Manager agent by sending malformed messages. ref: N25B-453 --- .../agents/bdi/bdi_program_manager.py | 19 ++++++++++++++++++- .../user_interrupt/user_interrupt_agent.py | 2 ++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/control_backend/agents/bdi/bdi_program_manager.py b/src/control_backend/agents/bdi/bdi_program_manager.py index da57c25..54c9983 100644 --- a/src/control_backend/agents/bdi/bdi_program_manager.py +++ b/src/control_backend/agents/bdi/bdi_program_manager.py @@ -31,6 +31,7 @@ class BDIProgramManager(BaseAgent): :ivar sub_socket: The ZMQ SUB socket used to receive program updates. :ivar _program: The current Program. :ivar _phase: The current Phase. + :ivar _goal_mapping: A mapping of goal IDs to goals. """ _program: Program @@ -39,6 +40,7 @@ class BDIProgramManager(BaseAgent): def __init__(self, **kwargs): super().__init__(**kwargs) self.sub_socket = None + self._goal_mapping: dict[str, Goal] = {} def _initialize_internal_state(self, program: Program): """ @@ -49,7 +51,7 @@ class BDIProgramManager(BaseAgent): """ self._program = program self._phase = program.phases[0] # start in first phase - self._goal_mapping: dict[str, Goal] = {} + self._goal_mapping = {} for phase in program.phases: for goal in phase.goals: self._populate_goal_mapping_with_goal(goal) @@ -107,6 +109,9 @@ class BDIProgramManager(BaseAgent): :param old: The ID of the old phase. :param new: The ID of the new phase. """ + if self._phase is None: + return + if old != str(self._phase.id): self.logger.warning( f"Phase transition desync detected! ASL requested move from '{old}', " @@ -146,6 +151,12 @@ class BDIProgramManager(BaseAgent): def _extract_current_beliefs(self) -> list[Belief]: """Extract beliefs from the current phase.""" + assert self._phase is not None, ( + "Invalid state, no phase set. Call this method only when " + "a program has been received and the end-phase has not " + "been reached." + ) + beliefs: list[Belief] = [] for norm in self._phase.norms: @@ -198,6 +209,12 @@ class BDIProgramManager(BaseAgent): :return: A list of Goal objects. """ + assert self._phase is not None, ( + "Invalid state, no phase set. Call this method only when " + "a program has been received and the end-phase has not " + "been reached." + ) + goals: list[Goal] = [] for goal in self._phase.goals: diff --git a/src/control_backend/agents/user_interrupt/user_interrupt_agent.py b/src/control_backend/agents/user_interrupt/user_interrupt_agent.py index a42861a..e2b2d87 100644 --- a/src/control_backend/agents/user_interrupt/user_interrupt_agent.py +++ b/src/control_backend/agents/user_interrupt/user_interrupt_agent.py @@ -335,6 +335,8 @@ class UserInterruptAgent(BaseAgent): belief_name = f"force_{asl}" else: self.logger.warning("Tried to send belief with unknown type") + return + belief = Belief(name=belief_name, arguments=None) self.logger.debug(f"Sending belief to BDI Core: {belief_name}") # Conditional norms are unachieved by removing the belief