CVE-2026-55530: PraisonAI: ast_grep_rewrite rewrites arbitrary files without the @require_approval gate enforced on every sibling mutation tool

Published Aug 25, 2026
·
Updated

Target: PraisonAI (MervinPraison/PraisonAI) Affected component: praisonaiagents/tools/astgreptool.py — astgreprewrite 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 @requireapproval, 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 | |---|---|---|---| | filetools.py | 212 | copyfile | high | | filetools.py | 239 | movefile | high | | filetools.py | 266 | deletefile | high | | edittools.py | 38 | EditTools.editfile | high | | edittools.py | 155 | editfile | high | | shelltools.py | 32 | executecommand | critical | | pythontools.py | 352 | executecode | critical |

astgreptool.py:149 astgreprewrite is structurally a sibling of these but has no decorator and no from ..approval import requireapproval import. With dryrun=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 validatepath workspace check, and no cwd= sandboxing. The function is registered as a top-level tool (init.py:182) and exposed via the codeintelligence built-in profile (profiles.py).

A secondary defect: on the dryrun=False path astgreprewrite returns the literal string No changes made to the caller even when it modified files (the "No changes made" return at astgreptool.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. PRAISONAIAUTOAPPROVE is removed from the environment first, so no env-bypass is in play.

python import os, tempfile, textwrap os.environ.pop("PRAISONAIAUTOAPPROVE", None)

workdir = tempfile.mkdtemp(prefix="poc-") target = os.path.join(workdir, "target.py") open(target, "w").write(textwrap.dedent(""" def safefunction(x): return x + 1

def hello(name): return 'hi ' + name """))

Positive: undecorated tool rewrites the file. from praisonaiagents.tools.astgreptool import astgreprewrite astgreprewrite( pattern="def $FN($$$): return $$$", replacement="def $FN($$$): import os; os.environ['POCCANARY']='1'; return $$$", lang="python", path=workdir, dryrun=False, )

Negative control: decorated sibling triggers the approval flow. from praisonaiagents.tools.edittools import editfile editfile(filepath=target, oldtext="def hello(name):", newtext="def hello(name): # X")

Result, verified: astgreprewrite rewrote target.py to contain the injected import os; os.environ['POCCANARY']='1' payload, no approval prompt fired, and the call returned No changes made. The subsequent editfile call in the same process rendered the Tool Approval Required panel and, on denial, raised PermissionError("Execution of editfile denied: User denied") without modifying its target. Same process, same approval backend — the only difference is the missing decorator on astgreprewrite.

Threat model

An LLM agent running locally whose tool surface includes astgreprewrite (via the codeintelligence 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 astgreprewrite with attacker-chosen pattern, replacement, and path (the dryrun field is in the LLM-visible tool schema, so dryrun=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/astgreptool.py +++ b/praisonaiagents/tools/astgreptool.py @@ from praisonaiagents.logging import getlogger from typing import Optional, List +from ..approval import requireapproval @@ +@requireapproval(risklevel="high") def astgreprewrite( pattern: str, replacement: str,

high matches the file-modifying siblings; critical is defensible given the write→exec escalation. In the same patch: add a validatepath workspace boundary check (cf. edittools.py:27); fix the No changes made return so it reflects actual modifications; apply the decorator to astgrepscan (astgreptool.py:243) if it can write. astgrepsearch is read-only and can stay undecorated. A regression test asserting astgreprewrite 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.

Other sources

PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.58, astgreprewrite lacks the @requireapproval decorator used by sibling mutation tools. With dryrun=False, an agent-controlled call can pass --update-all and a broad path to rewrite matching files without the expected authorization gate. This issue is fixed in version 1.6.58.

MITRE

Affected Software

2 affected componentsFixes available
PraisonAI<1.6.58
pip/praisonaiagents<1.6.58
1.6.58

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/praisonaiagents to a version that resolves this vulnerability.

    Fixed in 1.6.58
  2. Upgrade

    Upgrade praisonaiagents to a version that resolves this vulnerability.

    Fixed in 1.6.58
  3. Configuration

    Add `from ..approval import require_approval` and decorate `ast_grep_rewrite` with `@require_approval(risk_level="high")` so it routes through the interactive approval flow before any on-disk mutation runs.

    praisonaiagents/tools/ast_grep_tool.py (ast_grep_rewrite) require_approval decorator gate = @require_approval(risk_level="high")
  4. Configuration

    Add a `_validate_path` workspace boundary check to `ast_grep_rewrite` to prevent writes outside the allowed workspace.

    praisonaiagents/tools/ast_grep_tool.py (ast_grep_rewrite) workspace boundary validation (_validate_path) = enable workspace boundary check
  5. Configuration

    Fix the `dry_run=False` path in `ast_grep_rewrite`/the tool output so it does not return the literal string `No changes made` when files were actually modified; ensure the return reflects real changes.

    praisonaiagents/tools/ast_grep_tool.py / caller output dry_run=False return value = return accurate modification status (no longer always 'No changes made')

Event History

Aug 25, 2026
CVE Published
via MITRE·02:45 PM
Data Sourced
via MITRE·02:45 PM
DescriptionSeverityWeakness
Advisory Published
via GitHub·02:46 PM
Data Sourced
via GitHub·02:46 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Which deployments should be considered exposed?

Deployments using the current master revision ce97667156a116c50b4a3d1aa21e09f048903fda or the reproduced PyPI package versions praisonaiagents 1.6.52 and earlier should be considered affected.

2

Does the approval system prevent this file-rewrite operation when no approval backend is configured?

No. The approval wrapper used by sibling mutation tools fails closed when approval is denied or no backend is configured, but ast_grep_rewrite lacks that wrapper and can rewrite files without passing through that gate.

3

What access does an attacker need?

The supplied CVSS vector indicates local attack access, no required privileges, and required user interaction. Integrity impact is high and availability impact is low, while confidentiality impact is listed as none.

4

How can I check whether my code contains the vulnerable behavior?

Inspect praisonaiagents/tools/ast_grep_tool.py and verify whether ast_grep_rewrite is protected by the same require_approval mechanism used by sibling tools such as copy_file, move_file, delete_file, edit_file, execute_command, and execute_code.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203