CVE-2026-47777
Description
Missing origin check in Mastodon's remote Collection consent verification lets attackers forge FeatureAuthorization to falsely include accounts.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Missing origin check in Mastodon's remote Collection consent verification lets attackers forge FeatureAuthorization to falsely include accounts.
Vulnerability
Mastodon, an open-source ActivityPub social network server (versions prior to 4.6.0-beta.1 on the main branch or nightly builds with EXPERIMENTAL_FEATURES including collections), fails to verify that the featureAuthorization object in a collection item references the same featuredObject as the item itself. The non_matching_uri_hosts? check was missing for the actor and approval URIs, allowing a forged authorization residing on the same domain as a different object to pass validation [1], [2].
Exploitation
An attacker must control a remote ActivityPub server capable of serving a forged FeatureAuthorization object. The attacker crafts a collection item where the featureAuthorization URI points to a legitimate authorization for a different object on the same domain, while the actual featuredObject is not authorized. The missing comparison between featuredObject and featureAuthorization's target allows the check to pass [1], [2].
Impact
A successful attack bypasses consent verification, making it appear that a remote account has consented to be featured in a Collection when it has not. The attacker can falsely include accounts in collections, violating user consent expectations, but does not lead to direct data disclosure, code execution, or privilege escalation [2].
Mitigation
The fix is included in commit 22203f8, which adds a non_matching_actor_and_approval_uris? check to ensure the featuredObject matches the featureAuthorization's target [1]. This is present in nightly images from nightly.2026-05-21-security onward and will be part of the stable release 4.6.0-beta.1 [2]. Servers not using the experimental EXPERIMENTAL_FEATURES=collections are unaffected. No workaround is available for affected builds; administrators should update to a patched version or disable the experimental feature [2].
AI Insight generated on Jun 15, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
122203f8aeb03Improve collection item verification (#39096)
4 files changed · +51 −5
app/services/activitypub/process_featured_item_service.rb+9 −2 modified@@ -11,7 +11,10 @@ def call(collection, uri_or_object, position: nil, request_id: nil) @collection = collection @request_id = request_id @item_json = uri_or_object.is_a?(String) ? fetch_resource(uri_or_object, true) : uri_or_object + @actor_uri = value_or_id(@item_json['featuredObject']) + @approval_uri = value_or_id(@item_json['featureAuthorization']) return if non_matching_uri_hosts?(@collection.uri, @item_json['id']) + return if non_matching_actor_and_approval_uris? with_redis_lock("collection_item:#{@item_json['id']}") do @collection_item = existing_item || pre_approved_item || new_item @@ -22,8 +25,6 @@ def call(collection, uri_or_object, position: nil, request_id: nil) object_uri: value_or_id(@item_json['featuredObject']) ) - @approval_uri = @item_json['featureAuthorization'] - verify_authorization! unless @collection_item&.account&.local? @collection_item @@ -48,6 +49,12 @@ def new_item ) end + def non_matching_actor_and_approval_uris? + return false if ActivityPub::TagManager.instance.local_uri?(@actor_uri) + + non_matching_uri_hosts?(@actor_uri, @approval_uri) + end + def verify_authorization! ActivityPub::VerifyFeaturedItemService.new.call(@collection_item, @approval_uri, request_id: @request_id) rescue Mastodon::RecursionLimitExceededError, Mastodon::UnexpectedResponseError, *Mastodon::HTTP_CONNECTION_ERRORS
app/services/activitypub/verify_featured_item_service.rb+10 −3 modified@@ -12,8 +12,11 @@ def call(collection_item, approval_uri, request_id: nil) return end - return if non_matching_uri_hosts?(approval_uri, @authorization['interactionTarget']) - return unless matching_type? && matching_collection_uri? + @collection_uri = value_or_id(@authorization['interactingObject']) + @actor_uri = value_or_id(@authorization['interactionTarget']) + + return if non_matching_uri_hosts?(approval_uri, @actor_uri) + return unless matching_type? && matching_collection_uri? && matching_actors? account = Account.where(uri: @collection_item.object_uri).first account ||= ActivityPub::FetchRemoteAccountService.new.call(@collection_item.object_uri, request_id:) @@ -29,6 +32,10 @@ def matching_type? end def matching_collection_uri? - @collection_item.collection.uri == @authorization['interactingObject'] + @collection_item.collection.uri == @collection_uri + end + + def matching_actors? + @collection_item.object_uri == @actor_uri end end
spec/services/activitypub/process_featured_item_service_spec.rb+11 −0 modified@@ -40,6 +40,17 @@ end.to_not change(CollectionItem, :count) end end + + context 'when the actor URI does not match the approval URI' do + let(:featured_object_uri) { 'https://example.com/actor/1' } + let(:feature_authorization_uri) { 'https://other.example.com/auth/1' } + + it 'does not create a collection item and returns `nil`' do + expect do + expect(subject.call(collection, object, position:)).to be_nil + end.to_not change(CollectionItem, :count) + end + end end context 'when the collection item is inlined' do
spec/services/activitypub/verify_featured_item_service_spec.rb+21 −0 modified@@ -85,4 +85,25 @@ expect(collection_item).to be_rejected end end + + context 'when the authorization references a different account' do + let(:verification_json) do + { + '@context' => 'https://www.w3.org/ns/activitystreams', + 'type' => 'FeatureAuthorization', + 'id' => approval_uri, + 'interactionTarget' => 'https://example.com/actor/2', + 'interactingObject' => collection.uri, + } + end + + before { featured_account } + + it 'does not verify the item' do + subject.call(collection_item, approval_uri) + + expect(collection_item.account_id).to be_nil + expect(collection_item).to be_pending + end + end end
Vulnerability mechanics
Root cause
"Missing verification that the `FeatureAuthorization` object's `interactionTarget` matches the collection item's `object_uri`, allowing an attacker to forge consent for a different account."
Attack vector
An attacker can forge a `FeatureAuthorization` object that resides on the same domain as the target object but references a different account than the one actually featured in the Collection. Because the missing `matching_actors?` check in `VerifyFeaturedItemService` [patch_id=6086852] did not confirm that the `interactionTarget` matches the collection item's `object_uri`, the forged authorization would be accepted. This allows the attacker to make it appear as if a remote account consented to being featured when it did not. The attack is network-based, requires no authentication, and only affects servers that have enabled the experimental Collections feature.
Affected code
The vulnerability resides in `app/services/activitypub/process_featured_item_service.rb` and `app/services/activitypub/verify_featured_item_service.rb`. The `process_featured_item_service` lacked a check that the actor URI and the approval URI belong to the same host, and `verify_featured_item_service` did not verify that the `interactionTarget` of the `FeatureAuthorization` matches the `object_uri` of the collection item being verified.
What the fix does
The patch adds two new checks. In `ProcessFeaturedItemService`, a new `non_matching_actor_and_approval_uris?` method ensures that the actor URI and the approval URI share the same host (unless the actor is local). In `VerifyFeaturedItemService`, a new `matching_actors?` method verifies that the `interactionTarget` of the `FeatureAuthorization` matches the `object_uri` of the collection item. Together these prevent an attacker from using a `FeatureAuthorization` that was issued for a different account to falsely authorize a collection item.
Preconditions
- configThe Mastodon server must have the experimental Collections feature enabled via the `EXPERIMENTAL_FEATURES` environment variable.
- inputThe attacker must be able to craft and deliver a forged `FeatureAuthorization` ActivityStreams object.
- networkThe attack is performed over the network against a Mastodon instance that processes remote Collection items.
Generated on Jun 15, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.