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 10:07:04 -07:00
committed by GitHub
parent 8b4252e7f6
commit af7ba2edef
8 changed files with 207 additions and 17 deletions
+23 -8
View File
@@ -31,18 +31,28 @@ func GetOrgRepositoryIDs(ctx context.Context, orgID int64) (repoIDs []int64, _ e
type SearchTeamRepoOptions struct {
db.ListOptions
TeamID int64
// PublicOnly restricts the result (and count) to non-private repositories.
PublicOnly bool
}
func (opts *SearchTeamRepoOptions) toCond() builder.Cond {
cond := builder.NewCond()
if opts.TeamID > 0 {
cond = cond.And(builder.In("id",
builder.Select("repo_id").
From("team_repo").
Where(builder.Eq{"team_id": opts.TeamID}),
))
}
if opts.PublicOnly {
cond = cond.And(builder.Eq{"is_private": false})
}
return cond
}
// GetTeamRepositories returns paginated repositories in team of organization.
func GetTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) (RepositoryList, error) {
sess := db.GetEngine(ctx)
if opts.TeamID > 0 {
sess = sess.In("id",
builder.Select("repo_id").
From("team_repo").
Where(builder.Eq{"team_id": opts.TeamID}),
)
}
sess := db.GetEngine(ctx).Where(opts.toCond())
if opts.PageSize > 0 {
sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
@@ -51,6 +61,11 @@ func GetTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) (Repo
Find(&repos)
}
// CountTeamRepositories returns the number of repositories in team of organization matching opts.
func CountTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toCond()).Count(new(Repository))
}
// AccessibleReposEnvironment operations involving the repositories that are
// accessible to a particular user
type AccessibleReposEnvironment interface {