fix: enforce public-only token scope and harden push options / locale parsing (#38323) (#38399)

Backport #38323 by @bircni

Three independent security hardening fixes, each with a regression test:

- **Locale DoS:** the `Locale` middleware passed the raw
`Accept-Language` header to `ParseAcceptLanguage`, whose guard only
counts `-` while the scanner aliases `_` to `-` — a large `_`-separated
header on an unauthenticated request burned CPU. The header is now
length-bounded before parsing.
- **Public-only token scope:** `GET /teams/{id}/repos`,
`.../repos/{org}/{repo}`, `/teams/{id}/activities/feeds`, and
`/users/{username}/orgs/{org}/permissions` still returned private
repo/activity/permission data to a public-only token. They now filter
via `TokenCanAccessRepo` / `ApplyPublicOnly` and reject non-public org
permissions.
- **Push-option visibility:** `repo.private` / `repo.template` push
options were applied to any existing repo, letting an owner/admin
silently flip visibility bypassing audit, webhooks, and notifications.
They are now honored only on push-to-create.

Co-authored-by: bircni <bircni@icloud.com>
This commit is contained in:
Giteabot
2026-07-10 19:07:04 +02:00
committed by GitHub
co-authored by bircni
parent 8b4252e7f6
commit af7ba2edef
8 changed files with 207 additions and 17 deletions
+10 -3
View File
@@ -160,10 +160,17 @@ func hookPostReceiveUpdateRepoByOptions(ctx *gitea_context.PrivateContext, opts
return false
}
// FIXME: these options are not quite right, for example: changing visibility should do more works than just setting the is_private flag
// These options should only be used for "push-to-create"
// Only honor these options while the repo is still empty (the push-to-create
// case). On a populated repo a bare "git push -o repo.private=..." would
// silently flip visibility, bypassing the audit log, webhooks and notifications.
if !repo.IsEmpty {
return true
}
// The repo is empty and being initialized by this push, so there is no
// dependent state (webhooks, notifications, visibility fan-out) to reconcile
// yet; setting the flags directly is sufficient in this push-to-create case.
if isPrivate.Has() && repo.IsPrivate != isPrivate.Value() {
// TODO: it needs to do more work
repo.IsPrivate = isPrivate.Value()
if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_private"); err != nil {
log.Error("failed to update repo is_private: %v", err)