From ad5f973a8dbbacfc5f81234b5e1ade8678330d36 Mon Sep 17 00:00:00 2001 From: Teknium Date: Mon, 23 Mar 2026 15:44:52 -0700 Subject: [PATCH] fix(vision): make SSRF redirect guard async for httpx.AsyncClient httpx.AsyncClient awaits event hooks. The sync _ssrf_redirect_guard returned None, causing 'object NoneType can't be used in await expression' on any vision_analyze call that followed redirects. Caught during live PTY testing of the merged SSRF protection. --- tools/vision_tools.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/vision_tools.py b/tools/vision_tools.py index 1b64c4eb2..6cc354371 100644 --- a/tools/vision_tools.py +++ b/tools/vision_tools.py @@ -97,11 +97,13 @@ async def _download_image(image_url: str, destination: Path, max_retries: int = # Create parent directories if they don't exist destination.parent.mkdir(parents=True, exist_ok=True) - def _ssrf_redirect_guard(response): + async def _ssrf_redirect_guard(response): """Re-validate each redirect target to prevent redirect-based SSRF. Without this, an attacker can host a public URL that 302-redirects to http://169.254.169.254/ and bypass the pre-flight is_safe_url check. + + Must be async because httpx.AsyncClient awaits event hooks. """ if response.is_redirect and response.next_request: redirect_url = str(response.next_request.url)