mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-25 19:20:16 +00:00
ba6f34488e
SSRF defense-in-depth: `urllib.request.urlopen` follows redirects by default, so a probe at `http://example.com/v1/models` could be redirected to `http://internal-service:8080/admin` — surfacing internal HTTP services to the authenticated user. The probe is already gated behind WebUI auth and the local-network check, so the practical attack surface is 'authenticated user enumerating internal services' (same as `curl` from their browser DevTools). Tightening the redirect default is cheap insurance. Implementation: - New module-level `_NoRedirectHandler` (subclasses `urllib.request.HTTPRedirectHandler`, overrides `redirect_request` to return None — urllib then raises `HTTPError(3xx)` rather than following). - New module-level `_PROBE_OPENER = urllib.request.build_opener(_NoRedirectHandler())`. - `probe_provider_endpoint` switches from `urlopen(req, …)` to `_PROBE_OPENER.open(req, …)`. - The existing `HTTPError` handler now categorizes 3xx as `unreachable` with a detail string mentioning 'redirect' so the user understands what happened. 3xx does NOT get its own error code in `PROBE_ERROR_CODES` — the error taxonomy contract stays the same shape (frontend i18n unchanged). Added regression test `test_probe_does_not_follow_redirects` in `tests/test_issue1499_onboarding_probe.py`. Spins up a tiny HTTP server that 302-redirects `/v1/models` to `/different-endpoint` (which would return `{'data': [{'id': 'should-not-see'}]}` if followed). Asserts the probe returns `{ok: False, error: 'unreachable', status: 302, detail: …'redirect'…}` and that the 'should-not-see' string never appears in the result. Mutation-verified: reverting `_PROBE_OPENER.open` back to `urlopen` causes the test to fail with "Probe followed a redirect — should have refused". Suite delta: 3917 → 3918 passing (+1). Reviewer-flagged in PR #1501. Per the 'reviewer-flagged-fix-in-release-not-followup' policy: <20 LOC defensive fix, regression test path obvious, ship in this release rather than punting.