GHSA-5QG5-RGP6-J5M5
Vulnerability from github – Published: 2026-11-12 20:37 – Updated: 2026-11-12 20:37Impact
An unauthenticated caller able to reach the query endpoint could exhaust database and server resources with ordinary, inexpensive requests.
The keyword-search arm read the entire corpus on every request and rebuilt an in-memory BM25 index from it, so the cost of serving a query scaled with the size of the source table rather than with the size of the result set. Each request cost the server and the tenant's database far more than it cost the caller to make, which makes this a materially more effective denial-of-service lever than flooding the endpoint with ordinary requests. The amplification factor grows with corpus size, so the largest deployments are the most exposed.
Hybrid search was enabled by default and configured per pipeline rather than per request, so a caller could not opt out of the keyword arm and an operator could not leave it available to some requests but not others.
Who is affected: any deployment of pgEdge RAG Server at 1.0.0 or earlier, including all alpha and beta pre-releases, whose query endpoint is reachable by an untrusted party. The endpoint required no authentication and applied no rate limiting.
In 1.0.0, internal/database/search.go:130 built the corpus query with no LIMIT:
query = fmt.Sprintf(`
SELECT ...
FROM %s%s`,
tableIdent,
filterClause)
FetchDocuments accepted no limit parameter at all, so neither a caller nor the orchestrator had any way to bound the read.
internal/config/loader.go:197 defaulted HybridEnabled to true, and internal/server/middleware.go:37 applied only logging, panic recovery and optional CORS.
Patches
Fixed in 2.0.0. Upgrade to 2.0.0 or later.
Reads are capped by a new per-pipeline search.bm25_max_documents, defaulting to 10000, plumbed through SearchBackend.FetchDocuments and applied as a parameterised LIMIT. There is deliberately no unlimited value: a non-positive setting is rejected at configuration load time rather than silently treated as unbounded.
Requests that reach the cap are logged, because keyword recall is partial at that point and an operator should not have to infer it. A new disable_hybrid request field lets a caller skip the expensive arm per query, and can only ever turn it off.
After upgrading, review the default search.bm25_max_documents value of 10000 against the size of your corpus. A cap far below corpus size silently reduces keyword recall.
Workarounds
Place the server behind an authenticating, rate-limiting reverse proxy.
Setting hybrid_enabled: false on each pipeline removes the keyword arm and with it the unbounded read, at the cost of keyword recall. This is the only mitigation available without a proxy.
Neither is a substitute for upgrading.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/pgEdge/pgedge-rag-server"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.0.1-0.20260729020411-a09d4872356b"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-125327"
],
"database_specific": {
"cwe_ids": [
"CWE-405",
"CWE-770"
],
"github_reviewed": true,
"github_reviewed_at": "2026-11-12T20:37:38Z",
"nvd_published_at": "2026-11-10T19:21:09Z",
"severity": "HIGH"
},
"details": "### Impact\n\nAn unauthenticated caller able to reach the query endpoint could exhaust database and server resources with ordinary, inexpensive requests.\n\nThe keyword-search arm read the entire corpus on every request and rebuilt an in-memory BM25 index from it, so the cost of serving a query scaled with the size of the source table rather than with the size of the result set. Each request cost the server and the tenant\u0027s database far more than it cost the caller to make, which makes this a materially more effective denial-of-service lever than flooding the endpoint with ordinary requests. The amplification factor grows with corpus size, so the largest deployments are the most exposed.\n\nHybrid search was enabled by default and configured per pipeline rather than per request, so a caller could not opt out of the keyword arm and an operator could not leave it available to some requests but not others.\n\n**Who is affected:** any deployment of pgEdge RAG Server at `1.0.0` or earlier, including all alpha and beta pre-releases, whose query endpoint is reachable by an untrusted party. The endpoint required no authentication and applied no rate limiting.\n\nIn `1.0.0`, `internal/database/search.go:130` built the corpus query with no `LIMIT`:\n\n```go\nquery = fmt.Sprintf(`\n SELECT ...\n FROM %s%s`,\n tableIdent,\n filterClause)\n```\n\n`FetchDocuments` accepted no limit parameter at all, so neither a caller nor the orchestrator had any way to bound the read.\n`internal/config/loader.go:197` defaulted `HybridEnabled` to true, and `internal/server/middleware.go:37` applied only logging, panic recovery and optional CORS.\n\n### Patches\n\n**Fixed in `2.0.0`. Upgrade to `2.0.0` or later.**\n\nReads are capped by a new per-pipeline `search.bm25_max_documents`, defaulting to 10000, plumbed through `SearchBackend.FetchDocuments` and applied as a parameterised `LIMIT`. There is deliberately no unlimited value: a non-positive setting is rejected at configuration load time rather than silently treated as unbounded.\n\nRequests that reach the cap are logged, because keyword recall is partial at that point and an operator should not have to infer it. A new `disable_hybrid` request field lets a caller skip the expensive arm per query, and can only ever turn it off.\n\n**After upgrading, review the default `search.bm25_max_documents` value of 10000 against the size of your corpus.** A cap far below corpus size silently reduces keyword recall.\n\n### Workarounds\n\nPlace the server behind an authenticating, rate-limiting reverse proxy.\n\nSetting `hybrid_enabled: false` on each pipeline removes the keyword arm and with it the unbounded read, at the cost of keyword recall. This is the only mitigation available without a proxy.\n\nNeither is a substitute for upgrading.",
"id": "GHSA-5qg5-rgp6-j5m5",
"modified": "2026-11-12T20:37:38Z",
"published": "2026-11-12T20:37:38Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/pgEdge/pgedge-rag-server/security/advisories/GHSA-5qg5-rgp6-j5m5"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-125327"
},
{
"type": "WEB",
"url": "https://github.com/pgEdge/pgedge-rag-server/pull/44"
},
{
"type": "WEB",
"url": "https://github.com/pgEdge/pgedge-rag-server/commit/a09d4872356b510c81f11591845445c33ff45a84"
},
{
"type": "PACKAGE",
"url": "https://github.com/pgEdge/pgedge-rag-server"
},
{
"type": "WEB",
"url": "https://github.com/pgEdge/pgedge-rag-server/releases/tag/v2.0.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "pgEdge RAG Server unbounded corpus read allows resource exhaustion from unauthenticated requests"
}
Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline
Sightings and recorded dates, grouped by day · UTC
How durations are calculated
Following the VTEM framework (Black Hat USA 2015), time to patch starts when the vendor or a coordinator was informed, total time to patch at discovery, and disclosure to patch at public disclosure; vendor response time runs from the report to its acknowledgement. Time to exploit and time unpatched start at the earliest discovery or report, falling back to public disclosure. Unpatched time ends at the first recorded patch, or the calculation time when no patch is recorded. Exploitation to patch shows the interval between those two events; a published proof of concept is a public exploit, not exploitation.
Each result shows its source dates. A negative interval means the end event came first; when date-only inputs leave the order open, the result says so. Date-only inputs are approximate. The earliest evidence wins: KEV report dates and sightings are labelled as such. Records describing another vulnerability are listed but never calculated from, and a PoC alone does not establish exploitation. Missing patch evidence does not prove that no patch exists. Filters below change the event list only.
Loading timeline…
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.