Dev.to Security πŸ” Cybersecurity πŸ‘ 0 πŸ“– 3 min read

CVE-2026-125325: CVE-2026-125325: Cross-Request Information Disclosure via Shared BM25 Index Race Condition in pgEdge RAG Server

CVE-2026-125325: Cross-Request Information Disclosure via Shared BM25 Index Race Condition in pgEdge RAG Server Vulnerability ID: CVE-2026-125325 CVSS Score: 5.9 Published: 2026-11-12 pgEdge RAG Server versions prio

CVE-2026-125325: Cross-Request Information Disclosure via Shared BM25 Index Race Condition in pgEdge RAG Server

Vulnerability ID: CVE-2026-125325
CVSS Score: 5.9
Published: 2026-11-12

pgEdge RAG Server versions prior to 2.0.0 contain a race condition in the hybrid search pipeline orchestrator (CWE-362, CWE-488). The server maintains a single shared, in-memory BM25 index instance across all incoming HTTP requests on a pipeline. Concurrent queries to the unauthenticated HTTP REST API interleave non-atomic clear, document addition, and search operations on this singleton index. As a result, documents fetched using one caller's search filter can be queried and returned to another concurrent caller, bypassing multi-tenant data isolation without generating any anomalous audit trail in server or database logs.

TL;DR

A concurrency flaw in pgEdge RAG Server (< 2.0.0) causes concurrent hybrid search requests to share and mutate a single in-memory BM25 keyword index. Attackers or concurrent users can receive document contents and synthesized LLM answers belonging to other tenants' filtered search scopes. Upgrading to version 2.0.0 resolves the issue by isolating BM25 index instances to individual requests.

⚠️ Exploit Status: POC

Technical Details

  • CVE ID: CVE-2026-125325 (GHSA-w437-9mwm-j6vq)
  • Vulnerability Class: CWE-362 (Race Condition) / CWE-488 (Exposure of Data Element to Wrong Session)
  • CVSS v3.1 Score: 5.9 Medium (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N)
  • Attack Vector: Network (Unauthenticated HTTP POST to /v1/pipelines/{name})
  • Primary Impact: Cross-request / Cross-tenant information disclosure of filtered RAG documents
  • Affected Versions: pgEdge RAG Server < 2.0.0
  • Patched Version: pgEdge RAG Server 2.0.0
  • EPSS / KEV Status: 0.27% (19.59th percentile) / Not Listed in CISA KEV

Affected Systems

  • pgEdge RAG Server (pgedge-rag-server)
  • pgEdge RAG Server: < 2.0.0 (including 1.0.0, 1.0.0-beta1 through 1.0.0-beta3-test, and 1.0.0-alpha1 through 1.0.0-alpha5) (Fixed in: 2.0.0)

Code Analysis

Commit: a09d487

Isolate BM25 index instances per request in Orchestrator.search and bound FetchDocuments with bm25_max_documents.

--- a/internal/pipeline/orchestrator.go
+++ b/internal/pipeline/orchestrator.go
@@ -33,7 +33,6 @@ type Orchestrator struct {
    completionProv Completer
    reranker       Reranker
    rerankTopK     int
-   bm25Index      *bm25.Index
    tokenBudget    int
    topN           int
    logger         *slog.Logger
@@ -66,7 +65,6 @@ func NewOrchestrator(cfg OrchestratorConfig) *Orchestrator {
        completionProv: cfg.CompletionProv,
        reranker:       cfg.Reranker,
        rerankTopK:     cfg.RerankTopK,
-       bm25Index:      bm25.NewIndex(),
        tokenBudget:    cfg.TokenBudget,
        topN:           cfg.TopN,
        logger:         logger,
@@ -345,7 +348,7 @@ func (o *Orchestrator) search(
            continue
        }

-       docs, err := o.dbPool.FetchDocuments(ctx, table, req.Filter)
+       docs, err := o.dbPool.FetchDocuments(ctx, table, req.Filter, maxBM25Docs)
        if err != nil {
            o.logger.Warn("failed to fetch documents for BM25",
                "table", table.Table, "error", err)
@@ -354,9 +357,28 @@ func (o *Orchestrator) search(
            continue
        }

-       o.bm25Index.Clear()
-       o.bm25Index.AddDocuments(docs)
-       bm25Results := o.bm25Index.Search(req.Query, topN*2)
+       if len(docs) >= maxBM25Docs {
+           o.logger.Warn(
+               "keyword search corpus truncated by bm25_max_documents; "+
+                   "keyword coverage for this request is partial",
+               "table", table.Table, "limit", maxBM25Docs,
+           )
+       }
+
+       index := bm25.NewIndex()
+       index.AddDocuments(docs)
+       bm25Results := index.Search(req.Query, topN*2)

Exploit Details

  • GitHub: Official concurrency regression test (TestSearch_ConcurrentRequestsDoNotShareKeywordIndex) demonstrating cross-tenant BM25 index leakage under concurrent load.

Mitigation Strategies

  • Upgrade pgEdge RAG Server to version 2.0.0 or later to ensure BM25 indexes are instantiated per request rather than shared across the pipeline.
  • Disable hybrid search (hybrid_enabled: false or vector_weight: 1.0) on pre-2.0.0 deployments to bypass the in-memory BM25 index entirely.
  • Disable raw source inclusion (allow_include_sources: false) in pipeline configurations to prevent raw cross-tenant document snippets from being returned in API responses.
  • Place pgEdge RAG Server behind an authenticating reverse proxy or API gateway with rate limiting to prevent unauthenticated concurrent query floods.

Remediation Steps:

  1. Audit running processes and installed packages using dpkg -l | grep pgedge-rag-server (Debian/Ubuntu) or rpm -qa | grep pgedge-rag-server (RHEL/CentOS) to identify version 1.x installations.
  2. Install pgEdge RAG Server version 2.0.0 or later (pgedge-rag-server2 package or compiled binary from release tag v2.0.0).
  3. Migrate pipeline configuration files to the v2.0.0 format and verify search.bm25_max_documents is set to a positive integer (default: 10000).
  4. Stop and disable any legacy v1.0.0 pgedge-rag-server systemd services listening on port 8080, and route all application traffic to the v2.0.0 service.

References

Read the full report for CVE-2026-125325 on our website for more details including interactive diagrams and full exploit analysis.

πŸ“° Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.