CVE-2026-55540
PraisonAI: [Path Traversal] agent tools escape the configured workspace via symlinks
PraisonAI is a multi-agent teams system. Prior to praisonai 4.6.51, is_path_within_directory() uses os.path.abspath() rather than os.path.realpath() for the workspace boundary. A symlink inside workspace can point outside and still pass the check, allowing read_file and other code tools to access files outside the configured workspace. This issue is fixed in version 4.6.58.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- None
- User interaction
- Required
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
7.1
PraisonAI: [Path Traversal] agent tools escape the configured workspace via symlinks
PraisonAI is a multi-agent teams system. Prior to praisonai 4.6.51, is_path_within_directory() uses os.path.abspath() rather than os.path.realpath() for the workspace boundary. A symlink inside workspace can point outside and still pass the check, allowing read_file and other code tools to access files outside the configured workspace. This issue is fixed in version 4.6.58.
- Record
CVE-2026-55540- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:L - Weakness
- CWE-22
- Updated
- 2026-11-02
EUVDEUVD-2026-65512
7.1
PraisonAI is a multi-agent teams system. Prior to praisonai 4.6.51, is_path_within_directory() uses os.path.abspath() rather than os.path.realpath() for the workspace boundary. A symlink inside workspace can point outside and still pass the check, allowing read_file and other code tools to access files outside the configured workspace. This issue is fixed in version 4.6.58.
- Record
EUVD-2026-65512- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:L - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-ch89-h4r2-c8f8
7.1
PraisonAI: [Path Traversal] agent tools escape the configured workspace via symlinks
Summary
PraisonAI's praisonai.code tool wrappers (exported as CODE_TOOLS for agents) expose a workspace setting that the module itself treats as a path-traversal security boundary — read_file, write_file, apply_diff, and search_replace explicitly call is_path_within_directory() and return "… is outside the workspace" on violations. That boundary is enforced unsoundly and inconsistently:
- The containment helper uses
os.path.abspath(), notrealpath()/Path.resolve(). A symlink located inside the workspace whose target is outside has anabspath()that is still inside the workspace, so it passes the check whileopen()follows the link. This bypasses read, write, apply_diff, and search_replace (CWE-59). list_files()resolvespathagainst the workspace but never calls the containment helper at all —../and absolute paths escape directly (CWE-22).execute_command()takes aworkspaceargument documented "for security validation" but performs nocwdcontainment check;code_execute_command()resolves a relativecwdagainst the workspace and also never validates it (and never even passesworkspaceto the low-level helper). A relativecwd="../outside"runs commands from outside the workspace (CWE-22). An attacker who can influence an agent that has these tools attached (untrusted prompt, indirect prompt injection, or a server-exposed agent) can read, overwrite, list, and execute from outside the configured workspace, bounded only by the process user's filesystem permissions.
Technical Detail
1. Unsound containment helper (symlink bypass — CWE-59)
# src/praisonai/praisonai/code/utils/file_utils.py — is_path_within_directory()
abs_file = os.path.abspath(file_path) # does NOT resolve symlinks
abs_dir = os.path.abspath(directory)
if not abs_dir.endswith(os.sep): abs_dir += os.sep
return abs_file.startswith(abs_dir) or abs_file == abs_dir.rstrip(os.sep)
read_file/write_file/apply_diff/search_replace call this with the configured workspace (e.g. read_file.py: # Security check - ensure path is within workspace). Because abspath() does not canonicalize symlinks, a link at WORKSPACE/link_to_secret.txt → /outside/secret.txt has abspath WORKSPACE/link_to_secret.txt (inside) and passes, while open() follows it to the real outside target.
2. list_files() has no containment check (CWE-22)
# src/praisonai/praisonai/code/tools/list_files.py
if workspace and not os.path.isabs(path):
abs_path = os.path.abspath(os.path.join(workspace, path)) # ../ collapses out of workspace
else:
abs_path = os.path.abspath(path) # absolute path used as-is
# ... os.path.isdir(abs_path) then listed. is_path_within_directory() is NEVER called.
3. execute_command() never validates cwd (CWE-22)
# src/praisonai/praisonai/code/tools/execute_command.py — workspace param doc: "for security validation"
if cwd:
if workspace and not os.path.isabs(cwd):
work_dir = os.path.abspath(os.path.join(workspace, cwd)) # ../ escapes; no containment check
else:
work_dir = os.path.abspath(cwd)
# subprocess.run(args, cwd=work_dir, ...) # no is_path_within_directory() anywhere
# src/praisonai/praisonai/code/agent_tools.py — code_execute_command()
if work_dir and _workspace_root and not os.path.isabs(work_dir):
work_dir = os.path.join(_workspace_root, work_dir) # joins, never validates
result = _execute_command(command=command, cwd=work_dir, timeout=120) # workspace not even passed
Note: execute_command rejects shell=True and runs shlex.split(command) via subprocess.run (no shell), so shell metacharacters (&&, >, pipes) do not work — but any binary still runs with attacker-chosen argv from the escaped cwd, which is sufficient to read/write outside the workspace.
The workspace is an…
- Record
GHSA-ch89-h4r2-c8f8- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:L - Weakness
- CWE-22
- Updated
- 2026-11-02
NVD
7.1
PraisonAI is a multi-agent teams system. Prior to praisonai 4.6.51, is_path_within_directory() uses os.path.abspath() rather than os.path.realpath() for the workspace boundary. A symlink inside workspace can point outside and still pass the check, allowing read_file and other code tools to access files outside the configured workspace. This issue is fixed in version 4.6.58.
- Record
CVE-2026-55540- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:L - Weakness
- CWE-22
- Updated
- 2026-11-02
OSV
7.1
PraisonAI: [Path Traversal] agent tools escape the configured workspace via symlinks
PraisonAI is a multi-agent teams system. Prior to praisonai 4.6.51, is_path_within_directory() uses os.path.abspath() rather than os.path.realpath() for the workspace boundary. A symlink inside workspace can point outside and still pass the check, allowing read_file and other code tools to access files outside the configured workspace. This issue is fixed in version 4.6.58.
- Record
CVE-2026-55540- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:L - Weakness
- CWE-22
- Updated
- 2026-11-02
OSVGHSA-ch89-h4r2-c8f8
7.1
PraisonAI: [Path Traversal] agent tools escape the configured workspace via symlinks
Summary
PraisonAI's praisonai.code tool wrappers (exported as CODE_TOOLS for agents) expose a workspace setting that the module itself treats as a path-traversal security boundary — read_file, write_file, apply_diff, and search_replace explicitly call is_path_within_directory() and return "… is outside the workspace" on violations. That boundary is enforced unsoundly and inconsistently:
- The containment helper uses
os.path.abspath(), notrealpath()/Path.resolve(). A symlink located inside the workspace whose target is outside has anabspath()that is still inside the workspace, so it passes the check whileopen()follows the link. This bypasses read, write, apply_diff, and search_replace (CWE-59). list_files()resolvespathagainst the workspace but never calls the containment helper at all —../and absolute paths escape directly (CWE-22).execute_command()takes aworkspaceargument documented "for security validation" but performs nocwdcontainment check;code_execute_command()resolves a relativecwdagainst the workspace and also never validates it (and never even passesworkspaceto the low-level helper). A relativecwd="../outside"runs commands from outside the workspace (CWE-22). An attacker who can influence an agent that has these tools attached (untrusted prompt, indirect prompt injection, or a server-exposed agent) can read, overwrite, list, and execute from outside the configured workspace, bounded only by the process user's filesystem permissions.
Technical Detail
1. Unsound containment helper (symlink bypass — CWE-59)
# src/praisonai/praisonai/code/utils/file_utils.py — is_path_within_directory()
abs_file = os.path.abspath(file_path) # does NOT resolve symlinks
abs_dir = os.path.abspath(directory)
if not abs_dir.endswith(os.sep): abs_dir += os.sep
return abs_file.startswith(abs_dir) or abs_file == abs_dir.rstrip(os.sep)
read_file/write_file/apply_diff/search_replace call this with the configured workspace (e.g. read_file.py: # Security check - ensure path is within workspace). Because abspath() does not canonicalize symlinks, a link at WORKSPACE/link_to_secret.txt → /outside/secret.txt has abspath WORKSPACE/link_to_secret.txt (inside) and passes, while open() follows it to the real outside target.
2. list_files() has no containment check (CWE-22)
# src/praisonai/praisonai/code/tools/list_files.py
if workspace and not os.path.isabs(path):
abs_path = os.path.abspath(os.path.join(workspace, path)) # ../ collapses out of workspace
else:
abs_path = os.path.abspath(path) # absolute path used as-is
# ... os.path.isdir(abs_path) then listed. is_path_within_directory() is NEVER called.
3. execute_command() never validates cwd (CWE-22)
# src/praisonai/praisonai/code/tools/execute_command.py — workspace param doc: "for security validation"
if cwd:
if workspace and not os.path.isabs(cwd):
work_dir = os.path.abspath(os.path.join(workspace, cwd)) # ../ escapes; no containment check
else:
work_dir = os.path.abspath(cwd)
# subprocess.run(args, cwd=work_dir, ...) # no is_path_within_directory() anywhere
# src/praisonai/praisonai/code/agent_tools.py — code_execute_command()
if work_dir and _workspace_root and not os.path.isabs(work_dir):
work_dir = os.path.join(_workspace_root, work_dir) # joins, never validates
result = _execute_command(command=command, cwd=work_dir, timeout=120) # workspace not even passed
Note: execute_command rejects shell=True and runs shlex.split(command) via subprocess.run (no shell), so shell metacharacters (&&, >, pipes) do not work — but any binary still runs with attacker-chosen argv from the escaped cwd, which is sufficient to read/write outside the workspace.
The workspace is an…
- Record
GHSA-ch89-h4r2-c8f8- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:L - Weakness
- CWE-22
- Updated
- 2026-11-02
OSVPYSEC-2026-3888
7.1
PraisonAI: [Path Traversal] agent tools escape the configured workspace via symlinks
Summary
PraisonAI's praisonai.code tool wrappers (exported as CODE_TOOLS for agents) expose a workspace setting that the module itself treats as a path-traversal security boundary — read_file, write_file, apply_diff, and search_replace explicitly call is_path_within_directory() and return "… is outside the workspace" on violations. That boundary is enforced unsoundly and inconsistently:
- The containment helper uses
os.path.abspath(), notrealpath()/Path.resolve(). A symlink located inside the workspace whose target is outside has anabspath()that is still inside the workspace, so it passes the check whileopen()follows the link. This bypasses read, write, apply_diff, and search_replace (CWE-59). list_files()resolvespathagainst the workspace but never calls the containment helper at all —../and absolute paths escape directly (CWE-22).execute_command()takes aworkspaceargument documented "for security validation" but performs nocwdcontainment check;code_execute_command()resolves a relativecwdagainst the workspace and also never validates it (and never even passesworkspaceto the low-level helper). A relativecwd="../outside"runs commands from outside the workspace (CWE-22). An attacker who can influence an agent that has these tools attached (untrusted prompt, indirect prompt injection, or a server-exposed agent) can read, overwrite, list, and execute from outside the configured workspace, bounded only by the process user's filesystem permissions.
Technical Detail
1. Unsound containment helper (symlink bypass — CWE-59)
# src/praisonai/praisonai/code/utils/file_utils.py — is_path_within_directory()
abs_file = os.path.abspath(file_path) # does NOT resolve symlinks
abs_dir = os.path.abspath(directory)
if not abs_dir.endswith(os.sep): abs_dir += os.sep
return abs_file.startswith(abs_dir) or abs_file == abs_dir.rstrip(os.sep)
read_file/write_file/apply_diff/search_replace call this with the configured workspace (e.g. read_file.py: # Security check - ensure path is within workspace). Because abspath() does not canonicalize symlinks, a link at WORKSPACE/link_to_secret.txt → /outside/secret.txt has abspath WORKSPACE/link_to_secret.txt (inside) and passes, while open() follows it to the real outside target.
2. list_files() has no containment check (CWE-22)
# src/praisonai/praisonai/code/tools/list_files.py
if workspace and not os.path.isabs(path):
abs_path = os.path.abspath(os.path.join(workspace, path)) # ../ collapses out of workspace
else:
abs_path = os.path.abspath(path) # absolute path used as-is
# ... os.path.isdir(abs_path) then listed. is_path_within_directory() is NEVER called.
3. execute_command() never validates cwd (CWE-22)
# src/praisonai/praisonai/code/tools/execute_command.py — workspace param doc: "for security validation"
if cwd:
if workspace and not os.path.isabs(cwd):
work_dir = os.path.abspath(os.path.join(workspace, cwd)) # ../ escapes; no containment check
else:
work_dir = os.path.abspath(cwd)
# subprocess.run(args, cwd=work_dir, ...) # no is_path_within_directory() anywhere
# src/praisonai/praisonai/code/agent_tools.py — code_execute_command()
if work_dir and _workspace_root and not os.path.isabs(work_dir):
work_dir = os.path.join(_workspace_root, work_dir) # joins, never validates
result = _execute_command(command=command, cwd=work_dir, timeout=120) # workspace not even passed
Note: execute_command rejects shell=True and runs shlex.split(command) via subprocess.run (no shell), so shell metacharacters (&&, >, pipes) do not work — but any binary still runs with attacker-chosen argv from the escaped cwd, which is sufficient to read/write outside the workspace.
The workspace is an…
- Record
PYSEC-2026-3888- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:L - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
praisonaiPyPI |
before 4.6.58 | 4.6.58 |
praisonaiPyPI |
before 4.6.58, 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.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-55540 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.39%, the 33rd percentile. Fixed in praisonai 4.6.58.
References
github.com/MervinPraison/PraisonAI/security/advisories/GHSA-ch89-h4r2-c8f8
github.com/MervinPraison/PraisonAI/commit/2f9677abb2ea68eab864ee8b6a828fd0141612e1
github.com/MervinPraison/PraisonAI/releases/tag/v4.6.58
github.com/advisories/GHSA-ch89-h4r2-c8f8
github.com/CVEProject/cvelistV5/tree/main/cves/2026/55xxx/CVE-2026-55540.json
nvd.nist.gov/vuln/detail/CVE-2026-55540
github.com/MervinPraison/PraisonAI
pypi.org/project/praisonai
Left out here: 698 versions and the rest of 3 source descriptions. The JSON record has them.