A CVE for an Archived Repository: Verba’s Generate Socket Hands Your OpenAI Key to a Caller-Chosen URL
CVE-2026-105145 was published by VulDB at 09:00 UTC on 4 October 2026. Its description is a single flat sentence: a vulnerability in Weaviate Verba up to 2.1.3, in the function get_environment of goldenverba/components/util.py, component generate_stream Endpoint, leading to information disclosure, remotely exploitable, exploit public, vendor unresponsive. VulDB scores it CVSS 4.0 6.9 and CVSS 3.1 5.3.
The advisory the record points at describes an unauthenticated remote attacker causing the server to send its own live LLM provider key to a URL of the attacker’s choosing, and self-scores it CVSS 4.0 9.2 Critical. Those are not the same finding at the same severity. We read the code.
The whole bug is an if with an else
Verba — “The Golden RAGtriever”, PyPI package goldenverba, 7,703 stars — builds generator configuration from a dictionary that arrives with the request. The helper that resolves each field is four lines in the current source:
def get_environment(config, value: str, env: str, error_msg: str) -> str:
if value in config:
token = config[value].value
else:
token = os.environ.get(env)
...
Request-supplied config wins; absence falls through to the process environment. That is a reasonable convenience right up until the two things it resolves are a credential and a destination, independently. In goldenverba/components/generation/OpenAIGenerator.py they are:
openai_key = get_environment(config, "API Key", "OPENAI_API_KEY", "No OpenAI API Key found")
openai_url = get_environment(config, "URL", "OPENAI_BASE_URL", "https://api.openai.com/v1")
...
headers = {"Authorization": f"Bearer {openai_key}", ...}
async with client.stream("POST", f"{openai_url}/chat/completions", json=data, headers=headers, timeout=None)
Supply URL and omit API Key, and the two lookups take different branches: the destination comes from the attacker, the bearer token comes from the server’s environment. The request then goes out with both. There is no pairing check anywhere — nothing requires that a credential only travel to the endpoint it was issued for.
Why nothing stops the request from arriving
Verba does have an origin check. Reading goldenverba/server/api.py in the current tree: CORS is mounted with allow_origins=["*"], with a comment saying real enforcement happens in a custom same-origin middleware below — and that middleware is registered as @app.middleware("http").
HTTP. The sink is @app.websocket("/ws/generate_stream"), whose handler opens with await websocket.accept() and no authentication dependency. A middleware bound to the HTTP stack does not cover the WebSocket route, and the socket accepts before anything is checked. The control exists, is documented in a comment, and sits on the wrong protocol — the same category of near-miss as Ollama’s approval prompt checking only the first word of a command line.
The advisory notes the same get_environment() fallback governs sibling secrets — it names OPENAI_EMBED_API_KEY, UPSTAGE_API_KEY, UNSTRUCTURED_API_KEY and EMBEDDING_SERVICE_KEY — wherever the matching URL field is also caller-controlled. One helper, several credentials.
This is not the Verba SSRF you already triaged
Verba 2.1.3 already carries two SSRF records, both assigned by VulnCheck and published 21 July 2026: CVE-2026-65317 (origin-middleware bypass plus attacker-controlled host and port on /api/connect) and CVE-2026-65318 (unauthenticated /ws/import_files HTMLReader fetching arbitrary URLs, reaching internal services and cloud metadata). We checked both records; both describe outbound GET requests, and neither mentions environment credentials riding along.
The new record is a different endpoint, a different HTTP method, and a different asset. The earlier two let an attacker reach things your server can reach. This one hands over a key that works against a third party from anywhere — billable quota, and whatever else that key is scoped to. If your 2.1.3 triage ended with “SSRF, blocked at egress, accepted,” the egress control does not help here: the whole point of the request is that it leaves for the internet.
It is the same asset class as the cross-site WebSocket hijack that let any web page spend a Headroom user’s OpenAI key, and the reason stolen model credentials keep showing up in stealer-log corpora and LLMjacking resale. Provider keys are bearer credentials with a meter attached.
No patch is possible from upstream
The repository is archived. GitHub’s API reports archived: true with the last push on 8 June 2026; the newest tag and release is v2.1.3, 14 July 2025. An archived repository accepts no pull requests and ships no releases. The affected range in the CVE — 2.1.0 through 2.1.3 — has no upper bound that will ever become a fixed version.
That makes this a cleaner instance of a problem we have covered from several angles: a reference MCP server left unpatched for four months, 6,900 stars of dormant code with exec() behind an open port, ten LightLLM CVEs with no fixes, and 15,465 MCP servers whose domains have lapsed. The AI tooling layer produces demo-grade projects that get deployed, get popular, and then stop. Archival is at least honest signalling — it is more than most abandoned projects give you — but the Docker images stay pullable and the running instances stay running.
What to do
- Treat any reachable Verba instance as a key-exfiltration endpoint, not a RAG demo. Hunt for
semitechnologies/verbaimages and listeners serving/ws/generate_stream. There is no patched version to upgrade to; the decision is retire or isolate. - Rotate every provider key that process could read.
OPENAI_API_KEYfirst, then the embedding, Upstage and Unstructured keys the same helper resolves. If the instance was internet-reachable at any point, rotate before you finish investigating. - Do not rely on egress filtering for this one. Unlike the two 2026 SSRF records, the attacker-chosen destination is a normal internet host. Network controls that block RFC1918 and metadata addresses do nothing. Inbound reachability of the WebSocket is the control that matters.
- Check your own code for the split-resolution pattern. Any helper where a credential and its destination are resolved independently — one from request input, one from the environment — is this bug. The fix is to bind them: a key belongs to an endpoint, and an endpoint the caller chose must not inherit a key the caller did not supply.
- Check that your auth middleware covers WebSocket routes. Framework middleware registered for HTTP does not run for
@app.websockethandlers. Enumerate socket routes separately and assert each one authenticates beforeaccept(). - Read the advisory, not the severity. “Information disclosure, CVSS 5.3” and “unauthenticated theft of a live provider credential” are the same record. If your queue is sorted by score, this one sits below a reflected XSS.
Verification note: we read the CVE 5.2 record for CVE-2026-105145 from the CVE Program API (PUBLISHED 4 October 2026, assigner VulDB, advisory-disclosed 3 October 2026, affected 2.1.0–2.1.3, CVSS 4.0 6.9 / CVSS 3.1 5.3), the researcher advisory it references, and the CVE records for CVE-2026-65317 and CVE-2026-65318 to confirm they describe different endpoints and GET-only SSRF. We then independently confirmed the cited code in the current weaviate/Verba repository: the get_environment fallback in components/util.py, the key-and-URL resolution plus bearer header and POST in OpenAIGenerator.py, the wildcard CORS with its enforcement comment, the @app.middleware("http") registration of the same-origin check, and the unauthenticated @app.websocket("/ws/generate_stream") handler accepting before any check. Archive status, last push date and release tags come from the GitHub API. We did not deploy Verba, connect to any instance, or attempt exploitation; the end-to-end key-disclosure result and the 9.2 self-score are the advisory author’s, and the sibling-key list is the advisory’s claim, which we did not separately reproduce.
Sources:
- NVD — CVE-2026-105145, Weaviate Verba
generate_streamendpointutil.py get_environmentinformation disclosure (published 4 October 2026; CVSS 4.0 6.9 / CVSS 3.1 5.3 by VulDB) - VulDB VDB-413370 — assigning CNA entry for CVE-2026-105145
- Researcher advisory — unauthenticated LLM API-key exfiltration via
/ws/generate_stream(referenced by the CVE record; CWE-918, CWE-522, CWE-306) - NVD — CVE-2026-65317, Verba SSRF via
/api/connectand same-origin middleware bypass (VulnCheck, 21 July 2026) - NVD — CVE-2026-65318, Verba unauthenticated SSRF via the
/ws/import_filesWebSocket HTMLReader (VulnCheck, 21 July 2026) - weaviate/Verba repository —
goldenverba/components/util.py,components/generation/OpenAIGenerator.py,server/api.py, archive status and release tags