summaryrefslogtreecommitdiff
path: root/internal/federation/dereferencing/status.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/federation/dereferencing/status.go')
-rw-r--r--internal/federation/dereferencing/status.go96
1 files changed, 93 insertions, 3 deletions
diff --git a/internal/federation/dereferencing/status.go b/internal/federation/dereferencing/status.go
index 397d2aa28..c8012178c 100644
--- a/internal/federation/dereferencing/status.go
+++ b/internal/federation/dereferencing/status.go
@@ -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