fix: make sure to use unaltered fields when saving a shadow copy for updated profiles or comments (#8533)

Follow-up of !6977

### Manual testing
- User **S** creates an organization **O** and posts a comment **C** (on a random issue);
- User **R** report as abuse the comment **C**, the organization **O** as well as the user **S**;
- User **S** changes the content of comment **C** and the description of organization **O** as well as the description of their own profile;
- Check (within DB) that shadow copies are being created (and linked to corresponding abuse reports) for comment **C**, organization **O** and user **S** and the content is the one from the moment when the reports were submitted (therefore before the updates made by **S**).

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8533
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: floss4good <floss4good@disroot.org>
Co-committed-by: floss4good <floss4good@disroot.org>
This commit is contained in:
floss4good 2025-07-20 23:52:58 +02:00 committed by Otto
commit 95e8bbd5f0
11 changed files with 173 additions and 11 deletions

View file

@ -8,9 +8,11 @@ import (
"forgejo.org/models/db"
issues_model "forgejo.org/models/issues"
"forgejo.org/models/moderation"
"forgejo.org/models/unittest"
user_model "forgejo.org/models/user"
webhook_model "forgejo.org/models/webhook"
"forgejo.org/modules/json"
"forgejo.org/modules/setting"
"forgejo.org/modules/test"
issue_service "forgejo.org/services/issue"
@ -148,3 +150,40 @@ func TestUpdateComment(t *testing.T) {
unittest.AssertNotExistsBean(t, &issues_model.ContentHistory{CommentID: comment.ID})
})
}
func TestCreateShadowCopyOnCommentUpdate(t *testing.T) {
defer unittest.OverrideFixtures("models/fixtures/ModerationFeatures")()
require.NoError(t, unittest.PrepareTestDatabase())
userAlexSmithID := int64(1002)
spamCommentID := int64(18) // posted by @alexsmith
abuseReportID := int64(1) // submitted for above comment
newCommentContent := "If anyone needs help, just contact me."
// Retrieve the abusive user (@alexsmith), their SPAM comment and the abuse report already created for this comment.
poster := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userAlexSmithID})
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: spamCommentID, PosterID: poster.ID})
report := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{
ID: abuseReportID,
ContentType: moderation.ReportedContentTypeComment,
ContentID: comment.ID,
})
// The report should not already have a shadow copy linked.
assert.False(t, report.ShadowCopyID.Valid)
// The abusive user is updating their comment.
oldContent := comment.Content
comment.Content = newCommentContent
require.NoError(t, issue_service.UpdateComment(t.Context(), comment, 0, poster, oldContent))
// Reload the report.
report = unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ID: report.ID})
// A shadow copy should have been created and linked to our report.
assert.True(t, report.ShadowCopyID.Valid)
// Retrieve the newly created shadow copy and unmarshal the stored JSON so that we can check the values.
shadowCopy := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReportShadowCopy{ID: report.ShadowCopyID.Int64})
shadowCopyCommentData := new(issues_model.CommentData)
require.NoError(t, json.Unmarshal([]byte(shadowCopy.RawValue), &shadowCopyCommentData))
// Check to see if the initial content of the comment was stored within the shadow copy.
assert.Equal(t, oldContent, shadowCopyCommentData.Content)
}