Skip to content

Commit 04903a4

Browse files
Merge pull request #3358 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to cbb1085 into stable-4.4
2 parents 2588d1a + d9bad1c commit 04903a4

File tree

20 files changed

+96
-22
lines changed

20 files changed

+96
-22
lines changed

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,28 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [4.4.12] - 2026-01-20
6+
7+
### Security
8+
9+
- Fix missing limits on various federated properties [GHSA-gg8q-rcg7-p79g](https://github.com/mastodon/mastodon/security/advisories/GHSA-gg8q-rcg7-p79g)
10+
- Fix remote user suspension bypass [GHSA-5h2f-wg8j-xqwp](https://github.com/mastodon/mastodon/security/advisories/GHSA-5h2f-wg8j-xqwp)
11+
- Fix missing length limits on some user-provided fields [GHSA-6x3w-9g92-gvf3](https://github.com/mastodon/mastodon/security/advisories/GHSA-6x3w-9g92-gvf3)
12+
- Fix missing access check for push notification settings update [GHSA-f3q8-7vw3-69v4](https://github.com/mastodon/mastodon/security/advisories/GHSA-f3q8-7vw3-69v4)
13+
14+
### Changed
15+
16+
- Skip tombstone creation on deleting from 404 (#37533 by @ClearlyClaire)
17+
18+
### Fixed
19+
20+
- Fix potential duplicate handling of quote accept/reject/delete (#37537 by @ClearlyClaire)
21+
- Fix `FeedManager#filter_from_home` error when handling a reblog of a deleted status (#37486 by @ClearlyClaire)
22+
- Fix needlessly complicated SQL query in status batch removal (#37469 by @ClearlyClaire)
23+
- Fix `Vary` parsing in cache control enforcement (#37426 by @MegaManSec)
24+
- Fix thread-unsafe ActivityPub activity dispatch (#37423 by @MegaManSec)
25+
- Fix SignatureParser accepting duplicate parameters in HTTP Signature header (#37375 by @shleeable)
26+
527
## [4.4.11] - 2026-01-07
628

729
### Security

FEDERATION.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,22 @@ Mastodon requires all `POST` requests to be signed, and MAY require `GET` reques
4848
### Additional documentation
4949

5050
- [Mastodon documentation](https://docs.joinmastodon.org/)
51+
52+
## Size limits
53+
54+
Mastodon imposes a few hard limits on federated content.
55+
These limits are intended to be very generous and way above what the Mastodon user experience is optimized for, so as to accomodate future changes and unusual or unforeseen usage patterns, while still providing some limits for performance reasons.
56+
The following table attempts to summary those limits.
57+
58+
| Limited property | Size limit | Consequence of exceeding the limit |
59+
| ------------------------------------------------------------- | ---------- | ---------------------------------- |
60+
| Serialized JSON-LD | 1MB | **Activity is rejected/dropped** |
61+
| Profile fields (actor `PropertyValue` attachments) name/value | 2047 | Field name/value is truncated |
62+
| Number of profile fields (actor `PropertyValue` attachments) | 50 | Fields list is truncated |
63+
| Poll options (number of `anyOf`/`oneOf` in a `Question`) | 500 | Items list is truncated |
64+
| Account username (actor `preferredUsername`) length | 2048 | **Actor will be rejected** |
65+
| Account display name (actor `name`) length | 2048 | Display name will be truncated |
66+
| Account note (actor `summary`) length | 20kB | Account note will be truncated |
67+
| Account `attributionDomains` | 256 | List will be truncated |
68+
| Account aliases (actor `alsoKnownAs`) | 256 | List will be truncated |
69+
| Custom emoji shortcode (`Emoji` `name`) | 2048 | Emoji will be rejected |

app/controllers/activitypub/inboxes_controller.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
class ActivityPub::InboxesController < ActivityPub::BaseController
44
include JsonLdHelper
55

6+
before_action :skip_large_payload
67
before_action :skip_unknown_actor_activity
78
before_action :require_actor_signature!
89
skip_before_action :authenticate_user!
@@ -16,6 +17,10 @@ def create
1617

1718
private
1819

20+
def skip_large_payload
21+
head 413 if request.content_length > ActivityPub::Activity::MAX_JSON_SIZE
22+
end
23+
1924
def skip_unknown_actor_activity
2025
head 202 if unknown_affected_account?
2126
end

app/controllers/api/web/push_subscriptions_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def update_session_with_subscription
6262
end
6363

6464
def set_push_subscription
65-
@push_subscription = ::Web::PushSubscription.find(params[:id])
65+
@push_subscription = ::Web::PushSubscription.where(user_id: active_session.user_id).find(params[:id])
6666
end
6767

6868
def subscription_params

app/lib/activitypub/activity.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class ActivityPub::Activity
55
include Redisable
66
include Lockable
77

8+
MAX_JSON_SIZE = 1.megabyte
89
SUPPORTED_TYPES = %w(Note Question).freeze
910
CONVERTED_TYPES = %w(Image Audio Video Article Page Event).freeze
1011

app/lib/activitypub/activity/delete.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def delete_status
5656
end
5757

5858
def revoke_quote
59-
@quote = Quote.find_by(approval_uri: object_uri, quoted_account: @account)
59+
@quote = Quote.find_by(approval_uri: object_uri, quoted_account: @account, state: [:pending, :accepted])
6060
return if @quote.nil?
6161

6262
ActivityPub::Forwarder.new(@account, @json, @quote.status).forward! if @quote.status.present?

app/lib/activitypub/activity/update.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ def update_status
3232
@status = Status.find_by(uri: object_uri, account_id: @account.id)
3333

3434
# Ignore updates for old unknown objects, since those are updates we are not interested in
35-
return if @status.nil? && object_too_old?
35+
# Also ignore unknown objects from suspended users for the same reasons
36+
return if @status.nil? && (@account.suspended? || object_too_old?)
3637

3738
# We may be getting `Create` and `Update` out of order
3839
@status ||= ActivityPub::Activity::Create.new(@json, @account, **@options).perform

app/lib/activitypub/parser/poll_parser.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
class ActivityPub::Parser::PollParser
44
include JsonLdHelper
55

6+
# Limit the number of items for performance purposes.
7+
# We truncate rather than error out to avoid missing the post entirely.
8+
MAX_ITEMS = 500
9+
610
def initialize(json)
711
@json = json
812
end
@@ -48,6 +52,6 @@ def cached_tallies
4852
private
4953

5054
def items
51-
@json['anyOf'] || @json['oneOf']
55+
(@json['anyOf'] || @json['oneOf'])&.take(MAX_ITEMS)
5256
end
5357
end

app/models/account.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ class Account < ApplicationRecord
7878
DISPLAY_NAME_LENGTH_LIMIT = (ENV['MAX_DISPLAY_NAME_CHARS'] || 30).to_i
7979
NOTE_LENGTH_LIMIT = (ENV['MAX_BIO_CHARS'] || 500).to_i
8080

81+
# Hard limits for federated content
82+
USERNAME_LENGTH_HARD_LIMIT = 2048
83+
DISPLAY_NAME_LENGTH_HARD_LIMIT = 2048
84+
NOTE_LENGTH_HARD_LIMIT = 20.kilobytes
85+
ATTRIBUTION_DOMAINS_HARD_LIMIT = 256
86+
ALSO_KNOWN_AS_HARD_LIMIT = 256
87+
8188
AUTOMATED_ACTOR_TYPES = %w(Application Service).freeze
8289

8390
include Attachmentable # Load prior to Avatar & Header concerns
@@ -109,7 +116,7 @@ class Account < ApplicationRecord
109116
validates_with UniqueUsernameValidator, if: -> { will_save_change_to_username? }
110117

111118
# Remote user validations, also applies to internal actors
112-
validates :username, format: { with: USERNAME_ONLY_RE }, if: -> { (remote? || actor_type_application?) && will_save_change_to_username? }
119+
validates :username, format: { with: USERNAME_ONLY_RE }, length: { maximum: USERNAME_LENGTH_HARD_LIMIT }, if: -> { (remote? || actor_type_application?) && will_save_change_to_username? }
113120

114121
# Remote user validations
115122
validates :uri, presence: true, unless: :local?, on: :create

app/models/custom_emoji.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class CustomEmoji < ApplicationRecord
2727
LOCAL_LIMIT = (ENV['MAX_EMOJI_SIZE'] || 256.kilobytes).to_i
2828
LIMIT = [LOCAL_LIMIT, (ENV['MAX_REMOTE_EMOJI_SIZE'] || 256.kilobytes).to_i].max
2929
MINIMUM_SHORTCODE_SIZE = 2
30+
MAX_SHORTCODE_SIZE = 128
31+
MAX_FEDERATED_SHORTCODE_SIZE = 2048
3032

3133
SHORTCODE_RE_FRAGMENT = '[a-zA-Z0-9_]{2,}'
3234

@@ -48,7 +50,8 @@ class CustomEmoji < ApplicationRecord
4850
validates_attachment :image, content_type: { content_type: IMAGE_MIME_TYPES }, presence: true
4951
validates_attachment_size :image, less_than: LIMIT, unless: :local?
5052
validates_attachment_size :image, less_than: LOCAL_LIMIT, if: :local?
51-
validates :shortcode, uniqueness: { scope: :domain }, format: { with: SHORTCODE_ONLY_RE }, length: { minimum: MINIMUM_SHORTCODE_SIZE }
53+
validates :shortcode, uniqueness: { scope: :domain }, format: { with: SHORTCODE_ONLY_RE }, length: { minimum: MINIMUM_SHORTCODE_SIZE, maximum: MAX_FEDERATED_SHORTCODE_SIZE }
54+
validates :shortcode, length: { maximum: MAX_SHORTCODE_SIZE }, if: :local?
5255

5356
scope :local, -> { where(domain: nil) }
5457
scope :remote, -> { where.not(domain: nil) }

0 commit comments

Comments
 (0)