From 73b8757d6f3516bc244ad1a330d7a56ebe34fab2 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 19:26:38 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITICAL]?= =?UTF-8?q?=20Fix=20event=20loop=20blocking=20in=20SSRF=20validation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: d3mocide <136547209+d3mocide@users.noreply.github.com> --- .jules/sentinel.md | 5 +++++ backend/routers/radio.py | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 67e84c6..c7c5a21 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -26,3 +26,8 @@ **Vulnerability:** TCP pollers (`aprs.py`, `beast_transport.py`, `cot_receiver.py`) were directly establishing outbound connections (`asyncio.open_connection()`) to user-provided or configurable hostnames/IPs without applying Server-Side Request Forgery (SSRF) validation. **Learning:** Poller components interacting with external configuration must be treated with the same scrutiny as web endpoints, as they can be weaponized to scan or access internal infrastructure. **Prevention:** Always wrap hostname/IP targets with `await validate_safe_host(host)` or `await validate_safe_url(url)` via `poller/security.py` before executing outbound requests/TCP connections. + +## 2026-07-10 - [Event Loop Blocking DoS via Synchronous SSRF Validation] +**Vulnerability:** In `backend/routers/radio.py`, the `_validate_request_url` event hook used to validate URLs in `httpx.AsyncClient` with `follow_redirects=True` called `validate_safe_url` synchronously. Because `validate_safe_url` performs blocking DNS resolution (`socket.getaddrinfo`), executing it in an async context blocked the backend's main event loop, creating a Denial of Service (DoS) vulnerability. +**Learning:** Using synchronous functions that perform network operations (like DNS resolution) within async event hooks or contexts blocks the asyncio event loop, causing severe latency and potential DoS under load. +**Prevention:** Always offload synchronous blocking calls (such as `socket.getaddrinfo` within SSRF validation) to a separate thread using `await asyncio.get_running_loop().run_in_executor(None, func, *args)` when used within an async context. diff --git a/backend/routers/radio.py b/backend/routers/radio.py index f69275b..a96f03a 100644 --- a/backend/routers/radio.py +++ b/backend/routers/radio.py @@ -1,3 +1,4 @@ +import asyncio import json from datetime import datetime, timedelta, timezone from pathlib import Path @@ -385,7 +386,8 @@ async def proxy_stream( async def _validate_request_url(request: httpx.Request): try: - validate_safe_url(str(request.url)) + loop = asyncio.get_running_loop() + await loop.run_in_executor(None, validate_safe_url, str(request.url)) except ValueError as e: # We raise a RequestError here so httpx catches it instead of crashing. raise httpx.RequestError(f"SSRF validation failed: {e}", request=request)