diff --git a/.drone.yml b/.drone.yml index 5b17e8261cd5..99da2b368a98 100644 --- a/.drone.yml +++ b/.drone.yml @@ -904,6 +904,9 @@ steps: settings: auto_tag: false auto_tag_suffix: linux-amd64 + tags: + - ${DRONE_TAG##v}-linux-amd64 + - ${DRONE_TAG:1:4}-linux-amd64 repo: gitea/gitea build_args: - GOPROXY=https://goproxy.io @@ -922,6 +925,9 @@ steps: dockerfile: Dockerfile.rootless auto_tag: false auto_tag_suffix: linux-amd64-rootless + tags: + - ${DRONE_TAG##v}-linux-amd64-rootless + - ${DRONE_TAG:1:4}-linux-amd64-rootless repo: gitea/gitea build_args: - GOPROXY=https://goproxy.io @@ -1128,6 +1134,9 @@ steps: settings: auto_tag: false auto_tag_suffix: linux-arm64 + tags: + - ${DRONE_TAG##v}-linux-arm64 + - ${DRONE_TAG:1:4}-linux-arm64 repo: gitea/gitea build_args: - GOPROXY=https://goproxy.io @@ -1146,6 +1155,9 @@ steps: dockerfile: Dockerfile.rootless auto_tag: false auto_tag_suffix: linux-arm64-rootless + tags: + - ${DRONE_TAG##v}-linux-arm64-rootless + - ${DRONE_TAG:1:4}-linux-arm64-rootless repo: gitea/gitea build_args: - GOPROXY=https://goproxy.io diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d8d7acfef13..bfd7efdcd1db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,15 @@ This changelog goes through all the changes that have been made in each release without substantial changes to our git log; to see the highlights of what has been added to each release, please refer to the [blog](https://blog.gitea.io). -## [1.16.9](https://github.com/go-gitea/gitea/releases/tag/v1.16.9) - 2022-06-21 +## [1.16.9](https://github.com/go-gitea/gitea/releases/tag/v1.16.9) - 2022-07-12 +* SECURITY + * Add write check for creating Commit status (#20332) (#20334) + * Check for permission when fetching user controlled issues (#20133) (#20196) * BUGFIXES + * Hide notify mail setting ui if not enabled (#20138) (#20337) + * Add write check for creating Commit status (#20332) (#20334) + * Only show Followers that current user can access (#20220) (#20253) * Release page show all tags in compare dropdown (#20070) (#20071) * Fix permission check for delete tag (#19985) (#20001) * Only log non ErrNotExist errors in git.GetNote (#19884) (#19905) diff --git a/models/issue_milestone.go b/models/issue_milestone.go index 87d38c121f21..eaa7ff102607 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -116,6 +116,11 @@ func getMilestoneByRepoID(e db.Engine, repoID, id int64) (*Milestone, error) { return m, nil } +// HasMilestoneByRepoID returns if the milestone exists in the repository. +func HasMilestoneByRepoID(repoID, id int64) (bool, error) { + return db.GetEngine(db.DefaultContext).ID(id).Where("repo_id=?", repoID).Exist(new(Milestone)) +} + // GetMilestoneByRepoID returns the milestone in a repository. func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) { return getMilestoneByRepoID(db.GetEngine(db.DefaultContext), repoID, id) @@ -251,6 +256,17 @@ func changeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) err } func changeMilestoneAssign(ctx context.Context, doer *user_model.User, issue *Issue, oldMilestoneID int64) error { + // Only check if milestone exists if we don't remove it. + if issue.MilestoneID > 0 { + has, err := HasMilestoneByRepoID(issue.RepoID, issue.MilestoneID) + if err != nil { + return fmt.Errorf("HasMilestoneByRepoID: %v", err) + } + if !has { + return fmt.Errorf("HasMilestoneByRepoID: issue doesn't exist") + } + } + if err := updateIssueCols(ctx, issue, "milestone_id"); err != nil { return err } diff --git a/models/user/user.go b/models/user/user.go index 5c46db222f75..983ab6b90f87 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -318,37 +318,45 @@ func (u *User) GenerateEmailActivateCode(email string) string { } // GetUserFollowers returns range of user's followers. -func GetUserFollowers(u *User, listOptions db.ListOptions) ([]*User, error) { - sess := db.GetEngine(db.DefaultContext). +func GetUserFollowers(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) { + sess := db.GetEngine(ctx). + Select("`user`.*"). + Join("LEFT", "follow", "`user`.id=follow.user_id"). Where("follow.follow_id=?", u.ID). - Join("LEFT", "follow", "`user`.id=follow.user_id") + And(isUserVisibleToViewerCond(viewer)) if listOptions.Page != 0 { sess = db.SetSessionPagination(sess, &listOptions) users := make([]*User, 0, listOptions.PageSize) - return users, sess.Find(&users) + count, err := sess.FindAndCount(&users) + return users, count, err } users := make([]*User, 0, 8) - return users, sess.Find(&users) + count, err := sess.FindAndCount(&users) + return users, count, err } // GetUserFollowing returns range of user's following. -func GetUserFollowing(u *User, listOptions db.ListOptions) ([]*User, error) { +func GetUserFollowing(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) { sess := db.GetEngine(db.DefaultContext). + Select("`user`.*"). + Join("LEFT", "follow", "`user`.id=follow.follow_id"). Where("follow.user_id=?", u.ID). - Join("LEFT", "follow", "`user`.id=follow.follow_id") + And(isUserVisibleToViewerCond(viewer)) if listOptions.Page != 0 { sess = db.SetSessionPagination(sess, &listOptions) users := make([]*User, 0, listOptions.PageSize) - return users, sess.Find(&users) + count, err := sess.FindAndCount(&users) + return users, count, err } users := make([]*User, 0, 8) - return users, sess.Find(&users) + count, err := sess.FindAndCount(&users) + return users, count, err } // NewGitSig generates and returns the signature of given user. @@ -1233,3 +1241,36 @@ func GetAdminUser() (*User, error) { return &admin, nil } + +func isUserVisibleToViewerCond(viewer *User) builder.Cond { + if viewer != nil && viewer.IsAdmin { + return builder.NewCond() + } + + if viewer == nil || viewer.IsRestricted { + return builder.Eq{ + "`user`.visibility": structs.VisibleTypePublic, + } + } + + return builder.Neq{ + "`user`.visibility": structs.VisibleTypePrivate, + }.Or( + builder.In("`user`.id", + builder. + Select("`follow`.user_id"). + From("follow"). + Where(builder.Eq{"`follow`.follow_id": viewer.ID})), + builder.In("`user`.id", + builder. + Select("`team_user`.uid"). + From("team_user"). + Join("INNER", "`team_user` AS t2", "`team_user`.id = `t2`.id"). + Where(builder.Eq{"`t2`.uid": viewer.ID})), + builder.In("`user`.id", + builder. + Select("`team_user`.uid"). + From("team_user"). + Join("INNER", "`team_user` AS t2", "`team_user`.org_id = `t2`.org_id"). + Where(builder.Eq{"`t2`.uid": viewer.ID}))) +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 7a2347650a05..d06adeed0ffa 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -954,7 +954,7 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route { }, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(false)) m.Group("/statuses", func() { m.Combo("/{sha}").Get(repo.GetCommitStatuses). - Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus) + Post(reqToken(), reqRepoWriter(unit.TypeCode), bind(api.CreateStatusOption{}), repo.NewCommitStatus) }, reqRepoReader(unit.TypeCode)) m.Group("/commits", func() { m.Get("", repo.GetAllCommits) diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index ec5f0456476e..2d8550bb5f1b 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -883,7 +883,7 @@ func dismissReview(ctx *context.APIContext, msg string, isDismiss bool) { return } - _, err := pull_service.DismissReview(review.ID, msg, ctx.User, isDismiss) + _, err := pull_service.DismissReview(review.ID, ctx.Repo.Repository.ID, msg, ctx.User, isDismiss) if err != nil { ctx.Error(http.StatusInternalServerError, "pull_service.DismissReview", err) return diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go index 1eacb89db2bf..d648d50d6949 100644 --- a/routers/api/v1/user/follower.go +++ b/routers/api/v1/user/follower.go @@ -24,13 +24,13 @@ func responseAPIUsers(ctx *context.APIContext, users []*user_model.User) { } func listUserFollowers(ctx *context.APIContext, u *user_model.User) { - users, err := user_model.GetUserFollowers(u, utils.GetListOptions(ctx)) + users, count, err := user_model.GetUserFollowers(ctx, u, ctx.User, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err) return } - ctx.SetTotalCountHeader(int64(u.NumFollowers)) + ctx.SetTotalCountHeader(count) responseAPIUsers(ctx, users) } @@ -90,13 +90,13 @@ func ListFollowers(ctx *context.APIContext) { } func listUserFollowing(ctx *context.APIContext, u *user_model.User) { - users, err := user_model.GetUserFollowing(u, utils.GetListOptions(ctx)) + users, count, err := user_model.GetUserFollowing(ctx, u, ctx.User, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetUserFollowing", err) return } - ctx.SetTotalCountHeader(int64(u.NumFollowing)) + ctx.SetTotalCountHeader(count) responseAPIUsers(ctx, users) } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 8d6dce024323..abc046590da2 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -57,17 +57,15 @@ const ( issueTemplateTitleKey = "IssueTemplateTitle" ) -var ( - // IssueTemplateCandidates issue templates - IssueTemplateCandidates = []string{ - "ISSUE_TEMPLATE.md", - "issue_template.md", - ".gitea/ISSUE_TEMPLATE.md", - ".gitea/issue_template.md", - ".github/ISSUE_TEMPLATE.md", - ".github/issue_template.md", - } -) +// IssueTemplateCandidates issue templates +var IssueTemplateCandidates = []string{ + "ISSUE_TEMPLATE.md", + "issue_template.md", + ".gitea/ISSUE_TEMPLATE.md", + ".gitea/issue_template.md", + ".github/ISSUE_TEMPLATE.md", + ".github/issue_template.md", +} // MustAllowUserComment checks to make sure if an issue is locked. // If locked and user has permissions to write to the repository, @@ -253,7 +251,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti } } - var issueList = models.IssueList(issues) + issueList := models.IssueList(issues) approvalCounts, err := issueList.GetApprovalCounts() if err != nil { ctx.ServerError("ApprovalCounts", err) @@ -319,8 +317,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti assigneeID = 0 // Reset ID to prevent unexpected selection of assignee. } - ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] = - issue_service.GetRefEndNamesAndURLs(issues, ctx.Repo.RepoLink) + ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] = issue_service.GetRefEndNamesAndURLs(issues, ctx.Repo.RepoLink) ctx.Data["ApprovalCounts"] = func(issueID int64, typ string) int64 { counts, ok := approvalCounts[issueID] @@ -452,7 +449,6 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.R } func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { - var err error ctx.Data["OpenProjects"], _, err = models.GetProjects(models.ProjectSearchOptions{ @@ -806,7 +802,8 @@ func NewIssue(ctx *context.Context) { body := ctx.FormString("body") ctx.Data["BodyQuery"] = body - ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanRead(unit.TypeProjects) + isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects) + ctx.Data["IsProjectsEnabled"] = isProjectsEnabled ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "comment") @@ -822,7 +819,7 @@ func NewIssue(ctx *context.Context) { } projectID := ctx.FormInt64("project") - if projectID > 0 { + if projectID > 0 && isProjectsEnabled { project, err := models.GetProjectByID(projectID) if err != nil { log.Error("GetProjectByID: %d: %v", projectID, err) @@ -1027,6 +1024,12 @@ func NewIssuePost(ctx *context.Context) { } if projectID > 0 { + if !ctx.Repo.CanRead(unit.TypeProjects) { + // User must also be able to see the project. + ctx.Error(http.StatusBadRequest, "user hasn't permissions to read projects") + return + } + if err := models.ChangeProjectAssign(issue, ctx.User, projectID, ctx.FormString("action")); err != nil { ctx.ServerError("ChangeProjectAssign", err) return @@ -1723,6 +1726,11 @@ func getActionIssues(ctx *context.Context) []*models.Issue { issueUnitEnabled := ctx.Repo.CanRead(unit.TypeIssues) prUnitEnabled := ctx.Repo.CanRead(unit.TypePullRequests) for _, issue := range issues { + if issue.RepoID != ctx.Repo.Repository.ID { + ctx.NotFound("some issue's RepoID is incorrect", errors.New("some issue's RepoID is incorrect")) + return nil + } + if issue.IsPull && !prUnitEnabled || !issue.IsPull && !issueUnitEnabled { ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil) return nil @@ -2525,7 +2533,7 @@ func filterXRefComments(ctx *context.Context, issue *models.Issue) error { // GetIssueAttachments returns attachments for the issue func GetIssueAttachments(ctx *context.Context) { issue := GetActionIssue(ctx) - var attachments = make([]*api.Attachment, len(issue.Attachments)) + attachments := make([]*api.Attachment, len(issue.Attachments)) for i := 0; i < len(issue.Attachments); i++ { attachments[i] = convert.ToReleaseAttachment(issue.Attachments[i]) } @@ -2539,7 +2547,7 @@ func GetCommentAttachments(ctx *context.Context) { ctx.NotFoundOrServerError("GetCommentByID", models.IsErrCommentNotExist, err) return } - var attachments = make([]*api.Attachment, 0) + attachments := make([]*api.Attachment, 0) if comment.Type == models.CommentTypeComment { if err := comment.LoadAttachments(); err != nil { ctx.ServerError("LoadAttachments", err) @@ -2684,7 +2692,7 @@ func handleTeamMentions(ctx *context.Context) { var isAdmin bool var err error var teams []*models.Team - var org = models.OrgFromUser(ctx.Repo.Owner) + org := models.OrgFromUser(ctx.Repo.Owner) // Admin has super access. if ctx.User.IsAdmin { isAdmin = true diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index de6550aacc93..5ae34cc9c646 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -5,6 +5,7 @@ package repo import ( + "errors" "fmt" "net/http" "net/url" @@ -532,7 +533,6 @@ func EditProjectBoard(ctx *context.Context) { // SetDefaultProjectBoard set default board for uncategorized issues/pulls func SetDefaultProjectBoard(ctx *context.Context) { - project, board := checkProjectBoardChangePermissions(ctx) if ctx.Written() { return @@ -632,10 +632,17 @@ func MoveIssues(ctx *context.Context) { } if len(movedIssues) != len(form.Issues) { - ctx.ServerError("IssuesNotFound", err) + ctx.ServerError("some issues do not exist", errors.New("some issues do not exist")) return } + for _, issue := range movedIssues { + if issue.RepoID != project.RepoID { + ctx.ServerError("Some issue's repoID is not equal to project's repoID", errors.New("Some issue's repoID is not equal to project's repoID")) + return + } + } + if err = models.MoveIssuesOnProjectBoard(board, sortedIssueIDs, project.ID); err != nil { ctx.ServerError("MoveIssuesOnProjectBoard", err) return diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go index 257aa737f652..383e2d6cabc8 100644 --- a/routers/web/repo/pull_review.go +++ b/routers/web/repo/pull_review.go @@ -5,6 +5,7 @@ package repo import ( + "errors" "fmt" "net/http" @@ -116,6 +117,11 @@ func UpdateResolveConversation(ctx *context.Context) { return } + if comment.Issue.RepoID != ctx.Repo.Repository.ID { + ctx.NotFound("comment's repoID is incorrect", errors.New("comment's repoID is incorrect")) + return + } + var permResult bool if permResult, err = models.CanMarkConversation(comment.Issue, ctx.User); err != nil { ctx.ServerError("CanMarkConversation", err) @@ -234,7 +240,7 @@ func SubmitReview(ctx *context.Context) { // DismissReview dismissing stale review by repo admin func DismissReview(ctx *context.Context) { form := web.GetForm(ctx).(*forms.DismissReviewForm) - comm, err := pull_service.DismissReview(form.ReviewID, form.Message, ctx.User, true) + comm, err := pull_service.DismissReview(form.ReviewID, ctx.Repo.Repository.ID, form.Message, ctx.User, true) if err != nil { ctx.ServerError("pull_service.DismissReview", err) return diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 1638362f456a..1d6c07ecb652 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -234,7 +234,7 @@ func Profile(ctx *context.Context) { ctx.Data["Keyword"] = keyword switch tab { case "followers": - items, err := user_model.GetUserFollowers(ctxUser, db.ListOptions{ + items, count, err := user_model.GetUserFollowers(ctx, ctxUser, ctx.User, db.ListOptions{ PageSize: setting.UI.User.RepoPagingNum, Page: page, }) @@ -244,9 +244,9 @@ func Profile(ctx *context.Context) { } ctx.Data["Cards"] = items - total = ctxUser.NumFollowers + total = int(count) case "following": - items, err := user_model.GetUserFollowing(ctxUser, db.ListOptions{ + items, count, err := user_model.GetUserFollowing(ctx, ctxUser, ctx.User, db.ListOptions{ PageSize: setting.UI.User.RepoPagingNum, Page: page, }) @@ -256,9 +256,10 @@ func Profile(ctx *context.Context) { } ctx.Data["Cards"] = items - total = ctxUser.NumFollowing + total = int(count) case "activity": - ctx.Data["Feeds"] = feed.RetrieveFeeds(ctx, models.GetFeedsOptions{RequestedUser: ctxUser, + ctx.Data["Feeds"] = feed.RetrieveFeeds(ctx, models.GetFeedsOptions{ + RequestedUser: ctxUser, Actor: ctx.User, IncludePrivate: showPrivate, OnlyPerformedBy: true, diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index 97c5757830a6..56cc5609d81d 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -35,6 +35,7 @@ func Account(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsAccount"] = true ctx.Data["Email"] = ctx.User.Email + ctx.Data["EnableNotifyMail"] = setting.Service.EnableNotifyMail loadAccountData(ctx) diff --git a/services/pull/review.go b/services/pull/review.go index 42292ac209c4..b80c55aeae86 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -271,7 +271,7 @@ func SubmitReview(doer *user_model.User, gitRepo *git.Repository, issue *models. } // DismissReview dismissing stale review by repo admin -func DismissReview(reviewID int64, message string, doer *user_model.User, isDismiss bool) (comment *models.Comment, err error) { +func DismissReview(reviewID, repoID int64, message string, doer *user_model.User, isDismiss bool) (comment *models.Comment, err error) { review, err := models.GetReviewByID(reviewID) if err != nil { return @@ -281,6 +281,16 @@ func DismissReview(reviewID int64, message string, doer *user_model.User, isDism return nil, fmt.Errorf("not need to dismiss this review because it's type is not Approve or change request") } + // load data for notify + if err = review.LoadAttributes(); err != nil { + return nil, err + } + + // Check if the review's repoID is the one we're currently expecting. + if review.Issue.RepoID != repoID { + return nil, fmt.Errorf("reviews's repository is not the same as the one we expect") + } + if err = models.DismissReview(review, isDismiss); err != nil { return } @@ -289,10 +299,6 @@ func DismissReview(reviewID int64, message string, doer *user_model.User, isDism return nil, nil } - // load data for notify - if err = review.LoadAttributes(); err != nil { - return - } if err = review.Issue.LoadPullRequest(); err != nil { return } diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl index 212aac7a435f..f7e1847c9121 100644 --- a/templates/user/settings/account.tmpl +++ b/templates/user/settings/account.tmpl @@ -43,6 +43,7 @@
+ {{if $.EnableNotifyMail}}
{{.i18n.Tr "settings.email_desc"}} @@ -70,6 +71,7 @@
+ {{end}} {{range .Emails}}
{{if not .IsPrimary}}