Hi — thanks for AWorld. While reading the Redis event bus I hit a deserialization pattern worth a look.
Every Message is pickled and pushed to a Redis stream, then pickle.loads'd on the way out:
aworld/events/redis_backend.py
async def publish(self, message: Message, **kwargs):
data = {"data": pickle.dumps(message)} # line 38
msg_id = await self.client.xadd(name=name, id="*", fields=data)
async def consume(self, message: Message = None, **kwargs):
...
data = pickle.loads(message_content.get(b"data")) # line 52 (also line 64, consume_nowait)
pickle.loads executes arbitrary code during deserialization, so the security boundary here is entirely the Redis instance — and it's the default transport, with no safe-serializer option.
The question this raises: is that Redis guaranteed single-tenant, network-isolated, and unreachable by anything an attacker can influence? In a multi-agent deployment the bus is often shared, and a single compromised or prompt-injected agent that can xadd to a task stream turns this into remote code execution on every consumer — after any input guardrail has already passed.
Possible direction: use a non-executing serializer for transport (JSON/msgpack over Message.to_dict()), or if pickle is required, sign payloads (HMAC) and verify before loads. Even a documented "AWorld's Redis bus must be a trusted, isolated instance" would help operators reason about it.
Surfaced with release-gate, an open-source pre-deploy checker that traces model/agent data into dangerous sinks. Happy to be wrong if the bus is provably isolated in all deployments.
Hi — thanks for AWorld. While reading the Redis event bus I hit a deserialization pattern worth a look.
Every Message is pickled and pushed to a Redis stream, then pickle.loads'd on the way out:
aworld/events/redis_backend.py
async def publish(self, message: Message, **kwargs):
data = {"data": pickle.dumps(message)} # line 38
msg_id = await self.client.xadd(name=name, id="*", fields=data)
async def consume(self, message: Message = None, **kwargs):
...
data = pickle.loads(message_content.get(b"data")) # line 52 (also line 64, consume_nowait)
pickle.loads executes arbitrary code during deserialization, so the security boundary here is entirely the Redis instance — and it's the default transport, with no safe-serializer option.
The question this raises: is that Redis guaranteed single-tenant, network-isolated, and unreachable by anything an attacker can influence? In a multi-agent deployment the bus is often shared, and a single compromised or prompt-injected agent that can xadd to a task stream turns this into remote code execution on every consumer — after any input guardrail has already passed.
Possible direction: use a non-executing serializer for transport (JSON/msgpack over Message.to_dict()), or if pickle is required, sign payloads (HMAC) and verify before loads. Even a documented "AWorld's Redis bus must be a trusted, isolated instance" would help operators reason about it.
Surfaced with release-gate, an open-source pre-deploy checker that traces model/agent data into dangerous sinks. Happy to be wrong if the bus is provably isolated in all deployments.