CVE-2026-61431
PraisonAI before 4.6.78 Path Traversal via ContextGatherer
PraisonAI before 4.6.78 contains a path traversal vulnerability in ContextGatherer that fails to validate include paths in .praisoncontext and .praisoninclude files. Attackers can supply absolute paths or parent directory traversal sequences to read arbitrary files outside the workspace and include their contents in the generated context bundle.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Passive
What an attacker can affect
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
6.8
PraisonAI before 4.6.78 Path Traversal via ContextGatherer
PraisonAI before 4.6.78 contains a path traversal vulnerability in ContextGatherer that fails to validate include paths in .praisoncontext and .praisoninclude files. Attackers can supply absolute paths or parent directory traversal sequences to read arbitrary files outside the workspace and include their contents in the generated context bundle.
- Record
CVE-2026-61431- CVSS 4.0
- 6.8 Medium
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N - CVSS 3.1
- 5.5 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N - Weakness
- CWE-22
- Updated
- 2026-11-02
EUVDEUVD-2026-42897
6.8
PraisonAI before 4.6.78 contains a path traversal vulnerability in ContextGatherer that fails to validate include paths in .praisoncontext and .praisoninclude files. Attackers can supply absolute paths or parent directory traversal sequences to read arbitrary files outside the workspace and include their contents in the generated context bundle.
- Record
EUVD-2026-42897- CVSS 4.0
- 6.8 Medium
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-q7m5-3jmv-vm48
5.5
PraisonAI: ContextGatherer include resolution permits absolute and traversal reads outside the workspace
ContextGatherer include resolution permits absolute and traversal reads outside the workspace
Summary
PraisonAI's praisonai.ui.context.ContextGatherer treats the configured directory as the project workspace, but project-controlled .praisoncontext and .praisoninclude files can name absolute paths or .. traversal paths. When context gathering runs, PraisonAI opens those outside paths and appends their contents to the generated context bundle. An attacker who can supply or modify a workspace repository can therefore cause process-readable files outside the intended project root to be sent to the caller or model as project context.
Technical Details
ContextGatherer.get_include_paths() reads include entries directly from .praisoncontext and .praisoninclude under the configured workspace. It stores each non-comment line as a raw include path:
include_file = os.path.join(self.directory, '.praisoncontext')
if os.path.exists(include_file):
with open(include_file, 'r') as f:
include_paths.extend(
line.strip() for line in f
if line.strip() and not line.startswith('#')
)
When .praisoncontext is present, gather_context() passes every include entry through os.path.join(self.directory, include_path) and then processes the result:
for include_path in self.include_paths:
full_path = os.path.join(self.directory, include_path)
process_path(full_path)
The .praisoninclude path has the same unsafe join after first processing the workspace:
process_path(self.directory)
for include_path in self.include_paths:
full_path = os.path.join(self.directory, include_path)
process_path(full_path)
There is no canonicalization or containment check before process_path() opens files or recursively walks directories. In Python, os.path.join(workspace, absolute_path) returns the absolute path and discards workspace; os.path.join(workspace, "../outside.py") remains outside the workspace once normalized by filesystem operations. add_file_content() then opens the supplied path and appends file contents to the context before display bookkeeping:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
context.append(
f"File: {file_path}\n\n{content}\n\n{'=' * 50}\n"
)
self.included_files.append(
Path(file_path).relative_to(self.directory)
)
For parent traversal paths, Path(file_path).relative_to(self.directory) raises after the outside file content has already been appended, so the caller receives the outside content even if an error is logged. For absolute paths, the outside content is appended as well. This violates the workspace invariant for a context-gathering feature: repository-local include metadata should select files within the project, not arbitrary process-readable host files.
PoV
The minimal vulnerable shape is a workspace containing only a normal source file and one include file:
workspace/
.praisoncontext # contains: ../outside_secret.py
inside.py
outside_secret.py # outside the workspace
Running ContextGatherer(directory="workspace").run() returns context containing outside_secret.py even though that file is outside the configured workspace. The same result occurs when .praisoncontext contains an absolute path to the outside file, and when .praisoninclude contains either the parent traversal path or the absolute path.
PoC
Save the self-contained script from the Appendix below as context_include_workspace_pov.py, then run it against a local checkout:
export PRAISONAI=/path/to/PraisonAI
PYTHONPATH="$PRAISONAI/src/praisonai" python context_include_workspace_pov.py
Expected vulnerable output:
{
"expectations": {
"control_inside_file_is_collected": true,
"control_without_include_does_not_read_outside": true,
"praisoncontext_absolute_path_discloses_outside":…
- Record
GHSA-q7m5-3jmv-vm48- CVSS 3.1
- 5.5 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N - Weakness
- CWE-22, CWE-200
- Updated
- 2026-11-02
GitHub AdvisoriesGHSA-w37c-cq55-frjp
6.8
Duplicate Advisory: PraisonAI: ContextGatherer include resolution permits absolute and traversal reads outside the workspace
Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-q7m5-3jmv-vm48. This link is maintained to preserve external references.
Original Description
PraisonAI before 4.6.78 contains a path traversal vulnerability in ContextGatherer that fails to validate include paths in .praisoncontext and .praisoninclude files. Attackers can supply absolute paths or parent directory traversal sequences to read arbitrary files outside the workspace and include their contents in the generated context bundle.
- Record
GHSA-w37c-cq55-frjp- CVSS 4.0
- 6.8 Medium
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 5.5 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N - Weakness
- CWE-22
- Updated
- 2026-11-02
NVD
6.8
PraisonAI before 4.6.78 contains a path traversal vulnerability in ContextGatherer that fails to validate include paths in .praisoncontext and .praisoninclude files. Attackers can supply absolute paths or parent directory traversal sequences to read arbitrary files outside the workspace and include their contents in the generated context bundle.
- Record
CVE-2026-61431- CVSS 4.0
- 6.8 Medium
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 5.5 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N - Weakness
- CWE-22
- Updated
- 2026-11-02
OSV
6.8
PraisonAI before 4.6.78 Path Traversal via ContextGatherer
PraisonAI before 4.6.78 contains a path traversal vulnerability in ContextGatherer that fails to validate include paths in .praisoncontext and .praisoninclude files. Attackers can supply absolute paths or parent directory traversal sequences to read arbitrary files outside the workspace and include their contents in the generated context bundle.
- Record
CVE-2026-61431- CVSS 4.0
- 6.8 Medium
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-22
- Updated
- 2026-11-02
OSVGHSA-q7m5-3jmv-vm48
5.5
PraisonAI: ContextGatherer include resolution permits absolute and traversal reads outside the workspace
ContextGatherer include resolution permits absolute and traversal reads outside the workspace
Summary
PraisonAI's praisonai.ui.context.ContextGatherer treats the configured directory as the project workspace, but project-controlled .praisoncontext and .praisoninclude files can name absolute paths or .. traversal paths. When context gathering runs, PraisonAI opens those outside paths and appends their contents to the generated context bundle. An attacker who can supply or modify a workspace repository can therefore cause process-readable files outside the intended project root to be sent to the caller or model as project context.
Technical Details
ContextGatherer.get_include_paths() reads include entries directly from .praisoncontext and .praisoninclude under the configured workspace. It stores each non-comment line as a raw include path:
include_file = os.path.join(self.directory, '.praisoncontext')
if os.path.exists(include_file):
with open(include_file, 'r') as f:
include_paths.extend(
line.strip() for line in f
if line.strip() and not line.startswith('#')
)
When .praisoncontext is present, gather_context() passes every include entry through os.path.join(self.directory, include_path) and then processes the result:
for include_path in self.include_paths:
full_path = os.path.join(self.directory, include_path)
process_path(full_path)
The .praisoninclude path has the same unsafe join after first processing the workspace:
process_path(self.directory)
for include_path in self.include_paths:
full_path = os.path.join(self.directory, include_path)
process_path(full_path)
There is no canonicalization or containment check before process_path() opens files or recursively walks directories. In Python, os.path.join(workspace, absolute_path) returns the absolute path and discards workspace; os.path.join(workspace, "../outside.py") remains outside the workspace once normalized by filesystem operations. add_file_content() then opens the supplied path and appends file contents to the context before display bookkeeping:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
context.append(
f"File: {file_path}\n\n{content}\n\n{'=' * 50}\n"
)
self.included_files.append(
Path(file_path).relative_to(self.directory)
)
For parent traversal paths, Path(file_path).relative_to(self.directory) raises after the outside file content has already been appended, so the caller receives the outside content even if an error is logged. For absolute paths, the outside content is appended as well. This violates the workspace invariant for a context-gathering feature: repository-local include metadata should select files within the project, not arbitrary process-readable host files.
PoV
The minimal vulnerable shape is a workspace containing only a normal source file and one include file:
workspace/
.praisoncontext # contains: ../outside_secret.py
inside.py
outside_secret.py # outside the workspace
Running ContextGatherer(directory="workspace").run() returns context containing outside_secret.py even though that file is outside the configured workspace. The same result occurs when .praisoncontext contains an absolute path to the outside file, and when .praisoninclude contains either the parent traversal path or the absolute path.
PoC
Save the self-contained script from the Appendix below as context_include_workspace_pov.py, then run it against a local checkout:
export PRAISONAI=/path/to/PraisonAI
PYTHONPATH="$PRAISONAI/src/praisonai" python context_include_workspace_pov.py
Expected vulnerable output:
{
"expectations": {
"control_inside_file_is_collected": true,
"control_without_include_does_not_read_outside": true,
"praisoncontext_absolute_path_discloses_outside":…
- Record
GHSA-q7m5-3jmv-vm48- CVSS 3.1
- 5.5 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N - Weakness
- CWE-200, CWE-22
- Updated
- 2026-11-02
OSVGHSA-w37c-cq55-frjp
6.8
Duplicate Advisory: PraisonAI: ContextGatherer include resolution permits absolute and traversal reads outside the workspace
Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-q7m5-3jmv-vm48. This link is maintained to preserve external references.
Original Description
PraisonAI before 4.6.78 contains a path traversal vulnerability in ContextGatherer that fails to validate include paths in .praisoncontext and .praisoninclude files. Attackers can supply absolute paths or parent directory traversal sequences to read arbitrary files outside the workspace and include their contents in the generated context bundle.
- Record
GHSA-w37c-cq55-frjp- CVSS 4.0
- 6.8 Medium
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 5.5 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N - Weakness
- CWE-22
- Updated
- 2026-11-02
OSVPYSEC-2026-4258
5.5
PraisonAI: ContextGatherer include resolution permits absolute and traversal reads outside the workspace
ContextGatherer include resolution permits absolute and traversal reads outside the workspace
Summary
PraisonAI's praisonai.ui.context.ContextGatherer treats the configured directory as the project workspace, but project-controlled .praisoncontext and .praisoninclude files can name absolute paths or .. traversal paths. When context gathering runs, PraisonAI opens those outside paths and appends their contents to the generated context bundle. An attacker who can supply or modify a workspace repository can therefore cause process-readable files outside the intended project root to be sent to the caller or model as project context.
Technical Details
ContextGatherer.get_include_paths() reads include entries directly from .praisoncontext and .praisoninclude under the configured workspace. It stores each non-comment line as a raw include path:
include_file = os.path.join(self.directory, '.praisoncontext')
if os.path.exists(include_file):
with open(include_file, 'r') as f:
include_paths.extend(
line.strip() for line in f
if line.strip() and not line.startswith('#')
)
When .praisoncontext is present, gather_context() passes every include entry through os.path.join(self.directory, include_path) and then processes the result:
for include_path in self.include_paths:
full_path = os.path.join(self.directory, include_path)
process_path(full_path)
The .praisoninclude path has the same unsafe join after first processing the workspace:
process_path(self.directory)
for include_path in self.include_paths:
full_path = os.path.join(self.directory, include_path)
process_path(full_path)
There is no canonicalization or containment check before process_path() opens files or recursively walks directories. In Python, os.path.join(workspace, absolute_path) returns the absolute path and discards workspace; os.path.join(workspace, "../outside.py") remains outside the workspace once normalized by filesystem operations. add_file_content() then opens the supplied path and appends file contents to the context before display bookkeeping:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
context.append(
f"File: {file_path}\n\n{content}\n\n{'=' * 50}\n"
)
self.included_files.append(
Path(file_path).relative_to(self.directory)
)
For parent traversal paths, Path(file_path).relative_to(self.directory) raises after the outside file content has already been appended, so the caller receives the outside content even if an error is logged. For absolute paths, the outside content is appended as well. This violates the workspace invariant for a context-gathering feature: repository-local include metadata should select files within the project, not arbitrary process-readable host files.
PoV
The minimal vulnerable shape is a workspace containing only a normal source file and one include file:
workspace/
.praisoncontext # contains: ../outside_secret.py
inside.py
outside_secret.py # outside the workspace
Running ContextGatherer(directory="workspace").run() returns context containing outside_secret.py even though that file is outside the configured workspace. The same result occurs when .praisoncontext contains an absolute path to the outside file, and when .praisoninclude contains either the parent traversal path or the absolute path.
PoC
Save the self-contained script from the Appendix below as context_include_workspace_pov.py, then run it against a local checkout:
export PRAISONAI=/path/to/PraisonAI
PYTHONPATH="$PRAISONAI/src/praisonai" python context_include_workspace_pov.py
Expected vulnerable output:
{
"expectations": {
"control_inside_file_is_collected": true,
"control_without_include_does_not_read_outside": true,
"praisoncontext_absolute_path_discloses_outside":…
- Record
PYSEC-2026-4258- CVSS 3.1
- 5.5 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
praisonaiPyPI |
through 4.6.77 | 4.6.78 |
praisonaiPyPI |
before 4.6.78, 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.78 |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected | ||
| 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-61431 is a medium vulnerability (CVSS 6.8) in Mervinpraison Praisonai, published July 10, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.37%, the 31st percentile. Fixed in praisonai 4.6.78.
References
github.com/MervinPraison/PraisonAI/security/advisories/GHSA-q7m5-3jmv-vm48
github.com/MervinPraison/PraisonAI/commit/1620b49f36945d8cc8ee5635b906c960df5097a0
vulncheck.com/advisories/praisonai-before-path-traversal-via-contextgatherer
nvd.nist.gov/vuln/detail/CVE-2026-61431
github.com/advisories/GHSA-q7m5-3jmv-vm48
github.com/advisories/GHSA-w37c-cq55-frjp
github.com/CVEProject/cvelistV5/tree/main/cves/2026/61xxx/CVE-2026-61431.json
github.com/MervinPraison/PraisonAI
pypi.org/project/praisonai
Left out here: 713 versions and the rest of 3 source descriptions. The JSON record has them.