diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 055fcdc231..f8c50af53c 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -57,6 +57,8 @@ import ( issue_service "code.gitea.io/gitea/services/issue" pull_service "code.gitea.io/gitea/services/pull" repo_service "code.gitea.io/gitea/services/repository" + + "gitea.com/go-chi/binding" ) const ( @@ -2218,10 +2220,20 @@ func UpdateIssueTitle(ctx *context.Context) { ctx.Error(http.StatusForbidden) return } - title := ctx.FormTrim("title") - if len(title) == 0 { - ctx.Error(http.StatusNoContent) + if util.IsEmptyString(title) { + ctx.Error(http.StatusBadRequest, "Title cannot be empty or spaces") + return + } + + // Creating a CreateIssueForm with the title so that we can validate the max title length + i := forms.CreateIssueForm{ + Title: title, + } + + bindingErr := binding.RawValidate(i) + if bindingErr.Has(binding.ERR_MAX_SIZE) { + ctx.Error(http.StatusBadRequest, "Title cannot be longer than 255 characters") return } diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go index 846de8f42c..ecdda68196 100644 --- a/tests/integration/issue_test.go +++ b/tests/integration/issue_test.go @@ -1005,6 +1005,66 @@ func TestUpdateIssueDeadline(t *testing.T) { assert.EqualValues(t, "2022-04-06", apiIssue.Deadline.Format("2006-01-02")) } +func TestUpdateIssueTitle(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + require.NoError(t, issueBefore.LoadAttributes(db.DefaultContext)) + assert.Equal(t, "issue1", issueBefore.Title) + + issueTitleUpdateTests := []struct { + title string + expectedHTTPCode int + }{ + { + title: "normal-title", + expectedHTTPCode: http.StatusOK, + }, + { + title: "extra-long-title-with-exactly-255-chars-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + expectedHTTPCode: http.StatusOK, + }, + { + title: "", + expectedHTTPCode: http.StatusBadRequest, + }, + { + title: " ", + expectedHTTPCode: http.StatusBadRequest, + }, + { + title: "extra-long-title-over-255-chars-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + expectedHTTPCode: http.StatusBadRequest, + }, + } + + session := loginUser(t, owner.Name) + issueURL := fmt.Sprintf("%s/%s/issues/%d", owner.Name, repo.Name, issueBefore.Index) + urlStr := issueURL + "/title" + + for _, issueTitleUpdateTest := range issueTitleUpdateTests { + req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ + "title": issueTitleUpdateTest.title, + "_csrf": GetCSRF(t, session, issueURL), + }) + + resp := session.MakeRequest(t, req, issueTitleUpdateTest.expectedHTTPCode) + + // JSON data is received only if the request succeeds + if issueTitleUpdateTest.expectedHTTPCode == http.StatusOK { + issueAfter := struct { + Title string `json:"title"` + }{} + + DecodeJSON(t, resp, &issueAfter) + assert.EqualValues(t, issueTitleUpdateTest.title, issueAfter.Title) + } + } +} + func TestIssueReferenceURL(t *testing.T) { defer tests.PrepareTestEnv(t)() session := loginUser(t, "user2")