From efa4d2a18258058bf8987037ee525f603ec1b9c8 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 25 Jun 2022 21:49:47 +0800 Subject: [PATCH 1/9] Check if project has the same repository id with issue when assign project to issue --- models/issues/issue_project.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index b83665c2bbc06..13eb5452fb073 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -132,6 +132,14 @@ func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.U return err } + newProject, err := project_model.GetProjectByID(ctx, 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 oldProjectID > 0 || newProjectID > 0 { if _, err := CreateCommentCtx(ctx, &CreateCommentOptions{ Type: CommentTypeProject, From aeba85e3227ee0b30a202acd08647cbe444c6aac Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 25 Jun 2022 23:48:00 +0800 Subject: [PATCH 2/9] Check if issue's repository id match project's repository id --- routers/web/repo/projects.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 0aa9b5effc00a..f054ad6e540bd 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" @@ -633,10 +634,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 = project_model.MoveIssuesOnProjectBoard(board, sortedIssueIDs); err != nil { ctx.ServerError("MoveIssuesOnProjectBoard", err) return From 5558a74968e60145cae27064288155a316a090f1 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 25 Jun 2022 19:01:08 +0200 Subject: [PATCH 3/9] Add more permission checking --- models/issues/issue.go | 5 ++++- models/issues/issue_project.go | 14 +++++++------- models/issues/milestone.go | 5 +++++ routers/api/v1/repo/pull_review.go | 2 +- routers/web/repo/issue.go | 7 ++++++- routers/web/repo/pull_review.go | 8 +++++++- routers/web/web.go | 2 +- services/issue/milestone.go | 8 ++++++++ services/issue/status.go | 6 +++--- services/pull/review.go | 16 +++++++++++----- 10 files changed, 53 insertions(+), 20 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 064f0d22abd01..af656a49407ca 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -743,7 +743,10 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use } // ChangeIssueStatus changes issue status to open or closed. -func ChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed bool) (*Comment, error) { +func ChangeIssueStatus(ctx context.Context, issue *Issue, repoID int64, doer *user_model.User, isClosed bool) (*Comment, error) { + if issue.RepoID != repoID { + return nil, fmt.Errorf("issue's repository is not correct") + } if err := issue.LoadRepo(ctx); err != nil { return nil, err } diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 13eb5452fb073..34a3d75e76415 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -124,21 +124,21 @@ func ChangeProjectAssign(issue *Issue, doer *user_model.User, newProjectID int64 func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error { oldProjectID := issue.projectID(ctx) - if _, err := db.GetEngine(ctx).Where("project_issue.issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{}); err != nil { + newProject, err := project_model.GetProjectByID(ctx, 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 := issue.LoadRepo(ctx); err != nil { + if _, err := db.GetEngine(ctx).Where("project_issue.issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{}); err != nil { return err } - newProject, err := project_model.GetProjectByID(ctx, newProjectID) - if err != nil { + if err := issue.LoadRepo(ctx); err != nil { return err } - if newProject.RepoID != issue.RepoID { - return fmt.Errorf("Issue's repository is not the same as project's repository") - } if oldProjectID > 0 || newProjectID > 0 { if _, err := CreateCommentCtx(ctx, &CreateCommentOptions{ diff --git a/models/issues/milestone.go b/models/issues/milestone.go index 6c1095910877f..b6e69d831397f 100644 --- a/models/issues/milestone.go +++ b/models/issues/milestone.go @@ -124,6 +124,11 @@ func NewMilestone(m *Milestone) (err error) { return committer.Commit() } +// HasMilestoneByRepoID returns if the milestone exists in the repository. +func HasMilestoneByRepoID(ctx context.Context, repoID, id int64) (bool, error) { + return db.GetEngine(ctx).ID(id).Where("repo_id=?", repoID).Exist(new(Milestone)) +} + // GetMilestoneByRepoID returns the milestone in a repository. func GetMilestoneByRepoID(ctx context.Context, repoID, id int64) (*Milestone, error) { m := new(Milestone) diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index 57548f2102911..1b61a40222d69 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -886,7 +886,7 @@ func dismissReview(ctx *context.APIContext, msg string, isDismiss bool) { return } - _, err := pull_service.DismissReview(ctx, review.ID, msg, ctx.Doer, isDismiss) + _, err := pull_service.DismissReview(ctx, review.ID, ctx.Repo.Repository.ID, msg, ctx.Doer, isDismiss) if err != nil { ctx.Error(http.StatusInternalServerError, "pull_service.DismissReview", err) return diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 5b72ff79af370..64bea6697de09 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1042,7 +1042,8 @@ func NewIssuePost(ctx *context.Context) { return } - if projectID > 0 { + // User must also be able to see the project. + if projectID > 0 && ctx.Repo.CanRead(unit.TypeProjects) { if err := issues_model.ChangeProjectAssign(issue, ctx.Doer, projectID); err != nil { ctx.ServerError("ChangeProjectAssign", err) return @@ -2503,6 +2504,10 @@ func UpdateIssueStatus(ctx *context.Context) { return } 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 + } if issue.IsClosed != isClosed { if err := issue_service.ChangeStatus(issue, ctx.Doer, isClosed); err != nil { if issues_model.IsErrDependenciesLeft(err) { diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go index cc7ae9bbfa3df..5a9f7a8138909 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" @@ -118,6 +119,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 = issues_model.CanMarkConversation(comment.Issue, ctx.Doer); err != nil { ctx.ServerError("CanMarkConversation", err) @@ -236,7 +242,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(ctx, form.ReviewID, form.Message, ctx.Doer, true) + comm, err := pull_service.DismissReview(ctx, form.ReviewID, ctx.Repo.Repository.ID, form.Message, ctx.Doer, true) if err != nil { ctx.ServerError("pull_service.DismissReview", err) return diff --git a/routers/web/web.go b/routers/web/web.go index 4896bdb1e46f3..5a600b4b08e48 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -898,7 +898,7 @@ func RegisterRoutes(m *web.Route) { m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel) m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone) - m.Post("/projects", reqRepoIssuesOrPullsWriter, repo.UpdateIssueProject) + m.Post("/projects", reqRepoIssuesOrPullsWriter, reqRepoProjectsReader, repo.UpdateIssueProject) m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee) m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest) m.Post("/dismiss_review", reqRepoAdmin, bindIgnErr(forms.DismissReviewForm{}), repo.DismissReview) diff --git a/services/issue/milestone.go b/services/issue/milestone.go index af337c3f142f5..5a92cf8dc3b10 100644 --- a/services/issue/milestone.go +++ b/services/issue/milestone.go @@ -15,6 +15,14 @@ import ( ) func changeMilestoneAssign(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, oldMilestoneID int64) error { + has, err := issues_model.HasMilestoneByRepoID(ctx, issue.RepoID, issue.MilestoneID) + if err != nil { + return fmt.Errorf("GetMilestoneByRepoID: %v", err) + } + if !has { + return fmt.Errorf("GetMilestoneByRepoID: issue doesn't exist") + } + if err := issues_model.UpdateIssueCols(ctx, issue, "milestone_id"); err != nil { return err } diff --git a/services/issue/status.go b/services/issue/status.go index 0da5c88762c81..5ac28bb5aee6c 100644 --- a/services/issue/status.go +++ b/services/issue/status.go @@ -15,13 +15,13 @@ import ( ) // ChangeStatus changes issue status to open or closed. -func ChangeStatus(issue *issues_model.Issue, doer *user_model.User, closed bool) error { - return changeStatusCtx(db.DefaultContext, issue, doer, closed) +func ChangeStatus(issue *issues_model.Issue, repoID int64, doer *user_model.User, closed bool) error { + return changeStatusCtx(db.DefaultContext, issue, repoID, doer, closed) } // changeStatusCtx changes issue status to open or closed. // TODO: if context is not db.DefaultContext we get a deadlock!!! -func changeStatusCtx(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, closed bool) error { +func changeStatusCtx(ctx context.Context, issue *issues_model.Issue, repoID int64, doer *user_model.User, closed bool) error { comment, err := issues_model.ChangeIssueStatus(ctx, issue, doer, closed) if err != nil { if issues_model.IsErrDependenciesLeft(err) && closed { diff --git a/services/pull/review.go b/services/pull/review.go index 6bb8877b0ffaf..22e0ae9853955 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -271,7 +271,7 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos } // DismissReview dismissing stale review by repo admin -func DismissReview(ctx context.Context, reviewID int64, message string, doer *user_model.User, isDismiss bool) (comment *issues_model.Comment, err error) { +func DismissReview(ctx context.Context, reviewID, repoID int64, message string, doer *user_model.User, isDismiss bool) (comment *issues_model.Comment, err error) { review, err := issues_model.GetReviewByID(ctx, reviewID) if err != nil { return @@ -281,6 +281,16 @@ func DismissReview(ctx context.Context, reviewID int64, message string, doer *us 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(ctx); 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 = issues_model.DismissReview(review, isDismiss); err != nil { return } @@ -289,10 +299,6 @@ func DismissReview(ctx context.Context, reviewID int64, message string, doer *us return nil, nil } - // load data for notify - if err = review.LoadAttributes(ctx); err != nil { - return - } if err = review.Issue.LoadPullRequest(); err != nil { return } From dbb22f8626e7aa5e3bcca58974bfe13d2c9db751 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 25 Jun 2022 19:04:30 +0200 Subject: [PATCH 4/9] Remove invalid argument --- models/issues/issue.go | 5 +---- services/issue/status.go | 6 +++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index af656a49407ca..064f0d22abd01 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -743,10 +743,7 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use } // ChangeIssueStatus changes issue status to open or closed. -func ChangeIssueStatus(ctx context.Context, issue *Issue, repoID int64, doer *user_model.User, isClosed bool) (*Comment, error) { - if issue.RepoID != repoID { - return nil, fmt.Errorf("issue's repository is not correct") - } +func ChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed bool) (*Comment, error) { if err := issue.LoadRepo(ctx); err != nil { return nil, err } diff --git a/services/issue/status.go b/services/issue/status.go index 5ac28bb5aee6c..0da5c88762c81 100644 --- a/services/issue/status.go +++ b/services/issue/status.go @@ -15,13 +15,13 @@ import ( ) // ChangeStatus changes issue status to open or closed. -func ChangeStatus(issue *issues_model.Issue, repoID int64, doer *user_model.User, closed bool) error { - return changeStatusCtx(db.DefaultContext, issue, repoID, doer, closed) +func ChangeStatus(issue *issues_model.Issue, doer *user_model.User, closed bool) error { + return changeStatusCtx(db.DefaultContext, issue, doer, closed) } // changeStatusCtx changes issue status to open or closed. // TODO: if context is not db.DefaultContext we get a deadlock!!! -func changeStatusCtx(ctx context.Context, issue *issues_model.Issue, repoID int64, doer *user_model.User, closed bool) error { +func changeStatusCtx(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, closed bool) error { comment, err := issues_model.ChangeIssueStatus(ctx, issue, doer, closed) if err != nil { if issues_model.IsErrDependenciesLeft(err) && closed { From 291adce6f44c506d867a79e0e065cbce5c5c3a47 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 25 Jun 2022 19:18:43 +0200 Subject: [PATCH 5/9] Fix errors --- models/issues/issue_project.go | 15 +++++++++------ services/issue/milestone.go | 15 +++++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 34a3d75e76415..aed78611eaca0 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -124,12 +124,15 @@ func ChangeProjectAssign(issue *Issue, doer *user_model.User, newProjectID int64 func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error { oldProjectID := issue.projectID(ctx) - newProject, err := project_model.GetProjectByID(ctx, 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") + // Only check if we add a new project and not remove it. + if newProjectID > 0 { + newProject, err := project_model.GetProjectByID(ctx, 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 := db.GetEngine(ctx).Where("project_issue.issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{}); err != nil { diff --git a/services/issue/milestone.go b/services/issue/milestone.go index 5a92cf8dc3b10..e3e392b9132c8 100644 --- a/services/issue/milestone.go +++ b/services/issue/milestone.go @@ -15,12 +15,15 @@ import ( ) func changeMilestoneAssign(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, oldMilestoneID int64) error { - has, err := issues_model.HasMilestoneByRepoID(ctx, issue.RepoID, issue.MilestoneID) - if err != nil { - return fmt.Errorf("GetMilestoneByRepoID: %v", err) - } - if !has { - return fmt.Errorf("GetMilestoneByRepoID: issue doesn't exist") + // Only check if milestone exists if we don't remove it. + if issue.MilestoneID > 0 { + has, err := issues_model.HasMilestoneByRepoID(ctx, issue.RepoID, issue.MilestoneID) + if err != nil { + return fmt.Errorf("GetMilestoneByRepoID: %v", err) + } + if !has { + return fmt.Errorf("GetMilestoneByRepoID: issue doesn't exist") + } } if err := issues_model.UpdateIssueCols(ctx, issue, "milestone_id"); err != nil { From e1d9bcbd114a96028dc7875b9c56cbef93f774b7 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 25 Jun 2022 19:32:40 +0200 Subject: [PATCH 6/9] Add generic check --- routers/web/repo/issue.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 64bea6697de09..49992528510fe 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1784,6 +1784,10 @@ func getActionIssues(ctx *context.Context) []*issues_model.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 From 443517cca9ce43353bced63198afb2ef3d85c7c3 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 25 Jun 2022 19:42:34 +0200 Subject: [PATCH 7/9] Remove duplicated check --- routers/web/repo/issue.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 49992528510fe..b95e27933efce 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -2508,10 +2508,6 @@ func UpdateIssueStatus(ctx *context.Context) { return } 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 - } if issue.IsClosed != isClosed { if err := issue_service.ChangeStatus(issue, ctx.Doer, isClosed); err != nil { if issues_model.IsErrDependenciesLeft(err) { From f4a69c82cbdbaec1f4ae87560e32cab5f88d3335 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 26 Jun 2022 12:48:44 +0200 Subject: [PATCH 8/9] Return error + add check for new issues --- routers/web/repo/issue.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index b95e27933efce..e6f9529e31e81 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -803,7 +803,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") @@ -819,7 +820,7 @@ func NewIssue(ctx *context.Context) { } projectID := ctx.FormInt64("project") - if projectID > 0 { + if projectID > 0 && isProjectsEnabled { project, err := project_model.GetProjectByID(ctx, projectID) if err != nil { log.Error("GetProjectByID: %d: %v", projectID, err) @@ -1042,8 +1043,12 @@ func NewIssuePost(ctx *context.Context) { return } - // User must also be able to see the project. - if projectID > 0 && ctx.Repo.CanRead(unit.TypeProjects) { + 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 := issues_model.ChangeProjectAssign(issue, ctx.Doer, projectID); err != nil { ctx.ServerError("ChangeProjectAssign", err) return From b53d1a0d4986f8ec072f9c32aefe2789ba2fbe23 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 27 Jun 2022 20:42:59 +0800 Subject: [PATCH 9/9] Apply suggestions from code review Co-authored-by: KN4CK3R --- models/issues/milestone.go | 2 +- services/issue/milestone.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/models/issues/milestone.go b/models/issues/milestone.go index b6e69d831397f..fba599e6ece48 100644 --- a/models/issues/milestone.go +++ b/models/issues/milestone.go @@ -124,7 +124,7 @@ func NewMilestone(m *Milestone) (err error) { return committer.Commit() } -// HasMilestoneByRepoID returns if the milestone exists in the repository. +// HasMilestoneByRepoID returns if the milestone exists in the repository. func HasMilestoneByRepoID(ctx context.Context, repoID, id int64) (bool, error) { return db.GetEngine(ctx).ID(id).Where("repo_id=?", repoID).Exist(new(Milestone)) } diff --git a/services/issue/milestone.go b/services/issue/milestone.go index e3e392b9132c8..d7c5fa45516f3 100644 --- a/services/issue/milestone.go +++ b/services/issue/milestone.go @@ -19,10 +19,10 @@ func changeMilestoneAssign(ctx context.Context, doer *user_model.User, issue *is if issue.MilestoneID > 0 { has, err := issues_model.HasMilestoneByRepoID(ctx, issue.RepoID, issue.MilestoneID) if err != nil { - return fmt.Errorf("GetMilestoneByRepoID: %v", err) + return fmt.Errorf("HasMilestoneByRepoID: %v", err) } if !has { - return fmt.Errorf("GetMilestoneByRepoID: issue doesn't exist") + return fmt.Errorf("HasMilestoneByRepoID: issue doesn't exist") } }