Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"aliases": [
"CVE-2026-56839"
],
"summary": "PraisonAI Code agent tools fail open without a workspace boundary",
"details": "# PraisonAI Code agent tools fail open without a workspace boundary\n\n## Summary\n\nPraisonAI Code's agent-compatible `CODE_TOOLS` wrappers keep a global workspace root initialized to `None`. If an application uses `CODE_TOOLS`, `code_read_file`, `code_search_replace`, or `code_apply_diff` before calling `set_workspace()`, the wrappers pass `workspace=None` into lower-level helpers that only enforce path containment when a workspace is truthy. Absolute paths outside the intended project workspace are then read and modified.\n\nThe official examples correctly call `set_workspace()` before `CODE_TOOLS`, and this report does not claim configured workspaces are ineffective. The issue is the fail-open default. PraisonAI's security documentation describes workspace boundaries as the path-traversal protection mechanism, and the already-published Python API arbitrary file write advisory (`GHSA-hvhp-v2gc-268q`) was fixed by defaulting an unset workspace to `os.getcwd()`. The adjacent read and edit paths reached through `CODE_TOOLS` still fail open.\n\n## Affected Components\n\n- Package: `praisonai`\n- Current upstream main tested: `2f9677abb2ea68eab864ee8b6a828fd0141612e1`\n- Latest tested release: `v4.6.57`\n- Primary files:\n - `src/praisonai/praisonai/code/agent_tools.py`\n - `src/praisonai/praisonai/code/tools/read_file.py`\n - `src/praisonai/praisonai/code/tools/search_replace.py`\n - `src/praisonai/praisonai/code/tools/apply_diff.py`\n\n## Root Cause\n\n`agent_tools.py` initializes `_workspace_root` to `None` and passes it directly to lower-level helpers:\n\n```python\n_workspace_root: Optional[str] = None\n...\nresult = _read_file(..., workspace=_workspace_root)\n...\nresult = _search_replace(..., workspace=_workspace_root)\n```\n\nThe lower-level helpers only enforce containment if `workspace` is set:\n\n```python\nif workspace:\n if not is_path_within_directory(abs_path, workspace):\n return {\"success\": False, ...}\n```\n\nThe already-hardened `write_file()` path uses `effective_workspace = workspace or os.getcwd()`. Current tests assert that `write_file(workspace=None)` must stay inside the current working directory. The same fail-closed default is missing from `read_file`, `search_replace`, `apply_diff`, and the agent wrappers that call them.\n\n## Local-Only Reproduction\n\nRun:\n\n```bash\nPYTHONPATH=/path/to/PraisonAI/src/praisonai:/path/to/PraisonAI/src/praisonai-agents \\\n python poc_code_tools_workspace_bypass.py\n```\n\nExpected vulnerable result:\n\n```text\n[poc] HIT: CODE_TOOLS wrappers read and edit outside workspace when workspace is unset\n```\n\nThe PoV creates a temporary workspace and a temporary file outside that workspace. With `get_workspace() == None`, `code_read_file()` reads the outside file, `code_search_replace()` modifies it, and `code_apply_diff()` modifies it again. After `set_workspace(workspace)`, the same outside path is rejected by all three wrappers.\n\nNo external services, model providers, or network access are used.\n\n## Impact\n\nIf an application exposes PraisonAI Code's agent-compatible `CODE_TOOLS` to an LLM before setting a workspace boundary, prompt-influenced tool calls can read and modify files outside the intended project workspace. The practical attack shape matches the existing PraisonAI prompt-content advisory pattern: untrusted content influences an agent that has been given file-editing tools.\n\nPractical impacts include:\n\n- reading host secrets or local configuration files accessible to the process user;\n- modifying arbitrary existing files when the attacker can supply or infer matching content for `code_search_replace` or `code_apply_diff`;\n- using `code_read_file` to first learn file content and then `code_apply_diff` to produce an exact modification;\n- bypassing the advertised workspace-boundary security posture unless the embedding application remembered to call `set_workspace()` first.\n\nThis issue does not claim `set_workspace()` is ineffective. The control works when configured. The vulnerability is the fail-open default for the advertised agent-tool bundle and adjacent read/edit helpers.\n\n## Affected-Version Sweep\n\nThe same behavior was reproduced on:\n\n- current upstream main: `2f9677abb2ea68eab864ee8b6a828fd0141612e1`\n- `v4.6.57`\n- `v4.6.56`\n- `v4.6.10`\n- `v4.6.9`\n- `v4.5.128`\n- `v4.5.126`\n- `v3.9.26`\n- `v3.9.24`\n\n## Suggested Fix\n\nRecommended fix:\n\n1. Make every low-level file helper compute `effective_workspace = workspace or os.getcwd()` before resolving paths.\n2. Make `code_read_file`, `code_list_files`, `code_apply_diff`, `code_search_replace`, and `code_execute_command` use `os.getcwd()` as the default workspace when `_workspace_root is None`.\n3. Keep allowing absolute paths only when they resolve inside the effective workspace.\n4. Add regression tests proving outside absolute paths are rejected before and after `set_workspace()`.\n5. Consider failing closed if `CODE_TOOLS` is used before a workspace is configured, or log a warning when the default current working directory is used.\n\n## Disclosure Route\n\nPraisonAI's official security documentation lists GitHub Security Advisories as the preferred reporting method and asks reports to include reproduction steps, affected versions, impact, and suggested fixes. The repository security policy page currently shows no configured `SECURITY.md`, but private vulnerability reporting is available.",
"summary": "Code Agent Tools Fail Open Without a Workspace Boundary",
"details": "## Summary\n\nPraisonAI Code's agent-compatible `CODE_TOOLS` wrappers keep a global workspace root initialized to `None`. If an application uses `CODE_TOOLS`, `code_read_file`, `code_search_replace`, or `code_apply_diff` before calling `set_workspace()`, the wrappers pass `workspace=None` into lower-level helpers that only enforce path containment when a workspace is truthy. Absolute paths outside the intended project workspace are then read and modified.\n\nThe official examples correctly call `set_workspace()` before `CODE_TOOLS`, and this report does not claim configured workspaces are ineffective. The issue is the fail-open default. PraisonAI's security documentation describes workspace boundaries as the path-traversal protection mechanism, and the already-published Python API arbitrary file write advisory (`GHSA-hvhp-v2gc-268q`) was fixed by defaulting an unset workspace to `os.getcwd()`. The adjacent read and edit paths reached through `CODE_TOOLS` still fail open.\n\n## Technical Details\n\n`agent_tools.py` initializes `_workspace_root` to `None` and passes it directly to lower-level helpers:\n\n```python\n_workspace_root: Optional[str] = None\n...\nresult = _read_file(..., workspace=_workspace_root)\n...\nresult = _search_replace(..., workspace=_workspace_root)\n```\n\nThe lower-level helpers only enforce containment if `workspace` is set:\n\n```python\nif workspace:\n if not is_path_within_directory(abs_path, workspace):\n return {\"success\": False, ...}\n```\n\nThe already-hardened `write_file()` path uses `effective_workspace = workspace or os.getcwd()`. Current tests assert that `write_file(workspace=None)` must stay inside the current working directory. The same fail-closed default is missing from `read_file`, `search_replace`, `apply_diff`, and the agent wrappers that call them.\n\n## PoV\n\nThe vulnerable primitive is exercised by the local reproduction in the PoC section below.\n\n## PoC\n\nRun:\n\n```bash\nPYTHONPATH=/path/to/PraisonAI/src/praisonai:/path/to/PraisonAI/src/praisonai-agents \\\n python poc_code_tools_workspace_bypass.py\n```\n\nExpected vulnerable result:\n\n```text\n[poc] HIT: CODE_TOOLS wrappers read and edit outside workspace when workspace is unset\n```\n\nThe PoV creates a temporary workspace and a temporary file outside that workspace. With `get_workspace() == None`, `code_read_file()` reads the outside file, `code_search_replace()` modifies it, and `code_apply_diff()` modifies it again. After `set_workspace(workspace)`, the same outside path is rejected by all three wrappers.\n\nNo external services, model providers, or network access are used.\n\n## Impact\n\nIf an application exposes PraisonAI Code's agent-compatible `CODE_TOOLS` to an model before setting a workspace boundary, prompt-influenced tool calls can read and modify files outside the intended project workspace. The practical attack shape matches the existing PraisonAI prompt-content advisory pattern: untrusted content influences an agent that has been given file-editing tools.\n\nPractical impacts include:\n\n- reading host secrets or local configuration files accessible to the process user;\n- modifying arbitrary existing files when the attacker can supply or infer matching content for `code_search_replace` or `code_apply_diff`;\n- using `code_read_file` to first learn file content and then `code_apply_diff` to produce an exact modification;\n- bypassing the advertised workspace-boundary security posture unless the embedding application remembered to call `set_workspace()` first.\n\nThis issue does not claim `set_workspace()` is ineffective. The control works when configured. The vulnerability is the fail-open default for the advertised agent-tool report package and adjacent read/edit helpers.\n\n### Severity\n\nRecommended severity: High for deployments where untrusted prompt/content can influence an agent with PraisonAI Code tools. If maintainers choose to score only the direct local Python API boundary, the issue can be scored Medium; the vector below follows the same practical agentic-content threat model used by the earlier `write_file(workspace=None)` advisory.\n\nSuggested CVSS 3.1 vector:\n\n```text\nCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N\n```\n\nthe PoV exercises the Python API without external services for deterministic reproduction. The practical attacker path is untrusted network content or prompt input influencing an model that has been given `CODE_TOOLS`. Confidentiality and integrity impact are high for files accessible to the PraisonAI process user.\n\n### CWE\n\n- CWE-22: Improper Limitation of a Pathname to a Restricted Directory\n- CWE-200: Exposure of Sensitive Information to an Unauthorized Actor\n- CWE-863: Incorrect Authorization\n\n## Suggested Fix\n\nRecommended fix:\n\n1. Make every low-level file helper compute `effective_workspace = workspace or os.getcwd()` before resolving paths.\n2. Make `code_read_file`, `code_list_files`, `code_apply_diff`, `code_search_replace`, and `code_execute_command` use `os.getcwd()` as the default workspace when `_workspace_root is None`.\n3. Keep allowing absolute paths only when they resolve inside the effective workspace.\n4. Add regression tests proving outside absolute paths are rejected before and after `set_workspace()`.\n5. Consider failing closed if `CODE_TOOLS` is used before a workspace is configured, or log a warning when the default current working directory is used.\n\n## Affected Package/Versions\n\n- Package: `praisonai`\n- Current upstream main tested: `2f9677abb2ea68eab864ee8b6a828fd0141612e1`\n- Latest tested release: `v4.6.57`\n- Primary files:\n- `src/praisonai/praisonai/code/agent_tools.py`\n- `src/praisonai/praisonai/code/tools/read_file.py`\n- `src/praisonai/praisonai/code/tools/search_replace.py`\n- `src/praisonai/praisonai/code/tools/apply_diff.py`\n\n### Version Sweep\n\nThe same behavior was reproduced on:\n\n- current upstream main: `2f9677abb2ea68eab864ee8b6a828fd0141612e1`\n- `v4.6.57`\n- `v4.6.56`\n- `v4.6.10`\n- `v4.6.9`\n- `v4.5.128`\n- `v4.5.126`\n- `v3.9.26`\n- `v3.9.24`\n\n## Advisory History\n\nThis is related to but distinct from:\n\n- `GHSA-hvhp-v2gc-268q`: `write_file()` workspace escape, patched in `4.6.40`; this report covers current sibling read/edit helpers through `CODE_TOOLS`.\n- `GHSA-grrg-5cg9-58pf`: `read_skill_file()` arbitrary file read; this report covers PraisonAI Code wrappers, not skill tools.\n- `GHSA-9cr9-25q5-8prj`: MCP workflow file-read path traversal; this report does not use MCP.\n- `GHSA-7j2f-xc8p-fjmq`: legacy `list_files` glob traversal; this report covers current read/edit wrappers.\n\n## References\n\n- https://docs.praison.ai/docs/features/code\n- https://docs.praison.ai/docs/code/overview\n- https://github.com/MervinPraison/PraisonAI/security/policy\n- https://github.com/advisories/GHSA-hvhp-v2gc-268q\n- https://github.com/advisories/GHSA-grrg-5cg9-58pf\n- https://github.com/advisories/GHSA-9cr9-25q5-8prj\n- https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-7j2f-xc8p-fjmq\n",
"severity": [
{
"type": "CVSS_V3",
Expand Down Expand Up @@ -59,4 +59,4 @@
"github_reviewed_at": "2026-06-18T13:59:26Z",
"nvd_published_at": null
}
}
}