GHSA-cv22-72px-f4gh
Gogs has an Authorization Bypass Allows Cross-Repository Label Modification in Gogs
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
Blast Radius
gogs.io/gogsReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.
Description
Summary
A broken access control vulnerability in Gogs allows authenticated users with write access to any repository to modify labels belonging to other repositories. The UpdateLabel function in the Web UI (internal/route/repo/issue.go) fails to verify that the label being modified belongs to the repository specified in the URL path, enabling cross-repository label tampering attacks.
Details
The vulnerability exists in the Web UI's label update endpoint POST /:username/:reponame/labels/edit. The handler function UpdateLabel uses an incorrect database query function that bypasses repository ownership validation:
Vulnerable Code (internal/route/repo/issue.go:1040-1054):
func UpdateLabel(c *context.Context, f form.CreateLabel) {
l, err := database.GetLabelByID(f.ID) // ❌ No repository validation
if err != nil {
c.NotFoundOrError(err, "get label by ID")
return
}
// ❌ Missing validation: l.RepoID != c.Repo.Repository.ID
l.Name = f.Title
l.Color = f.Color
if err := database.UpdateLabel(l); err != nil {
c.Error(err, "update label")
return
}
c.RawRedirect(c.Repo.MakeURL("labels"))
}
Root Cause:
- The function calls
database.GetLabelByID(f.ID)which internally passesrepoID=0to the ORM layer - According to code comments in
internal/database/issue_label.go:147-166, passingrepoID=0causes the ORM to ignore repository restrictions - No validation checks whether
l.RepoID == c.Repo.Repository.IDbefore updating - The middleware
reqRepoWriter()only validates write access to the repository in the URL path, not the label's actual repository
Inconsistency with Other Functions:
NewLabel: Correctly setsRepoID = c.Repo.Repository.IDDeleteLabel: Correctly usesdatabase.DeleteLabel(c.Repo.Repository.ID, id)- API
EditLabel: Correctly usesdatabase.GetLabelOfRepoByID(c.Repo.Repository.ID, id)
- Only
UpdateLabelin Web UI uses the vulnerable pattern
PoC
Prerequisites:
- Two user accounts: Alice (attacker) and Bob (victim)
- alice has written access to repo-a
- Bob owns repo-b with labels
Step 1: Identify Target Label ID
- Login as bob, navigate to bob/repo-b/labels
- Open browser DevTools (F12) → Network tab
- Click edit on any label
- Observe the form data: id=<LABEL_ID>
- Example: id=1
Step 2: Execute Attack
# Login as alice, get session cookie
# Open DevTools → Application → Cookies → i_like_gogs
# Copy the cookie value
# Send malicious request
curl -X POST "http://localhost:3000/alice/repo-a/labels/edit" \
-H "Cookie: i_like_gogs=<ALICE_SESSION_COOKIE>" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "id=1&title=HACKED-BY-ALICE&color=%23000000"
# Expected response: 302 Found (redirect)
Step 3: Verify Impact
- Login as bob
- Navigate to bob/repo-b/labels
- Observe: Label "P0-Critical" is now "HACKED-BY-ALICE" with black color
Impact
-
Issue Classification Disruption: Modify critical labels (e.g., "P0-Critical" → "P3-Low") causing urgent issues to be deprioritized
-
Security Issue Concealment: Change "security" labels to "documentation" to hide vulnerability reports from security teams
-
Workflow** Sabotage**: Alter labels used in CI/CD automation, breaking deployment pipelines
-
Mass Disruption: Batch modifies all labels across multiple repositories using ID enumeration
Recommended Fix:
func UpdateLabel(c *context.Context, f form.CreateLabel) {
l, err := database.GetLabelOfRepoByID(c.Repo.Repository.ID, f.ID)
if err != nil {
c.NotFoundOrError(err, "get label of repository by ID")
return
}
// Now label ownership is validated at database layer
l.Name = f.Title
l.Color = f.Color
if err := database.UpdateLabel(l); err != nil {
c.Error(err, "update label")
return
}
c.RawRedirect(c.Repo.MakeURL("labels"))
}
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐹Go | gogs.io/gogs | all versions | 0.14.0 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for gogs.io/gogs. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update gogs.io/gogs to 0.14.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-cv22-72px-f4gh is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-cv22-72px-f4gh is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-cv22-72px-f4gh. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-cv22-72px-f4gh in your dependencies?
O3 detects GHSA-cv22-72px-f4gh across Go dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.