Skip to content

praisonaiagents: ast_grep_rewrite rewrites arbitrary files without the @require_approval gate enforced on every sibling mutation tool

Moderate severity GitHub Reviewed Published Jun 13, 2026 in MervinPraison/PraisonAI • Updated Aug 25, 2026

Package

pip praisonaiagents (pip)

Affected versions

< 1.6.58

Patched versions

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 <= 1.6.52).

Summary

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

File Line Symbol Risk level
file_tools.py 212 copy_file high
file_tools.py 239 move_file high
file_tools.py 266 delete_file high
edit_tools.py 38 EditTools.edit_file high
edit_tools.py 155 edit_file high
shell_tools.py 32 execute_command critical
python_tools.py 352 execute_code critical

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).

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.

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

--- 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 — kai@snailsploit.com — PGP on request.

References

@MervinPraison MervinPraison published to MervinPraison/PraisonAI Jun 13, 2026
Published to the GitHub Advisory Database Aug 25, 2026
Reviewed Aug 25, 2026
Last updated Aug 25, 2026

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Local
Attack complexity
Low
Privileges required
None
User interaction
Required
Scope
Unchanged
Confidentiality
None
Integrity
High
Availability
Low

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L

EPSS score

Exploit Prediction Scoring System (EPSS)

This score estimates the probability of this vulnerability being exploited within the next 30 days. Data provided by FIRST.
(5th percentile)

Weaknesses

Missing Authorization

The product does not perform an authorization check when an actor attempts to access a resource or perform an action. Learn more on MITRE.

CVE ID

CVE-2026-55530

GHSA ID

GHSA-cfxv-8fw8-rwpv

Credits

Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.