Skip to content

Commit f0b875f

Browse files
Merge pull request #3419 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to 1e5ac0f into stable-4.4
2 parents 418370d + f926bf9 commit f0b875f

24 files changed

+248
-81
lines changed

CHANGELOG.md

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

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

5+
## [4.4.14] - 2026-02-24
6+
7+
### Security
8+
9+
- Reject unconfirmed FASPs (#37926 by @oneiros, [GHSA-qgmm-vr4c-ggjg](https://github.com/mastodon/mastodon/security/advisories/GHSA-qgmm-vr4c-ggjg))
10+
- Re-use custom socket class for FASP requests (#37925 by @oneiros, [GHSA-46w6-g98f-wxqm](https://github.com/mastodon/mastodon/security/advisories/GHSA-46w6-g98f-wxqm))
11+
12+
### Added
13+
14+
- Add `--suspended-only` option to `tootctl emoji purge` (#37828 and #37861 by @ClearlyClaire and @mjankowski)
15+
16+
### Fixed
17+
18+
- Fix custom emojis not being purged on domain suspension (#37808 by @ClearlyClaire)
19+
- Fix processing of object updates with duplicate hashtags (#37756 by @ClearlyClaire)
20+
521
## [4.4.13] - 2026-02-03
622

723
### Security

app/controllers/api/fasp/base_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def validate_signature!
4747
provider = nil
4848

4949
Linzer.verify!(request.rack_request, no_older_than: 5.minutes) do |keyid|
50-
provider = Fasp::Provider.find(keyid)
50+
provider = Fasp::Provider.confirmed.find(keyid)
5151
Linzer.new_ed25519_public_key(provider.provider_public_key_pem, keyid)
5252
end
5353

app/lib/fasp/request.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def perform_request(verb, path, body: nil)
2929
response = HTTP
3030
.headers(headers)
3131
.use(http_signature: { key:, covered_components: COVERED_COMPONENTS })
32-
.send(verb, url, body:)
32+
.send(verb, url, body:, socket_class: ::Request::Socket)
3333

3434
validate!(response)
3535

app/lib/request.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,5 +349,5 @@ def check_private_address(_address, _host)
349349
end
350350
end
351351

352-
private_constant :ClientLimit, :Socket, :ProxySocket
352+
private_constant :ClientLimit
353353
end

app/models/fasp/provider.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class Fasp::Provider < ApplicationRecord
3434
before_create :create_keypair
3535
after_commit :update_remote_capabilities
3636

37+
scope :confirmed, -> { where(confirmed: true) }
3738
scope :with_capability, lambda { |capability_name|
3839
where('fasp_providers.capabilities @> ?::jsonb', "[{\"id\": \"#{capability_name}\", \"enabled\": true}]")
3940
}

app/services/activitypub/process_status_update_service.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def update_tags!
209209
Tag.find_or_create_by_names([tag]).filter(&:valid?)
210210
rescue ActiveRecord::RecordInvalid
211211
[]
212-
end
212+
end.uniq
213213

214214
return unless @status.distributable?
215215

app/services/block_domain_service.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ def process_domain_block!
2929
suspend_accounts!
3030
end
3131

32-
DomainClearMediaWorker.perform_async(domain_block.id) if domain_block.reject_media?
32+
if domain_block.suspend?
33+
# Account images and attachments are already handled by `suspend_accounts!`
34+
PurgeCustomEmojiWorker.perform_async(blocked_domain)
35+
elsif domain_block.reject_media?
36+
DomainClearMediaWorker.perform_async(domain_block.id)
37+
end
3338
end
3439

3540
def silence_accounts!
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
class PurgeCustomEmojiWorker
4+
include Sidekiq::IterableJob
5+
6+
def build_enumerator(domain, cursor:)
7+
return if domain.blank?
8+
9+
active_record_batches_enumerator(CustomEmoji.by_domain_and_subdomains(domain), cursor:)
10+
end
11+
12+
def each_iteration(custom_emojis, _domain)
13+
AttachmentBatch.new(CustomEmoji, custom_emojis).delete
14+
end
15+
end

docker-compose.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ services:
5959
web:
6060
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
6161
# build: .
62-
image: ghcr.io/glitch-soc/mastodon:v4.4.13
62+
image: ghcr.io/glitch-soc/mastodon:v4.4.14
6363
restart: always
6464
env_file: .env.production
6565
command: bundle exec puma -C config/puma.rb
@@ -83,7 +83,7 @@ services:
8383
# build:
8484
# dockerfile: ./streaming/Dockerfile
8585
# context: .
86-
image: ghcr.io/glitch-soc/mastodon-streaming:v4.4.13
86+
image: ghcr.io/glitch-soc/mastodon-streaming:v4.4.14
8787
restart: always
8888
env_file: .env.production
8989
command: node ./streaming/index.js
@@ -102,7 +102,7 @@ services:
102102
sidekiq:
103103
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
104104
# build: .
105-
image: ghcr.io/glitch-soc/mastodon:v4.4.13
105+
image: ghcr.io/glitch-soc/mastodon:v4.4.14
106106
restart: always
107107
env_file: .env.production
108108
command: bundle exec sidekiq

lib/mastodon/cli/emoji.rb

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,27 @@ def export(path)
109109
end
110110

111111
option :remote_only, type: :boolean
112+
option :suspended_only, type: :boolean
112113
desc 'purge', 'Remove all custom emoji'
113114
long_desc <<-LONG_DESC
114115
Removes all custom emoji.
115116
116117
With the --remote-only option, only remote emoji will be deleted.
118+
119+
With the --suspended-only option, only emoji from suspended servers will be deleted.
117120
LONG_DESC
118121
def purge
119-
scope = options[:remote_only] ? CustomEmoji.remote : CustomEmoji
120-
scope.in_batches.destroy_all
122+
if options[:suspended_only]
123+
DomainBlock.where(severity: :suspend).find_each do |domain_block|
124+
CustomEmoji.by_domain_and_subdomains(domain_block.domain).find_in_batches do |custom_emojis|
125+
AttachmentBatch.new(CustomEmoji, custom_emojis).delete
126+
end
127+
end
128+
else
129+
scope = options[:remote_only] ? CustomEmoji.remote : CustomEmoji
130+
scope.in_batches.destroy_all
131+
end
132+
121133
say('OK', :green)
122134
end
123135

0 commit comments

Comments
 (0)