about summary refs log tree commit diff
path: root/spec/services
diff options
context:
space:
mode:
authorClaire <claire.github-309c@sitedethib.com>2023-02-10 22:16:37 +0100
committerGitHub <noreply@github.com>2023-02-10 22:16:37 +0100
commit0c9eac80d887cdf7f1efa582b21006248d2f83eb (patch)
tree208ac4a6745b34f45e5d69b96262ce92aa59c59f /spec/services
parent719bb799be9a03b767bd9f55d30abf2b7bc318ec (diff)
Fix unbounded recursion in post discovery (#23506)
* Add a limit to how many posts can get fetched as a result of a single request

* Add tests

* Always pass `request_id` when processing `Announce` activities

---------

Co-authored-by: nametoolong <nametoolong@users.noreply.github.com>
Diffstat (limited to 'spec/services')
-rw-r--r--spec/services/activitypub/fetch_remote_status_service_spec.rb94
-rw-r--r--spec/services/fetch_remote_status_service_spec.rb2
2 files changed, 95 insertions, 1 deletions
diff --git a/spec/services/activitypub/fetch_remote_status_service_spec.rb b/spec/services/activitypub/fetch_remote_status_service_spec.rb
index 7359ca0b4..a81dcad81 100644
--- a/spec/services/activitypub/fetch_remote_status_service_spec.rb
+++ b/spec/services/activitypub/fetch_remote_status_service_spec.rb
@@ -223,4 +223,98 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
       end
     end
   end
+
+  context 'statuses referencing other statuses' do
+    before do
+      stub_const 'ActivityPub::FetchRemoteStatusService::DISCOVERIES_PER_REQUEST', 5
+    end
+
+    context 'using inReplyTo' do
+      let(:object) do
+        {
+          '@context': 'https://www.w3.org/ns/activitystreams',
+          id: "https://foo.bar/@foo/1",
+          type: 'Note',
+          content: 'Lorem ipsum',
+          inReplyTo: 'https://foo.bar/@foo/2',
+          attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
+        }
+      end
+
+      before do
+        8.times do |i|
+          status_json = {
+            '@context': 'https://www.w3.org/ns/activitystreams',
+            id: "https://foo.bar/@foo/#{i}",
+            type: 'Note',
+            content: 'Lorem ipsum',
+            inReplyTo: "https://foo.bar/@foo/#{i + 1}",
+            attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
+            to: 'as:Public',
+          }.with_indifferent_access
+          stub_request(:get, "https://foo.bar/@foo/#{i}").to_return(status: 200, body: status_json.to_json, headers: { 'Content-Type': 'application/activity+json' })
+        end
+      end
+
+      it 'creates at least some statuses' do
+        expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) }.to change { sender.statuses.count }.by_at_least(2)
+      end
+
+      it 'creates no more account than the limit allows' do
+        expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) }.to change { sender.statuses.count }.by_at_most(5)
+      end
+    end
+
+    context 'using replies' do
+      let(:object) do
+        {
+          '@context': 'https://www.w3.org/ns/activitystreams',
+          id: "https://foo.bar/@foo/1",
+          type: 'Note',
+          content: 'Lorem ipsum',
+          replies: {
+            type: 'Collection',
+            id: 'https://foo.bar/@foo/1/replies',
+            first: {
+              type: 'CollectionPage',
+              partOf: 'https://foo.bar/@foo/1/replies',
+              items: ['https://foo.bar/@foo/2'],
+            },
+          },
+          attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
+        }
+      end
+
+      before do
+        8.times do |i|
+          status_json = {
+            '@context': 'https://www.w3.org/ns/activitystreams',
+            id: "https://foo.bar/@foo/#{i}",
+            type: 'Note',
+            content: 'Lorem ipsum',
+            replies: {
+              type: 'Collection',
+              id: "https://foo.bar/@foo/#{i}/replies",
+              first: {
+                type: 'CollectionPage',
+                partOf: "https://foo.bar/@foo/#{i}/replies",
+                items: ["https://foo.bar/@foo/#{i+1}"],
+              },
+            },
+            attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
+            to: 'as:Public',
+          }.with_indifferent_access
+          stub_request(:get, "https://foo.bar/@foo/#{i}").to_return(status: 200, body: status_json.to_json, headers: { 'Content-Type': 'application/activity+json' })
+        end
+      end
+
+      it 'creates at least some statuses' do
+        expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) }.to change { sender.statuses.count }.by_at_least(2)
+      end
+
+      it 'creates no more account than the limit allows' do
+        expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) }.to change { sender.statuses.count }.by_at_most(5)
+      end
+    end
+  end
 end
diff --git a/spec/services/fetch_remote_status_service_spec.rb b/spec/services/fetch_remote_status_service_spec.rb
index fe5f1aed1..4f6ad6496 100644
--- a/spec/services/fetch_remote_status_service_spec.rb
+++ b/spec/services/fetch_remote_status_service_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe FetchRemoteStatusService, type: :service do
   end
 
   context 'protocol is :activitypub' do
-    subject { described_class.new.call(note[:id], prefetched_body) }
+    subject { described_class.new.call(note[:id], prefetched_body: prefetched_body) }
     let(:prefetched_body) { Oj.dump(note) }
 
     before do