diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index a27a19418a..b4a5b3f23d 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -48,8 +48,6 @@ class ActivityPub::Activity::Create < ActivityPub::Activity def create_status return reject_payload! if unsupported_object_type? || non_matching_uri_hosts?(@account.uri, object_uri) || tombstone_exists? || !related_to_local_activity? - return reject_payload! if (reply_to_local? || reply_to_local_account?) && reject_reply_to_local? - return reject_payload! if (!reply_to_local_account_following? || !reply_to_local_status_following?) && reject_reply_exclude_followers? with_redis_lock("create:#{object_uri}") do return if delete_arrived_first?(object_uri) || poll_vote? @@ -63,7 +61,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity end end - @status + @status || reject_payload! end def audience_to @@ -90,7 +88,9 @@ class ActivityPub::Activity::Create < ActivityPub::Activity process_tags process_audience - return unless valid_status? + return nil unless valid_status? + return nil if (reply_to_local? || reply_to_local_account? || reply_to_local_from_tags?) && reject_reply_to_local? + return nil if (!reply_to_local_account_following? || !reply_to_local_status_following? || !reply_to_local_from_tags_following?) && reject_reply_exclude_followers? ApplicationRecord.transaction do @status = Status.create!(@params) @@ -148,14 +148,6 @@ class ActivityPub::Activity::Create < ActivityPub::Activity !Admin::NgWord.reject?("#{@params[:spoiler_text]}\n#{@params[:text]}") && !Admin::NgWord.hashtag_reject?(@tags.size) end - def reply_to_local_account? - accounts_in_audience.any?(&:local?) - end - - def reply_to_local_account_following? - !reply_to_local_account? || accounts_in_audience.none? { |account| account.local? && !account.following?(@account) } - end - def accounts_in_audience return @accounts_in_audience if @accounts_in_audience @@ -420,6 +412,22 @@ class ActivityPub::Activity::Create < ActivityPub::Activity @skip_download ||= DomainBlock.reject_media?(@account.domain) end + def reply_to_local_account? + accounts_in_audience.any?(&:local?) + end + + def reply_to_local_account_following? + !reply_to_local_account? || accounts_in_audience.none? { |account| account.local? && !account.following?(@account) } + end + + def reply_to_local_from_tags? + (@mentions.nil? || @mentions.any? { |m| m.account.local? }) + end + + def reply_to_local_from_tags_following? + (@mentions.nil? || @mentions.none? { |m| m.account.local? && !m.account.following?(@account) }) + end + def reply_to_local? !replied_to_status.nil? && replied_to_status.account.local? end diff --git a/spec/lib/activitypub/activity/announce_spec.rb b/spec/lib/activitypub/activity/announce_spec.rb index 8ad892975d..5e3f679af1 100644 --- a/spec/lib/activitypub/activity/announce_spec.rb +++ b/spec/lib/activitypub/activity/announce_spec.rb @@ -97,6 +97,22 @@ RSpec.describe ActivityPub::Activity::Announce do end end + context 'with domain block' do + before do + Fabricate(:account) + Fabricate(:domain_block, domain: 'example.com', severity: :suspend) + subject.perform + end + + let(:object_json) do + ActivityPub::TagManager.instance.uri_for(status) + end + + it 'does not creates a reblog by sender of status', pending: 'considering spec' do + expect(sender.reblogged?(status)).to be false + end + end + context 'when the status belongs to a local user' do before do subject.perform diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb index 354787f820..9c4bba3e62 100644 --- a/spec/lib/activitypub/activity/create_spec.rb +++ b/spec/lib/activitypub/activity/create_spec.rb @@ -29,10 +29,11 @@ RSpec.describe ActivityPub::Activity::Create do subject { described_class.new(json, sender) } let(:sender_software) { 'mastodon' } + let(:custom_before) { false } before do Fabricate(:instance_info, domain: 'example.com', software: sender_software) - subject.perform + subject.perform unless custom_before end context 'when object has been edited' do @@ -648,6 +649,80 @@ RSpec.describe ActivityPub::Activity::Create do end end + context 'with mentions domain block reject_reply' do + before do + Fabricate(:domain_block, domain: 'example.com', severity: :noop, reject_reply: true) + subject.perform + end + + let(:custom_before) { true } + let(:recipient) { Fabricate(:account) } + + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + content: 'Lorem ipsum', + tag: [ + { + type: 'Mention', + href: ActivityPub::TagManager.instance.uri_for(recipient), + }, + ], + } + end + + it 'creates status' do + status = sender.statuses.first + + expect(status).to be_nil + end + end + + context 'with mentions domain block reject_reply_exclude_followers' do + before do + Fabricate(:domain_block, domain: 'example.com', severity: :noop, reject_reply_exclude_followers: true) + recipient.follow!(sender) if follow + subject.perform + end + + let(:custom_before) { true } + let(:follow) { false } + let(:recipient) { Fabricate(:account) } + + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + content: 'Lorem ipsum', + tag: [ + { + type: 'Mention', + href: ActivityPub::TagManager.instance.uri_for(recipient), + }, + ], + } + end + + context 'when follower' do + let(:follow) { true } + + it 'creates status' do + status = sender.statuses.first + + expect(status).to_not be_nil + end + end + + context 'when not follower' do + it 'creates status' do + status = sender.statuses.first + + expect(status).to be_nil + end + end + end + context 'with media attachments' do let(:object_json) do { diff --git a/spec/lib/activitypub/activity/follow_spec.rb b/spec/lib/activitypub/activity/follow_spec.rb index c1829cb8d7..ee007082a7 100644 --- a/spec/lib/activitypub/activity/follow_spec.rb +++ b/spec/lib/activitypub/activity/follow_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe ActivityPub::Activity::Follow do - let(:sender) { Fabricate(:account) } + let(:sender) { Fabricate(:account, domain: 'example.com', inbox_url: 'https://example.com/inbox') } let(:recipient) { Fabricate(:account) } let(:json) do @@ -82,6 +82,35 @@ RSpec.describe ActivityPub::Activity::Follow do expect(sender.follow_requests.find_by(target_account: recipient).uri).to eq 'foo' end end + + context 'when domain block reject_straight_follow' do + before do + Fabricate(:domain_block, domain: 'example.com', reject_straight_follow: true) + subject.perform + end + + it 'does not create a follow from sender to recipient' do + expect(sender.following?(recipient)).to be false + end + + it 'creates a follow request' do + expect(sender.requested?(recipient)).to be true + expect(sender.follow_requests.find_by(target_account: recipient).uri).to eq 'foo' + end + end + + context 'when domain block reject_new_follow' do + before do + Fabricate(:domain_block, domain: 'example.com', reject_new_follow: true) + stub_request(:post, 'https://example.com/inbox').to_return(status: 200, body: '', headers: {}) + subject.perform + end + + it 'does not create a follow from sender to recipient' do + expect(sender.following?(recipient)).to be false + expect(sender.requested?(recipient)).to be false + end + end end context 'when a follow relationship already exists' do diff --git a/spec/lib/activitypub/activity/like_spec.rb b/spec/lib/activitypub/activity/like_spec.rb index 640d61ab36..7635616b16 100644 --- a/spec/lib/activitypub/activity/like_spec.rb +++ b/spec/lib/activitypub/activity/like_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe ActivityPub::Activity::Like do - let(:sender) { Fabricate(:account) } + let(:sender) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/') } let(:recipient) { Fabricate(:account) } let(:status) { Fabricate(:status, account: recipient) } @@ -28,4 +28,30 @@ RSpec.describe ActivityPub::Activity::Like do expect(sender.favourited?(status)).to be true end end + + describe '#perform when domain_block' do + subject { described_class.new(json, sender) } + + before do + Fabricate(:domain_block, domain: 'example.com', severity: :noop, reject_favourite: true) + subject.perform + end + + it 'does not create a favourite from sender to status' do + expect(sender.favourited?(status)).to be false + end + end + + describe '#perform when normal domain_block' do + subject { described_class.new(json, sender) } + + before do + Fabricate(:domain_block, domain: 'example.com', severity: :suspend) + subject.perform + end + + it 'does not create a favourite from sender to status', pending: 'considering spec' do + expect(sender.favourited?(status)).to be false + end + end end diff --git a/spec/lib/status_reach_finder_spec.rb b/spec/lib/status_reach_finder_spec.rb index 0db2666b9e..57946d3a70 100644 --- a/spec/lib/status_reach_finder_spec.rb +++ b/spec/lib/status_reach_finder_spec.rb @@ -154,5 +154,67 @@ describe StatusReachFinder do end end end + + context 'with extended domain block' do + subject do + described_class.new(status) + end + + before do + bob.follow!(alice) + tom.follow!(alice) + Fabricate(:domain_block, domain: 'example.com', severity: 'noop', **properties) + end + + let(:properties) { {} } + let(:visibility) { :public } + let(:searchability) { :public } + let(:dissubscribable) { false } + let(:spoiler_text) { '' } + let(:status) { Fabricate(:status, account: alice, visibility: visibility, searchability: searchability, spoiler_text: spoiler_text) } + let(:alice) { Fabricate(:account, username: 'alice', dissubscribable: dissubscribable) } + let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com', protocol: :activitypub, uri: 'https://example.com/', inbox_url: 'https://example.com/inbox') } + let(:tom) { Fabricate(:account, username: 'tom', domain: 'tom.com', protocol: :activitypub, uri: 'https://tom.com/', inbox_url: 'https://tom.com/inbox') } + + context 'when reject_send_not_public_searchability' do + let(:properties) { { reject_send_not_public_searchability: true } } + let(:searchability) { :private } + + it 'does not include the inbox of blocked domain' do + expect(subject.inboxes).to_not include 'https://example.com/inbox' + expect(subject.inboxes).to include 'https://tom.com/inbox' + end + end + + context 'when reject_send_public_unlisted' do + let(:properties) { { reject_send_public_unlisted: true } } + let(:visibility) { :public_unlisted } + + it 'does not include the inbox of blocked domain' do + expect(subject.inboxes).to_not include 'https://example.com/inbox' + expect(subject.inboxes).to include 'https://tom.com/inbox' + end + + context 'when reject_send_dissubscribable' do + let(:properties) { { reject_send_dissubscribable: true } } + let(:dissubscribable) { true } + + it 'does not include the inbox of blocked domain' do + expect(subject.inboxes).to_not include 'https://example.com/inbox' + expect(subject.inboxes).to include 'https://tom.com/inbox' + end + end + + context 'when reject_send_sensitive' do + let(:properties) { { reject_send_sensitive: true } } + let(:spoiler_text) { 'CW' } + + it 'does not include the inbox of blocked domain' do + expect(subject.inboxes).to_not include 'https://example.com/inbox' + expect(subject.inboxes).to include 'https://tom.com/inbox' + end + end + end + end end end