diff --git a/models/issues/comment.go b/models/issues/comment.go index 9dd2ca23837..02de587fb25 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -34,6 +34,7 @@ import ( "gitea.dev/modules/util" "xorm.io/builder" + "xorm.io/xorm" ) // ErrCommentNotExist represents a "CommentNotExist" kind of error. @@ -398,12 +399,19 @@ func (c *Comment) LoadIssue(ctx context.Context) (err error) { return err } +var ( + _ xorm.BeforeInsertProcessor = (*Comment)(nil) + _ xorm.BeforeUpdateProcessor = (*Comment)(nil) +) + // BeforeInsert will be invoked by XORM before inserting a record func (c *Comment) BeforeInsert() { c.PatchQuoted = c.Patch if !utf8.ValidString(c.Patch) { c.PatchQuoted = strconv.Quote(c.Patch) } + + sanitizeDatabaseStringContent(&c.Content) } // BeforeUpdate will be invoked by XORM before updating a record @@ -412,6 +420,8 @@ func (c *Comment) BeforeUpdate() { if !utf8.ValidString(c.Patch) { c.PatchQuoted = strconv.Quote(c.Patch) } + + sanitizeDatabaseStringContent(&c.Content) } // AfterLoad is invoked from XORM after setting the values of all fields of this object. diff --git a/models/issues/issue.go b/models/issues/issue.go index 0ce15737f75..fb3803010e0 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -11,6 +11,7 @@ import ( "regexp" "slices" "strconv" + "strings" "gitea.dev/models/db" project_model "gitea.dev/models/project" @@ -25,6 +26,7 @@ import ( "gitea.dev/modules/util" "xorm.io/builder" + "xorm.io/xorm" ) // ErrIssueNotExist represents a "IssueNotExist" kind of error. @@ -123,6 +125,27 @@ func init() { db.RegisterModel(new(IssueIndex)) } +func sanitizeDatabaseStringContent(s *string) { + // postgresql doesn't like "\x00" + // this is only a simple workaround for issues like https://github.com/go-gitea/gitea/issues/28890 and https://github.com/go-gitea/gitea/issues/40003 + *s = strings.ReplaceAll(*s, "\x00", " ") +} + +var ( + _ xorm.BeforeInsertProcessor = (*Issue)(nil) + _ xorm.BeforeUpdateProcessor = (*Issue)(nil) +) + +func (issue *Issue) BeforeInsert() { + sanitizeDatabaseStringContent(&issue.Title) + sanitizeDatabaseStringContent(&issue.Content) +} + +func (issue *Issue) BeforeUpdate() { + sanitizeDatabaseStringContent(&issue.Title) + sanitizeDatabaseStringContent(&issue.Content) +} + // LoadTotalTimes load total tracked time func (issue *Issue) LoadTotalTimes(ctx context.Context) (err error) { opts := FindTrackedTimesOptions{IssueID: issue.ID} diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go index 6cb28110969..f6dd99edb52 100644 --- a/tests/integration/issue_test.go +++ b/tests/integration/issue_test.go @@ -720,3 +720,15 @@ func TestIssueRefSelectorEnabledForPoster(t *testing.T) { _, hasURL := sel.Attr("data-url-update-issueref") assert.True(t, hasURL, "data-url-update-issueref must be set for the issue poster") } + +func TestIssueCommentContent(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + issue := &issues_model.Issue{Title: "title\x00content", Content: "body\x00content"} + require.NoError(t, db.Insert(t.Context(), issue)) + unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID, Title: "title content", Content: "body content"}) + + comment := &issues_model.Comment{Content: "body\x00content"} + require.NoError(t, db.Insert(t.Context(), comment)) + unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: comment.ID, Content: "body content"}) +}