CVE-2026-55530
PraisonAI: ast_grep_rewrite rewrites arbitrary files without the @require_approval gate enforced on every sibling mutation tool
PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.58, ast_grep_rewrite lacks the @require_approval decorator used by sibling mutation tools. With dry_run=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.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Required
What an attacker can affect
- Confidentiality
- None
- Integrity
- High
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
6.1
PraisonAI: ast_grep_rewrite rewrites arbitrary files without the @require_approval gate enforced on every sibling mutation tool
PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.58, ast_grep_rewrite lacks the @require_approval decorator used by sibling mutation tools. With dry_run=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.
- Record
CVE-2026-55530- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L - Weakness
- CWE-862
- Updated
- 2026-11-02
EUVDEUVD-2026-65501
6.1
PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.58, ast_grep_rewrite lacks the @require_approval decorator used by sibling mutation tools. With dry_run=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.
- Record
EUVD-2026-65501- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-cfxv-8fw8-rwpv
6.1
praisonaiagents: ast_grep_rewrite rewrites arbitrary files without the @require_approval gate enforced on every sibling mutation tool
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,…
- Record
GHSA-cfxv-8fw8-rwpv- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L - Weakness
- CWE-862
- Updated
- 2026-11-02
NVD
6.1
PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.58, ast_grep_rewrite lacks the @require_approval decorator used by sibling mutation tools. With dry_run=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.
- Record
CVE-2026-55530- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L - Weakness
- CWE-862
- Updated
- 2026-11-02
OSV
6.1
PraisonAI: ast_grep_rewrite rewrites arbitrary files without the @require_approval gate enforced on every sibling mutation tool
PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.58, ast_grep_rewrite lacks the @require_approval decorator used by sibling mutation tools. With dry_run=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.
- Record
CVE-2026-55530- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L - Weakness
- CWE-862
- Updated
- 2026-11-02
OSVGHSA-cfxv-8fw8-rwpv
6.1
praisonaiagents: ast_grep_rewrite rewrites arbitrary files without the @require_approval gate enforced on every sibling mutation tool
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,…
- Record
GHSA-cfxv-8fw8-rwpv- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L - Weakness
- CWE-862
- Updated
- 2026-11-02
OSVPYSEC-2026-3901
6.1
praisonaiagents: ast_grep_rewrite rewrites arbitrary files without the @require_approval gate enforced on every sibling mutation tool
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,…
- Record
PYSEC-2026-3901- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
praisonaiagentsPyPI |
before 1.6.58 | 1.6.58 |
praisonaiagentsPyPI |
before 1.6.58, plus listed versions 0.0.10.0.100.0.1000.0.1010.0.102 Show 45 moreShow less0.0.1030.0.1040.0.1050.0.1060.0.1070.0.1080.0.1090.0.110.0.1100.0.1110.0.1120.0.1130.0.1140.0.1150.0.1160.0.1170.0.1180.0.1190.0.120.0.1200.0.1210.0.1220.0.1230.0.1240.0.1250.0.1260.0.1270.0.1280.0.1290.0.130.0.1300.0.1310.0.1320.0.1330.0.1340.0.1350.0.1360.0.1370.0.1380.0.1390.0.140.0.1400.0.1410.0.1420.0.143 |
1.6.58 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-55530 is a medium vulnerability (CVSS 6.1) in Mervinpraison Praisonai, published August 25, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.17%, the 5th percentile. Fixed in praisonaiagents 1.6.58.
References
github.com/MervinPraison/PraisonAI/security/advisories/GHSA-cfxv-8fw8-rwpv
github.com/MervinPraison/PraisonAI/commit/2f9677abb2ea68eab864ee8b6a828fd0141612e1
github.com/MervinPraison/PraisonAI/releases/tag/v4.6.58
github.com/advisories/GHSA-cfxv-8fw8-rwpv
github.com/CVEProject/cvelistV5/tree/main/cves/2026/55xxx/CVE-2026-55530.json
nvd.nist.gov/vuln/detail/CVE-2026-55530
github.com/MervinPraison/PraisonAI
pypi.org/project/praisonaiagents
Left out here: 535 versions and the rest of 3 source descriptions. The JSON record has them.