CVE-2026-47395
PraisonAI CLI automatically resolves @url mentions in prompt text and can read loopback URLs into model context
PraisonAI is a multi-agent teams system. Prior to version 4.6.40 of PraisonAI, corresponding to version 1.6.40 of praisonaiagents, PraisonAI's direct-prompt CLI automatically expands @url: mentions in raw prompt text before agent execution begins. If a prompt contains @url:<http-or-https-url>, the CLI calls MentionsParser.process(...). The @url: handler then performs a direct urllib.request.urlopen() request to the attacker-controlled URL and returns the response body. That response body is prepended to the final model prompt context. There is no loopback/private-address restriction, no metadata-service restriction, and no approval gate before the fetch. As a result, attacker-influenced prompt text can cause the operator's machine to fetch localhost-only HTTP resources and inject the response into model context. PraisonAI version 4.6.40 and praisonaiagents version 1.6.40 contain a fix.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Required
What an attacker can affect
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
5.5
PraisonAI CLI automatically resolves @url mentions in prompt text and can read loopback URLs into model context
PraisonAI is a multi-agent teams system. Prior to version 4.6.40 of PraisonAI, corresponding to version 1.6.40 of praisonaiagents, PraisonAI's direct-prompt CLI automatically expands @url: mentions in raw prompt text before agent execution begins. If a prompt contains @url:<http-or-https-url>, the CLI calls MentionsParser.process(...). The @url: handler then performs a direct urllib.request.urlopen() request to the attacker-controlled URL and returns the response body. That response body is prepended to the final model prompt context. There is no loopback/private-address restriction, no metadata-service restriction, and no approval gate before the fetch. As a result, attacker-influenced prompt text can cause the operator's machine to fetch localhost-only HTTP resources and inject the response into model context. PraisonAI version 4.6.40 and praisonaiagents version 1.6.40 contain a fix.
- Record
CVE-2026-47395- CVSS 3.1
- 5.5 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
EUVDEUVD-2026-46303
5.5
PraisonAI is a multi-agent teams system. Prior to version 4.6.40 of PraisonAI, corresponding to version 1.6.40 of praisonaiagents, PraisonAI's direct-prompt CLI automatically expands @url: mentions in raw prompt text before agent execution begins. If a prompt contains @url:<http-or-https-url>, the CLI calls MentionsParser.process(...). The @url: handler then performs a direct urllib.request.urlopen() request to the attacker-controlled URL and returns the response body. That response body is prepended to the final model prompt context. There is no loopback/private-address restriction, no metadata-service restriction, and no approval gate before the fetch. As a result, attacker-influenced prompt text can cause the operator's machine to fetch localhost-only HTTP resources and inject the response into model context. PraisonAI version 4.6.40 and praisonaiagents version 1.6.40 contain a fix.
- Record
EUVD-2026-46303- CVSS 3.1
- 5.5 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-5cxw-77wg-jrf3
5.5
PraisonAI CLI automatically resolves @url mentions in prompt text and can read loopback URLs into model context
Summary
PraisonAI's direct-prompt CLI automatically expands @url: mentions in raw prompt text before agent execution begins.
If a prompt contains @url:<http-or-https-url>, the CLI calls MentionsParser.process(...). The @url: handler then performs a direct urllib.request.urlopen() request to the attacker-controlled URL and returns the response body. That response body is prepended to the final model prompt context.
There is no loopback/private-address restriction, no metadata-service restriction, and no approval gate before the fetch.
As a result, attacker-influenced prompt text can cause the operator's machine to fetch localhost-only HTTP resources and inject the response into model context.
Example:
@url:http://localhost.:8766/ summarize this
This causes PraisonAI to make an HTTP request to the local machine and prepend the fetched response body to the prompt that the model receives.
This is a narrow local SSRF / local content disclosure issue in automatic prompt preprocessing. It is not a remote server takeover.
Details
The affected direct-prompt CLI path is in:
src/praisonai/praisonai/cli/main.py
The CLI imports and instantiates MentionsParser on the direct prompt path:
from praisonaiagents.tools.mentions import MentionsParser
parser = MentionsParser(workspace_path=os.getcwd())
if parser.has_mentions(prompt):
mention_context, prompt = parser.process(prompt)
if mention_context:
prompt = f"{mention_context}# Task:\n{prompt}"
This means raw prompt text is interpreted as a mention language before query rewriting, prompt expansion, tool execution, or LLM invocation.
The affected mention implementation is in:
src/praisonai-agents/praisonaiagents/tools/mentions.py
@url: is a first-class mention type:
PATTERNS = {
"file": re.compile(r'@file:([^\s]+)'),
"web": re.compile(r'@web:([^\s]+(?:\s+[^\s@]+)*)'),
"doc": re.compile(r'@doc:([^\s]+)'),
"rule": re.compile(r'@rule:([^\s]+)'),
"url": re.compile(r'@url:(https?://[^\s]+)'),
}
The URL mention handler performs an unrestricted HTTP request:
req = urllib.request.Request(
url,
headers={'User-Agent': 'Mozilla/5.0 (compatible; PraisonAI/1.0)'}
)
with urllib.request.urlopen(req, timeout=10) as response:
content = response.read().decode('utf-8', errors='ignore')
There is no validation rejecting:
127.0.0.1
localhost
localhost.
private RFC1918 addresses
link-local addresses
cloud metadata endpoints
other local-only HTTP services
The returned body is added to the generated mention context and then prepended to the prompt.
The resulting chain is:
attacker-influenced prompt text
-> @url:http://localhost.:8766/
-> direct-prompt CLI calls MentionsParser.process(...)
-> _process_url_mention(...)
-> urllib.request.urlopen(attacker URL)
-> loopback HTTP response body is read
-> response body is injected into model prompt context
PoC
The following PoC is non-destructive. It starts a local HTTP server on 127.0.0.1:8766, passes a prompt containing @url:http://localhost.:8766/ through the real MentionsParser.process(...) implementation, and confirms that the local response body is injected into the generated prompt context.
Full PoC
#!/usr/bin/env python3
"""Self-contained local replay for PraisonAI CLI @url mention loopback fetch."""
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"
PRAISON_ROOT = REPO_ROOT / "src" / "praisonai"
AGENTS_ROOT = REPO_ROOT / "src" / "praisonai-agents"
CLI_MAIN = PRAISON_ROOT / "praisonai/cli/main.py"
MENTIONS = AGENTS_ROOT / "praisonaiagents/tools/mentions.py"
def verify_source() -> None:
expected = {
CLI_MAIN: [
"from…
- Record
GHSA-5cxw-77wg-jrf3- CVSS 3.1
- 5.5 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
NVD
5.5
PraisonAI is a multi-agent teams system. Prior to version 4.6.40 of PraisonAI, corresponding to version 1.6.40 of praisonaiagents, PraisonAI's direct-prompt CLI automatically expands @url: mentions in raw prompt text before agent execution begins. If a prompt contains @url:<http-or-https-url>, the CLI calls MentionsParser.process(...). The @url: handler then performs a direct urllib.request.urlopen() request to the attacker-controlled URL and returns the response body. That response body is prepended to the final model prompt context. There is no loopback/private-address restriction, no metadata-service restriction, and no approval gate before the fetch. As a result, attacker-influenced prompt text can cause the operator's machine to fetch localhost-only HTTP resources and inject the response into model context. PraisonAI version 4.6.40 and praisonaiagents version 1.6.40 contain a fix.
- Record
CVE-2026-47395- CVSS 3.1
- 5.5 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
OSV
5.5
PraisonAI CLI automatically resolves @url mentions in prompt text and can read loopback URLs into model context
PraisonAI is a multi-agent teams system. Prior to version 4.6.40 of PraisonAI, corresponding to version 1.6.40 of praisonaiagents, PraisonAI's direct-prompt CLI automatically expands @url: mentions in raw prompt text before agent execution begins. If a prompt contains @url:<http-or-https-url>, the CLI calls MentionsParser.process(...). The @url: handler then performs a direct urllib.request.urlopen() request to the attacker-controlled URL and returns the response body. That response body is prepended to the final model prompt context. There is no loopback/private-address restriction, no metadata-service restriction, and no approval gate before the fetch. As a result, attacker-influenced prompt text can cause the operator's machine to fetch localhost-only HTTP resources and inject the response into model context. PraisonAI version 4.6.40 and praisonaiagents version 1.6.40 contain a fix.
- Record
CVE-2026-47395- CVSS 3.1
- 5.5 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
OSVGHSA-5cxw-77wg-jrf3
5.5
PraisonAI CLI automatically resolves @url mentions in prompt text and can read loopback URLs into model context
Summary
PraisonAI's direct-prompt CLI automatically expands @url: mentions in raw prompt text before agent execution begins.
If a prompt contains @url:<http-or-https-url>, the CLI calls MentionsParser.process(...). The @url: handler then performs a direct urllib.request.urlopen() request to the attacker-controlled URL and returns the response body. That response body is prepended to the final model prompt context.
There is no loopback/private-address restriction, no metadata-service restriction, and no approval gate before the fetch.
As a result, attacker-influenced prompt text can cause the operator's machine to fetch localhost-only HTTP resources and inject the response into model context.
Example:
@url:http://localhost.:8766/ summarize this
This causes PraisonAI to make an HTTP request to the local machine and prepend the fetched response body to the prompt that the model receives.
This is a narrow local SSRF / local content disclosure issue in automatic prompt preprocessing. It is not a remote server takeover.
Details
The affected direct-prompt CLI path is in:
src/praisonai/praisonai/cli/main.py
The CLI imports and instantiates MentionsParser on the direct prompt path:
from praisonaiagents.tools.mentions import MentionsParser
parser = MentionsParser(workspace_path=os.getcwd())
if parser.has_mentions(prompt):
mention_context, prompt = parser.process(prompt)
if mention_context:
prompt = f"{mention_context}# Task:\n{prompt}"
This means raw prompt text is interpreted as a mention language before query rewriting, prompt expansion, tool execution, or LLM invocation.
The affected mention implementation is in:
src/praisonai-agents/praisonaiagents/tools/mentions.py
@url: is a first-class mention type:
PATTERNS = {
"file": re.compile(r'@file:([^\s]+)'),
"web": re.compile(r'@web:([^\s]+(?:\s+[^\s@]+)*)'),
"doc": re.compile(r'@doc:([^\s]+)'),
"rule": re.compile(r'@rule:([^\s]+)'),
"url": re.compile(r'@url:(https?://[^\s]+)'),
}
The URL mention handler performs an unrestricted HTTP request:
req = urllib.request.Request(
url,
headers={'User-Agent': 'Mozilla/5.0 (compatible; PraisonAI/1.0)'}
)
with urllib.request.urlopen(req, timeout=10) as response:
content = response.read().decode('utf-8', errors='ignore')
There is no validation rejecting:
127.0.0.1
localhost
localhost.
private RFC1918 addresses
link-local addresses
cloud metadata endpoints
other local-only HTTP services
The returned body is added to the generated mention context and then prepended to the prompt.
The resulting chain is:
attacker-influenced prompt text
-> @url:http://localhost.:8766/
-> direct-prompt CLI calls MentionsParser.process(...)
-> _process_url_mention(...)
-> urllib.request.urlopen(attacker URL)
-> loopback HTTP response body is read
-> response body is injected into model prompt context
PoC
The following PoC is non-destructive. It starts a local HTTP server on 127.0.0.1:8766, passes a prompt containing @url:http://localhost.:8766/ through the real MentionsParser.process(...) implementation, and confirms that the local response body is injected into the generated prompt context.
Full PoC
#!/usr/bin/env python3
"""Self-contained local replay for PraisonAI CLI @url mention loopback fetch."""
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"
PRAISON_ROOT = REPO_ROOT / "src" / "praisonai"
AGENTS_ROOT = REPO_ROOT / "src" / "praisonai-agents"
CLI_MAIN = PRAISON_ROOT / "praisonai/cli/main.py"
MENTIONS = AGENTS_ROOT / "praisonaiagents/tools/mentions.py"
def verify_source() -> None:
expected = {
CLI_MAIN: [
"from…
- Record
GHSA-5cxw-77wg-jrf3- CVSS 3.1
- 5.5 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
OSVPYSEC-2026-2903
5.5
PraisonAI CLI automatically resolves @url mentions in prompt text and can read loopback URLs into model context
Summary
PraisonAI's direct-prompt CLI automatically expands @url: mentions in raw prompt text before agent execution begins.
If a prompt contains @url:<http-or-https-url>, the CLI calls MentionsParser.process(...). The @url: handler then performs a direct urllib.request.urlopen() request to the attacker-controlled URL and returns the response body. That response body is prepended to the final model prompt context.
There is no loopback/private-address restriction, no metadata-service restriction, and no approval gate before the fetch.
As a result, attacker-influenced prompt text can cause the operator's machine to fetch localhost-only HTTP resources and inject the response into model context.
Example:
@url:http://localhost.:8766/ summarize this
This causes PraisonAI to make an HTTP request to the local machine and prepend the fetched response body to the prompt that the model receives.
This is a narrow local SSRF / local content disclosure issue in automatic prompt preprocessing. It is not a remote server takeover.
Details
The affected direct-prompt CLI path is in:
src/praisonai/praisonai/cli/main.py
The CLI imports and instantiates MentionsParser on the direct prompt path:
from praisonaiagents.tools.mentions import MentionsParser
parser = MentionsParser(workspace_path=os.getcwd())
if parser.has_mentions(prompt):
mention_context, prompt = parser.process(prompt)
if mention_context:
prompt = f"{mention_context}# Task:\n{prompt}"
This means raw prompt text is interpreted as a mention language before query rewriting, prompt expansion, tool execution, or LLM invocation.
The affected mention implementation is in:
src/praisonai-agents/praisonaiagents/tools/mentions.py
@url: is a first-class mention type:
PATTERNS = {
"file": re.compile(r'@file:([^\s]+)'),
"web": re.compile(r'@web:([^\s]+(?:\s+[^\s@]+)*)'),
"doc": re.compile(r'@doc:([^\s]+)'),
"rule": re.compile(r'@rule:([^\s]+)'),
"url": re.compile(r'@url:(https?://[^\s]+)'),
}
The URL mention handler performs an unrestricted HTTP request:
req = urllib.request.Request(
url,
headers={'User-Agent': 'Mozilla/5.0 (compatible; PraisonAI/1.0)'}
)
with urllib.request.urlopen(req, timeout=10) as response:
content = response.read().decode('utf-8', errors='ignore')
There is no validation rejecting:
127.0.0.1
localhost
localhost.
private RFC1918 addresses
link-local addresses
cloud metadata endpoints
other local-only HTTP services
The returned body is added to the generated mention context and then prepended to the prompt.
The resulting chain is:
attacker-influenced prompt text
-> @url:http://localhost.:8766/
-> direct-prompt CLI calls MentionsParser.process(...)
-> _process_url_mention(...)
-> urllib.request.urlopen(attacker URL)
-> loopback HTTP response body is read
-> response body is injected into model prompt context
PoC
The following PoC is non-destructive. It starts a local HTTP server on 127.0.0.1:8766, passes a prompt containing @url:http://localhost.:8766/ through the real MentionsParser.process(...) implementation, and confirms that the local response body is injected into the generated prompt context.
Full PoC
#!/usr/bin/env python3
"""Self-contained local replay for PraisonAI CLI @url mention loopback fetch."""
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"
PRAISON_ROOT = REPO_ROOT / "src" / "praisonai"
AGENTS_ROOT = REPO_ROOT / "src" / "praisonai-agents"
CLI_MAIN = PRAISON_ROOT / "praisonai/cli/main.py"
MENTIONS = AGENTS_ROOT / "praisonaiagents/tools/mentions.py"
def verify_source() -> None:
expected = {
CLI_MAIN: [
"from…
- Record
PYSEC-2026-2903- CVSS 3.1
- 5.5 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N - Updated
- 2026-11-02
OSVPYSEC-2026-2942
5.5
PraisonAI CLI automatically resolves @url mentions in prompt text and can read loopback URLs into model context
Summary
PraisonAI's direct-prompt CLI automatically expands @url: mentions in raw prompt text before agent execution begins.
If a prompt contains @url:<http-or-https-url>, the CLI calls MentionsParser.process(...). The @url: handler then performs a direct urllib.request.urlopen() request to the attacker-controlled URL and returns the response body. That response body is prepended to the final model prompt context.
There is no loopback/private-address restriction, no metadata-service restriction, and no approval gate before the fetch.
As a result, attacker-influenced prompt text can cause the operator's machine to fetch localhost-only HTTP resources and inject the response into model context.
Example:
@url:http://localhost.:8766/ summarize this
This causes PraisonAI to make an HTTP request to the local machine and prepend the fetched response body to the prompt that the model receives.
This is a narrow local SSRF / local content disclosure issue in automatic prompt preprocessing. It is not a remote server takeover.
Details
The affected direct-prompt CLI path is in:
src/praisonai/praisonai/cli/main.py
The CLI imports and instantiates MentionsParser on the direct prompt path:
from praisonaiagents.tools.mentions import MentionsParser
parser = MentionsParser(workspace_path=os.getcwd())
if parser.has_mentions(prompt):
mention_context, prompt = parser.process(prompt)
if mention_context:
prompt = f"{mention_context}# Task:\n{prompt}"
This means raw prompt text is interpreted as a mention language before query rewriting, prompt expansion, tool execution, or LLM invocation.
The affected mention implementation is in:
src/praisonai-agents/praisonaiagents/tools/mentions.py
@url: is a first-class mention type:
PATTERNS = {
"file": re.compile(r'@file:([^\s]+)'),
"web": re.compile(r'@web:([^\s]+(?:\s+[^\s@]+)*)'),
"doc": re.compile(r'@doc:([^\s]+)'),
"rule": re.compile(r'@rule:([^\s]+)'),
"url": re.compile(r'@url:(https?://[^\s]+)'),
}
The URL mention handler performs an unrestricted HTTP request:
req = urllib.request.Request(
url,
headers={'User-Agent': 'Mozilla/5.0 (compatible; PraisonAI/1.0)'}
)
with urllib.request.urlopen(req, timeout=10) as response:
content = response.read().decode('utf-8', errors='ignore')
There is no validation rejecting:
127.0.0.1
localhost
localhost.
private RFC1918 addresses
link-local addresses
cloud metadata endpoints
other local-only HTTP services
The returned body is added to the generated mention context and then prepended to the prompt.
The resulting chain is:
attacker-influenced prompt text
-> @url:http://localhost.:8766/
-> direct-prompt CLI calls MentionsParser.process(...)
-> _process_url_mention(...)
-> urllib.request.urlopen(attacker URL)
-> loopback HTTP response body is read
-> response body is injected into model prompt context
PoC
The following PoC is non-destructive. It starts a local HTTP server on 127.0.0.1:8766, passes a prompt containing @url:http://localhost.:8766/ through the real MentionsParser.process(...) implementation, and confirms that the local response body is injected into the generated prompt context.
Full PoC
#!/usr/bin/env python3
"""Self-contained local replay for PraisonAI CLI @url mention loopback fetch."""
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"
PRAISON_ROOT = REPO_ROOT / "src" / "praisonai"
AGENTS_ROOT = REPO_ROOT / "src" / "praisonai-agents"
CLI_MAIN = PRAISON_ROOT / "praisonai/cli/main.py"
MENTIONS = AGENTS_ROOT / "praisonaiagents/tools/mentions.py"
def verify_source() -> None:
expected = {
CLI_MAIN: [
"from…
- Record
PYSEC-2026-2942- CVSS 3.1
- 5.5 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
praisonaiagentsPyPI |
through 1.6.39 | 1.6.40 |
praisonaiPyPI |
through 4.6.39 | 4.6.40 |
praisonaiagentsPyPI |
before 1.6.40, plus listed versions 0.0.10.0.100.0.1000.0.1010.0.102 Show 45 moreShow less0.0.1030.0.1040.0.1050.0.1060.0.1070.0.1080.0.1090.0.110.0.1100.0.1110.0.1120.0.1130.0.1140.0.1150.0.1160.0.1170.0.1180.0.1190.0.120.0.1200.0.1210.0.1220.0.1230.0.1240.0.1250.0.1260.0.1270.0.1280.0.1290.0.130.0.1300.0.1310.0.1320.0.1330.0.1340.0.1350.0.1360.0.1370.0.1380.0.1390.0.140.0.1400.0.1410.0.1420.0.143 |
1.6.40 |
praisonaiPyPI |
before 4.6.40, plus listed versions 0.0.10.0.100.0.110.0.120.0.13 Show 45 moreShow less0.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.6.40 |
History
| Recorded | Changed | Sources |
|---|---|---|
| aliases | EUVD | |
| first indexed | CVE List, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-47395 is a medium vulnerability (CVSS 5.5) in Mervinpraison Praisonai, published May 29, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.18%, the 7th percentile. Fixed in praisonaiagents 1.6.40.
References
github.com/MervinPraison/PraisonAI/security/advisories/GHSA-5cxw-77wg-jrf3
github.com/MervinPraison/PraisonAI/pull/1684
github.com/MervinPraison/PraisonAI/commit/b0d8f777528f3253a0cfb0a3ef65455da6ae32f6
github.com/advisories/GHSA-5cxw-77wg-jrf3
github.com/CVEProject/cvelistV5/tree/main/cves/2026/47xxx/CVE-2026-47395.json
nvd.nist.gov/vuln/detail/CVE-2026-47395
github.com/MervinPraison/PraisonAI
pypi.org/project/praisonai
pypi.org/project/praisonaiagents
Left out here: 1,199 versions and the rest of 4 source descriptions. The JSON record has them.