mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-03 14:54:28 +00:00
af7ba2edef
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>
203 lines
5.8 KiB
Go
203 lines
5.8 KiB
Go
// Copyright 2022 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repo
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"gitea.dev/models/db"
|
|
org_model "gitea.dev/models/organization"
|
|
user_model "gitea.dev/models/user"
|
|
|
|
"xorm.io/builder"
|
|
)
|
|
|
|
// GetOrgRepositories get repos belonging to the given organization
|
|
func GetOrgRepositories(ctx context.Context, orgID int64) (RepositoryList, error) {
|
|
var orgRepos []*Repository
|
|
err := db.GetEngine(ctx).Where("owner_id = ?", orgID).Find(&orgRepos)
|
|
return orgRepos, err
|
|
}
|
|
|
|
// GetOrgRepositoryIDs get repo IDs belonging to the given organization
|
|
func GetOrgRepositoryIDs(ctx context.Context, orgID int64) (repoIDs []int64, _ error) {
|
|
err := db.GetEngine(ctx).Table("repository").Where("owner_id = ?", orgID).Cols("id").Find(&repoIDs)
|
|
return repoIDs, err
|
|
}
|
|
|
|
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).Where(opts.toCond())
|
|
if opts.PageSize > 0 {
|
|
sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
|
|
}
|
|
var repos []*Repository
|
|
return repos, sess.OrderBy("repository.name").
|
|
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 {
|
|
CountRepos(ctx context.Context) (int64, error)
|
|
RepoIDs(ctx context.Context) ([]int64, error)
|
|
MirrorRepos(ctx context.Context) (RepositoryList, error)
|
|
AddKeyword(keyword string)
|
|
SetSort(db.SearchOrderBy)
|
|
}
|
|
|
|
type accessibleReposEnv struct {
|
|
org *org_model.Organization
|
|
user *user_model.User
|
|
team *org_model.Team
|
|
teamIDs []int64
|
|
keyword string
|
|
orderBy db.SearchOrderBy
|
|
}
|
|
|
|
// AccessibleReposEnv builds an AccessibleReposEnvironment for the repositories in `org`
|
|
// that are accessible to the specified user.
|
|
func AccessibleReposEnv(ctx context.Context, org *org_model.Organization, userID int64) (AccessibleReposEnvironment, error) {
|
|
var user *user_model.User
|
|
|
|
if userID > 0 {
|
|
u, err := user_model.GetUserByID(ctx, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
user = u
|
|
}
|
|
|
|
teamIDs, err := org.GetUserTeamIDs(ctx, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &accessibleReposEnv{
|
|
org: org,
|
|
user: user,
|
|
teamIDs: teamIDs,
|
|
orderBy: db.SearchOrderByRecentUpdated,
|
|
}, nil
|
|
}
|
|
|
|
// AccessibleTeamReposEnv an AccessibleReposEnvironment for the repositories in `org`
|
|
// that are accessible to the specified team.
|
|
func AccessibleTeamReposEnv(org *org_model.Organization, team *org_model.Team) AccessibleReposEnvironment {
|
|
return &accessibleReposEnv{
|
|
org: org,
|
|
team: team,
|
|
orderBy: db.SearchOrderByRecentUpdated,
|
|
}
|
|
}
|
|
|
|
func (env *accessibleReposEnv) cond() builder.Cond {
|
|
cond := builder.NewCond()
|
|
if env.team != nil {
|
|
cond = cond.And(builder.Eq{"team_repo.team_id": env.team.ID})
|
|
} else {
|
|
if env.user == nil || !env.user.IsRestricted {
|
|
cond = cond.Or(builder.Eq{
|
|
"`repository`.owner_id": env.org.ID,
|
|
"`repository`.is_private": false,
|
|
})
|
|
}
|
|
if len(env.teamIDs) > 0 {
|
|
cond = cond.Or(builder.In("team_repo.team_id", env.teamIDs))
|
|
}
|
|
}
|
|
if env.keyword != "" {
|
|
cond = cond.And(builder.Like{"`repository`.lower_name", strings.ToLower(env.keyword)})
|
|
}
|
|
return cond
|
|
}
|
|
|
|
func (env *accessibleReposEnv) CountRepos(ctx context.Context) (int64, error) {
|
|
repoCount, err := db.GetEngine(ctx).
|
|
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
|
|
Where(env.cond()).
|
|
Distinct("`repository`.id").
|
|
Count(&Repository{})
|
|
if err != nil {
|
|
return 0, fmt.Errorf("count user repositories in organization: %w", err)
|
|
}
|
|
return repoCount, nil
|
|
}
|
|
|
|
func (env *accessibleReposEnv) RepoIDs(ctx context.Context) ([]int64, error) {
|
|
var repoIDs []int64
|
|
return repoIDs, db.GetEngine(ctx).
|
|
Table("repository").
|
|
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
|
|
Where(env.cond()).
|
|
GroupBy("`repository`.id,`repository`." + strings.Fields(string(env.orderBy))[0]).
|
|
OrderBy(string(env.orderBy)).
|
|
Cols("`repository`.id").
|
|
Find(&repoIDs)
|
|
}
|
|
|
|
func (env *accessibleReposEnv) MirrorRepoIDs(ctx context.Context) ([]int64, error) {
|
|
repoIDs := make([]int64, 0, 10)
|
|
return repoIDs, db.GetEngine(ctx).
|
|
Table("repository").
|
|
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true).
|
|
Where(env.cond()).
|
|
GroupBy("`repository`.id, `repository`.updated_unix").
|
|
OrderBy(string(env.orderBy)).
|
|
Cols("`repository`.id").
|
|
Find(&repoIDs)
|
|
}
|
|
|
|
func (env *accessibleReposEnv) MirrorRepos(ctx context.Context) (RepositoryList, error) {
|
|
repoIDs, err := env.MirrorRepoIDs(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("MirrorRepoIDs: %w", err)
|
|
}
|
|
|
|
repos := make([]*Repository, 0, len(repoIDs))
|
|
if len(repoIDs) == 0 {
|
|
return repos, nil
|
|
}
|
|
|
|
return repos, db.GetEngine(ctx).
|
|
In("`repository`.id", repoIDs).
|
|
Find(&repos)
|
|
}
|
|
|
|
func (env *accessibleReposEnv) AddKeyword(keyword string) {
|
|
env.keyword = keyword
|
|
}
|
|
|
|
func (env *accessibleReposEnv) SetSort(orderBy db.SearchOrderBy) {
|
|
env.orderBy = orderBy
|
|
}
|