GHSA-cfxv-8fw8-rwpv: Medium severity pip/praisonaiagents vulnerability

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.

Affected Software

1 affected componentFixes available
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.52
  3. Configuration

    Add `from ..approval import require_approval` and decorate `ast_grep_rewrite` with `@require_approval(risk_level="high")` so file-rewriting requires interactive approval before the body runs (the bug described is that `ast_grep_rewrite` has no decorator/import).

    praisonaiagents/tools/ast_grep_tool.py (ast_grep_rewrite) @require_approval decorator = enabled
  4. Configuration

    Fix `ast_grep_rewrite` so that when `dry_run=False` it does not return the literal string `No changes made` on the modified-write path (the material says this incorrect return occurs even when files are rewritten).

    praisonaiagents/tools/ast_grep_tool.py (ast_grep_rewrite) dry_run=False return value = reflect actual modifications
  5. Configuration

    Implement the `_validate_path` workspace boundary check (workspace boundary restriction) for `ast_grep_rewrite` so `path` cannot escape the intended workspace (the material notes there is currently no `_validate_path` workspace check and that `path="/"` enables filesystem-wide rewrite).

    praisonaiagents/tools/ast_grep_tool.py (ast_grep_rewrite) _validate_path workspace boundary check = enforced
  6. Configuration

    Add `cwd=` sandboxing for the `subprocess.run` invocation used by `ast_grep_rewrite` so rewriting is constrained to the workspace rather than relying solely on `path` input (material notes there is no `cwd=` sandboxing).

    praisonaiagents/tools/ast_grep_tool.py (ast_grep_rewrite) cwd sandboxing = sandboxed to workspace
  7. Configuration

    Apply `@require_approval(risk_level="high")` to `ast_grep_scan` (listed as `ast_grep_tool.py:243`) if it can write to disk; the material states `ast_grep_search` is read-only and `ast_grep_scan` is the candidate for adding approval when applicable.

    praisonaiagents/tools/ast_grep_tool.py (ast_grep_scan) @require_approval decorator = enabled (if writable)

Event History

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

Frequently Asked Questions

1

Does the approval workflow protect this operation by default?

No. Unlike the listed file-editing and code-execution tools, ast_grep_rewrite was not wrapped with @require_approval, so its on-disk changes can occur without the interactive approval check. The fail-closed behavior when approval is denied or unavailable therefore does not apply to this entry point.

2

What does an attacker need to exploit this issue?

The attacker needs a way to cause ast_grep_rewrite to be invoked with chosen rewrite inputs. The advisory rates the issue as local, requires no privileges, and requires user interaction.

3

Which installations should be investigated?

Investigate deployments using the master revision ce97667156a116c50b4a3d1aa21e09f048903fda or pypi praisonaiagents releases through 1.6.52, particularly where agents or workflows can invoke ast_grep_rewrite. The referenced repository release is v4.6.58.

4

What can be done before patching is available?

Do not expose or invoke ast_grep_rewrite in agent workflows until the affected installation is updated. Do not rely on the approval backend alone to block this operation, because the missing approval wrapper bypasses that control.

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