Every CVE Search/ Get API key

CVE-2026-55540

PraisonAI: [Path Traversal] agent tools escape the configured workspace via symlinks

7.1 HighFix available

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

0.39%Up 0.1 points since Aug 26, 2026
90%50%10%1%

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 HighCVSS: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 HighCVSS: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:

  1. The containment helper uses os.path.abspath(), not realpath()/Path.resolve(). A symlink located inside the workspace whose target is outside has an abspath() that is still inside the workspace, so it passes the check while open() follows the link. This bypasses read, write, apply_diff, and search_replace (CWE-59).
  2. list_files() resolves path against the workspace but never calls the containment helper at all — ../ and absolute paths escape directly (CWE-22).
  3. execute_command() takes a workspace argument documented "for security validation" but performs no cwd containment check; code_execute_command() resolves a relative cwd against the workspace and also never validates it (and never even passes workspace to the low-level helper). A relative cwd="../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 HighCVSS: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 HighCVSS: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 HighCVSS: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:

  1. The containment helper uses os.path.abspath(), not realpath()/Path.resolve(). A symlink located inside the workspace whose target is outside has an abspath() that is still inside the workspace, so it passes the check while open() follows the link. This bypasses read, write, apply_diff, and search_replace (CWE-59).
  2. list_files() resolves path against the workspace but never calls the containment helper at all — ../ and absolute paths escape directly (CWE-22).
  3. execute_command() takes a workspace argument documented "for security validation" but performs no cwd containment check; code_execute_command() resolves a relative cwd against the workspace and also never validates it (and never even passes workspace to the low-level helper). A relative cwd="../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 HighCVSS: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:

  1. The containment helper uses os.path.abspath(), not realpath()/Path.resolve(). A symlink located inside the workspace whose target is outside has an abspath() that is still inside the workspace, so it passes the check while open() follows the link. This bypasses read, write, apply_diff, and search_replace (CWE-59).
  2. list_files() resolves path against the workspace but never calls the containment helper at all — ../ and absolute paths escape directly (CWE-22).
  3. execute_command() takes a workspace argument documented "for security validation" but performs no cwd containment check; code_execute_command() resolves a relative cwd against the workspace and also never validates it (and never even passes workspace to the low-level helper). A relative cwd="../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 HighCVSS: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

Affected packages
PackageAffectedFixed
praisonai
PyPI
before 4.6.58
4.6.58
praisonai
PyPI
before 4.6.58, plus listed versions
0.0.10.0.100.0.110.0.120.0.13
Show 45 moreShow less
0.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

History
RecordedChangedSources
first indexedCVE 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

This page doesn't list everything

Left out here: 698 versions and the rest of 3 source descriptions. The JSON record has them.

MoveEnter Open