Summary
pgAdmin 4 validates the configured LLM API endpoint against ALLOWED_LLM_API_URLS, but the providers then issued the request through urllib.request.urlopen(), whose default handler follows a Location header. The allowlist was therefore applied only to the URL pgAdmin was configured with, not to any destination a redirect led on to.
No legitimate LLM API replies with a redirect, so redirects are now refused outright rather than each hop being re-validated. urlopen_no_redirect() installs a handler that raises HTTPError instead, and the outbound calls in all four providers and in the model-refresh endpoints go through it.
A follow-up in the same change maps http_error_308 onto http_error_301, exactly as Python 3.11 itself does. HTTPRedirectHandler only gained http_error_308() in 3.11, so on the 3.9 and 3.10 interpreters pgAdmin still supports a 308 was never recognised as a redirect and surfaced as a bare HTTP Error 308: Permanent Redirect. The redirect was still not followed, so this was never a security gap, but the caller got no explanation of why the request had failed.
Not a CVE
This is hardening rather than a fix for an exploitable flaw: returning the redirect in the first place requires control of a host that is already on the operator's allowlist. No CVE has been requested.
Affected versions
pgAdmin 4 >= 9.15, < 9.18. ALLOWED_LLM_API_URLS was introduced in 9.15, so there was no allowlist for a redirect to bypass before then.
Fix
Fixed in pgAdmin 4 9.18 by commit 9a009fd2b.
Credit
Reported by Ziya Abdullayev.
Summary
pgAdmin 4 validates the configured LLM API endpoint against
ALLOWED_LLM_API_URLS, but the providers then issued the request throughurllib.request.urlopen(), whose default handler follows aLocationheader. The allowlist was therefore applied only to the URL pgAdmin was configured with, not to any destination a redirect led on to.No legitimate LLM API replies with a redirect, so redirects are now refused outright rather than each hop being re-validated.
urlopen_no_redirect()installs a handler that raisesHTTPErrorinstead, and the outbound calls in all four providers and in the model-refresh endpoints go through it.A follow-up in the same change maps
http_error_308ontohttp_error_301, exactly as Python 3.11 itself does.HTTPRedirectHandleronly gainedhttp_error_308()in 3.11, so on the 3.9 and 3.10 interpreters pgAdmin still supports a 308 was never recognised as a redirect and surfaced as a bareHTTP Error 308: Permanent Redirect. The redirect was still not followed, so this was never a security gap, but the caller got no explanation of why the request had failed.Not a CVE
This is hardening rather than a fix for an exploitable flaw: returning the redirect in the first place requires control of a host that is already on the operator's allowlist. No CVE has been requested.
Affected versions
pgAdmin 4 >= 9.15, < 9.18.
ALLOWED_LLM_API_URLSwas introduced in 9.15, so there was no allowlist for a redirect to bypass before then.Fix
Fixed in pgAdmin 4 9.18 by commit
9a009fd2b.Credit
Reported by Ziya Abdullayev.