The web_crawl() function in praisonaiagents/tools/web_crawl_tools.py accepts arbitrary URLs from AI agents with zero validation. No scheme allowlisting, hostname/IP blocklisting, or private network checks are applied before fetching. This allows an attacker (or prompt injection in crawled content) to force the agent to fetch cloud metadata endpoints, internal services, or local files via file:// URLs.
The web_crawl() function at web_crawl_tools.py:182 accepts a URL string or list of URLs and passes them directly to HTTP clients without any SSRF protections:
# web_crawl_tools.py:182-234
def web_crawl(
urls: Union[str, List[str]],
provider: Optional[str] = None,
) -> Union[Dict[str, Any], List[Dict[str, Any]]]:
# Normalize to list
single_url = isinstance(urls, str)
# ...
url_list = [urls] if single_url else urls
# No URL validation whatsoever — urls flow directly to providers
if selected == "tavily":
results = _crawl_with_tavily(url_list)
elif selected == "crawl4ai":
results = _crawl_with_crawl4ai(url_list)
else:
results = _crawl_with_httpx(url_list) # Always-available fallback
The _crawl_with_httpx() fallback at line 133 makes the actual requests:
# web_crawl_tools.py:140-150
try:
import httpx
with httpx.Client(follow_redirects=True, timeout=30.0) as client:
response = client.get(url) # Line 143: fetches ANY URL, follows redirects
except ImportError:
import urllib.request
with urllib.request.urlopen(url, timeout=30) as response: # Line 149: supports file://
content = response.read().decode('utf-8', errors='ignore')
The specific vulnerabilities are:
http://, https://, file://, ftp://, gopher:// are all accepted169.254.169.254, 127.0.0.1, 10.x.x.x, 172.16.x.x, 192.168.x.x are all reachable1.5.128Exploitability
AV:NAC:LPR:LUI:NScope
S:CImpact
C:HI:NA:N7.7/CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N