pgAdmin 4 ships an LLM panel and a classic LFI+SSRF arrives with it (CVE-2026-7817)
pgAdmin 4 9.15 patches an authenticated LFI and SSRF in its new LLM API configuration endpoints. The bug class is decades old; the surface is brand new.
What is this?
On May 11, 2026, the pgAdmin project disclosed CVE-2026-7817, a pair of bugs in the LLM API configuration endpoints that ship with pgAdmin 4 9.13 and 9.14. The reporter, credited as j3seer, found that two user-settable preferences — api_key_file and api_url — flow into the LLM provider client with no validation. An authenticated pgAdmin user can read arbitrary files the server process can open (local file inclusion) and coerce the server into making HTTP requests to internal targets such as cloud metadata services (server-side request forgery). The fix landed in pgAdmin 4 9.15 via commit 24485fe96.
The bug class is forty years old. What is new is the surface: pgAdmin’s LLM panel — an assistant for query authoring — is the seam through which both vectors reach the network and the filesystem. As more legacy products add “ask the assistant” panels in 2026, this exact category of finding is going to keep recurring.
How it works
The LLM panel lets a user point pgAdmin at an OpenAI-compatible endpoint by configuring two values: a URL for the API base, and a path to a file containing the key. Both values are stored as user preferences and consumed by the chat path and the model-list endpoints.
The vulnerable shape, simplified from the GitHub issue and the 9.15 patch:
# Vulnerable (< 9.15) — pseudocode of the LLM client construction
api_url = preferences.get("llm.api_url") # arbitrary string
api_key_path = preferences.get("llm.api_key_file") # arbitrary path
with open(api_key_path, "r") as f: # LFI: any file readable by pgAdmin
api_key = f.read()
client = OpenAIClient(base_url=api_url, # SSRF: any URL, including
api_key=api_key) # http://169.254.169.254/...
Two reachable behaviours fall out of that:
- LFI via
api_key_file. The path is opened by the pgAdmin service account. The contents are then handed to the provider client as an API key. The natural way to observe the read is the error path — a malformed key triggers a response that confirms the read happened, and in some configurations echoes a fragment. Pointing the field at a service-account config, a private key, or a credentials file is enough to confirm exfiltration. - SSRF via
api_url. The string is used verbatim as the base URL for outbound HTTP. Setting it tohttp://169.254.169.254/latest/meta-data/iam/security-credentials/on a cloud-hosted pgAdmin causes the server to fetch instance-metadata credentials on the attacker’s behalf. The chat and model-list endpoints both reach this code path.
Exploitation requires authentication, which is the only thing keeping the CVSS at 6.5 (CVSS 3.1) / 7.1 (CVSS 4.0). On a pgAdmin instance with self-registration, weak SSO mapping, or a leaked low-privilege account, that prerequisite is easy.
The 9.15 patch is small and worth reading. It does three things at the LLM-preferences boundary: it confines api_key_file to the user’s private storage (server mode) or home directory (desktop mode), it requires the file to be printable ASCII and caps the read at 1024 bytes, and it gates api_url against a new allow-list (config.ALLOWED_LLM_API_URLS) checked at every entry point.
Why it matters
Three things, in order of how often you will see them in 2026.
First, the bolt-on LLM panel is a new attack surface for old bugs. pgAdmin shipped a small AI feature and inherited a CWE-552 (LFI) and a CWE-918 (SSRF) at the seam. The same pattern is showing up across DB tooling, IDEs, ticketing systems, and observability dashboards as each adds “configure your LLM provider here” preferences. Every one of those preference fields that reaches a file open or an HTTP client without validation is a candidate.
Second, authenticated does not mean safe. The pgAdmin role required to set LLM preferences is held by anyone who can log in and edit their own settings. For a multi-tenant pgAdmin behind SSO — a common shape in fintech and platform engineering — the practical attacker is anyone with a corporate identity and a reason to point a tool at their own pgAdmin tab.
Third, SSRF to cloud metadata remains the single highest-payoff post-auth pivot on managed PostgreSQL fleets. If pgAdmin is running in EC2, GCE, or AKS with an instance role attached, the SSRF reads the role’s STS credentials. Those credentials then read the database secret, the RDS snapshot bucket, and whatever else the role has been granted. CVE-2026-7817 is interesting less for what it is than for which environment it sits in.
This is the same pattern flagged earlier in 2026 in LMDeploy CVE-2026-33626 (SSRF in an inference server) and LiteLLM CVE-2026-42208 (SQLi in an LLM proxy): traditional web vulnerabilities in software that did not exist three years ago, sitting close to credentials and to model traffic.
Defenses
- Upgrade to pgAdmin 4 9.15 or later. The patch is in release notes for 9.15 and bundled with seven other security fixes (CVE-2026-7813 through CVE-2026-7820) in the same release. Do not pick this one off.
- Audit existing LLM preferences before upgrading. Pull the preferences table from your pgAdmin database and look for
api_key_filevalues pointing outside the user storage area andapi_urlvalues that are nothttps://api.openai.com,https://api.anthropic.com, or whatever provider you actually use. Anything else is either misconfiguration or evidence of exploitation. - Set
config.ALLOWED_LLM_API_URLSexplicitly. Once on 9.15, define the allow-list inconfig_local.pywith the two or three provider endpoints your team actually uses. The defaults are conservative but the value of the control comes from being narrow. - Block egress from pgAdmin to RFC1918 and link-local. On cloud-hosted pgAdmin, deny outbound traffic from the pgAdmin host to
169.254.0.0/16,127.0.0.0/8, and the relevant RFC1918 ranges, except for the database itself. This neutralises the SSRF impact even if a future variant slips past the allow-list. On AWS, prefer IMDSv2 with a hop limit of 1 so a server-side request cannot reach the metadata service in the first place. - Run pgAdmin as a dedicated low-privilege user. The LFI vector only reads what the pgAdmin process can read. A service account whose home directory is empty and whose group memberships do not include
ssl-cert,shadow, or the cloud-init credentials path strips most of the value of the read primitive. - Generalise the lesson to your own LLM panels. If you ship a product with an “add your LLM provider” page, treat the API URL like any other user-controlled URL fed to an HTTP client (allow-list scheme, host, port; resolve once and pin) and treat the key path like any other user-controlled file path (confine to a writable user directory, cap the read length, refuse symlinks). The pgAdmin 9.15 patch is a workable reference shape.
Status
| Item | Reference | Date | Notes |
|---|---|---|---|
| Issue filed | pgAdmin GitHub #9900 | 2026-05-01 | Reported by j3seer |
| CVE assigned | CVE-2026-7817 | 2026-05-11 | NVD published |
| pgAdmin 4 9.15 released | pgAdmin project | 2026-05-11 | Eight security fixes total |
| Patch commit | 24485fe96 | 2026-05-11 | Allow-list + path containment + ASCII shape |
| Vendor write-up | SentinelOne | 2026-05-18 | Includes detection guidance |
| Severity | NVD | — | CVSS 3.1: 6.5 / CVSS 4.0: 7.1 |
| Affected versions | pgAdmin project | — | 9.13 ≤ version < 9.15 |
The right takeaway is not “pgAdmin had a bug.” It is “the LLM-configuration boundary is becoming a standard place to find LFI and SSRF in mature products, and the validation patterns to defend it are now known.” Apply the patch; then apply the pattern.
Sources
- → https://github.com/pgadmin-org/pgadmin4/issues/9900
- → https://www.cve.org/CVERecord?id=CVE-2026-7817
- → https://www.pgadmin.org/docs/pgadmin4/9.15/release_notes_9_15.html
- → https://www.postgresql.org/about/news/pgadmin-4-v915-released-3295/
- → https://www.sentinelone.com/vulnerability-database/cve-2026-7817/