mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-08 22:13:26 +09:00
Merge remote-tracking branch 'origin/main' into various-sec-fixes
# Conflicts: # routers/api/v1/api.go
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
actions_model "gitea.dev/models/actions"
|
||||
@@ -104,8 +105,8 @@ func (b *Basic) VerifyAuthToken(req *http.Request, w http.ResponseWriter, store
|
||||
store.GetData()["IsApiToken"] = true
|
||||
store.GetData()["ApiTokenScope"] = token.Scope
|
||||
return u, nil
|
||||
} else if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) {
|
||||
log.Error("GetAccessTokenBySha: %v", err)
|
||||
} else if !errors.Is(err, util.ErrNotExist) {
|
||||
log.Error("GetAccessTokenBySHA: %v", err)
|
||||
}
|
||||
|
||||
// check task token
|
||||
|
||||
@@ -128,7 +128,7 @@ func (o *OAuth2) userFromToken(ctx context.Context, tokenSHA string, store DataS
|
||||
}
|
||||
t, err := auth_model.GetAccessTokenBySHA(ctx, tokenSHA)
|
||||
if err != nil {
|
||||
if auth_model.IsErrAccessTokenNotExist(err) {
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
// check task token
|
||||
if task, err := actions_model.GetRunningTaskByToken(ctx, tokenSHA); err == nil {
|
||||
log.Trace("Basic Authorization: Valid AccessToken for task[%d]", task.ID)
|
||||
|
||||
@@ -78,8 +78,3 @@ func (b *Base) FormOptionalBool(key string) optional.Option[bool] {
|
||||
v = v || strings.EqualFold(s, "on")
|
||||
return optional.Some(v)
|
||||
}
|
||||
|
||||
func (b *Base) SetFormString(key, value string) {
|
||||
_ = b.Req.FormValue(key) // force parse form
|
||||
b.Req.Form.Set(key, value)
|
||||
}
|
||||
|
||||
+33
-15
@@ -179,20 +179,28 @@ func OrgAssignment(orgAssignmentOpts OrgAssignmentOptions) func(ctx *Context) {
|
||||
ctx.ServerError("UserShouldSeeAllOrgTeams", err)
|
||||
return
|
||||
}
|
||||
if ctx.Org.IsMember {
|
||||
if shouldSeeAllTeams {
|
||||
ctx.Org.Teams, err = org.LoadTeams(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("LoadTeams", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ctx.Org.Teams, err = org.GetUserTeams(ctx, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserTeams", err)
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case shouldSeeAllTeams:
|
||||
ctx.Org.Teams, err = org.LoadTeams(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("LoadTeams", err)
|
||||
return
|
||||
}
|
||||
case ctx.IsSigned:
|
||||
// Signed-in non-members still see teams whose visibility tier
|
||||
// includes them (public for any signed-in user, plus limited
|
||||
// for org members), and any team they directly belong to.
|
||||
ctx.Org.Teams, _, err = organization.SearchTeam(ctx, &organization.SearchTeamOptions{
|
||||
OrgID: org.ID,
|
||||
UserID: ctx.Doer.ID,
|
||||
IncludeVisibilities: organization.VisibleTeamVisibilitiesFor(ctx.Org.IsMember, true),
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("SearchTeam", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if ctx.Org.IsMember {
|
||||
ctx.Data["NumTeams"] = len(ctx.Org.Teams)
|
||||
}
|
||||
|
||||
@@ -203,7 +211,6 @@ func OrgAssignment(orgAssignmentOpts OrgAssignmentOptions) func(ctx *Context) {
|
||||
if strings.EqualFold(team.LowerName, teamName) {
|
||||
teamExists = true
|
||||
ctx.Org.Team = team
|
||||
ctx.Org.IsTeamMember = true
|
||||
ctx.Data["Team"] = ctx.Org.Team
|
||||
break
|
||||
}
|
||||
@@ -214,13 +221,24 @@ func OrgAssignment(orgAssignmentOpts OrgAssignmentOptions) func(ctx *Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Membership in a visible team is not implied by its presence in
|
||||
// ctx.Org.Teams; admins/org owners keep the privileged flag set
|
||||
// earlier in this function.
|
||||
if !ctx.Org.IsOwner {
|
||||
ctx.Org.IsTeamMember, err = organization.IsTeamMember(ctx, org.ID, ctx.Org.Team.ID, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("IsTeamMember", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.Data["IsTeamMember"] = ctx.Org.IsTeamMember
|
||||
if opts.RequireTeamMember && !ctx.Org.IsTeamMember {
|
||||
ctx.NotFound(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Org.IsTeamAdmin = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.HasAdminAccess()
|
||||
isTeamOwnerOrAdmin := ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.HasAdminAccess()
|
||||
ctx.Org.IsTeamAdmin = ctx.Org.IsOwner || (ctx.Org.IsTeamMember && isTeamOwnerOrAdmin)
|
||||
ctx.Data["IsTeamAdmin"] = ctx.Org.IsTeamAdmin
|
||||
if opts.RequireTeamAdmin && !ctx.Org.IsTeamAdmin {
|
||||
ctx.NotFound(err)
|
||||
|
||||
@@ -836,6 +836,7 @@ func ToTeams(ctx context.Context, teams []*organization.Team, loadOrgs bool) ([]
|
||||
Permission: api.AccessLevelName(t.AccessMode.ToString()),
|
||||
Units: t.GetUnitNames(),
|
||||
UnitsMap: t.GetUnitsMap(),
|
||||
Visibility: api.TeamVisibility(t.Visibility.String()),
|
||||
}
|
||||
|
||||
if loadOrgs {
|
||||
|
||||
@@ -70,6 +70,7 @@ type CreateTeamForm struct {
|
||||
Permission string
|
||||
RepoAccess string
|
||||
CanCreateOrgRepo bool
|
||||
Visibility string `binding:"OmitEmpty;In(public,limited,private)"`
|
||||
}
|
||||
|
||||
// Validate validates the fields
|
||||
|
||||
@@ -110,7 +110,7 @@ func UpdateTeam(ctx context.Context, t *organization.Team, authChanged, includeA
|
||||
|
||||
sess := db.GetEngine(ctx)
|
||||
if _, err = sess.ID(t.ID).Cols("name", "lower_name", "description",
|
||||
"can_create_org_repo", "authorize", "includes_all_repositories").Update(t); err != nil {
|
||||
"can_create_org_repo", "authorize", "includes_all_repositories", "visibility").Update(t); err != nil {
|
||||
return fmt.Errorf("update: %w", err)
|
||||
}
|
||||
|
||||
|
||||
+78
-65
@@ -4,14 +4,15 @@
|
||||
package pull
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
@@ -767,8 +768,6 @@ func CloseRepoBranchesPulls(ctx context.Context, doer *user_model.User, repo *re
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
var commitMessageTrailersPattern = regexp.MustCompile(`(?:^|\n\n)(?:[\w-]+[ \t]*:[^\n]+\n*(?:[ \t]+[^\n]+\n*)*)+$`)
|
||||
|
||||
// GetSquashMergeCommitMessages returns the commit messages between head and merge base (if there is one)
|
||||
func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequest) string {
|
||||
if err := pr.LoadIssue(ctx); err != nil {
|
||||
@@ -819,54 +818,44 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ
|
||||
return ""
|
||||
}
|
||||
|
||||
posterSig := pr.Issue.Poster.NewGitSig().String()
|
||||
mergeMessage := strings.TrimSpace(pr.Issue.Content) // use PR's title and description as squash commit message
|
||||
if setting.Repository.PullRequest.PopulateSquashCommentWithCommitMessages {
|
||||
mergeMessage = formatSquashMergeCommitMessages(limitedCommits) // use PR's commit messages as squash commit message
|
||||
}
|
||||
coAuthors := collectSquashMergeCommitCoAuthors(ctx, gitRepo, pr, headCommitRef, mergeBaseRef, limit, limitedCommits)
|
||||
return buildSquashMergeCommitMessages(mergeMessage, coAuthors)
|
||||
}
|
||||
|
||||
uniqueAuthors := make(container.Set[string])
|
||||
authors := make([]string, 0, len(limitedCommits))
|
||||
stringBuilder := strings.Builder{}
|
||||
|
||||
if !setting.Repository.PullRequest.PopulateSquashCommentWithCommitMessages {
|
||||
// use PR's title and description as squash commit message
|
||||
message := strings.TrimSpace(pr.Issue.Content)
|
||||
stringBuilder.WriteString(message)
|
||||
if stringBuilder.Len() > 0 {
|
||||
stringBuilder.WriteRune('\n')
|
||||
if !commitMessageTrailersPattern.MatchString(message) {
|
||||
// TODO: this trailer check doesn't work with the separator line added below for the co-authors
|
||||
stringBuilder.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// use PR's commit messages as squash commit message
|
||||
// commits list is in reverse chronological order
|
||||
maxMsgSize := setting.Repository.PullRequest.DefaultMergeMessageSize
|
||||
for _, commit := range slices.Backward(limitedCommits) {
|
||||
msg := strings.TrimSpace(commit.MessageUTF8())
|
||||
if msg == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// This format follows GitHub's squash commit message style,
|
||||
// even if there are other "* " in the commit message body, they are written as-is.
|
||||
// Maybe, ideally, we should indent those lines too.
|
||||
_, _ = fmt.Fprintf(&stringBuilder, "* %s\n\n", msg)
|
||||
if maxMsgSize > 0 && stringBuilder.Len() >= maxMsgSize {
|
||||
tmp := stringBuilder.String()
|
||||
wasValidUtf8 := utf8.ValidString(tmp)
|
||||
tmp = tmp[:maxMsgSize] + "..."
|
||||
if wasValidUtf8 {
|
||||
// If the message was valid UTF-8 before truncation, ensure it remains valid after truncation
|
||||
// For non-utf8 messages, we can't do much about it, end users should use utf-8 as much as possible
|
||||
tmp = strings.ToValidUTF8(tmp, "")
|
||||
}
|
||||
stringBuilder.Reset()
|
||||
stringBuilder.WriteString(tmp)
|
||||
break
|
||||
}
|
||||
}
|
||||
func buildSquashMergeCommitMessages(mergeMessage string, coAuthors []string) string {
|
||||
if len(coAuthors) == 0 {
|
||||
return mergeMessage
|
||||
}
|
||||
|
||||
// collect co-authors
|
||||
msgContent, msgSep, msgTrailer := git.CommitMessageSplitTrailer(mergeMessage)
|
||||
if (msgSep == "" || msgSep == "\n\n") && msgTrailer == "" {
|
||||
msgContent = strings.TrimRightFunc(msgContent, unicode.IsSpace)
|
||||
msgSep = "\n\n---------\n\n"
|
||||
}
|
||||
var sb strings.Builder
|
||||
sb.WriteString(msgContent)
|
||||
sb.WriteString(msgSep)
|
||||
if msgTrailer = strings.TrimSpace(msgTrailer); msgTrailer != "" {
|
||||
sb.WriteString(msgTrailer)
|
||||
sb.WriteRune('\n')
|
||||
}
|
||||
for _, author := range coAuthors {
|
||||
sb.WriteString(git.CoAuthoredByTrailer + ": ")
|
||||
sb.WriteString(author)
|
||||
sb.WriteRune('\n')
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func collectSquashMergeCommitCoAuthors(ctx context.Context, gitRepo *git.Repository, pr *issues_model.PullRequest, headCommitRef, mergeBaseRef git.RefName, limitFirst int, limitedCommits []*git.Commit) []string {
|
||||
posterSig := pr.Issue.Poster.NewGitSig().String()
|
||||
uniqueAuthors := make(container.Set[string])
|
||||
authors := make([]string, 0, len(limitedCommits))
|
||||
|
||||
for _, commit := range limitedCommits {
|
||||
authorString := commit.Author.String()
|
||||
if uniqueAuthors.Add(authorString) && authorString != posterSig {
|
||||
@@ -880,14 +869,14 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ
|
||||
}
|
||||
|
||||
// collect the remaining authors
|
||||
if limit >= 0 && setting.Repository.PullRequest.DefaultMergeMessageAllAuthors {
|
||||
skip := limit
|
||||
limit = 30
|
||||
if limitFirst >= 0 && setting.Repository.PullRequest.DefaultMergeMessageAllAuthors {
|
||||
skip := limitFirst
|
||||
batchLimit := 30
|
||||
for {
|
||||
commits, err := gitRepo.CommitsBetween(headCommitRef, mergeBaseRef, limit, skip)
|
||||
commits, err := gitRepo.CommitsBetween(headCommitRef, mergeBaseRef, batchLimit, skip)
|
||||
if err != nil {
|
||||
log.Error("Unable to get commits between: %s %s Error: %v", pr.HeadBranch, pr.MergeBase, err)
|
||||
return ""
|
||||
return authors
|
||||
}
|
||||
if len(commits) == 0 {
|
||||
break
|
||||
@@ -901,22 +890,46 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ
|
||||
}
|
||||
}
|
||||
}
|
||||
skip += limit
|
||||
skip += batchLimit
|
||||
}
|
||||
}
|
||||
return authors
|
||||
}
|
||||
|
||||
func formatSquashMergeCommitMessages(commits []*git.Commit) string {
|
||||
maxMsgSize := setting.Repository.PullRequest.DefaultMergeMessageSize
|
||||
sb := &bytes.Buffer{}
|
||||
// commits list is in reverse chronological order
|
||||
for _, commit := range slices.Backward(commits) {
|
||||
msg := strings.TrimSpace(commit.MessageUTF8())
|
||||
if msg == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// This format follows GitHub's squash commit message style,
|
||||
// even if there are other "* " in the commit message body, they are written as-is.
|
||||
// Maybe, ideally, we should indent those lines too.
|
||||
_, _ = fmt.Fprintf(sb, "* %s\n\n", msg)
|
||||
if maxMsgSize > 0 && sb.Len() >= maxMsgSize {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if stringBuilder.Len() > 0 && len(authors) > 0 {
|
||||
// TODO: this separator line doesn't work with the trailer check (commitMessageTrailersPattern) above
|
||||
stringBuilder.WriteString("---------\n\n")
|
||||
buf := bytes.TrimSpace(sb.Bytes())
|
||||
if maxMsgSize > 0 && len(buf) > maxMsgSize {
|
||||
buf = buf[:maxMsgSize]
|
||||
for {
|
||||
r, sz := utf8.DecodeLastRune(buf)
|
||||
if r == utf8.RuneError && sz == 1 {
|
||||
buf = buf[:len(buf)-1]
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
buf = append(buf, '.', '.', '.')
|
||||
}
|
||||
|
||||
for _, author := range authors {
|
||||
stringBuilder.WriteString(git.CoAuthoredByTrailer + ": ")
|
||||
stringBuilder.WriteString(author)
|
||||
stringBuilder.WriteRune('\n')
|
||||
}
|
||||
|
||||
return stringBuilder.String()
|
||||
buf = append(buf, '\n', '\n')
|
||||
return util.UnsafeBytesToString(buf)
|
||||
}
|
||||
|
||||
// GetIssuesAllCommitStatus returns a map of issue ID to a list of all statuses for the most recent commit as well as a map of issue ID to only the commit's latest status
|
||||
|
||||
+44
-15
@@ -11,28 +11,33 @@ import (
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unit"
|
||||
"gitea.dev/models/unittest"
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/gitrepo"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TODO TestPullRequest_PushToBaseRepo
|
||||
|
||||
func TestPullRequest_CommitMessageTrailersPattern(t *testing.T) {
|
||||
// Not a valid trailer section
|
||||
assert.False(t, commitMessageTrailersPattern.MatchString(""))
|
||||
assert.False(t, commitMessageTrailersPattern.MatchString("No trailer."))
|
||||
assert.False(t, commitMessageTrailersPattern.MatchString("Signed-off-by: Bob <bob@example.com>\nNot a trailer due to following text."))
|
||||
assert.False(t, commitMessageTrailersPattern.MatchString("Message body not correctly separated from trailer section by empty line.\nSigned-off-by: Bob <bob@example.com>"))
|
||||
// Valid trailer section
|
||||
assert.True(t, commitMessageTrailersPattern.MatchString("Signed-off-by: Bob <bob@example.com>"))
|
||||
assert.True(t, commitMessageTrailersPattern.MatchString("Signed-off-by: Bob <bob@example.com>\nOther-Trailer: Value"))
|
||||
assert.True(t, commitMessageTrailersPattern.MatchString("Message body correctly separated from trailer section by empty line.\n\nSigned-off-by: Bob <bob@example.com>"))
|
||||
assert.True(t, commitMessageTrailersPattern.MatchString("Multiple trailers.\n\nSigned-off-by: Bob <bob@example.com>\nOther-Trailer: Value"))
|
||||
assert.True(t, commitMessageTrailersPattern.MatchString("Newline after trailer section.\n\nSigned-off-by: Bob <bob@example.com>\n"))
|
||||
assert.True(t, commitMessageTrailersPattern.MatchString("No space after colon is accepted.\n\nSigned-off-by:Bob <bob@example.com>"))
|
||||
assert.True(t, commitMessageTrailersPattern.MatchString("Additional whitespace is accepted.\n\nSigned-off-by \t : \tBob <bob@example.com> "))
|
||||
assert.True(t, commitMessageTrailersPattern.MatchString("Folded value.\n\nFolded-trailer: This is\n a folded\n trailer value\nOther-Trailer: Value"))
|
||||
func TestPullRequest_FormatSquashMergeCommitMessages(t *testing.T) {
|
||||
oldest := &git.Commit{CommitMessage: git.CommitMessage{MessageRaw: "commit msg 1"}}
|
||||
newest := &git.Commit{CommitMessage: git.CommitMessage{MessageRaw: "commit msg 2\n\nCommit description."}}
|
||||
|
||||
defer test.MockVariableValue(&setting.Repository.PullRequest.DefaultMergeMessageSize, 0)()
|
||||
|
||||
assert.Equal(t, "* commit msg 1\n\n* commit msg 2\n\nCommit description.\n\n", formatSquashMergeCommitMessages([]*git.Commit{newest, oldest}))
|
||||
|
||||
utf8Msg := &git.Commit{CommitMessage: git.CommitMessage{MessageRaw: "🌞"}}
|
||||
setting.Repository.PullRequest.DefaultMergeMessageSize = 3
|
||||
assert.Equal(t, "* ...\n\n", formatSquashMergeCommitMessages([]*git.Commit{utf8Msg}))
|
||||
setting.Repository.PullRequest.DefaultMergeMessageSize = 4
|
||||
assert.Equal(t, "* ...\n\n", formatSquashMergeCommitMessages([]*git.Commit{utf8Msg}))
|
||||
setting.Repository.PullRequest.DefaultMergeMessageSize = 5
|
||||
assert.Equal(t, "* ...\n\n", formatSquashMergeCommitMessages([]*git.Commit{utf8Msg}))
|
||||
setting.Repository.PullRequest.DefaultMergeMessageSize = 6
|
||||
assert.Equal(t, "* 🌞\n\n", formatSquashMergeCommitMessages([]*git.Commit{utf8Msg}))
|
||||
}
|
||||
|
||||
func TestPullRequest_GetDefaultMergeMessage_InternalTracker(t *testing.T) {
|
||||
@@ -88,3 +93,27 @@ func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) {
|
||||
|
||||
assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo2:branch2 into master", mergeMessage)
|
||||
}
|
||||
|
||||
func TestBuildSquashMergeCommitMessages(t *testing.T) {
|
||||
cases := []struct {
|
||||
msg string
|
||||
coAuthors []string
|
||||
expected string
|
||||
}{
|
||||
{"title", nil, "title"},
|
||||
{"title", []string{"the-user"}, "title\n\n---------\n\nCo-authored-by: the-user\n"},
|
||||
{"title\n\n", []string{"the-user"}, "title\n\n---------\n\nCo-authored-by: the-user\n"},
|
||||
{"title\n\nKey: val", []string{"the-user"}, "title\n\nKey: val\nCo-authored-by: the-user\n"},
|
||||
{"title\n\n----\nKey: val", []string{"the-user"}, "title\n\n----\nKey: val\nCo-authored-by: the-user\n"},
|
||||
{"title\n\n----\nKey: val\n\n", []string{"the-user"}, "title\n\n----\nKey: val\nCo-authored-by: the-user\n"},
|
||||
|
||||
{"title\n\nbody", nil, "title\n\nbody"},
|
||||
{"title\n\nbody", []string{"the-user"}, "title\n\nbody\n\n---------\n\nCo-authored-by: the-user\n"},
|
||||
{"title\n\nbody\n\nKey: val", []string{"the-user"}, "title\n\nbody\n\nKey: val\nCo-authored-by: the-user\n"},
|
||||
{"title\n\nbody\n\n----\nKey: val", []string{"the-user"}, "title\n\nbody\n\n----\nKey: val\nCo-authored-by: the-user\n"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
msg := buildSquashMergeCommitMessages(c.msg, c.coAuthors)
|
||||
assert.Equal(t, c.expected, msg, "msg: %s", c.msg)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user