Check for permission when fetching user controlled issues (#20133) (#20196)

* Check if project has the same repository id with issue when assign project to issue

* Check if issue's repository id match project's repository id

* Add more permission checking

* Remove invalid argument

* Fix errors

* Add generic check

* Remove duplicated check

* Return error + add check for new issues

* Apply suggestions from code review

Co-authored-by: Gusted <williamzijl7@hotmail.com>
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
Gusted 2022-07-01 17:39:10 +02:00 committed by GitHub
parent df0b330af7
commit 6162fb0a19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 83 additions and 29 deletions

View File

@ -116,6 +116,11 @@ func getMilestoneByRepoID(e db.Engine, repoID, id int64) (*Milestone, error) {
return m, nil 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. // GetMilestoneByRepoID returns the milestone in a repository.
func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) { func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) {
return getMilestoneByRepoID(db.GetEngine(db.DefaultContext), repoID, id) 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 { 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 { if err := updateIssueCols(ctx, issue, "milestone_id"); err != nil {
return err return err
} }

View File

@ -150,6 +150,17 @@ func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.U
e := db.GetEngine(ctx) e := db.GetEngine(ctx)
oldProjectID := issue.projectID(e) oldProjectID := issue.projectID(e)
// Only check if we add a new project and not remove it.
if newProjectID > 0 {
newProject, err := GetProjectByID(newProjectID)
if err != nil {
return err
}
if newProject.RepoID != issue.RepoID {
return fmt.Errorf("issue's repository is not the same as project's repository")
}
}
if _, err := e.Where("project_issue.issue_id=?", issue.ID).Delete(&ProjectIssue{}); err != nil { if _, err := e.Where("project_issue.issue_id=?", issue.ID).Delete(&ProjectIssue{}); err != nil {
return err return err
} }

View File

@ -883,7 +883,7 @@ func dismissReview(ctx *context.APIContext, msg string, isDismiss bool) {
return 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 { if err != nil {
ctx.Error(http.StatusInternalServerError, "pull_service.DismissReview", err) ctx.Error(http.StatusInternalServerError, "pull_service.DismissReview", err)
return return

View File

@ -57,9 +57,8 @@ const (
issueTemplateTitleKey = "IssueTemplateTitle" issueTemplateTitleKey = "IssueTemplateTitle"
) )
var (
// IssueTemplateCandidates issue templates // IssueTemplateCandidates issue templates
IssueTemplateCandidates = []string{ var IssueTemplateCandidates = []string{
"ISSUE_TEMPLATE.md", "ISSUE_TEMPLATE.md",
"issue_template.md", "issue_template.md",
".gitea/ISSUE_TEMPLATE.md", ".gitea/ISSUE_TEMPLATE.md",
@ -67,7 +66,6 @@ var (
".github/ISSUE_TEMPLATE.md", ".github/ISSUE_TEMPLATE.md",
".github/issue_template.md", ".github/issue_template.md",
} }
)
// MustAllowUserComment checks to make sure if an issue is locked. // MustAllowUserComment checks to make sure if an issue is locked.
// If locked and user has permissions to write to the repository, // If locked and user has permissions to write to the repository,
@ -245,7 +243,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
} }
} }
var issueList = models.IssueList(issues) issueList := models.IssueList(issues)
approvalCounts, err := issueList.GetApprovalCounts() approvalCounts, err := issueList.GetApprovalCounts()
if err != nil { if err != nil {
ctx.ServerError("ApprovalCounts", err) ctx.ServerError("ApprovalCounts", err)
@ -311,8 +309,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
assigneeID = 0 // Reset ID to prevent unexpected selection of assignee. assigneeID = 0 // Reset ID to prevent unexpected selection of assignee.
} }
ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] = ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] = issue_service.GetRefEndNamesAndURLs(issues, ctx.Repo.RepoLink)
issue_service.GetRefEndNamesAndURLs(issues, ctx.Repo.RepoLink)
ctx.Data["ApprovalCounts"] = func(issueID int64, typ string) int64 { ctx.Data["ApprovalCounts"] = func(issueID int64, typ string) int64 {
counts, ok := approvalCounts[issueID] counts, ok := approvalCounts[issueID]
@ -442,7 +439,6 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.R
} }
func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
var err error var err error
ctx.Data["OpenProjects"], _, err = models.GetProjects(models.ProjectSearchOptions{ ctx.Data["OpenProjects"], _, err = models.GetProjects(models.ProjectSearchOptions{
@ -796,7 +792,8 @@ func NewIssue(ctx *context.Context) {
body := ctx.FormString("body") body := ctx.FormString("body")
ctx.Data["BodyQuery"] = 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 ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "comment") upload.AddUploadContext(ctx, "comment")
@ -812,7 +809,7 @@ func NewIssue(ctx *context.Context) {
} }
projectID := ctx.FormInt64("project") projectID := ctx.FormInt64("project")
if projectID > 0 { if projectID > 0 && isProjectsEnabled {
project, err := models.GetProjectByID(projectID) project, err := models.GetProjectByID(projectID)
if err != nil { if err != nil {
log.Error("GetProjectByID: %d: %v", projectID, err) log.Error("GetProjectByID: %d: %v", projectID, err)
@ -1017,6 +1014,12 @@ func NewIssuePost(ctx *context.Context) {
} }
if projectID > 0 { 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); err != nil { if err := models.ChangeProjectAssign(issue, ctx.User, projectID); err != nil {
ctx.ServerError("ChangeProjectAssign", err) ctx.ServerError("ChangeProjectAssign", err)
return return
@ -1713,6 +1716,11 @@ func getActionIssues(ctx *context.Context) []*models.Issue {
issueUnitEnabled := ctx.Repo.CanRead(unit.TypeIssues) issueUnitEnabled := ctx.Repo.CanRead(unit.TypeIssues)
prUnitEnabled := ctx.Repo.CanRead(unit.TypePullRequests) prUnitEnabled := ctx.Repo.CanRead(unit.TypePullRequests)
for _, issue := range issues { 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 { if issue.IsPull && !prUnitEnabled || !issue.IsPull && !issueUnitEnabled {
ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil) ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil)
return nil return nil
@ -2515,7 +2523,7 @@ func filterXRefComments(ctx *context.Context, issue *models.Issue) error {
// GetIssueAttachments returns attachments for the issue // GetIssueAttachments returns attachments for the issue
func GetIssueAttachments(ctx *context.Context) { func GetIssueAttachments(ctx *context.Context) {
issue := GetActionIssue(ctx) 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++ { for i := 0; i < len(issue.Attachments); i++ {
attachments[i] = convert.ToReleaseAttachment(issue.Attachments[i]) attachments[i] = convert.ToReleaseAttachment(issue.Attachments[i])
} }
@ -2529,7 +2537,7 @@ func GetCommentAttachments(ctx *context.Context) {
ctx.NotFoundOrServerError("GetCommentByID", models.IsErrCommentNotExist, err) ctx.NotFoundOrServerError("GetCommentByID", models.IsErrCommentNotExist, err)
return return
} }
var attachments = make([]*api.Attachment, 0) attachments := make([]*api.Attachment, 0)
if comment.Type == models.CommentTypeComment { if comment.Type == models.CommentTypeComment {
if err := comment.LoadAttachments(); err != nil { if err := comment.LoadAttachments(); err != nil {
ctx.ServerError("LoadAttachments", err) ctx.ServerError("LoadAttachments", err)
@ -2674,7 +2682,7 @@ func handleTeamMentions(ctx *context.Context) {
var isAdmin bool var isAdmin bool
var err error var err error
var teams []*models.Team var teams []*models.Team
var org = models.OrgFromUser(ctx.Repo.Owner) org := models.OrgFromUser(ctx.Repo.Owner)
// Admin has super access. // Admin has super access.
if ctx.User.IsAdmin { if ctx.User.IsAdmin {
isAdmin = true isAdmin = true

View File

@ -5,6 +5,7 @@
package repo package repo
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
@ -531,7 +532,6 @@ func EditProjectBoard(ctx *context.Context) {
// SetDefaultProjectBoard set default board for uncategorized issues/pulls // SetDefaultProjectBoard set default board for uncategorized issues/pulls
func SetDefaultProjectBoard(ctx *context.Context) { func SetDefaultProjectBoard(ctx *context.Context) {
project, board := checkProjectBoardChangePermissions(ctx) project, board := checkProjectBoardChangePermissions(ctx)
if ctx.Written() { if ctx.Written() {
return return
@ -631,10 +631,17 @@ func MoveIssues(ctx *context.Context) {
} }
if len(movedIssues) != len(form.Issues) { 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 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); err != nil { if err = models.MoveIssuesOnProjectBoard(board, sortedIssueIDs); err != nil {
ctx.ServerError("MoveIssuesOnProjectBoard", err) ctx.ServerError("MoveIssuesOnProjectBoard", err)
return return

View File

@ -5,6 +5,7 @@
package repo package repo
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
@ -116,6 +117,11 @@ func UpdateResolveConversation(ctx *context.Context) {
return 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 var permResult bool
if permResult, err = models.CanMarkConversation(comment.Issue, ctx.User); err != nil { if permResult, err = models.CanMarkConversation(comment.Issue, ctx.User); err != nil {
ctx.ServerError("CanMarkConversation", err) ctx.ServerError("CanMarkConversation", err)
@ -234,7 +240,7 @@ func SubmitReview(ctx *context.Context) {
// DismissReview dismissing stale review by repo admin // DismissReview dismissing stale review by repo admin
func DismissReview(ctx *context.Context) { func DismissReview(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.DismissReviewForm) 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 { if err != nil {
ctx.ServerError("pull_service.DismissReview", err) ctx.ServerError("pull_service.DismissReview", err)
return return

View File

@ -271,7 +271,7 @@ func SubmitReview(doer *user_model.User, gitRepo *git.Repository, issue *models.
} }
// DismissReview dismissing stale review by repo admin // 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) review, err := models.GetReviewByID(reviewID)
if err != nil { if err != nil {
return 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") 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 { if err = models.DismissReview(review, isDismiss); err != nil {
return return
} }
@ -289,10 +299,6 @@ func DismissReview(reviewID int64, message string, doer *user_model.User, isDism
return nil, nil return nil, nil
} }
// load data for notify
if err = review.LoadAttributes(); err != nil {
return
}
if err = review.Issue.LoadPullRequest(); err != nil { if err = review.Issue.LoadPullRequest(); err != nil {
return return
} }