mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-02 09:49:34 +00:00
de4b8277e9
Backport #38406 by @bircni Addresses a batch of privately reported security issues, grouped by area: - **SSRF** - migration PR-patch/asset fetches, OAuth2 avatar & OpenID discovery, pull-mirror URL re-validation, and the outbound proxy path. - **Access-token scope** - prevent scope escalation on token creation; keep public-only tokens confined (feeds, packages, Actions listings, star/watch lists, limited/private owners). - **Access control / disclosure** - go-get default-branch leak, webhook authorization-header leak, watch clearing on private transitions, label/attachment scoping. - **Denial of service** - input bounds for npm dist-tags, Debian control files, Arch file lists, and SSH keys. ### 📌 Attention for site admins Not breaking - existing configs keep working - but two changes are worth a look: - **New SSRF protection** Outbound requests (migrations, OAuth2 avatars, OpenID discovery, pull mirrors, proxy path) are now validated against the allow/block host lists. If your instance legitimately reaches internal hosts, you may need to add them to `[security].ALLOWED_HOST_LIST` (and the relevant `ALLOW_LOCALNETWORKS` settings). - **Deprecation** `[webhook].ALLOWED_HOST_LIST` is deprecated and will be removed in a future release. Use `[security].ALLOWED_HOST_LIST` instead; the old key still works for now. Co-authored-by: bircni <bircni@icloud.com> Co-authored-by: TheFox0x7 <thefox0x7@gmail.com> Co-authored-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Zettat123 <zettat123@gmail.com>
162 lines
4.5 KiB
Go
162 lines
4.5 KiB
Go
// Copyright 2022 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package actions
|
|
|
|
import (
|
|
"context"
|
|
"slices"
|
|
|
|
"gitea.dev/models/db"
|
|
repo_model "gitea.dev/models/repo"
|
|
"gitea.dev/modules/base"
|
|
"gitea.dev/modules/container"
|
|
"gitea.dev/modules/optional"
|
|
"gitea.dev/modules/timeutil"
|
|
|
|
"xorm.io/builder"
|
|
)
|
|
|
|
type ActionJobList []*ActionRunJob
|
|
|
|
func (jobs ActionJobList) GetRunIDs() []int64 {
|
|
return container.FilterSlice(jobs, func(j *ActionRunJob) (int64, bool) {
|
|
return j.RunID, j.RunID != 0
|
|
})
|
|
}
|
|
|
|
// SortMatrixGroupsByName natural-sorts each contiguous run of jobs that share a JobID
|
|
// so matrix expansions (e.g. "test (1)", "test (2)", "test (10)") appear in human order.
|
|
// Input is expected to be in DB id order so JobID groups are contiguous; cross-group order is preserved.
|
|
func (jobs ActionJobList) SortMatrixGroupsByName() {
|
|
for i := 0; i < len(jobs); {
|
|
j := i + 1
|
|
for j < len(jobs) && jobs[j].JobID == jobs[i].JobID {
|
|
j++
|
|
}
|
|
slices.SortFunc(jobs[i:j], func(a, b *ActionRunJob) int {
|
|
return base.NaturalSortCompare(a.Name, b.Name)
|
|
})
|
|
i = j
|
|
}
|
|
}
|
|
|
|
func (jobs ActionJobList) LoadRepos(ctx context.Context) error {
|
|
repoIDs := container.FilterSlice(jobs, func(j *ActionRunJob) (int64, bool) {
|
|
return j.RepoID, j.RepoID != 0 && j.Repo == nil
|
|
})
|
|
if len(repoIDs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
repos := make(map[int64]*repo_model.Repository, len(repoIDs))
|
|
if err := db.GetEngine(ctx).In("id", repoIDs).Find(&repos); err != nil {
|
|
return err
|
|
}
|
|
for _, j := range jobs {
|
|
if j.RepoID > 0 && j.Repo == nil {
|
|
j.Repo = repos[j.RepoID]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (jobs ActionJobList) LoadRuns(ctx context.Context, withRepo bool) error {
|
|
if withRepo {
|
|
if err := jobs.LoadRepos(ctx); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
runIDs := jobs.GetRunIDs()
|
|
runs := make(map[int64]*ActionRun, len(runIDs))
|
|
if err := db.GetEngine(ctx).In("id", runIDs).Find(&runs); err != nil {
|
|
return err
|
|
}
|
|
for _, j := range jobs {
|
|
if j.Run == nil {
|
|
j.Run = runs[j.RunID]
|
|
}
|
|
if j.Run != nil {
|
|
j.Run.Repo = j.Repo
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (jobs ActionJobList) LoadAttributes(ctx context.Context, withRepo bool) error {
|
|
return jobs.LoadRuns(ctx, withRepo)
|
|
}
|
|
|
|
type FindRunJobOptions struct {
|
|
db.ListOptions
|
|
RunID int64
|
|
RunAttemptID optional.Option[int64] // use optional to allow filtering by zero (legacy jobs have run_attempt_id=0)
|
|
RepoID int64
|
|
OwnerID int64
|
|
CommitSHA string
|
|
Statuses []Status
|
|
UpdatedBefore timeutil.TimeStamp
|
|
ConcurrencyGroup string
|
|
OrderBy db.SearchOrderBy
|
|
// AccessibleRepoIDsSubQuery, when non-nil, restricts results to the repo IDs selected by the
|
|
// subquery (the caller's accessible repos). A nil value means no restriction. Using a subquery
|
|
// instead of a materialized ID slice avoids exceeding DB parameter limits for large owners.
|
|
AccessibleRepoIDsSubQuery *builder.Builder
|
|
}
|
|
|
|
var JobOrderByMap = map[string]map[string]db.SearchOrderBy{
|
|
"asc": {"id": "`action_run_job`.id ASC"},
|
|
"desc": {"id": "`action_run_job`.id DESC"},
|
|
}
|
|
|
|
func (opts FindRunJobOptions) ToConds() builder.Cond {
|
|
cond := builder.NewCond()
|
|
if opts.RunID > 0 {
|
|
cond = cond.And(builder.Eq{"`action_run_job`.run_id": opts.RunID})
|
|
}
|
|
if opts.RunAttemptID.Has() {
|
|
cond = cond.And(builder.Eq{"`action_run_job`.run_attempt_id": opts.RunAttemptID.Value()})
|
|
}
|
|
if opts.RepoID > 0 {
|
|
cond = cond.And(builder.Eq{"`action_run_job`.repo_id": opts.RepoID})
|
|
}
|
|
if opts.CommitSHA != "" {
|
|
cond = cond.And(builder.Eq{"`action_run_job`.commit_sha": opts.CommitSHA})
|
|
}
|
|
if len(opts.Statuses) > 0 {
|
|
cond = cond.And(builder.In("`action_run_job`.status", opts.Statuses))
|
|
}
|
|
if opts.UpdatedBefore > 0 {
|
|
cond = cond.And(builder.Lt{"`action_run_job`.updated": opts.UpdatedBefore})
|
|
}
|
|
if opts.ConcurrencyGroup != "" {
|
|
if opts.RepoID == 0 {
|
|
panic("Invalid FindRunJobOptions: repo_id is required")
|
|
}
|
|
cond = cond.And(builder.Eq{"`action_run_job`.concurrency_group": opts.ConcurrencyGroup})
|
|
}
|
|
if opts.AccessibleRepoIDsSubQuery != nil {
|
|
cond = cond.And(builder.In("`action_run_job`.repo_id", opts.AccessibleRepoIDsSubQuery))
|
|
}
|
|
return cond
|
|
}
|
|
|
|
func (opts FindRunJobOptions) ToJoins() []db.JoinFunc {
|
|
if opts.OwnerID > 0 {
|
|
return []db.JoinFunc{
|
|
func(sess db.Engine) error {
|
|
sess.Join("INNER", "repository", "repository.id = repo_id AND repository.owner_id = ?", opts.OwnerID)
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (opts FindRunJobOptions) ToOrders() string {
|
|
return string(opts.OrderBy)
|
|
}
|
|
|
|
var _ db.FindOptionsOrder = FindRunJobOptions{}
|