Files
vuln-osv/data/PyPI/GHSA-cfxv-8fw8-rwpv.json
T

1 line
12 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"schema_version":"1.9.0","id":"GHSA-cfxv-8fw8-rwpv","published":"2026-08-25T14:46:13Z","modified":"2026-09-10T12:25:57.634780109Z","aliases":["CVE-2026-55530","PYSEC-2026-3901"],"summary":"praisonaiagents: ast_grep_rewrite rewrites arbitrary files without the @require_approval gate enforced on every sibling mutation tool","details":"**Target:** PraisonAI (`MervinPraison/PraisonAI`)\n**Affected component:** `praisonaiagents/tools/ast_grep_tool.py` — `ast_grep_rewrite`\n**Affected versions:** master at `ce97667156a116c50b4a3d1aa21e09f048903fda`; reproduced against the current `praisonaiagents` PyPI release (`praisonaiagents` <= 1.6.52).\n\n## Summary\n\nTools in `praisonaiagents/tools/` that modify on-disk state or run code are uniformly wrapped with `@require_approval`, which routes the call through an interactive approval flow before the body runs and fails closed — on denial (or with no approval backend configured) it raises `PermissionError` and the side effect does not occur. This is applied at every sibling mutation entry point:\n\n| File | Line | Symbol | Risk level |\n|---|---|---|---|\n| `file_tools.py` | 212 | `copy_file` | high |\n| `file_tools.py` | 239 | `move_file` | high |\n| `file_tools.py` | 266 | `delete_file` | high |\n| `edit_tools.py` | 38 | `EditTools.edit_file` | high |\n| `edit_tools.py` | 155 | `edit_file` | high |\n| `shell_tools.py` | 32 | `execute_command` | critical |\n| `python_tools.py` | 352 | `execute_code` | critical |\n\n`ast_grep_tool.py:149` `ast_grep_rewrite` is structurally a sibling of these but has no decorator and no `from ..approval import require_approval` import. With `dry_run=False` (LLM-controllable), it builds `sg --pattern <P> --rewrite <R> --lang <L> --update-all <path>` (lines 204–211) and calls `subprocess.run(cmd, ...)` (line 215), modifying every file under `path` matching the pattern. There is no approval gate, no `_validate_path` workspace check, and no `cwd=` sandboxing. The function is registered as a top-level tool (`__init__.py:182`) and exposed via the `code_intelligence` built-in profile (`profiles.py`).\n\nA secondary defect: on the `dry_run=False` path `ast_grep_rewrite` returns the literal string `No changes made` to the caller even when it modified files (the \"No changes made\" return at `ast_grep_tool.py:230` is reached on this path), so an operator inspecting tool output sees no record that a write occurred.\n\n## Proof of concept\n\nSingle script, clean venv, `praisonaiagents` from PyPI, `ast-grep` CLI installed. `PRAISONAI_AUTO_APPROVE` is removed from the environment first, so no env-bypass is in play.\n\n```python\nimport os, tempfile, textwrap\nos.environ.pop(\"PRAISONAI_AUTO_APPROVE\", None)\n\nworkdir = tempfile.mkdtemp(prefix=\"poc-\")\ntarget = os.path.join(workdir, \"target.py\")\nopen(target, \"w\").write(textwrap.dedent(\"\"\"\n def safe_function(x):\n return x + 1\n\n def hello(name):\n return 'hi ' + name\n\"\"\"))\n\n# Positive: undecorated tool rewrites the file.\nfrom praisonaiagents.tools.ast_grep_tool import ast_grep_rewrite\nast_grep_rewrite(\n pattern=\"def $FN($$$): return $$$\",\n replacement=\"def $FN($$$): import os; os.environ['POC_CANARY']='1'; return $$$\",\n lang=\"python\", path=workdir, dry_run=False,\n)\n\n# Negative control: decorated sibling triggers the approval flow.\nfrom praisonaiagents.tools.edit_tools import edit_file\nedit_file(file_path=target, old_text=\"def hello(name):\", new_text=\"def hello(name): # X\")\n```\n\nResult, verified: `ast_grep_rewrite` rewrote `target.py` to contain the injected `import os; os.environ['POC_CANARY']='1'` payload, no approval prompt fired, and the call returned `No changes made`. The subsequent `edit_file` call in the same process rendered the Tool Approval Required panel and, on denial, raised `PermissionError(\"Execution of edit_file denied: User denied\")` without modifying its target. Same process, same approval backend — the only difference is the missing decorator on `ast_grep_rewrite`.\n\n## Threat model\n\nAn LLM agent running locally whose tool surface includes `ast_grep_rewrite` (via the `code_intelligence` profile or direct import). Triggers: the operator asks the agent to refactor code, or prompt-injection in fetched docs / RAG context / any LLM-visible input steers the agent to call `ast_grep_rewrite` with attacker-chosen `pattern`, `replacement`, and `path` (the `dry_run` field is in the LLM-visible tool schema, so `dry_run=False` is requestable). The agent can then rewrite any file the host process can write — source trees, build configs, dotfiles, the agent's own source. With `path=\"/\"` the rewrite is filesystem-wide. Because the rewrite injects arbitrary text, pointing it at a file that is later imported or executed turns this write primitive into code execution — the basis for the escalation noted in the CVSS line. No operator prompt and no audit record of the modification.\n\n## Suggested fix\n\n```diff\n--- a/praisonaiagents/tools/ast_grep_tool.py\n+++ b/praisonaiagents/tools/ast_grep_tool.py\n@@\n from praisonaiagents._logging import get_logger\n from typing import Optional, List\n+from ..approval import require_approval\n@@\n+@require_approval(risk_level=\"high\")\n def ast_grep_rewrite(\n pattern: str,\n replacement: str,\n```\n\n`high` matches the file-modifying siblings; `critical` is defensible given the write→exec escalation. In the same patch: add a `_validate_path` workspace boundary check (cf. `edit_tools.py:27`); fix the `No changes made` return so it reflects actual modifications; apply the decorator to `ast_grep_scan` (`ast_grep_tool.py:243`) if it can write. `ast_grep_search` is read-only and can stay undecorated. A regression test asserting `ast_grep_rewrite` requires approval (alongside the other mutation tools) would have caught this at review time.\n\n## Coordinated disclosure\n\n- Kai Aizen / SnailSploit — `kai@snailsploit.com` — PGP on request.","affected":[{"package":{"name":"praisonaiagents","ecosystem":"PyPI","purl":"pkg:pypi/praisonaiagents"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.6.58"}]}],"versions":["0.0.1","0.0.10","0.0.100","0.0.101","0.0.102","0.0.103","0.0.104","0.0.105","0.0.106","0.0.107","0.0.108","0.0.109","0.0.11","0.0.110","0.0.111","0.0.112","0.0.113","0.0.114","0.0.115","0.0.116","0.0.117","0.0.118","0.0.119","0.0.12","0.0.120","0.0.121","0.0.122","0.0.123","0.0.124","0.0.125","0.0.126","0.0.127","0.0.128","0.0.129","0.0.13","0.0.130","0.0.131","0.0.132","0.0.133","0.0.134","0.0.135","0.0.136","0.0.137","0.0.138","0.0.139","0.0.14","0.0.140","0.0.141","0.0.142","0.0.143","0.0.144","0.0.145","0.0.146","0.0.147","0.0.148","0.0.149","0.0.15","0.0.150","0.0.151","0.0.152","0.0.153","0.0.154","0.0.155","0.0.156","0.0.157","0.0.158","0.0.159","0.0.16","0.0.160","0.0.161","0.0.162","0.0.163","0.0.164","0.0.165","0.0.166","0.0.167","0.0.168","0.0.169","0.0.17","0.0.170","0.0.171","0.0.172","0.0.173","0.0.174","0.0.175","0.0.176","0.0.177","0.0.178","0.0.179","0.0.18","0.0.180","0.0.181","0.0.182","0.0.183","0.0.184","0.0.185","0.0.187","0.0.188","0.0.189","0.0.19","0.0.190","0.0.191","0.0.192","0.0.193","0.0.194","0.0.195","0.0.196","0.0.197","0.0.198","0.0.199","0.0.2","0.0.20","0.0.21","0.0.22","0.0.23","0.0.24","0.0.25","0.0.26","0.0.27","0.0.28","0.0.29","0.0.3","0.0.30","0.0.31","0.0.32","0.0.33","0.0.34","0.0.35","0.0.36","0.0.37","0.0.38","0.0.39","0.0.4","0.0.40","0.0.41","0.0.42","0.0.43","0.0.44","0.0.45","0.0.46","0.0.47","0.0.48","0.0.49","0.0.5","0.0.50","0.0.51","0.0.52","0.0.53","0.0.54","0.0.56","0.0.57","0.0.58","0.0.59","0.0.6","0.0.60","0.0.61","0.0.62","0.0.63","0.0.64","0.0.65","0.0.66","0.0.67","0.0.68","0.0.69","0.0.7","0.0.70","0.0.71","0.0.72","0.0.73","0.0.74","0.0.75","0.0.76","0.0.77","0.0.78","0.0.79","0.0.8","0.0.80","0.0.81","0.0.82","0.0.83","0.0.84","0.0.85","0.0.86","0.0.87","0.0.88","0.0.89","0.0.9","0.0.90","0.0.91","0.0.92","0.0.93","0.0.94","0.0.95","0.0.96","0.0.97","0.0.98","0.0.99","0.1.0","0.1.1","0.1.10","0.1.11","0.1.12","0.1.13","0.1.14","0.1.15","0.1.16","0.1.17","0.1.18","0.1.19","0.1.2","0.1.20","0.1.21","0.1.22","0.1.23","0.1.24","0.1.25","0.1.26","0.1.27","0.1.3","0.1.4","0.1.5","0.1.6","0.1.7","0.1.8","0.1.9","0.10.0","0.10.1","0.10.10","0.10.2","0.10.3","0.10.4","0.10.5","0.10.6","0.10.7","0.10.8","0.10.9","0.11.0","0.11.1","0.11.10","0.11.11","0.11.12","0.11.13","0.11.14","0.11.15","0.11.16","0.11.17","0.11.18","0.11.19","0.11.2","0.11.20","0.11.21","0.11.22","0.11.23","0.11.24","0.11.25","0.11.27","0.11.28","0.11.29","0.11.3","0.11.30","0.11.31","0.11.4","0.11.5","0.11.6","0.11.7","0.11.8","0.11.9","0.12.0","0.12.1","0.12.10","0.12.11","0.12.12","0.12.13","0.12.14","0.12.15","0.12.16","0.12.17","0.12.18","0.12.19","0.12.2","0.12.20","0.12.21","0.12.3","0.12.4","0.12.5","0.12.6","0.12.7","0.12.8","0.12.9","0.13.0","0.13.1","0.13.10","0.13.11","0.13.12","0.13.13","0.13.14","0.13.15","0.13.16","0.13.17","0.13.18","0.13.19","0.13.2","0.13.20","0.13.21","0.13.22","0.13.23","0.13.3","0.13.4","0.13.5","0.13.6","0.13.7","0.13.8","0.13.9","0.14.0","0.14.1","0.14.10","0.14.11","0.14.12","0.14.14","0.14.15","0.14.16","0.14.2","0.14.3","0.14.4","0.14.5","0.14.6","0.14.7","0.14.8","0.14.9","0.15.0","0.15.1","0.15.2","0.15.3","0.2.0","0.2.1","0.2.2","0.3.0","0.3.1","0.3.2","0.3.3","0.3.4","0.4.0","0.4.1","0.5.0","0.5.1","0.5.2","0.5.3","0.6.0","0.6.1","0.6.2","0.6.3","0.6.4","0.6.5","0.6.6","0.6.7","0.6.8","0.7.0","0.7.1","0.8.0","0.8.1","0.9.0","0.9.1","1.0.0","1.1.0","1.2.0","1.2.1","1.2.2","1.2.3","1.2.4","1.3.0","1.3.1","1.4.0","1.4.1","1.4.2","1.4.3","1.4.4","1.4.5","1.4.6","1.4.7","1.4.8","1.5.0","1.5.1","1.5.10","1.5.100","1.5.101","1.5.102","1.5.103","1.5.104","1.5.105","1.5.106","1.5.107","1.5.108","1.5.109","1.5.11","1.5.110","1.5.111","1.5.112","1.5.113","1.5.114","1.5.115","1.5.116","1.5.117","1.5.118","1.5.119","1.5.12","1.5.120","1.5.121","1.5.122","1.5.123","1.5.124","1.5.125","1.5.126","1.5.127","1.5.128","1.5.129","1.5.13","1.5.130","1.5.131","1.5.132","1.5.133","1.5.134","1.5.135","1.5.136","1.5.137","1.5.138","1.5.139","1.5.14","1.5.140","1.5.141","1.5.142","1.5.143","1.5.144","1.5.145","1.5.146","1.5.147","1.5.148","1.5.149","1.5.15","1.5.16","1.5.17","1.5.18","1.5.19","1.5.2","1.5.20","1.5.21","1.5.22","1.5.23","1.5.24","1.5.25","1.5.26","1.5.27","1.5.28","1.5.29","1.5.3","1.5.30","1.5.31","1.5.32","1.5.33","1.5.34","1.5.35","1.5.36","1.5.37","1.5.38","1.5.39","1.5.40","1.5.41","1.5.42","1.5.43","1.5.44","1.5.45","1.5.46","1.5.47","1.5.48","1.5.49","1.5.5","1.5.50","1.5.51","1.5.52","1.5.53","1.5.54","1.5.55","1.5.56","1.5.57","1.5.58","1.5.59","1.5.6","1.5.60","1.5.61","1.5.62","1.5.63","1.5.64","1.5.65","1.5.66","1.5.67","1.5.68","1.5.69","1.5.7","1.5.70","1.5.71","1.5.72","1.5.73","1.5.74","1.5.75","1.5.76","1.5.77","1.5.78","1.5.79","1.5.8","1.5.80","1.5.81","1.5.82","1.5.83","1.5.84","1.5.85","1.5.86","1.5.87","1.5.88","1.5.89","1.5.9","1.5.90","1.5.91","1.5.92","1.5.93","1.5.94","1.5.95","1.5.96","1.5.97","1.5.98","1.5.99","1.6.1","1.6.10","1.6.11","1.6.12","1.6.13","1.6.14","1.6.15","1.6.16","1.6.17","1.6.18","1.6.19","1.6.2","1.6.20","1.6.21","1.6.22","1.6.23","1.6.24","1.6.25","1.6.26","1.6.27","1.6.28","1.6.29","1.6.3","1.6.30","1.6.31","1.6.32","1.6.33","1.6.34","1.6.35","1.6.36","1.6.37","1.6.38","1.6.39","1.6.4","1.6.40","1.6.41","1.6.42","1.6.43","1.6.44","1.6.45","1.6.46","1.6.47","1.6.48","1.6.5","1.6.50","1.6.51","1.6.52","1.6.53","1.6.54","1.6.55","1.6.56","1.6.57","1.6.6","1.6.7","1.6.8","1.6.9"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/08/GHSA-cfxv-8fw8-rwpv/GHSA-cfxv-8fw8-rwpv.json"}}],"references":[{"type":"WEB","url":"https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-cfxv-8fw8-rwpv"},{"type":"WEB","url":"https://github.com/MervinPraison/PraisonAI/commit/2f9677abb2ea68eab864ee8b6a828fd0141612e1"},{"type":"PACKAGE","url":"https://github.com/MervinPraison/PraisonAI"},{"type":"WEB","url":"https://github.com/MervinPraison/PraisonAI/releases/tag/v4.6.58"}],"database_specific":{"cwe_ids":["CWE-862"],"github_reviewed":true,"github_reviewed_at":"2026-08-25T14:46:13Z","nvd_published_at":null,"severity":"MODERATE"},"severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L"}]}