Labels: bug, events, sse
Severity: S1.
| Finding (path:line) |
Issue |
platform/events/emitter.py:90-91 |
_drain calls each destination with no try/except -> one destination raising kills delivery to all remaining destinations for that event AND every queued event behind it (silent loss of audit + progress + SSE for the rest of the request) |
platform/events/emitter.py:170-183 |
_redis_stream opens+closes a new redis.from_url() connection per emit -> 200 events = 200 sequential TCP handshakes |
platform/tasks/progress.py:140-152 |
stream_events() is an infinite while True generator with no max-lifetime, no CancelledError handling, no disconnect detection |
api/routers/tasks.py:465-495 + scans.py:228-287 |
SSE generators never call request.is_disconnected() (unlike sse_events.py:97) -> leaked coroutines + stale Redis XREAD slots for abandoned clients |
api/events.py:49-51 |
get_user_queue hands the same queue to a second concurrent SSE connection for one user; the first close deletes it -> the second connection orphaned / events to a dead queue (multi-tab breakage) |
api/routers/sse_events.py:30 + api/metrics.py:47-50 |
No global max-connections guard (per-minute rate limit does not cap concurrent holds -> DoS); the ACTIVE_SSE gauge is never inc/dec'd so there is no visibility to enforce one |
platform/events/domain_events.py:1-228 |
The 10-event catalog has no persistence, no subscriber registry, no dispatch; only LlmCallCompleted is published via an untyped self.bus whose class is absent -> dead code, modules cannot react |
tools/aila_fuzz_reporter/__main__.py:118-131 + base.py:212-238 |
seen_hashes is in-memory only -> every crash re-POSTed after restart; and stack_hash_of() (frame-based dedup) is exported but unused (scrapers hash the filename) -> client/server dedup keys never agree |
Acceptance: each destination isolated so one failure does not drop the rest; a pooled Redis connection; SSE generators check disconnect + bound lifetime + handle CancelledError; per-user queue supports concurrent connections; a concurrent-connection cap with a live gauge; domain events wired to a real bus or removed; fuzz dedup persisted and frame-hash aligned with the backend.
Labels: bug, events, sse
Severity: S1.
platform/events/emitter.py:90-91_draincalls each destination with no try/except -> one destination raising kills delivery to all remaining destinations for that event AND every queued event behind it (silent loss of audit + progress + SSE for the rest of the request)platform/events/emitter.py:170-183_redis_streamopens+closes a newredis.from_url()connection per emit -> 200 events = 200 sequential TCP handshakesplatform/tasks/progress.py:140-152stream_events()is an infinitewhile Truegenerator with no max-lifetime, no CancelledError handling, no disconnect detectionapi/routers/tasks.py:465-495+scans.py:228-287request.is_disconnected()(unlikesse_events.py:97) -> leaked coroutines + stale Redis XREAD slots for abandoned clientsapi/events.py:49-51get_user_queuehands the same queue to a second concurrent SSE connection for one user; the first close deletes it -> the second connection orphaned / events to a dead queue (multi-tab breakage)api/routers/sse_events.py:30+api/metrics.py:47-50ACTIVE_SSEgauge is never inc/dec'd so there is no visibility to enforce oneplatform/events/domain_events.py:1-228LlmCallCompletedis published via an untypedself.buswhose class is absent -> dead code, modules cannot reacttools/aila_fuzz_reporter/__main__.py:118-131+base.py:212-238seen_hashesis in-memory only -> every crash re-POSTed after restart; andstack_hash_of()(frame-based dedup) is exported but unused (scrapers hash the filename) -> client/server dedup keys never agreeAcceptance: each destination isolated so one failure does not drop the rest; a pooled Redis connection; SSE generators check disconnect + bound lifetime + handle CancelledError; per-user queue supports concurrent connections; a concurrent-connection cap with a live gauge; domain events wired to a real bus or removed; fuzz dedup persisted and frame-hash aligned with the backend.