GitHub Advisoryadvisory · PyPI

GHSA-cfxv-8fw8-rwpv praisonaiagents: ast_grep_rewrite rewrites arbitrary files without the @require_approval gate enforced on every sibling mutation tool

MEDIUM Published 2026-08-25 Modified 2026-08-25 Official page ↗
Aliases
CVE-2026-55530
Ecosystem
PyPI
Packages
praisonaiagents
Severity
Moderate
CVSS vector
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L
Weaknesses
CWE-862
Reviewed
2026-08-25

Affected packages

• PyPI praisonaiagents: < 1.6.58 → fixed in 1.6.58

Description

Target: PraisonAI (`MervinPraison/PraisonAI`) Affected component: `praisonaiagents/tools/ast_grep_tool.py` — `ast_grep_rewrite` Affected versions: master at `ce97667156a116c50b4a3d1aa21e09f048903fda`; reproduced against the current `praisonaiagents` PyPI release (`praisonaiagents` --rewrite --lang --update-all ` (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`). A 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. Proof of concept Single 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. ```python import os, tempfile, textwrap os.environ.pop("PRAISONAI_AUTO_APPROVE", None) workdir = tempfile.mkdtemp(prefix="poc-") target = os.path.join(workdir, "target.py") open(target, "w").write(textwrap.dedent(""" def safe_function(x): return x + 1 def hello(name): return 'hi ' + name """)) Positive: undecorated tool rewrites the file. from praisonaiagents.tools.ast_grep_tool import ast_grep_rewrite ast_grep_rewrite( pattern="def $FN($$$): return $$$", replacement="def $FN($$$): import os; os.environ['POC_CANARY']='1'; return $$$", lang="python", path=workdir, dry_run=False, ) Negative control: decorated sibling triggers the approval flow. from praisonaiagents.tools.edit_tools import edit_file edit_file(file_path=target, old_text="def hello(name):", new_text="def hello(name): # X") ``` Result, 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`. Threat model An 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. Suggested fix ```diff --- a/praisonaiagents/tools/ast_grep_tool.py +++ b/praisonaiagents/tools/ast_grep_tool.py @@ from praisonaiagents._logging import get_logger from typing import Optional, List +from ..approval import require_approval @@ +@require_approval(risk_level="high") def ast_grep_rewrite( pattern: str, replacement: str, ``` `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. Coordinated disclosure • Kai Aizen / SnailSploit — `[email protected]` — PGP on request.

Related

Alias · CVE (1)

  • CVE-2026-55530 PraisonAI is a multi-agent teams system. vulnerability

Weakness · CWE (1)

  • CWE-862 Missing Authorization weakness

References