diff options
author | 2024-02-27 13:22:05 +0100 | |
---|---|---|
committer | 2024-02-27 12:22:05 +0000 | |
commit | 9cadc764b389df970c767608e7a061f3bd777dfa (patch) | |
tree | e49218fff3af5800b5305720a04a4f74e7b5c2cb /internal | |
parent | [chore]: Bump github.com/tdewolff/minify/v2 from 2.20.17 to 2.20.18 (#2689) (diff) | |
download | gotosocial-9cadc764b389df970c767608e7a061f3bd777dfa.tar.xz |
[feature] Add experimental `instance-federation-spam-filter` option (#2685)
* [chore] Move `visibility` to `filter/visibility`
* [feature] Add experimental instance-federation-spam-filter option
Diffstat (limited to 'internal')
70 files changed, 1520 insertions, 128 deletions
diff --git a/internal/ap/extract.go b/internal/ap/extract.go index 2b49c6477..d9288c162 100644 --- a/internal/ap/extract.go +++ b/internal/ap/extract.go @@ -33,6 +33,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/text" + "github.com/superseriousbusiness/gotosocial/internal/util" ) // ExtractObjects will extract object vocab.Types from given implementing interface. @@ -776,11 +777,21 @@ func extractHashtag(i Hashtaggable) (*gtsmodel.Tag, error) { } tagName := strings.TrimPrefix(name, "#") - yeah := func() *bool { t := true; return &t } + // Extract href for the tag, if set. + // + // Fine if not, it's only used for spam + // checking anyway so not critical. + var href string + hrefProp := i.GetActivityStreamsHref() + if hrefProp != nil && hrefProp.IsIRI() { + href = hrefProp.GetIRI().String() + } + return >smodel.Tag{ Name: tagName, - Useable: yeah(), // Assume true by default. - Listable: yeah(), // Assume true by default. + Useable: util.Ptr(true), // Assume true by default. + Listable: util.Ptr(true), // Assume true by default. + Href: href, }, nil } diff --git a/internal/api/activitypub/emoji/emojiget_test.go b/internal/api/activitypub/emoji/emojiget_test.go index 57c412928..4d687a049 100644 --- a/internal/api/activitypub/emoji/emojiget_test.go +++ b/internal/api/activitypub/emoji/emojiget_test.go @@ -29,6 +29,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/middleware" @@ -36,7 +37,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/activitypub/users/user_test.go b/internal/api/activitypub/users/user_test.go index a52c351ac..4d55aad3d 100644 --- a/internal/api/activitypub/users/user_test.go +++ b/internal/api/activitypub/users/user_test.go @@ -24,6 +24,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/middleware" @@ -31,7 +32,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/client/accounts/account_test.go b/internal/api/client/accounts/account_test.go index e247ba21d..2f8664756 100644 --- a/internal/api/client/accounts/account_test.go +++ b/internal/api/client/accounts/account_test.go @@ -30,6 +30,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -37,7 +38,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/client/admin/admin_test.go b/internal/api/client/admin/admin_test.go index 13c6d13e9..4a429e57a 100644 --- a/internal/api/client/admin/admin_test.go +++ b/internal/api/client/admin/admin_test.go @@ -30,6 +30,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -37,7 +38,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/client/bookmarks/bookmarks_test.go b/internal/api/client/bookmarks/bookmarks_test.go index b689ca24c..cb796e9e8 100644 --- a/internal/api/client/bookmarks/bookmarks_test.go +++ b/internal/api/client/bookmarks/bookmarks_test.go @@ -35,6 +35,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -42,7 +43,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/client/favourites/favourites_test.go b/internal/api/client/favourites/favourites_test.go index 2826b2c4e..bd0ebce2e 100644 --- a/internal/api/client/favourites/favourites_test.go +++ b/internal/api/client/favourites/favourites_test.go @@ -23,13 +23,13 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/processing" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/client/followrequests/followrequest_test.go b/internal/api/client/followrequests/followrequest_test.go index 3ef8951b9..fc9843b4a 100644 --- a/internal/api/client/followrequests/followrequest_test.go +++ b/internal/api/client/followrequests/followrequest_test.go @@ -29,6 +29,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -36,7 +37,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/client/instance/instance_test.go b/internal/api/client/instance/instance_test.go index 42bd9eeb3..8bfe444e5 100644 --- a/internal/api/client/instance/instance_test.go +++ b/internal/api/client/instance/instance_test.go @@ -29,6 +29,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -36,7 +37,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/client/lists/lists_test.go b/internal/api/client/lists/lists_test.go index 8504f7ee2..844d54cbb 100644 --- a/internal/api/client/lists/lists_test.go +++ b/internal/api/client/lists/lists_test.go @@ -23,13 +23,13 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/processing" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/client/media/mediacreate_test.go b/internal/api/client/media/mediacreate_test.go index a2cf9417a..41a1fc16f 100644 --- a/internal/api/client/media/mediacreate_test.go +++ b/internal/api/client/media/mediacreate_test.go @@ -37,6 +37,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/media" @@ -45,7 +46,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/client/media/mediaupdate_test.go b/internal/api/client/media/mediaupdate_test.go index abc26247c..bb4e0f4ad 100644 --- a/internal/api/client/media/mediaupdate_test.go +++ b/internal/api/client/media/mediaupdate_test.go @@ -34,6 +34,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/media" @@ -42,7 +43,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/client/polls/polls_test.go b/internal/api/client/polls/polls_test.go index acf972e2a..5a3c83580 100644 --- a/internal/api/client/polls/polls_test.go +++ b/internal/api/client/polls/polls_test.go @@ -23,13 +23,13 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/processing" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/client/reports/reports_test.go b/internal/api/client/reports/reports_test.go index 3e21ef1ba..b36017d69 100644 --- a/internal/api/client/reports/reports_test.go +++ b/internal/api/client/reports/reports_test.go @@ -23,13 +23,13 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/processing" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/client/search/search_test.go b/internal/api/client/search/search_test.go index 653858a1c..5ba198062 100644 --- a/internal/api/client/search/search_test.go +++ b/internal/api/client/search/search_test.go @@ -29,6 +29,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -36,7 +37,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/client/statuses/status_test.go b/internal/api/client/statuses/status_test.go index 921afb3b5..6d0710ef0 100644 --- a/internal/api/client/statuses/status_test.go +++ b/internal/api/client/statuses/status_test.go @@ -23,13 +23,13 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/processing" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/client/streaming/streaming_test.go b/internal/api/client/streaming/streaming_test.go index df4009890..1d94a87ec 100644 --- a/internal/api/client/streaming/streaming_test.go +++ b/internal/api/client/streaming/streaming_test.go @@ -35,6 +35,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -42,7 +43,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/client/user/user_test.go b/internal/api/client/user/user_test.go index 7cad3975d..efff89b13 100644 --- a/internal/api/client/user/user_test.go +++ b/internal/api/client/user/user_test.go @@ -23,13 +23,13 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/processing" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/fileserver/fileserver_test.go b/internal/api/fileserver/fileserver_test.go index 120ebacb5..b58433b9f 100644 --- a/internal/api/fileserver/fileserver_test.go +++ b/internal/api/fileserver/fileserver_test.go @@ -23,6 +23,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/media" @@ -31,7 +32,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/api/wellknown/webfinger/webfinger_test.go b/internal/api/wellknown/webfinger/webfinger_test.go index 4c4aba205..67ac5a64e 100644 --- a/internal/api/wellknown/webfinger/webfinger_test.go +++ b/internal/api/wellknown/webfinger/webfinger_test.go @@ -23,6 +23,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -30,7 +31,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/cleaner/media_test.go b/internal/cleaner/media_test.go index a2fdb0858..c27890f55 100644 --- a/internal/cleaner/media_test.go +++ b/internal/cleaner/media_test.go @@ -28,6 +28,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/cleaner" "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" @@ -35,7 +36,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/config/config.go b/internal/config/config.go index a4cd83227..292b6b7ee 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -78,6 +78,7 @@ type Configuration struct { WebAssetBaseDir string `name:"web-asset-base-dir" usage:"Directory to serve static assets from, accessible at example.org/assets/"` InstanceFederationMode string `name:"instance-federation-mode" usage:"Set instance federation mode."` + InstanceFederationSpamFilter bool `name:"instance-federation-spam-filter" usage:"Enable basic spam filter heuristics for messages coming from other instances, and drop messages identified as spam"` InstanceExposePeers bool `name:"instance-expose-peers" usage:"Allow unauthenticated users to query /api/v1/instance/peers?filter=open"` InstanceExposeSuspended bool `name:"instance-expose-suspended" usage:"Expose suspended instances via web UI, and allow unauthenticated users to query /api/v1/instance/peers?filter=suspended"` InstanceExposeSuspendedWeb bool `name:"instance-expose-suspended-web" usage:"Expose list of suspended instances as webpage on /about/suspended"` diff --git a/internal/config/defaults.go b/internal/config/defaults.go index a8343400b..78474539f 100644 --- a/internal/config/defaults.go +++ b/internal/config/defaults.go @@ -59,6 +59,7 @@ var Defaults = Configuration{ WebAssetBaseDir: "./web/assets/", InstanceFederationMode: InstanceFederationModeDefault, + InstanceFederationSpamFilter: false, InstanceExposePeers: false, InstanceExposeSuspended: false, InstanceExposeSuspendedWeb: false, diff --git a/internal/config/flags.go b/internal/config/flags.go index ae8a3ba36..516ba0101 100644 --- a/internal/config/flags.go +++ b/internal/config/flags.go @@ -84,6 +84,7 @@ func (s *ConfigState) AddServerFlags(cmd *cobra.Command) { // Instance cmd.Flags().String(InstanceFederationModeFlag(), cfg.InstanceFederationMode, fieldtag("InstanceFederationMode", "usage")) + cmd.Flags().Bool(InstanceFederationSpamFilterFlag(), cfg.InstanceFederationSpamFilter, fieldtag("InstanceFederationSpamFilter", "usage")) cmd.Flags().Bool(InstanceExposePeersFlag(), cfg.InstanceExposePeers, fieldtag("InstanceExposePeers", "usage")) cmd.Flags().Bool(InstanceExposeSuspendedFlag(), cfg.InstanceExposeSuspended, fieldtag("InstanceExposeSuspended", "usage")) cmd.Flags().Bool(InstanceExposeSuspendedWebFlag(), cfg.InstanceExposeSuspendedWeb, fieldtag("InstanceExposeSuspendedWeb", "usage")) diff --git a/internal/config/helpers.gen.go b/internal/config/helpers.gen.go index 0379a541b..9549d67c1 100644 --- a/internal/config/helpers.gen.go +++ b/internal/config/helpers.gen.go @@ -775,6 +775,31 @@ func GetInstanceFederationMode() string { return global.GetInstanceFederationMod // SetInstanceFederationMode safely sets the value for global configuration 'InstanceFederationMode' field func SetInstanceFederationMode(v string) { global.SetInstanceFederationMode(v) } +// GetInstanceFederationSpamFilter safely fetches the Configuration value for state's 'InstanceFederationSpamFilter' field +func (st *ConfigState) GetInstanceFederationSpamFilter() (v bool) { + st.mutex.RLock() + v = st.config.InstanceFederationSpamFilter + st.mutex.RUnlock() + return +} + +// SetInstanceFederationSpamFilter safely sets the Configuration value for state's 'InstanceFederationSpamFilter' field +func (st *ConfigState) SetInstanceFederationSpamFilter(v bool) { + st.mutex.Lock() + defer st.mutex.Unlock() + st.config.InstanceFederationSpamFilter = v + st.reloadToViper() +} + +// InstanceFederationSpamFilterFlag returns the flag name for the 'InstanceFederationSpamFilter' field +func InstanceFederationSpamFilterFlag() string { return "instance-federation-spam-filter" } + +// GetInstanceFederationSpamFilter safely fetches the value for global configuration 'InstanceFederationSpamFilter' field +func GetInstanceFederationSpamFilter() bool { return global.GetInstanceFederationSpamFilter() } + +// SetInstanceFederationSpamFilter safely sets the value for global configuration 'InstanceFederationSpamFilter' field +func SetInstanceFederationSpamFilter(v bool) { global.SetInstanceFederationSpamFilter(v) } + // GetInstanceExposePeers safely fetches the Configuration value for state's 'InstanceExposePeers' field func (st *ConfigState) GetInstanceExposePeers() (v bool) { st.mutex.RLock() diff --git a/internal/db/bundb/bundb_test.go b/internal/db/bundb/bundb_test.go index 037727090..adfd605a5 100644 --- a/internal/db/bundb/bundb_test.go +++ b/internal/db/bundb/bundb_test.go @@ -20,10 +20,10 @@ package bundb_test import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/federation/dereferencing/dereferencer_test.go b/internal/federation/dereferencing/dereferencer_test.go index c726467de..7f79e8ac0 100644 --- a/internal/federation/dereferencing/dereferencer_test.go +++ b/internal/federation/dereferencing/dereferencer_test.go @@ -22,11 +22,11 @@ import ( "github.com/superseriousbusiness/activity/streams/vocab" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/federation/dereferencing" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/federation/federatingdb/create.go b/internal/federation/federatingdb/create.go index e2540b739..cfb0f319b 100644 --- a/internal/federation/federatingdb/create.go +++ b/internal/federation/federatingdb/create.go @@ -21,13 +21,11 @@ import ( "context" "errors" "fmt" - "strings" "codeberg.org/gruf/go-logger/v2/level" "github.com/miekg/dns" "github.com/superseriousbusiness/activity/streams/vocab" "github.com/superseriousbusiness/gotosocial/internal/ap" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtserror" @@ -35,7 +33,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/id" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/messages" - "github.com/superseriousbusiness/gotosocial/internal/util" ) // Create adds a new entry to the database which must be able to be @@ -321,26 +318,45 @@ func (f *federatingDB) createStatusable( statusable ap.Statusable, forwarded bool, ) error { - - // Check whether we should accept this new status, - // we do this BEFORE even handling forwards to us. - accept, err := f.shouldAcceptStatusable(ctx, + // Check whether this status is both + // relevant, and doesn't look like spam. + err := f.spamFilter.StatusableOK(ctx, receiver, requester, statusable, ) - if err != nil { - return gtserror.Newf("error checking status acceptibility: %w", err) - } - if !accept { - // This is a status sent with no relation to receiver, i.e. - // - receiving account does not follow requesting account - // - received status does not mention receiving account + switch { + case err == nil: + // No problem! + + case gtserror.IsNotRelevant(err): + // This case is quite common if a remote (Mastodon) + // instance forwards a message to us which is a reply + // from someone else to a status we've also replied to. // - // We just pretend that all is fine (dog with cuppa, flames everywhere) - log.Trace(ctx, "status failed acceptability check") + // It does this to try to ensure thread completion, but + // we have our own thread fetching mechanism anyway. + log.Debugf(ctx, + "status %s is not relevant to receiver (%v); dropping it", + ap.GetJSONLDId(statusable), err, + ) return nil + + case gtserror.IsSpam(err): + // Log this at a higher level so admins can + // gauge how much spam is being sent to them. + // + // TODO: add Prometheus metrics for this. + log.Infof(ctx, + "status %s looked like spam (%v); dropping it", + ap.GetJSONLDId(statusable), err, + ) + return nil + + default: + // A real error has occurred. + return gtserror.Newf("error checking relevancy/spam: %w", err) } // If we do have a forward, we should ignore the content @@ -378,52 +394,6 @@ func (f *federatingDB) createStatusable( return nil } -func (f *federatingDB) shouldAcceptStatusable(ctx context.Context, receiver *gtsmodel.Account, requester *gtsmodel.Account, statusable ap.Statusable) (bool, error) { - host := config.GetHost() - accountDomain := config.GetAccountDomain() - - // Check whether status mentions the receiver, - // this is the quickest check so perform it first. - mentions, _ := ap.ExtractMentions(statusable) - for _, mention := range mentions { - - // Extract placeholder mention vars. - accURI := mention.TargetAccountURI - name := mention.NameString - - switch { - case accURI != "" && - accURI == receiver.URI || accURI == receiver.URL: - // Mention target is receiver, - // they are mentioned in status. - return true, nil - - case accURI == "" && name != "": - // Only a name was provided, extract the user@domain parts. - user, domain, err := util.ExtractNamestringParts(name) - if err != nil { - return false, gtserror.Newf("error extracting mention name parts: %w", err) - } - - // Check if the name points to our receiving local user. - isLocal := (domain == host || domain == accountDomain) - if isLocal && strings.EqualFold(user, receiver.Username) { - return true, nil - } - } - } - - // Check whether receiving account follows the requesting account. - follows, err := f.state.DB.IsFollowing(ctx, receiver.ID, requester.ID) - if err != nil { - return false, gtserror.Newf("error checking follow status: %w", err) - } - - // Status will only be acceptable - // if receiver follows requester. - return follows, nil -} - /* FOLLOW HANDLERS */ diff --git a/internal/federation/federatingdb/db.go b/internal/federation/federatingdb/db.go index 75ef3a2a7..2174a8003 100644 --- a/internal/federation/federatingdb/db.go +++ b/internal/federation/federatingdb/db.go @@ -22,12 +22,14 @@ import ( "github.com/superseriousbusiness/activity/pub" "github.com/superseriousbusiness/activity/streams/vocab" + "github.com/superseriousbusiness/gotosocial/internal/filter/spam" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" ) -// DB wraps the pub.Database interface with a couple of custom functions for GoToSocial. +// DB wraps the pub.Database interface with +// a couple of custom functions for GoToSocial. type DB interface { pub.Database Undo(ctx context.Context, undo vocab.ActivityStreamsUndo) error @@ -36,20 +38,28 @@ type DB interface { Announce(ctx context.Context, announce vocab.ActivityStreamsAnnounce) error } -// FederatingDB uses the underlying DB interface to implement the go-fed pub.Database interface. -// It doesn't care what the underlying implementation of the DB interface is, as long as it works. +// FederatingDB uses the given state interface +// to implement the go-fed pub.Database interface. type federatingDB struct { - state *state.State - converter *typeutils.Converter - filter *visibility.Filter + state *state.State + converter *typeutils.Converter + visFilter *visibility.Filter + spamFilter *spam.Filter } -// New returns a DB interface using the given database and config -func New(state *state.State, converter *typeutils.Converter, filter *visibility.Filter) DB { +// New returns a DB that satisfies the pub.Database +// interface, using the given state and filters. +func New( + state *state.State, + converter *typeutils.Converter, + visFilter *visibility.Filter, + spamFilter *spam.Filter, +) DB { fdb := federatingDB{ - state: state, - converter: converter, - filter: filter, + state: state, + converter: converter, + visFilter: visFilter, + spamFilter: spamFilter, } return &fdb } diff --git a/internal/federation/federatingdb/federatingdb_test.go b/internal/federation/federatingdb/federatingdb_test.go index 54c724057..0f227164d 100644 --- a/internal/federation/federatingdb/federatingdb_test.go +++ b/internal/federation/federatingdb/federatingdb_test.go @@ -23,12 +23,12 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/messages" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/federation/federator_test.go b/internal/federation/federator_test.go index 172675bd2..e10c7576c 100644 --- a/internal/federation/federator_test.go +++ b/internal/federation/federator_test.go @@ -23,12 +23,12 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/filter/spam/spam.go b/internal/filter/spam/spam.go new file mode 100644 index 000000000..386c2719b --- /dev/null +++ b/internal/filter/spam/spam.go @@ -0,0 +1,32 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +package spam + +import "github.com/superseriousbusiness/gotosocial/internal/state" + +// Filter packages logic for checking whether +// given statuses should be considered spam. +type Filter struct { + state *state.State +} + +// NewFilter returns a new spam Filter +// that will use the provided state. +func NewFilter(state *state.State) *Filter { + return &Filter{state: state} +} diff --git a/internal/filter/spam/spam_test.go b/internal/filter/spam/spam_test.go new file mode 100644 index 000000000..d9d867a07 --- /dev/null +++ b/internal/filter/spam/spam_test.go @@ -0,0 +1,59 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +package spam_test + +import ( + "github.com/stretchr/testify/suite" + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/filter/spam" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/state" + "github.com/superseriousbusiness/gotosocial/testrig" +) + +type FilterStandardTestSuite struct { + // standard suite interfaces + suite.Suite + db db.DB + state state.State + + // standard suite models + testAccounts map[string]*gtsmodel.Account + + filter *spam.Filter +} + +func (suite *FilterStandardTestSuite) SetupSuite() { + suite.testAccounts = testrig.NewTestAccounts() +} + +func (suite *FilterStandardTestSuite) SetupTest() { + suite.state.Caches.Init() + + testrig.InitTestConfig() + testrig.InitTestLog() + + suite.db = testrig.NewTestDB(&suite.state) + suite.filter = spam.NewFilter(&suite.state) + + testrig.StandardDBSetup(suite.db, nil) +} + +func (suite *FilterStandardTestSuite) TearDownTest() { + testrig.StandardDBTeardown(suite.db) +} diff --git a/internal/filter/spam/statusable.go b/internal/filter/spam/statusable.go new file mode 100644 index 000000000..60598f920 --- /dev/null +++ b/internal/filter/spam/statusable.go @@ -0,0 +1,472 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +package spam + +import ( + "context" + "errors" + "net/url" + "slices" + "strings" + + "github.com/miekg/dns" + "github.com/superseriousbusiness/gotosocial/internal/ap" + "github.com/superseriousbusiness/gotosocial/internal/config" + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/gtscontext" + "github.com/superseriousbusiness/gotosocial/internal/gtserror" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/log" + "github.com/superseriousbusiness/gotosocial/internal/regexes" + "github.com/superseriousbusiness/gotosocial/internal/util" +) + +// preppedMention represents a partially-parsed +// mention, prepared for spam checking purposes. +type preppedMention struct { + *gtsmodel.Mention + uri *url.URL + domain string + user string + local bool +} + +// StatusableOK returns no error if the given statusable looks OK, +// ie., relevant to the receiver, and not spam. +// +// This should only be used for Creates of statusables, NOT Announces! +// +// If the statusable does not pass relevancy or spam checks, either +// a Spam or NotRelevant error will be returned. Callers should use +// gtserror.IsSpam() and gtserror.IsNotRelevant() to check for this. +// +// If the returned error is not nil, but neither Spam or NotRelevant, +// then it's an actual database error. +// +// The decision is made based on the following heuristics, in order: +// +// 1. Receiver follow requester. Return nil. +// 2. Statusable doesn't mention receiver. Return NotRelevant. +// +// If instance-federation-spam-filter = false, then return nil now. +// Otherwise check: +// +// 3. Receiver is locked and is followed by requester. Return nil. +// 4. Five or more people are mentioned. Return Spam. +// 5. Receiver follow (requests) a mentioned account. Return nil. +// 6. Statusable has a media attachment. Return Spam. +// 7. Statusable contains non-mention, non-hashtag links. Return Spam. +func (f *Filter) StatusableOK( + ctx context.Context, + receiver *gtsmodel.Account, + requester *gtsmodel.Account, + statusable ap.Statusable, +) error { + // HEURISTIC 1: Check whether receiving account follows the requesting account. + // If so, we know it's OK and don't need to do any other checks. + follows, err := f.state.DB.IsFollowing(ctx, receiver.ID, requester.ID) + if err != nil { + return gtserror.Newf("db error checking follow status: %w", err) + } + + if follows { + // Looks fine. + return nil + } + + // HEURISTIC 2: Check whether statusable mentions the + // receiver. If not, we don't want to process this message. + rawMentions, _ := ap.ExtractMentions(statusable) + mentions := prepMentions(ctx, rawMentions) + mentioned := f.isMentioned(ctx, receiver, mentions) + if !mentioned { + // This is a random message fired + // into our inbox, just drop it. + err := errors.New("receiver does not follow requester, and is not mentioned") + return gtserror.SetNotRelevant(err) + } + + // Receiver is mentioned, but not by someone + // they follow. Check if we need to do more + // granular spam filtering. + if !config.GetInstanceFederationSpamFilter() { + // Filter is not enabled, allow it + // through without further checks. + return nil + } + + // More granular spam filtering time! + // + // HEURISTIC 3: Does requester follow locked receiver? + followedBy, err := f.lockedFollowedBy(ctx, receiver, requester) + if err != nil { + return gtserror.Newf("db error checking follow status: %w", err) + } + + // If receiver is locked, and is followed + // by requester, this likely means they're + // interested in the message. Allow it. + if followedBy { + return nil + } + + // HEURISTIC 4: How many people are mentioned? + // If it's 5 or more we can assume this is spam. + mentionsLen := len(mentions) + if mentionsLen >= 5 { + err := errors.New("status mentions 5 or more people") + return gtserror.SetSpam(err) + } + + // HEURISTIC 5: Four or fewer people are mentioned, + // do we follow (request) at least one of them? + // If so, we're probably interested in the message. + knowsOne := f.knowsOneMentioned(ctx, receiver, mentions) + if knowsOne { + return nil + } + + // HEURISTIC 6: Are there any media attachments? + attachments, _ := ap.ExtractAttachments(statusable) + hasAttachments := len(attachments) != 0 + if hasAttachments { + err := errors.New("status has attachment(s)") + return gtserror.SetSpam(err) + } + + // HEURISTIC 7: Are there any links in the post + // aside from mentions and hashtags? Include the + // summary/content warning when checking. + hashtags, _ := ap.ExtractHashtags(statusable) + hasErrantLinks := f.errantLinks(ctx, statusable, mentions, hashtags) + if hasErrantLinks { + err := errors.New("status has one or more non-mention, non-hashtag links") + return gtserror.SetSpam(err) + } + + // Looks OK. + return nil +} + +// prepMentions prepares a slice of mentions +// for spam checking by parsing out the namestring +// and targetAccountURI values, if present. +func prepMentions( + ctx context.Context, + mentions []*gtsmodel.Mention, +) []preppedMention { + var ( + host = config.GetHost() + accountDomain = config.GetAccountDomain() + ) + + parsedMentions := make([]preppedMention, 0, len(mentions)) + for _, mention := range mentions { + // Start by just embedding + // the original mention. + parsedMention := preppedMention{ + Mention: mention, + } + + // Try to parse namestring if present. + if mention.NameString != "" { + user, domain, err := util.ExtractNamestringParts(mention.NameString) + if err != nil { + // Malformed mention, + // just log + ignore. + log.Debugf(ctx, + "malformed mention namestring: %v", + err, + ) + continue + } + + parsedMention.domain = domain + parsedMention.user = user + } + + // Try to parse URI if present. + if mention.TargetAccountURI != "" { + targetURI, err := url.Parse(mention.TargetAccountURI) + if err != nil { + // Malformed mention, + // just log + ignore. + log.Debugf(ctx, + "malformed mention uri: %v", + err, + ) + continue + } + + parsedMention.uri = targetURI + + // Set host from targetURI if + // it wasn't set by namestring. + if parsedMention.domain == "" { + parsedMention.domain = targetURI.Host + } + } + + // It's a mention of a local account if the target host is us. + parsedMention.local = parsedMention.domain == host || parsedMention.domain == accountDomain + + // Done with this one. + parsedMentions = append(parsedMentions, parsedMention) + } + + return parsedMentions +} + +// isMentioned returns true if the +// receiver is targeted by at least +// one of the given mentions. +func (f *Filter) isMentioned( + ctx context.Context, + receiver *gtsmodel.Account, + mentions []preppedMention, +) bool { + return slices.ContainsFunc( + mentions, + func(mention preppedMention) bool { + // Check if receiver mentioned by URI. + if accURI := mention.TargetAccountURI; accURI != "" && + (accURI == receiver.URI || accURI == receiver.URL) { + return true + } + + // Check if receiver mentioned by namestring. + if mention.local && strings.EqualFold(mention.user, receiver.Username) { + return true + } + + // Mention doesn't + // target receiver. + return false + }, + ) +} + +// lockedFollowedBy returns true +// if receiver account is locked, +// and requester follows receiver. +func (f *Filter) lockedFollowedBy( + ctx context.Context, + receiver *gtsmodel.Account, + requester *gtsmodel.Account, +) (bool, error) { + // If receiver is not locked, + // return early to avoid a db call. + if !*receiver.Locked { + return false, nil + } + + return f.state.DB.IsFollowing(ctx, requester.ID, receiver.ID) +} + +// knowsOneMentioned returns true if the +// receiver follows or has follow requested +// at least one of the mentioned accounts. +func (f *Filter) knowsOneMentioned( + ctx context.Context, + receiver *gtsmodel.Account, + mentions []preppedMention, +) bool { + return slices.ContainsFunc( + mentions, + func(mention preppedMention) bool { + var ( + acc *gtsmodel.Account + err error + ) + + // Try to get target account without + // dereffing. After all, if they're not + // in our db we definitely don't know them. + if mention.TargetAccountURI != "" { + acc, err = f.state.DB.GetAccountByURI( + gtscontext.SetBarebones(ctx), + mention.TargetAccountURI, + ) + } else if mention.user != "" { + acc, err = f.state.DB.GetAccountByUsernameDomain( + gtscontext.SetBarebones(ctx), + mention.user, + mention.domain, + ) + } + + if err != nil && !errors.Is(err, db.ErrNoEntries) { + // Proper error. + log.Errorf(ctx, "db error getting mentioned account: %v", err) + return false + } + + if acc == nil { + // We don't know this nerd! + return false + } + + if acc.ID == receiver.ID { + // This is us, doesn't count. + return false + } + + follows, err := f.state.DB.IsFollowing(ctx, receiver.ID, acc.ID) + if err != nil { + // Proper error. + log.Errorf(ctx, "db error checking follow status: %v", err) + return false + } + + if follows { + // We follow this nerd. + return true + } + + // We don't follow this nerd, but + // have we requested to follow them? + followRequested, err := f.state.DB.IsFollowRequested(ctx, receiver.ID, acc.ID) + if err != nil { + // Proper error. + log.Errorf(ctx, "db error checking follow req status: %v", err) + return false + } + + return followRequested + }, + ) +} + +// errantLinks returns true if any http/https +// link discovered in the statusable content + cw +// is not either a mention link, or a hashtag link. +func (f *Filter) errantLinks( + ctx context.Context, + statusable ap.Statusable, + mentions []preppedMention, + hashtags []*gtsmodel.Tag, +) bool { + // Concatenate the cw with the + // content to check for links in both. + cw := ap.ExtractSummary(statusable) + content := ap.ExtractContent(statusable) + concat := cw + " " + content.Content + + // Store link string alongside link + // URI to avoid stringifying twice. + type preppedLink struct { + *url.URL + str string + } + + // Find + parse every http/https link in the status. + rawLinks := regexes.LinkScheme.FindAllString(concat, -1) + links := make([]preppedLink, 0, len(rawLinks)) + for _, rawLink := range rawLinks { + linkURI, err := url.Parse(rawLink) + if err != nil { + log.Debugf(ctx, + "malformed link in status: %v", + err, + ) + // Ignore bad links + // for spam checking. + continue + } + + links = append(links, preppedLink{ + URL: linkURI, + str: rawLink, + }) + } + + // For each link in the status, try to + // match it to a hashtag or a mention. + // If we can't, we have an errant link. + for _, link := range links { + hashtagLink := slices.ContainsFunc( + hashtags, + func(hashtag *gtsmodel.Tag) bool { + // If a link is to the href + // of a hashtag, it's fine. + return strings.EqualFold( + link.str, + hashtag.Href, + ) + }, + ) + + if hashtagLink { + // This link is accounted for. + // Move to the next one. + continue + } + + mentionLink := slices.ContainsFunc( + mentions, + func(mention preppedMention) bool { + // If link is straight up to the URI + // of a mentioned account, it's fine. + if strings.EqualFold( + link.str, + mention.TargetAccountURI, + ) { + return true + } + + // Link might be to an account URL rather + // than URI. This is a bit trickier because + // we can't predict the format of such URLs, + // and it's difficult to reconstruct them + // while also taking account of different + // host + account-domain values. + // + // So, just check if this link is on the same + // host as the mentioned account, or at least + // shares a host with it. + if link.Host == mention.domain { + // Same host. + return true + } + + // Shares a host if it has at least two + // components from the right in common. + common := dns.CompareDomainName( + link.Host, + mention.domain, + ) + return common >= 2 + }, + ) + + if mentionLink { + // This link is accounted for. + // Move to the next one. + continue + } + + // Not a hashtag link + // or a mention link, + // so it's errant. + return true + } + + // All links OK, or + // no links found. + return false +} diff --git a/internal/filter/spam/statusable_test.go b/internal/filter/spam/statusable_test.go new file mode 100644 index 000000000..db88454ee --- /dev/null +++ b/internal/filter/spam/statusable_test.go @@ -0,0 +1,780 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +package spam_test + +import ( + "bytes" + "context" + "io" + "testing" + + "github.com/stretchr/testify/suite" + "github.com/superseriousbusiness/gotosocial/internal/ap" + "github.com/superseriousbusiness/gotosocial/internal/gtserror" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/id" +) + +type StatusableTestSuite struct { + FilterStandardTestSuite +} + +const ( + // Message that mentions 5 people (including receiver), + // and contains a errant link. + spam1 = `{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "ostatus": "http://ostatus.org#", + "atomUri": "ostatus:atomUri", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "sensitive": "as:sensitive", + "toot": "http://joinmastodon.org/ns#", + "votersCount": "toot:votersCount" + } + ], + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "type": "Note", + "summary": null, + "inReplyTo": null, + "published": "2024-02-24T07:06:14Z", + "url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562", + "attributedTo": "http://fossbros-anonymous.io/users/foss_satan", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "http://fossbros-anonymous.io/users/foss_satan/followers", + "https://example.org/users/9gol6f8zff", + "https://example.net/users/nityosan", + "https://a.misskey.instance.com/users/9c06ylkgsx", + "https://another.misskey.instance.com/users/9eklgce5yk", + "http://localhost:8080/users/the_mighty_zork" + ], + "sensitive": false, + "atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "inReplyToAtomUri": null, + "content": "<p><a href=\"https://spammylink.org/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"\">spammylink.org/</span><span class=\"invisible\"></span></a></p><p><span class=\"h-card\" translate=\"no\"><a href=\"https://example.org/@Nao_ya_ia22\" class=\"u-url mention\">@<span>Nao_ya_ia22</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://another.misskey.instance.com/@mendako\" class=\"u-url mention\">@<span>mendako</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\">@<span>the_mighty_zork</span></a></span></p>", + "attachment": [], + "tag": [ + { + "type": "Mention", + "href": "https://example.org/users/9gol6f8zff", + "name": "@Nao_ya_ia22@example.org" + }, + { + "type": "Mention", + "href": "https://example.net/users/nityosan", + "name": "@nityosan@example.net" + }, + { + "type": "Mention", + "href": "https://a.misskey.instance.com/users/9c06ylkgsx", + "name": "@FIzxive@a.misskey.instance.com" + }, + { + "type": "Mention", + "href": "https://another.misskey.instance.com/users/9eklgce5yk", + "name": "@mendako@another.misskey.instance.com" + }, + { + "type": "Mention", + "href": "http://localhost:8080/users/the_mighty_zork", + "name": "@the_mighty_zork@localhost:8080" + } + ], + "replies": { + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true", + "partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "items": [] + } + } +}` + + // Message that mentions 4 people (including receiver), + // and contains a errant link. + spam2 = `{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "ostatus": "http://ostatus.org#", + "atomUri": "ostatus:atomUri", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "sensitive": "as:sensitive", + "toot": "http://joinmastodon.org/ns#", + "votersCount": "toot:votersCount" + } + ], + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "type": "Note", + "summary": null, + "inReplyTo": null, + "published": "2024-02-24T07:06:14Z", + "url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562", + "attributedTo": "http://fossbros-anonymous.io/users/foss_satan", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "http://fossbros-anonymous.io/users/foss_satan/followers", + "https://example.net/users/nityosan", + "https://a.misskey.instance.com/users/9c06ylkgsx", + "https://another.misskey.instance.com/users/9eklgce5yk", + "http://localhost:8080/users/the_mighty_zork" + ], + "sensitive": false, + "atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "inReplyToAtomUri": null, + "content": "<p><a href=\"https://spammylink.org/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"\">spammylink.org/</span><span class=\"invisible\"></span></a></p><p><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://another.misskey.instance.com/@mendako\" class=\"u-url mention\">@<span>mendako</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\">@<span>the_mighty_zork</span></a></span></p>", + "attachment": [], + "tag": [ + { + "type": "Mention", + "href": "https://example.net/users/nityosan", + "name": "@nityosan@example.net" + }, + { + "type": "Mention", + "href": "https://a.misskey.instance.com/users/9c06ylkgsx", + "name": "@FIzxive@a.misskey.instance.com" + }, + { + "type": "Mention", + "href": "https://another.misskey.instance.com/users/9eklgce5yk", + "name": "@mendako@another.misskey.instance.com" + }, + { + "type": "Mention", + "href": "http://localhost:8080/users/the_mighty_zork", + "name": "@the_mighty_zork@localhost:8080" + } + ], + "replies": { + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true", + "partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "items": [] + } + } +}` + + // Message that mentions 4 people (including receiver), + // but contains no errant links. + spam3 = `{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "ostatus": "http://ostatus.org#", + "atomUri": "ostatus:atomUri", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "sensitive": "as:sensitive", + "toot": "http://joinmastodon.org/ns#", + "votersCount": "toot:votersCount" + } + ], + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "type": "Note", + "summary": null, + "inReplyTo": null, + "published": "2024-02-24T07:06:14Z", + "url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562", + "attributedTo": "http://fossbros-anonymous.io/users/foss_satan", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "http://fossbros-anonymous.io/users/foss_satan/followers", + "https://example.net/users/nityosan", + "https://a.misskey.instance.com/users/9c06ylkgsx", + "https://another.misskey.instance.com/users/9eklgce5yk", + "http://localhost:8080/users/the_mighty_zork" + ], + "sensitive": false, + "atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "inReplyToAtomUri": null, + "content": "<p><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://another.misskey.instance.com/@mendako\" class=\"u-url mention\">@<span>mendako</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\">@<span>the_mighty_zork</span></a></span></p>", + "attachment": [], + "tag": [ + { + "type": "Mention", + "href": "https://example.net/users/nityosan", + "name": "@nityosan@example.net" + }, + { + "type": "Mention", + "href": "https://a.misskey.instance.com/users/9c06ylkgsx", + "name": "@FIzxive@a.misskey.instance.com" + }, + { + "type": "Mention", + "href": "https://another.misskey.instance.com/users/9eklgce5yk", + "name": "@mendako@another.misskey.instance.com" + }, + { + "type": "Mention", + "href": "http://localhost:8080/users/the_mighty_zork", + "name": "@the_mighty_zork@localhost:8080" + } + ], + "replies": { + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true", + "partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "items": [] + } + } +}` + + // Message that mentions 4 people (including receiver), + // contains no errant links, but 1 attachment. + spam4 = `{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "ostatus": "http://ostatus.org#", + "atomUri": "ostatus:atomUri", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "sensitive": "as:sensitive", + "toot": "http://joinmastodon.org/ns#", + "votersCount": "toot:votersCount" + } + ], + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "type": "Note", + "summary": null, + "inReplyTo": null, + "published": "2024-02-24T07:06:14Z", + "url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562", + "attributedTo": "http://fossbros-anonymous.io/users/foss_satan", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "http://fossbros-anonymous.io/users/foss_satan/followers", + "https://example.net/users/nityosan", + "https://a.misskey.instance.com/users/9c06ylkgsx", + "https://another.misskey.instance.com/users/9eklgce5yk", + "http://localhost:8080/users/the_mighty_zork" + ], + "sensitive": false, + "atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "inReplyToAtomUri": null, + "content": "<p><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://another.misskey.instance.com/@mendako\" class=\"u-url mention\">@<span>mendako</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\">@<span>the_mighty_zork</span></a></span></p>", + "attachment": [ + { + "blurhash": "LNJRdVM{00Rj%Mayt7j[4nWBofRj", + "mediaType": "image/jpeg", + "name": "", + "type": "Document", + "url": "http://fossbros-anonymous.io/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/original/01F8MH6NEM8D7527KZAECTCR76.jpg" + } + ], + "tag": [ + { + "type": "Mention", + "href": "https://example.net/users/nityosan", + "name": "@nityosan@example.net" + }, + { + "type": "Mention", + "href": "https://a.misskey.instance.com/users/9c06ylkgsx", + "name": "@FIzxive@a.misskey.instance.com" + }, + { + "type": "Mention", + "href": "https://another.misskey.instance.com/users/9eklgce5yk", + "name": "@mendako@another.misskey.instance.com" + }, + { + "type": "Mention", + "href": "http://localhost:8080/users/the_mighty_zork", + "name": "@the_mighty_zork@localhost:8080" + } + ], + "replies": { + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true", + "partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "items": [] + } + } +}` + + // Message that mentions 4 people (including receiver), + // and contains a errant link, and receiver follows + // another mentioned account. + spam5 = `{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "ostatus": "http://ostatus.org#", + "atomUri": "ostatus:atomUri", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "sensitive": "as:sensitive", + "toot": "http://joinmastodon.org/ns#", + "votersCount": "toot:votersCount" + } + ], + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "type": "Note", + "summary": null, + "inReplyTo": null, + "published": "2024-02-24T07:06:14Z", + "url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562", + "attributedTo": "http://fossbros-anonymous.io/users/foss_satan", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "http://fossbros-anonymous.io/users/foss_satan/followers", + "https://example.net/users/nityosan", + "https://a.misskey.instance.com/users/9c06ylkgsx", + "http://localhost:8080/users/admin", + "http://localhost:8080/users/the_mighty_zork" + ], + "sensitive": false, + "atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "inReplyToAtomUri": null, + "content": "<p><a href=\"https://spammylink.org/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"\">spammylink.org/</span><span class=\"invisible\"></span></a></p><p><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@admin\" class=\"u-url mention\">@<span>admin</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\">@<span>the_mighty_zork</span></a></span></p>", + "attachment": [], + "tag": [ + { + "type": "Mention", + "href": "https://example.net/users/nityosan", + "name": "@nityosan@example.net" + }, + { + "type": "Mention", + "href": "https://a.misskey.instance.com/users/9c06ylkgsx", + "name": "@FIzxive@a.misskey.instance.com" + }, + { + "type": "Mention", + "href": "http://localhost:8080/users/admin", + "name": "@admin@localhost:8080" + }, + { + "type": "Mention", + "href": "http://localhost:8080/users/the_mighty_zork", + "name": "@the_mighty_zork@localhost:8080" + } + ], + "replies": { + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true", + "partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "items": [] + } + } +}` + + // Message that mentions 3 people, contains a + // errant link, and receiver follows another + // mentioned account. However, receiver is not mentioned. + spam6 = `{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "ostatus": "http://ostatus.org#", + "atomUri": "ostatus:atomUri", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "sensitive": "as:sensitive", + "toot": "http://joinmastodon.org/ns#", + "votersCount": "toot:votersCount" + } + ], + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "type": "Note", + "summary": null, + "inReplyTo": null, + "published": "2024-02-24T07:06:14Z", + "url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562", + "attributedTo": "http://fossbros-anonymous.io/users/foss_satan", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "http://fossbros-anonymous.io/users/foss_satan/followers", + "https://example.net/users/nityosan", + "https://a.misskey.instance.com/users/9c06ylkgsx", + "http://localhost:8080/users/admin" + ], + "sensitive": false, + "atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "inReplyToAtomUri": null, + "content": "<p><a href=\"https://spammylink.org/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"\">spammylink.org/</span><span class=\"invisible\"></span></a></p><p><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@admin\" class=\"u-url mention\">@<span>admin</span></a></span></p>", + "attachment": [], + "tag": [ + { + "type": "Mention", + "href": "https://example.net/users/nityosan", + "name": "@nityosan@example.net" + }, + { + "type": "Mention", + "href": "https://a.misskey.instance.com/users/9c06ylkgsx", + "name": "@FIzxive@a.misskey.instance.com" + }, + { + "type": "Mention", + "href": "http://localhost:8080/users/admin", + "name": "@admin@localhost:8080" + } + ], + "replies": { + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true", + "partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "items": [] + } + } +}` + + // Message that mentions 4 people (including receiver), + // and hash a hashtag, but contains no errant links. + spam7 = `{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "ostatus": "http://ostatus.org#", + "atomUri": "ostatus:atomUri", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "sensitive": "as:sensitive", + "toot": "http://joinmastodon.org/ns#", + "votersCount": "toot:votersCount" + } + ], + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "type": "Note", + "summary": null, + "inReplyTo": null, + "published": "2024-02-24T07:06:14Z", + "url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562", + "attributedTo": "http://fossbros-anonymous.io/users/foss_satan", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "http://fossbros-anonymous.io/users/foss_satan/followers", + "https://example.net/users/nityosan", + "https://a.misskey.instance.com/users/9c06ylkgsx", + "https://another.misskey.instance.com/users/9eklgce5yk", + "http://localhost:8080/users/the_mighty_zork" + ], + "sensitive": false, + "atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "inReplyToAtomUri": null, + "content": "<p><a href=\"https://fossbros-anonymous.io/tags/gotosocial\" class=\"mention hashtag\" rel=\"tag\">#<span>gotosocial</span></a> smells<br/><br/><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://another.misskey.instance.com/@mendako\" class=\"u-url mention\">@<span>mendako</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\">@<span>the_mighty_zork</span></a></span></p>", + "attachment": [], + "tag": [ + { + "type": "Mention", + "href": "https://example.net/users/nityosan", + "name": "@nityosan@example.net" + }, + { + "type": "Mention", + "href": "https://a.misskey.instance.com/users/9c06ylkgsx", + "name": "@FIzxive@a.misskey.instance.com" + }, + { + "type": "Mention", + "href": "https://another.misskey.instance.com/users/9eklgce5yk", + "name": "@mendako@another.misskey.instance.com" + }, + { + "type": "Mention", + "href": "http://localhost:8080/users/the_mighty_zork", + "name": "@the_mighty_zork@localhost:8080" + }, + { + "type": "Hashtag", + "href": "https://fossbros-anonymous.io/tags/gotosocial", + "name": "#gotosocial" + } + ], + "replies": { + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true", + "partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "items": [] + } + } + }` + + // Same as spam7, except message doesn't + // have a hashtag in the tags array. + spam8 = `{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "ostatus": "http://ostatus.org#", + "atomUri": "ostatus:atomUri", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "sensitive": "as:sensitive", + "toot": "http://joinmastodon.org/ns#", + "votersCount": "toot:votersCount" + } + ], + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "type": "Note", + "summary": null, + "inReplyTo": null, + "published": "2024-02-24T07:06:14Z", + "url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562", + "attributedTo": "http://fossbros-anonymous.io/users/foss_satan", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "http://fossbros-anonymous.io/users/foss_satan/followers", + "https://example.net/users/nityosan", + "https://a.misskey.instance.com/users/9c06ylkgsx", + "https://another.misskey.instance.com/users/9eklgce5yk", + "http://localhost:8080/users/the_mighty_zork" + ], + "sensitive": false, + "atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562", + "inReplyToAtomUri": null, + "content": "<p><a href=\"https://fossbros-anonymous.io/tags/gotosocial\" class=\"mention hashtag\" rel=\"tag\">#<span>gotosocial</span></a> smells<br/><br/><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://another.misskey.instance.com/@mendako\" class=\"u-url mention\">@<span>mendako</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\">@<span>the_mighty_zork</span></a></span></p>", + "attachment": [], + "tag": [ + { + "type": "Mention", + "href": "https://example.net/users/nityosan", + "name": "@nityosan@example.net" + }, + { + "type": "Mention", + "href": "https://a.misskey.instance.com/users/9c06ylkgsx", + "name": "@FIzxive@a.misskey.instance.com" + }, + { + "type": "Mention", + "href": "https://another.misskey.instance.com/users/9eklgce5yk", + "name": "@mendako@another.misskey.instance.com" + }, + { + "type": "Mention", + "href": "http://localhost:8080/users/the_mighty_zork", + "name": "@the_mighty_zork@localhost:8080" + } + ], + "replies": { + "id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true", + "partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies", + "items": [] + } + } + }` +) + +func (suite *StatusableTestSuite) TestStatusableOK() { + var ( + ctx = context.Background() + receiver = suite.testAccounts["local_account_1"] + requester = suite.testAccounts["remote_account_1"] + ) + + type testStruct struct { + message string + check func(error) + } + + for _, test := range []testStruct{ + { + // SPAM: status mentions 5 or more people + message: spam1, + check: func(err error) { + suite.True(gtserror.IsSpam(err), "expected Spam, got %+v", err) + }, + }, + { + // SPAM: receiver doesn't know a mentioned account, and status has attachments or errant links + message: spam2, + check: func(err error) { + suite.True(gtserror.IsSpam(err), "expected Spam, got %+v", err) + }, + }, + { + // NOT SPAM: receiver doesn't know a mentioned account, but status has no attachments or errant links + message: spam3, + check: func(err error) { + suite.NoError(err, "expected not spam, got %+v", err) + }, + }, + { + // SPAM: receiver doesn't know a mentioned account, and status has attachments or errant links + message: spam4, + check: func(err error) { + suite.True(gtserror.IsSpam(err), "expected Spam, got %+v", err) + }, + }, + { + // NOT SPAM: receiver knows a mentioned account + message: spam5, + check: func(err error) { + suite.NoError(err, "expected not spam, got %+v", err) + }, + }, + { + // SPAM: receiver does not follow requester, and is not mentioned + message: spam6, + check: func(err error) { + suite.True(gtserror.IsNotRelevant(err), "expected NotRelevant, got %+v", err) + }, + }, + { + // NOT SPAM: receiver doesn't know a mentioned account, but status has no attachments or errant links + message: spam7, + check: func(err error) { + suite.NoError(err, "expected not spam, got %+v", err) + }, + }, + { + // SPAM: receiver doesn't know a mentioned account, and status has attachments or errant links + message: spam8, + check: func(err error) { + suite.True(gtserror.IsSpam(err), "expected Spam, got %+v", err) + }, + }, + } { + rc := io.NopCloser(bytes.NewReader([]byte(test.message))) + + statusable, err := ap.ResolveStatusable(ctx, rc) + if err != nil { + suite.FailNow(err.Error()) + } + + err = suite.filter.StatusableOK(ctx, receiver, requester, statusable) + test.check(err) + } + + // Put a follow in place from receiver to requester. + fID := id.NewULID() + if err := suite.state.DB.PutFollow(ctx, >smodel.Follow{ + ID: fID, + URI: "http://localhost:8080/users/the_mighty_zork/follows/" + fID, + AccountID: receiver.ID, + TargetAccountID: requester.ID, + }); err != nil { + suite.FailNow(err.Error()) + } + + // Run all the tests again. They should all + // be OK since receiver now follows requester. + for _, test := range []testStruct{ + { + message: spam1, + check: func(err error) { + suite.NoError(err, "expected not spam, got %+v", err) + }, + }, + { + message: spam2, + check: func(err error) { + suite.NoError(err, "expected not spam, got %+v", err) + }, + }, + { + message: spam3, + check: func(err error) { + suite.NoError(err, "expected not spam, got %+v", err) + }, + }, + { + message: spam4, + check: func(err error) { + suite.NoError(err, "expected not spam, got %+v", err) + }, + }, + { + message: spam5, + check: func(err error) { + suite.NoError(err, "expected not spam, got %+v", err) + }, + }, + { + message: spam6, + check: func(err error) { + suite.NoError(err, "expected not spam, got %+v", err) + }, + }, + { + message: spam7, + check: func(err error) { + suite.NoError(err, "expected not spam, got %+v", err) + }, + }, + { + message: spam8, + check: func(err error) { + suite.NoError(err, "expected not spam, got %+v", err) + }, + }, + } { + rc := io.NopCloser(bytes.NewReader([]byte(test.message))) + + statusable, err := ap.ResolveStatusable(ctx, rc) + if err != nil { + suite.FailNow(err.Error()) + } + + err = suite.filter.StatusableOK(ctx, receiver, requester, statusable) + test.check(err) + } +} + +func TestStatusableTestSuite(t *testing.T) { + suite.Run(t, &StatusableTestSuite{}) +} diff --git a/internal/visibility/account.go b/internal/filter/visibility/account.go index 410daa1ce..410daa1ce 100644 --- a/internal/visibility/account.go +++ b/internal/filter/visibility/account.go diff --git a/internal/visibility/boostable.go b/internal/filter/visibility/boostable.go index 7c8bda324..7c8bda324 100644 --- a/internal/visibility/boostable.go +++ b/internal/filter/visibility/boostable.go diff --git a/internal/visibility/boostable_test.go b/internal/filter/visibility/boostable_test.go index fd29e7305..fd29e7305 100644 --- a/internal/visibility/boostable_test.go +++ b/internal/filter/visibility/boostable_test.go diff --git a/internal/visibility/filter.go b/internal/filter/visibility/filter.go index c9f007ccf..c9f007ccf 100644 --- a/internal/visibility/filter.go +++ b/internal/filter/visibility/filter.go diff --git a/internal/visibility/filter_test.go b/internal/filter/visibility/filter_test.go index 41f06079a..2d83ea759 100644 --- a/internal/visibility/filter_test.go +++ b/internal/filter/visibility/filter_test.go @@ -20,9 +20,9 @@ package visibility_test import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/state" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/visibility/home_timeline.go b/internal/filter/visibility/home_timeline.go index 0a3fbde4e..0a3fbde4e 100644 --- a/internal/visibility/home_timeline.go +++ b/internal/filter/visibility/home_timeline.go diff --git a/internal/visibility/home_timeline_test.go b/internal/filter/visibility/home_timeline_test.go index d8211c8dd..d8211c8dd 100644 --- a/internal/visibility/home_timeline_test.go +++ b/internal/filter/visibility/home_timeline_test.go diff --git a/internal/visibility/public_timeline.go b/internal/filter/visibility/public_timeline.go index bad7cf991..bad7cf991 100644 --- a/internal/visibility/public_timeline.go +++ b/internal/filter/visibility/public_timeline.go diff --git a/internal/visibility/status.go b/internal/filter/visibility/status.go index 5e2052ae4..5e2052ae4 100644 --- a/internal/visibility/status.go +++ b/internal/filter/visibility/status.go diff --git a/internal/visibility/status_test.go b/internal/filter/visibility/status_test.go index ad6bc66df..ad6bc66df 100644 --- a/internal/visibility/status_test.go +++ b/internal/filter/visibility/status_test.go diff --git a/internal/visibility/tag_timeline.go b/internal/filter/visibility/tag_timeline.go index b2c9dbf29..b2c9dbf29 100644 --- a/internal/visibility/tag_timeline.go +++ b/internal/filter/visibility/tag_timeline.go diff --git a/internal/gtserror/error.go b/internal/gtserror/error.go index 9fd9812dc..dc4c5a504 100644 --- a/internal/gtserror/error.go +++ b/internal/gtserror/error.go @@ -37,6 +37,8 @@ const ( wrongTypeKey smtpKey malformedKey + notRelevantKey + spamKey ) // IsUnretrievable indicates that a call to retrieve a resource @@ -127,3 +129,30 @@ func IsMalformed(err error) bool { func SetMalformed(err error) error { return errors.WithValue(err, malformedKey, struct{}{}) } + +// IsNotRelevant checks error for a stored "notRelevant" flag. +// This error is used when determining whether or not to store +// + process an incoming AP message. +func IsNotRelevant(err error) bool { + _, ok := errors.Value(err, notRelevantKey).(struct{}) + return ok +} + +// SetNotRelevant will wrap the given error to store a "notRelevant" flag, +// returning wrapped error. See IsNotRelevant() for example use-cases. +func SetNotRelevant(err error) error { + return errors.WithValue(err, notRelevantKey, struct{}{}) +} + +// IsSpam checks error for a stored "spam" flag. This error is used when +// determining whether or not to store + process an incoming AP message. +func IsSpam(err error) bool { + _, ok := errors.Value(err, spamKey).(struct{}) + return ok +} + +// SetSpam will wrap the given error to store a "spam" flag, +// returning wrapped error. See IsSpam() for example use-cases. +func SetSpam(err error) error { + return errors.WithValue(err, spamKey, struct{}{}) +} diff --git a/internal/gtsmodel/tag.go b/internal/gtsmodel/tag.go index 514389f23..470bee094 100644 --- a/internal/gtsmodel/tag.go +++ b/internal/gtsmodel/tag.go @@ -27,4 +27,5 @@ type Tag struct { Name string `bun:",unique,nullzero,notnull"` // (lowercase) name of the tag without the hash prefix Useable *bool `bun:",nullzero,notnull,default:true"` // Tag is useable on this instance. Listable *bool `bun:",nullzero,notnull,default:true"` // Tagged statuses can be listed on this instance. + Href string `bun:"-"` // Href of the hashtag. Will only be set on freshly-extracted hashtags from remote AP messages. Not stored in the database. } diff --git a/internal/media/media_test.go b/internal/media/media_test.go index a719116d1..0980bf295 100644 --- a/internal/media/media_test.go +++ b/internal/media/media_test.go @@ -20,13 +20,13 @@ package media_test import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" gtsmodel "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/processing/account/account.go b/internal/processing/account/account.go index ea6882271..79f6ecfc1 100644 --- a/internal/processing/account/account.go +++ b/internal/processing/account/account.go @@ -19,6 +19,7 @@ package account import ( "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -26,7 +27,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/text" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" ) // Processor wraps functionality for updating, creating, and deleting accounts in response to API requests. diff --git a/internal/processing/account/account_test.go b/internal/processing/account/account_test.go index 2eea5f438..244fc89d6 100644 --- a/internal/processing/account/account_test.go +++ b/internal/processing/account/account_test.go @@ -24,6 +24,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/messages" @@ -35,7 +36,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/processing/admin/admin_test.go b/internal/processing/admin/admin_test.go index 1bd355b3d..97b055158 100644 --- a/internal/processing/admin/admin_test.go +++ b/internal/processing/admin/admin_test.go @@ -23,6 +23,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/messages" @@ -33,7 +34,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/processing/common/common.go b/internal/processing/common/common.go index 5abf50674..e4a49cc45 100644 --- a/internal/processing/common/common.go +++ b/internal/processing/common/common.go @@ -19,9 +19,9 @@ package common import ( "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" ) // Processor provides a processor with logic diff --git a/internal/processing/fedi/fedi.go b/internal/processing/fedi/fedi.go index eeef94113..b08f0eefd 100644 --- a/internal/processing/fedi/fedi.go +++ b/internal/processing/fedi/fedi.go @@ -19,10 +19,10 @@ package fedi import ( "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/processing/common" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" ) type Processor struct { diff --git a/internal/processing/polls/poll_test.go b/internal/processing/polls/poll_test.go index 59e9aeb60..847612503 100644 --- a/internal/processing/polls/poll_test.go +++ b/internal/processing/polls/poll_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/suite" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" @@ -32,7 +33,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/processing/polls" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/processing/processor.go b/internal/processing/processor.go index d4ef41bea..bb46d31a9 100644 --- a/internal/processing/processor.go +++ b/internal/processing/processor.go @@ -21,6 +21,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/cleaner" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" mm "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -42,7 +43,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/text" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" ) // Processor groups together processing functions and diff --git a/internal/processing/processor_test.go b/internal/processing/processor_test.go index 148ec42ed..2a1d47e46 100644 --- a/internal/processing/processor_test.go +++ b/internal/processing/processor_test.go @@ -25,6 +25,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -34,7 +35,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/stream" "github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/processing/search/search.go b/internal/processing/search/search.go index 5321cb89d..6c0ab2457 100644 --- a/internal/processing/search/search.go +++ b/internal/processing/search/search.go @@ -19,9 +19,9 @@ package search import ( "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" ) type Processor struct { diff --git a/internal/processing/status/status.go b/internal/processing/status/status.go index eaeb12b39..18f8e741a 100644 --- a/internal/processing/status/status.go +++ b/internal/processing/status/status.go @@ -19,13 +19,13 @@ package status import ( "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/processing/common" "github.com/superseriousbusiness/gotosocial/internal/processing/polls" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/text" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" ) type Processor struct { diff --git a/internal/processing/status/status_test.go b/internal/processing/status/status_test.go index 37c7d6147..171e4b488 100644 --- a/internal/processing/status/status_test.go +++ b/internal/processing/status/status_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/processing" @@ -31,7 +32,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/processing/timeline/home.go b/internal/processing/timeline/home.go index 126c9f668..d12dd98c4 100644 --- a/internal/processing/timeline/home.go +++ b/internal/processing/timeline/home.go @@ -23,6 +23,7 @@ import ( apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -30,7 +31,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/timeline" "github.com/superseriousbusiness/gotosocial/internal/typeutils" "github.com/superseriousbusiness/gotosocial/internal/util" - "github.com/superseriousbusiness/gotosocial/internal/visibility" ) // HomeTimelineGrab returns a function that satisfies GrabFunction for home timelines. diff --git a/internal/processing/timeline/list.go b/internal/processing/timeline/list.go index 64de288db..7356d1978 100644 --- a/internal/processing/timeline/list.go +++ b/internal/processing/timeline/list.go @@ -23,6 +23,7 @@ import ( apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -30,7 +31,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/timeline" "github.com/superseriousbusiness/gotosocial/internal/typeutils" "github.com/superseriousbusiness/gotosocial/internal/util" - "github.com/superseriousbusiness/gotosocial/internal/visibility" ) // ListTimelineGrab returns a function that satisfies GrabFunction for list timelines. diff --git a/internal/processing/timeline/timeline.go b/internal/processing/timeline/timeline.go index bf9864398..b791791ee 100644 --- a/internal/processing/timeline/timeline.go +++ b/internal/processing/timeline/timeline.go @@ -18,9 +18,9 @@ package timeline import ( + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" ) type Processor struct { diff --git a/internal/processing/workers/surface.go b/internal/processing/workers/surface.go index 4012dcacc..09162b131 100644 --- a/internal/processing/workers/surface.go +++ b/internal/processing/workers/surface.go @@ -19,10 +19,10 @@ package workers import ( "github.com/superseriousbusiness/gotosocial/internal/email" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/processing/stream" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" ) // surface wraps functions for 'surfacing' the result diff --git a/internal/processing/workers/workers.go b/internal/processing/workers/workers.go index 02e2f3f5b..c0612de27 100644 --- a/internal/processing/workers/workers.go +++ b/internal/processing/workers/workers.go @@ -20,12 +20,12 @@ package workers import ( "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/processing/account" "github.com/superseriousbusiness/gotosocial/internal/processing/media" "github.com/superseriousbusiness/gotosocial/internal/processing/stream" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/internal/workers" ) diff --git a/internal/processing/workers/workers_test.go b/internal/processing/workers/workers_test.go index cbdd10f9a..3ab19d38d 100644 --- a/internal/processing/workers/workers_test.go +++ b/internal/processing/workers/workers_test.go @@ -25,6 +25,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -34,7 +35,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/stream" "github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/timeline/timeline_test.go b/internal/timeline/timeline_test.go index d142ed1ff..ffc6d6e53 100644 --- a/internal/timeline/timeline_test.go +++ b/internal/timeline/timeline_test.go @@ -22,10 +22,10 @@ import ( "sort" "github.com/stretchr/testify/suite" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/transport/transport_test.go b/internal/transport/transport_test.go index b68f9695a..3a884d53f 100644 --- a/internal/transport/transport_test.go +++ b/internal/transport/transport_test.go @@ -24,6 +24,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/processing" @@ -31,7 +32,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/typeutils/converter_test.go b/internal/typeutils/converter_test.go index 38376c87a..716a39c29 100644 --- a/internal/typeutils/converter_test.go +++ b/internal/typeutils/converter_test.go @@ -21,11 +21,11 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/activity/streams/vocab" "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/processing" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go index 9003dcca3..519888e21 100644 --- a/internal/typeutils/internaltofrontend_test.go +++ b/internal/typeutils/internaltofrontend_test.go @@ -2000,8 +2000,8 @@ func (suite *InternalToFrontendTestSuite) TestRelationshipFollowRequested() { b, err := json.MarshalIndent(relationship, "", " ") if err != nil { - suite.FailNow(err.Error()) - } + suite.FailNow(err.Error()) + } suite.Equal(`{ "id": "01F8MH5NBDF2MV7CTC4Q5128HF", @@ -2034,10 +2034,10 @@ func (suite *InternalToFrontendTestSuite) TestRelationshipFollowRequested() { b, err = json.MarshalIndent(relationship, "", " ") if err != nil { - suite.FailNow(err.Error()) - } + suite.FailNow(err.Error()) + } - suite.Equal(`{ + suite.Equal(`{ "id": "01F8MH17FWEB39HZJ76B6VXSKF", "following": false, "showing_reblogs": false, |