From 54f2b2307544be2ea81cfa3d10cb94396931735a Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 02:57:37 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITICAL]?= =?UTF-8?q?=20Fix=20SyntaxError=20in=20httpx=20event=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed a `SyntaxError: keyword argument repeated` in `app/api/url_upload.py` where `event_hooks` was being passed twice as a keyword argument to `httpx.AsyncClient`. Combined both SSRF redirect validation hooks into a single list `event_hooks={"response": [validate_redirect, verify_redirect]}`. This restores critical SSRF protections via redirect validation and fixes the application crash. Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- .jules/sentinel.md | 4 ++++ app/api/url_upload.py | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index af243c59..0cd6a6b4 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -28,3 +28,7 @@ **Vulnerability:** The `/process-url` endpoint used `httpx.AsyncClient(follow_redirects=True)` after validating the initial user-provided URL against SSRF protections. However, it did not validate the target URLs of any subsequent HTTP redirects, allowing an attacker to provide a safe URL that redirects to an internal/private IP, bypassing the security check. **Learning:** Initial URL validation is insufficient when the HTTP client is configured to follow redirects automatically. The client must be explicitly configured to validate every redirect target. **Prevention:** When using `httpx.AsyncClient(follow_redirects=True)` for user-provided URLs, always implement a redirect validator hook function (e.g., using `event_hooks={'response': [validate_redirect]}`) that resolves the `Location` header and passes it through the same SSRF validation logic before the redirect is followed. +## 2026-03-27 - SyntaxError: keyword argument repeated in httpx event hooks +**Vulnerability:** The `/process-url` endpoint in `app/api/url_upload.py` initialized `httpx.AsyncClient` with the `event_hooks` keyword argument twice. This caused a Python SyntaxError, effectively crashing the API endpoint and preventing any execution. +**Learning:** Python does not allow duplicate keyword arguments. In scenarios where multiple hooks (like local and module-level SSRF interceptors) must be provided to a client, they must be merged into a single list value. +**Prevention:** Combine multiple callables for the same event key into a single list, e.g., `event_hooks={"response": [hook1, hook2]}`. diff --git a/app/api/url_upload.py b/app/api/url_upload.py index 1df8d020..9f20039c 100644 --- a/app/api/url_upload.py +++ b/app/api/url_upload.py @@ -194,11 +194,10 @@ async def process_url( async with httpx.AsyncClient( timeout=settings.http_request_timeout, follow_redirects=True, - event_hooks={"response": [validate_redirect]}, + event_hooks={"response": [validate_redirect, verify_redirect]}, headers={ "User-Agent": "DocuElevate/1.0", # Identify ourselves }, - event_hooks={"response": [verify_redirect]}, ) as client: async with client.stream("GET", url) as response: response.raise_for_status()