mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-10-29 14:32:25 -05:00
[bugfix] check remote status permissibility (#2703)
* add more stringent checks for remote status permissibility * add check for inreplyto of a remote status being a boost * do not permit inReplyTo boost wrapper statuses * change comment wording * fix calls to NewFederator() * add code comments for NotPermitted() and SetNotPermitted() * improve comment * check that existing != nil before attempting delete * ensure replying account isn't suspended * use a debug log instead of info. check for boost using ID * shorten log string length. make info level * add note that replying to boost wrapper status shouldn't be able to happen anyways * update to use onFail() function
This commit is contained in:
parent
f487fc5d4b
commit
d85727e184
9 changed files with 154 additions and 16 deletions
|
|
@ -503,9 +503,16 @@ func (d *Dereferencer) enrichStatus(
|
|||
latestStatus.FetchedAt = time.Now()
|
||||
latestStatus.Local = status.Local
|
||||
|
||||
// Ensure the status' poll remains consistent, else reset the poll.
|
||||
if err := d.fetchStatusPoll(ctx, status, latestStatus); err != nil {
|
||||
return nil, nil, gtserror.Newf("error populating poll for status %s: %w", uri, err)
|
||||
// Check if this is a permitted status we should accept.
|
||||
permit, err := d.isPermittedStatus(ctx, status, latestStatus)
|
||||
if err != nil {
|
||||
return nil, nil, gtserror.Newf("error checking permissibility for status %s: %w", uri, err)
|
||||
}
|
||||
|
||||
if !permit {
|
||||
// Return a checkable error type that can be ignored.
|
||||
err := gtserror.Newf("dropping unpermitted status: %s", uri)
|
||||
return nil, nil, gtserror.SetNotPermitted(err)
|
||||
}
|
||||
|
||||
// Ensure the status' mentions are populated, and pass in existing to check for changes.
|
||||
|
|
@ -513,6 +520,11 @@ func (d *Dereferencer) enrichStatus(
|
|||
return nil, nil, gtserror.Newf("error populating mentions for status %s: %w", uri, err)
|
||||
}
|
||||
|
||||
// Ensure the status' poll remains consistent, else reset the poll.
|
||||
if err := d.fetchStatusPoll(ctx, status, latestStatus); err != nil {
|
||||
return nil, nil, gtserror.Newf("error populating poll for status %s: %w", uri, err)
|
||||
}
|
||||
|
||||
// Now that we know who this status replies to (handled by ASStatusToStatus)
|
||||
// and who it mentions, we can add a ThreadID to it if necessary.
|
||||
if err := d.threadStatus(ctx, latestStatus); err != nil {
|
||||
|
|
@ -550,6 +562,84 @@ func (d *Dereferencer) enrichStatus(
|
|||
return latestStatus, apubStatus, nil
|
||||
}
|
||||
|
||||
// isPermittedStatus returns whether the given status
|
||||
// is permitted to be stored on this instance, checking
|
||||
// whether the author is suspended, and passes visibility
|
||||
// checks against status being replied-to (if any).
|
||||
func (d *Dereferencer) isPermittedStatus(
|
||||
ctx context.Context,
|
||||
existing *gtsmodel.Status,
|
||||
status *gtsmodel.Status,
|
||||
) (
|
||||
permitted bool, // is permitted?
|
||||
err error,
|
||||
) {
|
||||
|
||||
// our failure condition handling
|
||||
// at the end of this function for
|
||||
// the case of permission = false.
|
||||
onFail := func() (bool, error) {
|
||||
if existing != nil {
|
||||
log.Infof(ctx, "deleting unpermitted: %s", existing.URI)
|
||||
|
||||
// Delete existing status from database as it's no longer permitted.
|
||||
if err := d.state.DB.DeleteStatusByID(ctx, existing.ID); err != nil {
|
||||
log.Errorf(ctx, "error deleting %s after permissivity fail: %v", existing.URI, err)
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if !status.Account.SuspendedAt.IsZero() {
|
||||
// The status author is suspended,
|
||||
// this shouldn't have reached here
|
||||
// but it's a fast check anyways.
|
||||
return onFail()
|
||||
}
|
||||
|
||||
if status.InReplyToURI == "" {
|
||||
// This status isn't in
|
||||
// reply to anything!
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if status.InReplyTo == nil {
|
||||
// If no inReplyTo has been set,
|
||||
// we return here for now as we
|
||||
// can't perform further checks.
|
||||
//
|
||||
// Worst case we allow something
|
||||
// through, and later on during
|
||||
// refetch it will get deleted.
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if status.InReplyTo.BoostOfID != "" {
|
||||
// We do not permit replies to
|
||||
// boost wrapper statuses. (this
|
||||
// shouldn't be able to happen).
|
||||
return onFail()
|
||||
}
|
||||
|
||||
// Check visibility of inReplyTo to status author.
|
||||
permitted, err = d.visibility.StatusVisible(ctx,
|
||||
status.Account,
|
||||
status.InReplyTo,
|
||||
)
|
||||
if err != nil {
|
||||
return false, gtserror.Newf("error checking in-reply-to visibility: %w", err)
|
||||
}
|
||||
|
||||
if permitted &&
|
||||
*status.InReplyTo.Replyable {
|
||||
// This status is visible AND
|
||||
// replyable, in this economy?!
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return onFail()
|
||||
}
|
||||
|
||||
// populateMentionTarget tries to populate the given
|
||||
// mention with the correct TargetAccount and (if not
|
||||
// yet set) TargetAccountURI, returning the populated
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue