diff --git a/models/issues/comment.go b/models/issues/comment.go index bf636d874bf..00db0966bc0 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. @@ -369,12 +370,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 @@ -383,6 +391,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 ca76dabc3ac..856e9da9807 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 469382af460..c3acd9e8c89 100644 --- a/tests/integration/issue_test.go +++ b/tests/integration/issue_test.go @@ -29,6 +29,7 @@ import ( "github.com/PuerkitoBio/goquery" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func getIssuesSelection(t testing.TB, htmlDoc *HTMLDoc) *goquery.Selection { @@ -780,3 +781,15 @@ func TestIssueReferenceURL(t *testing.T) { ref, _ = htmlDoc.Find(`.timeline-item.comment:not(.issue-content-comment) .reference-issue`).Attr("data-reference") assert.Equal(t, setting.AppURL+"user2/repo1/issues/1#issuecomment-2", ref) } + +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"}) +}