GHSA-JJ5M-H57J-5GV7
Vulnerability from github – Published: 2026-02-17 18:40 – Updated: 2026-02-19 21:14
VLAI?
Summary
Gogs Allows Cross-Repository Comment Deletion via DeleteComment
Details
IDOR: Cross-Repository Comment Deletion via DeleteComment
Summary
The POST /:owner/:repo/issues/comments/:id/delete endpoint does not verify that the comment belongs to the repository specified in the URL. This allows a repository administrator to delete comments from any other repository by supplying arbitrary comment IDs, bypassing authorization controls.
Vulnerability Details
| Field | Value |
|---|---|
| Affected File | internal/route/repo/issue.go |
| Affected Function | DeleteComment (lines 955-968) |
| Secondary File | internal/database/comment.go |
| Secondary Function | DeleteCommentByID (lines 505-520) |
Root Cause
The vulnerability exists due to insufficient authorization validation in the comment deletion flow:
1. Missing Repository Ownership Check in DeleteComment
In internal/route/repo/issue.go, the function retrieves a comment by ID without verifying repository ownership:
func DeleteComment(c *context.Context) {
comment, err := database.GetCommentByID(c.ParamsInt64(":id"))
if err != nil {
c.NotFoundOrError(err, "get comment by ID")
return
}
// Only checks if user is comment poster OR admin of the CURRENT repo (from URL)
if c.UserID() != comment.PosterID && !c.Repo.IsAdmin() {
c.NotFound()
return
} else if comment.Type != database.CommentTypeComment {
c.Status(http.StatusNoContent)
return
}
// No verification that comment.IssueID belongs to c.Repo.Repository.ID!
if err = database.DeleteCommentByID(c.User, comment.ID); err != nil {
c.Error(err, "delete comment by ID")
return
}
c.Status(http.StatusOK)
}
2. Database Layer Performs No Authorization
In internal/database/comment.go, the deletion function performs no repository validation:
func DeleteCommentByID(doer *User, id int64) error {
comment, err := GetCommentByID(id)
if err != nil {
if IsErrCommentNotExist(err) {
return nil
}
return err
}
// Directly deletes without checking repository ownership
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.ID(comment.ID).Delete(new(Comment)); err != nil {
// ...
}
// ...
}
Proof of Concept
Prerequisites
- Two users: Alice (attacker) and Bob (victim)
- Alice is admin of
alice/attacker-repo - Bob has created an issue with a comment on
bob/victim-repo - Attacker needs to obtain the comment ID from victim's repository (e.g., ID: 42)
HTTP Request
POST /alice/attacker-repo/issues/comments/42/delete HTTP/1.1
Host: gogs.example.com
Cookie: i_like_gogs=<alice_session_token>
Severity ?
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.13.4"
},
"package": {
"ecosystem": "Go",
"name": "gogs.io/gogs"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.14.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-25120"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": true,
"github_reviewed_at": "2026-02-17T18:40:44Z",
"nvd_published_at": "2026-02-19T07:17:45Z",
"severity": "MODERATE"
},
"details": "# IDOR: Cross-Repository Comment Deletion via DeleteComment\n\n## Summary\n\nThe `POST /:owner/:repo/issues/comments/:id/delete` endpoint does not verify that the comment belongs to the repository specified in the URL. This allows a repository administrator to delete comments from any other repository by supplying arbitrary comment IDs, bypassing authorization controls.\n\n## Vulnerability Details\n\n| Field | Value |\n|-------|-------|\n| Affected File | `internal/route/repo/issue.go` |\n| Affected Function | `DeleteComment` (lines 955-968) |\n| Secondary File | `internal/database/comment.go` |\n| Secondary Function | `DeleteCommentByID` (lines 505-520) |\n\n## Root Cause\n\nThe vulnerability exists due to insufficient authorization validation in the comment deletion flow:\n\n### 1. Missing Repository Ownership Check in DeleteComment\n\nIn `internal/route/repo/issue.go`, the function retrieves a comment by ID without verifying repository ownership:\n\n```go\nfunc DeleteComment(c *context.Context) {\n comment, err := database.GetCommentByID(c.ParamsInt64(\":id\"))\n if err != nil {\n c.NotFoundOrError(err, \"get comment by ID\")\n return\n }\n\n // Only checks if user is comment poster OR admin of the CURRENT repo (from URL)\n if c.UserID() != comment.PosterID \u0026\u0026 !c.Repo.IsAdmin() {\n c.NotFound()\n return\n } else if comment.Type != database.CommentTypeComment {\n c.Status(http.StatusNoContent)\n return\n }\n\n // No verification that comment.IssueID belongs to c.Repo.Repository.ID!\n if err = database.DeleteCommentByID(c.User, comment.ID); err != nil {\n c.Error(err, \"delete comment by ID\")\n return\n }\n\n c.Status(http.StatusOK)\n}\n```\n\n### 2. Database Layer Performs No Authorization\n\nIn `internal/database/comment.go`, the deletion function performs no repository validation:\n\n```go\nfunc DeleteCommentByID(doer *User, id int64) error {\n comment, err := GetCommentByID(id)\n if err != nil {\n if IsErrCommentNotExist(err) {\n return nil\n }\n return err\n }\n\n // Directly deletes without checking repository ownership\n sess := x.NewSession()\n defer sess.Close()\n if err = sess.Begin(); err != nil {\n return err\n }\n\n if _, err = sess.ID(comment.ID).Delete(new(Comment)); err != nil {\n // ...\n }\n // ...\n}\n```\n\n## Proof of Concept\n\n### Prerequisites\n\n1. Two users: **Alice** (attacker) and **Bob** (victim)\n2. Alice is admin of `alice/attacker-repo`\n3. Bob has created an issue with a comment on `bob/victim-repo`\n4. Attacker needs to obtain the comment ID from victim\u0027s repository (e.g., ID: 42)\n\n### HTTP Request\n\n```http\nPOST /alice/attacker-repo/issues/comments/42/delete HTTP/1.1\nHost: gogs.example.com\nCookie: i_like_gogs=\u003calice_session_token\u003e\n\n```",
"id": "GHSA-jj5m-h57j-5gv7",
"modified": "2026-02-19T21:14:32Z",
"published": "2026-02-17T18:40:44Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/gogs/gogs/security/advisories/GHSA-jj5m-h57j-5gv7"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-25120"
},
{
"type": "WEB",
"url": "https://github.com/gogs/gogs/commit/1b226ca48dc8b3e95cc1c41229d72819c960a1b7"
},
{
"type": "PACKAGE",
"url": "https://github.com/gogs/gogs"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Gogs Allows Cross-Repository Comment Deletion via DeleteComment"
}
Loading…
Loading…
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Loading…
Loading…