{"id":"CVE-2026-25120","aliases":["GHSA-jj5m-h57j-5gv7","GO-2026-4501"],"url":"https://o3.security/vulnerability/CVE-2026-25120","summary":"Gogs Allows Cross-Repository Comment Deletion via DeleteComment","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 && !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's 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=<alice_session_token>\n\n```","published":"2026-02-19T01:59:39.257Z","modified":"2026-08-12T03:51:31.237856899Z","cvss":null,"epss":null,"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"Go","name":"gogs.io/gogs","fixedVersion":"0.14.0"}],"fix":{"url":"https://github.com/gogs/gogs/commit/1b226ca48dc8b3e95cc1c41229d72819c960a1b7","label":"gogs/gogs@1b226ca"},"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/25xxx/CVE-2026-25120.json"},{"type":"ADVISORY","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":"FIX","url":"https://github.com/gogs/gogs/commit/1b226ca48dc8b3e95cc1c41229d72819c960a1b7"},{"type":"PACKAGE","url":"https://github.com/gogs/gogs"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:31.237856899Z"}}