CVE-2026-57114
PraisonAI: Jobs webhook SSRF protection bypass via DNS rebinding
PraisonAI is a multi-agent teams system. From version 4.5.126 to before version 4.6.59, PraisonAI's Async Jobs API validates webhook_url when a job request is parsed and again when the internal Job object is constructed. That validation blocks direct loopback/private targets, but it is not bound to the later network request. When a job completes, _send_webhook() passes the original hostname to httpx.AsyncClient.post() with no send-time validation, IP pinning, or guarded transport. An attacker-controlled hostname can therefore resolve to a public IP during Pydantic validation and later resolve to loopback/private/cloud-metadata infrastructure during webhook delivery. This bypasses the intended SSRF guard in current supported releases. This appears to be an incomplete fix / patch bypass for GHSA-8frj-8q3m-xhgm. This issue has been patched in version 4.6.59.
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
What each source reports
CVE List Score used
7.2
PraisonAI: Jobs webhook SSRF protection bypass via DNS rebinding
PraisonAI is a multi-agent teams system. From version 4.5.126 to before version 4.6.59, PraisonAI's Async Jobs API validates webhook_url when a job request is parsed and again when the internal Job object is constructed. That validation blocks direct loopback/private targets, but it is not bound to the later network request. When a job completes, _send_webhook() passes the original hostname to httpx.AsyncClient.post() with no send-time validation, IP pinning, or guarded transport. An attacker-controlled hostname can therefore resolve to a public IP during Pydantic validation and later resolve to loopback/private/cloud-metadata infrastructure during webhook delivery. This bypasses the intended SSRF guard in current supported releases. This appears to be an incomplete fix / patch bypass for GHSA-8frj-8q3m-xhgm. This issue has been patched in version 4.6.59.
- Record
CVE-2026-57114- CVSS 3.1
- 7.2 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N - Weakness
- CWE-367, CWE-918
- Updated
- 2026-11-02
EUVDEUVD-2026-108890
7.2
PraisonAI is a multi-agent teams system. From version 4.5.126 to before version 4.6.59, PraisonAI's Async Jobs API validates webhook_url when a job request is parsed and again when the internal Job object is constructed. That validation blocks direct loopback/private targets, but it is not bound to the later network request. When a job completes, _send_webhook() passes the original hostname to httpx.AsyncClient.post() with no send-time validation, IP pinning, or guarded transport. An attacker-controlled hostname can therefore resolve to a public IP during Pydantic validation and later resolve to loopback/private/cloud-metadata infrastructure during webhook delivery. This bypasses the intended SSRF guard in current supported releases. This appears to be an incomplete fix / patch bypass for GHSA-8frj-8q3m-xhgm. This issue has been patched in version 4.6.59.
- Record
EUVD-2026-108890- CVSS 3.1
- 7.2 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-rjvw-7vvw-549v
7.2
PraisonAI: Jobs webhook SSRF protection bypass via DNS rebinding
Jobs webhook SSRF protection bypass via DNS rebinding
Summary
PraisonAI's Async Jobs API validates webhook_url when a job request is parsed
and again when the internal Job object is constructed. That validation blocks
direct loopback/private targets, but it is not bound to the later network
request. When a job completes, _send_webhook() passes the original hostname to
httpx.AsyncClient.post() with no send-time validation, IP pinning, or guarded
transport.
An attacker-controlled hostname can therefore resolve to a public IP during Pydantic validation and later resolve to loopback/private/cloud-metadata infrastructure during webhook delivery. This bypasses the intended SSRF guard in current supported releases.
This appears to be an incomplete fix / patch bypass for GHSA-8frj-8q3m-xhgm
("Server-Side Request Forgery via Unvalidated webhook_url in Jobs API"). I defer
to maintainers on whether this should be a new advisory/CVE or an amendment to
the prior advisory, but current supported releases still appear affected.
Affected Component
Package:
praisonai
Files:
src/praisonai/praisonai/jobs/models.py
src/praisonai/praisonai/jobs/executor.py
src/praisonai/praisonai/jobs/router.py
Relevant code paths:
JobSubmitRequest.validate_webhook_url()
Job.validate_webhook_url()
JobExecutor._send_webhook()
POST /api/v1/runs
Affected Versions
Validated affected:
v4.5.126(f00763937bf7f4d091e84533692fc0576fca9b99);v4.5.128(b4e3a8a8);v4.6.56(d3c4a2af);v4.6.57(e90d92231853161ad931f3498da57651a9f8b528);- current
main(2f9677abb2ea68eab864ee8b6a828fd0141612e1,v4.6.57-4-g2f9677ab).
Suggested affected range for maintainer confirmation:
>= 4.5.126, <= 4.6.57
No patched version is known to me at submission time.
v4.5.124 and earlier are covered by the older unvalidated-webhook advisory.
This report is scoped to patched-era releases where direct loopback/private
webhook URLs are rejected but DNS rebinding still bypasses the guard.
Root Cause
Current validation is a time-of-check/time-of-use boundary:
JobSubmitRequest.webhook_urlis validated withurlparse()andsocket.gethostbyname().- The resolved address is rejected when it is private, loopback, link-local, or multicast.
- The original URL string is stored on the
Job. - After job completion,
_send_webhook()creates a freshhttpx.AsyncClientand POSTs to the original URL. httpxresolves the hostname again. There is no revalidation of the address that is actually connected to.
The first DNS answer is therefore trusted for a later, independent DNS lookup. An attacker who controls DNS for the webhook hostname can return a public address during validation and an internal address during delivery.
Local Reproduction
The PoV is local-only. It starts a loopback HTTP server, monkeypatches resolver
behavior in-process, and uses the real PraisonAI Job validator plus
JobExecutor._send_webhook() sender.
Run from a PraisonAI checkout:
env PYTHONPATH=src/praisonai python3 poc_jobs_webhook_dns_rebinding_ssrf.py
Observed output on current main:
DIRECT_LOOPBACK_BLOCKED: {"Job": true, "JobSubmitRequest": true}
ACCEPTED_WEBHOOK_URL: http://rebind.test:<port>/hook
INTERNAL_SERVER_HIT: true
INTERNAL_REQUEST_HOST: rebind.test:<port>
INTERNAL_REQUEST_PATH: /hook
WEBHOOK_PAYLOAD_KEYS: completed_at,duration_seconds,error,job_id,result,status
WEBHOOK_PAYLOAD_STATUS: succeeded
PRAI-CAND-005 CONFIRMED: Jobs webhook validation is bypassed by DNS rebinding
The direct control proves that the current guard is meant to reject loopback webhook destinations. The rebind case proves the same blocked destination class is reached when the hostname changes between validation and delivery.
Full Local PoV Script
#!/usr/bin/env python3
"""Local PoV for PraisonAI Jobs webhook DNS-rebinding SSRF.
The PoV uses only loopback…
- Record
GHSA-rjvw-7vvw-549v- CVSS 3.1
- 7.2 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N - Weakness
- CWE-367, CWE-918
- Updated
- 2026-11-02
NVD
7.2
PraisonAI is a multi-agent teams system. From version 4.5.126 to before version 4.6.59, PraisonAI's Async Jobs API validates webhook_url when a job request is parsed and again when the internal Job object is constructed. That validation blocks direct loopback/private targets, but it is not bound to the later network request. When a job completes, _send_webhook() passes the original hostname to httpx.AsyncClient.post() with no send-time validation, IP pinning, or guarded transport. An attacker-controlled hostname can therefore resolve to a public IP during Pydantic validation and later resolve to loopback/private/cloud-metadata infrastructure during webhook delivery. This bypasses the intended SSRF guard in current supported releases. This appears to be an incomplete fix / patch bypass for GHSA-8frj-8q3m-xhgm. This issue has been patched in version 4.6.59.
- Record
CVE-2026-57114- CVSS 3.1
- 7.2 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N - Weakness
- CWE-367, CWE-918
- Updated
- 2026-11-02
OSV
7.2
PraisonAI: Jobs webhook SSRF protection bypass via DNS rebinding
PraisonAI is a multi-agent teams system. From version 4.5.126 to before version 4.6.59, PraisonAI's Async Jobs API validates webhook_url when a job request is parsed and again when the internal Job object is constructed. That validation blocks direct loopback/private targets, but it is not bound to the later network request. When a job completes, _send_webhook() passes the original hostname to httpx.AsyncClient.post() with no send-time validation, IP pinning, or guarded transport. An attacker-controlled hostname can therefore resolve to a public IP during Pydantic validation and later resolve to loopback/private/cloud-metadata infrastructure during webhook delivery. This bypasses the intended SSRF guard in current supported releases. This appears to be an incomplete fix / patch bypass for GHSA-8frj-8q3m-xhgm. This issue has been patched in version 4.6.59.
- Record
CVE-2026-57114- CVSS 3.1
- 7.2 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N - Weakness
- CWE-367, CWE-918
- Updated
- 2026-11-02
OSVGHSA-rjvw-7vvw-549v
7.2
PraisonAI: Jobs webhook SSRF protection bypass via DNS rebinding
Jobs webhook SSRF protection bypass via DNS rebinding
Summary
PraisonAI's Async Jobs API validates webhook_url when a job request is parsed
and again when the internal Job object is constructed. That validation blocks
direct loopback/private targets, but it is not bound to the later network
request. When a job completes, _send_webhook() passes the original hostname to
httpx.AsyncClient.post() with no send-time validation, IP pinning, or guarded
transport.
An attacker-controlled hostname can therefore resolve to a public IP during Pydantic validation and later resolve to loopback/private/cloud-metadata infrastructure during webhook delivery. This bypasses the intended SSRF guard in current supported releases.
This appears to be an incomplete fix / patch bypass for GHSA-8frj-8q3m-xhgm
("Server-Side Request Forgery via Unvalidated webhook_url in Jobs API"). I defer
to maintainers on whether this should be a new advisory/CVE or an amendment to
the prior advisory, but current supported releases still appear affected.
Affected Component
Package:
praisonai
Files:
src/praisonai/praisonai/jobs/models.py
src/praisonai/praisonai/jobs/executor.py
src/praisonai/praisonai/jobs/router.py
Relevant code paths:
JobSubmitRequest.validate_webhook_url()
Job.validate_webhook_url()
JobExecutor._send_webhook()
POST /api/v1/runs
Affected Versions
Validated affected:
v4.5.126(f00763937bf7f4d091e84533692fc0576fca9b99);v4.5.128(b4e3a8a8);v4.6.56(d3c4a2af);v4.6.57(e90d92231853161ad931f3498da57651a9f8b528);- current
main(2f9677abb2ea68eab864ee8b6a828fd0141612e1,v4.6.57-4-g2f9677ab).
Suggested affected range for maintainer confirmation:
>= 4.5.126, <= 4.6.57
No patched version is known to me at submission time.
v4.5.124 and earlier are covered by the older unvalidated-webhook advisory.
This report is scoped to patched-era releases where direct loopback/private
webhook URLs are rejected but DNS rebinding still bypasses the guard.
Root Cause
Current validation is a time-of-check/time-of-use boundary:
JobSubmitRequest.webhook_urlis validated withurlparse()andsocket.gethostbyname().- The resolved address is rejected when it is private, loopback, link-local, or multicast.
- The original URL string is stored on the
Job. - After job completion,
_send_webhook()creates a freshhttpx.AsyncClientand POSTs to the original URL. httpxresolves the hostname again. There is no revalidation of the address that is actually connected to.
The first DNS answer is therefore trusted for a later, independent DNS lookup. An attacker who controls DNS for the webhook hostname can return a public address during validation and an internal address during delivery.
Local Reproduction
The PoV is local-only. It starts a loopback HTTP server, monkeypatches resolver
behavior in-process, and uses the real PraisonAI Job validator plus
JobExecutor._send_webhook() sender.
Run from a PraisonAI checkout:
env PYTHONPATH=src/praisonai python3 poc_jobs_webhook_dns_rebinding_ssrf.py
Observed output on current main:
DIRECT_LOOPBACK_BLOCKED: {"Job": true, "JobSubmitRequest": true}
ACCEPTED_WEBHOOK_URL: http://rebind.test:<port>/hook
INTERNAL_SERVER_HIT: true
INTERNAL_REQUEST_HOST: rebind.test:<port>
INTERNAL_REQUEST_PATH: /hook
WEBHOOK_PAYLOAD_KEYS: completed_at,duration_seconds,error,job_id,result,status
WEBHOOK_PAYLOAD_STATUS: succeeded
PRAI-CAND-005 CONFIRMED: Jobs webhook validation is bypassed by DNS rebinding
The direct control proves that the current guard is meant to reject loopback webhook destinations. The rebind case proves the same blocked destination class is reached when the hostname changes between validation and delivery.
Full Local PoV Script
#!/usr/bin/env python3
"""Local PoV for PraisonAI Jobs webhook DNS-rebinding SSRF.
The PoV uses only loopback…
- Record
GHSA-rjvw-7vvw-549v- CVSS 3.1
- 7.2 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N - Weakness
- CWE-367, CWE-918
- Updated
- 2026-11-02
OSVPYSEC-2026-3519
7.2
PraisonAI: Jobs webhook SSRF protection bypass via DNS rebinding
Jobs webhook SSRF protection bypass via DNS rebinding
Summary
PraisonAI's Async Jobs API validates webhook_url when a job request is parsed
and again when the internal Job object is constructed. That validation blocks
direct loopback/private targets, but it is not bound to the later network
request. When a job completes, _send_webhook() passes the original hostname to
httpx.AsyncClient.post() with no send-time validation, IP pinning, or guarded
transport.
An attacker-controlled hostname can therefore resolve to a public IP during Pydantic validation and later resolve to loopback/private/cloud-metadata infrastructure during webhook delivery. This bypasses the intended SSRF guard in current supported releases.
This appears to be an incomplete fix / patch bypass for GHSA-8frj-8q3m-xhgm
("Server-Side Request Forgery via Unvalidated webhook_url in Jobs API"). I defer
to maintainers on whether this should be a new advisory/CVE or an amendment to
the prior advisory, but current supported releases still appear affected.
Affected Component
Package:
praisonai
Files:
src/praisonai/praisonai/jobs/models.py
src/praisonai/praisonai/jobs/executor.py
src/praisonai/praisonai/jobs/router.py
Relevant code paths:
JobSubmitRequest.validate_webhook_url()
Job.validate_webhook_url()
JobExecutor._send_webhook()
POST /api/v1/runs
Affected Versions
Validated affected:
v4.5.126(f00763937bf7f4d091e84533692fc0576fca9b99);v4.5.128(b4e3a8a8);v4.6.56(d3c4a2af);v4.6.57(e90d92231853161ad931f3498da57651a9f8b528);- current
main(2f9677abb2ea68eab864ee8b6a828fd0141612e1,v4.6.57-4-g2f9677ab).
Suggested affected range for maintainer confirmation:
>= 4.5.126, <= 4.6.57
No patched version is known to me at submission time.
v4.5.124 and earlier are covered by the older unvalidated-webhook advisory.
This report is scoped to patched-era releases where direct loopback/private
webhook URLs are rejected but DNS rebinding still bypasses the guard.
Root Cause
Current validation is a time-of-check/time-of-use boundary:
JobSubmitRequest.webhook_urlis validated withurlparse()andsocket.gethostbyname().- The resolved address is rejected when it is private, loopback, link-local, or multicast.
- The original URL string is stored on the
Job. - After job completion,
_send_webhook()creates a freshhttpx.AsyncClientand POSTs to the original URL. httpxresolves the hostname again. There is no revalidation of the address that is actually connected to.
The first DNS answer is therefore trusted for a later, independent DNS lookup. An attacker who controls DNS for the webhook hostname can return a public address during validation and an internal address during delivery.
Local Reproduction
The PoV is local-only. It starts a loopback HTTP server, monkeypatches resolver
behavior in-process, and uses the real PraisonAI Job validator plus
JobExecutor._send_webhook() sender.
Run from a PraisonAI checkout:
env PYTHONPATH=src/praisonai python3 poc_jobs_webhook_dns_rebinding_ssrf.py
Observed output on current main:
DIRECT_LOOPBACK_BLOCKED: {"Job": true, "JobSubmitRequest": true}
ACCEPTED_WEBHOOK_URL: http://rebind.test:<port>/hook
INTERNAL_SERVER_HIT: true
INTERNAL_REQUEST_HOST: rebind.test:<port>
INTERNAL_REQUEST_PATH: /hook
WEBHOOK_PAYLOAD_KEYS: completed_at,duration_seconds,error,job_id,result,status
WEBHOOK_PAYLOAD_STATUS: succeeded
PRAI-CAND-005 CONFIRMED: Jobs webhook validation is bypassed by DNS rebinding
The direct control proves that the current guard is meant to reject loopback webhook destinations. The rebind case proves the same blocked destination class is reached when the hostname changes between validation and delivery.
Full Local PoV Script
#!/usr/bin/env python3
"""Local PoV for PraisonAI Jobs webhook DNS-rebinding SSRF.
The PoV uses only loopback…
- Record
PYSEC-2026-3519- CVSS 3.1
- 7.2 High
CVSS: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
| Package | Affected | Fixed |
|---|---|---|
praisonaiPyPI |
4.5.126 to 4.6.58 | 4.6.59 |
praisonaiPyPI |
4.5.126 up to 4.6.59, plus listed versions 4.5.1274.5.1284.5.1294.5.1304.5.131 Show 45 moreShow less4.5.1324.5.1334.5.1344.5.1354.5.1364.5.1374.5.1394.5.1404.5.1434.5.1444.5.1454.5.1494.6.104.6.114.6.124.6.134.6.144.6.154.6.164.6.184.6.194.6.204.6.214.6.224.6.234.6.244.6.254.6.264.6.274.6.284.6.294.6.304.6.314.6.324.6.334.6.344.6.354.6.364.6.374.6.384.6.394.6.404.6.414.6.424.6.43 |
4.6.59 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-57114 is a high vulnerability (CVSS 7.2) in Mervinpraison Praisonai, published June 18, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.26%, the 18th percentile. Fixed in praisonai 4.6.59.
References
Left out here: 15 versions and the rest of 3 source descriptions. The JSON record has them.