From 94ee9c5a297c6727c79d591d8ed2d96bfc31d70a Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 15 Jan 2026 14:17:34 +0100 Subject: [PATCH 1/8] Update SECURITY.md (#37503) --- SECURITY.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 385c946512..bd8f5ab25d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -17,5 +17,4 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through | ------- | ---------------- | | 4.4.x | Yes | | 4.3.x | Until 2026-05-06 | -| 4.2.x | Until 2026-01-08 | -| < 4.2 | No | +| < 4.3 | No | From 444a360c11d4b757802db14930ece72c6c224297 Mon Sep 17 00:00:00 2001 From: Shlee Date: Fri, 9 Jan 2026 23:20:50 +0700 Subject: [PATCH 2/8] SharedConnectionPool - NoMethodError: undefined method 'site' for Integer (#37374) --- app/lib/connection_pool/shared_connection_pool.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/lib/connection_pool/shared_connection_pool.rb b/app/lib/connection_pool/shared_connection_pool.rb index 1cfcc5823b..c7dd747eda 100644 --- a/app/lib/connection_pool/shared_connection_pool.rb +++ b/app/lib/connection_pool/shared_connection_pool.rb @@ -41,12 +41,17 @@ class ConnectionPool::SharedConnectionPool < ConnectionPool # ConnectionPool 2.4+ calls `checkin(force: true)` after fork. # When this happens, we should remove all connections from Thread.current - ::Thread.current.keys.each do |name| # rubocop:disable Style/HashEachMethods - next unless name.to_s.start_with?("#{@key}-") + connection_keys = ::Thread.current.keys.select { |key| key.to_s.start_with?("#{@key}-") && !key.to_s.start_with?("#{@key_count}-") } + count_keys = ::Thread.current.keys.select { |key| key.to_s.start_with?("#{@key_count}-") } - @available.push(::Thread.current[name]) - ::Thread.current[name] = nil + connection_keys.each do |key| + @available.push(::Thread.current[key]) + ::Thread.current[key] = nil end + count_keys.each do |key| + ::Thread.current[key] = nil + end + elsif ::Thread.current[key(preferred_tag)] if ::Thread.current[key_count(preferred_tag)] == 1 @available.push(::Thread.current[key(preferred_tag)]) From eff2d57cdbc98daee9b9bb37c2c0c9fbf79e2b0d Mon Sep 17 00:00:00 2001 From: Shlee Date: Thu, 8 Jan 2026 17:47:53 +0700 Subject: [PATCH 3/8] Fix SignatureParser accepting duplicate parameters in HTTP Signature header (#37375) Co-authored-by: Claire --- app/lib/signature_parser.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/lib/signature_parser.rb b/app/lib/signature_parser.rb index 7a75080d98..00a45b8251 100644 --- a/app/lib/signature_parser.rb +++ b/app/lib/signature_parser.rb @@ -25,9 +25,13 @@ class SignatureParser # Use `skip` instead of `scan` as we only care about the subgroups while scanner.skip(PARAM_RE) + key = scanner[:key] + # Detect a duplicate key + raise Mastodon::SignatureVerificationError, 'Error parsing signature with duplicate keys' if params.key?(key) + # This is not actually correct with regards to quoted pairs, but it's consistent # with our previous implementation, and good enough in practice. - params[scanner[:key]] = scanner[:value] || scanner[:quoted_value][1...-1] + params[key] = scanner[:value] || scanner[:quoted_value][1...-1] scanner.skip(/\s*/) return params if scanner.eos? From 06bae4a93626504f2abdeba0d8efc5b327260bf6 Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Sat, 10 Jan 2026 03:20:59 +1100 Subject: [PATCH 4/8] Fix thread-unsafe ActivityPub activity dispatch (#37423) --- app/lib/activitypub/activity.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb index 322f3e27ad..f5b55fff6a 100644 --- a/app/lib/activitypub/activity.rb +++ b/app/lib/activitypub/activity.rb @@ -22,13 +22,13 @@ class ActivityPub::Activity class << self def factory(json, account, **options) @json = json - klass&.new(json, account, **options) + klass_for(json)&.new(json, account, **options) end private - def klass - case @json['type'] + def klass_for(json) + case json['type'] when 'Create' ActivityPub::Activity::Create when 'Announce' From 7c9e17c20a6e50818790a64a26672736a4aca265 Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Sat, 10 Jan 2026 03:21:18 +1100 Subject: [PATCH 5/8] Fix Vary parsing in cache control enforcement (#37426) --- app/controllers/concerns/cache_concern.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/concerns/cache_concern.rb b/app/controllers/concerns/cache_concern.rb index 1823b5b8ed..d43bdd904e 100644 --- a/app/controllers/concerns/cache_concern.rb +++ b/app/controllers/concerns/cache_concern.rb @@ -19,7 +19,7 @@ module CacheConcern # from being used as cache keys, while allowing to `Vary` on them (to not serve # anonymous cached data to authenticated requests when authentication matters) def enforce_cache_control! - vary = response.headers['Vary']&.split&.map { |x| x.strip.downcase } + vary = response.headers['Vary'].to_s.split(',').map { |x| x.strip.downcase }.reject(&:empty?) return unless vary.present? && %w(cookie authorization signature).any? { |header| vary.include?(header) && request.headers[header].present? } response.cache_control.replace(private: true, no_store: true) From b28fdf2d4989965f560cb34e818b4ebf0fe21e84 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 13 Jan 2026 11:18:26 +0100 Subject: [PATCH 6/8] Simplify status batch removal SQL query (#37469) --- app/services/batched_remove_status_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/batched_remove_status_service.rb b/app/services/batched_remove_status_service.rb index de4ee16e91..51aee35c48 100644 --- a/app/services/batched_remove_status_service.rb +++ b/app/services/batched_remove_status_service.rb @@ -31,7 +31,7 @@ class BatchedRemoveStatusService < BaseService # transaction lock the database, but we use the delete method instead # of destroy to avoid all callbacks. We rely on foreign keys to # cascade the delete faster without loading the associations. - statuses_and_reblogs.each_slice(50) { |slice| Status.where(id: slice.map(&:id)).delete_all } + statuses_and_reblogs.each_slice(50) { |slice| Status.unscoped.where(id: slice.pluck(:id)).delete_all } # Since we skipped all callbacks, we also need to manually # deindex the statuses From 04ecb5c319573fef76cf2792d546eeb7a28b5589 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 14 Jan 2026 11:51:23 +0100 Subject: [PATCH 7/8] Fix `FeedManager#filter_from_home` error when handling a reblog of a deleted status (#37486) --- app/lib/feed_manager.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index 4d17001822..cf4bb2991d 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -441,6 +441,7 @@ class FeedManager return :filter if status.reply? && (status.in_reply_to_id.nil? || status.in_reply_to_account_id.nil?) return :skip_home if timeline_type != :list && crutches[:exclusive_list_users][status.account_id].present? return :filter if crutches[:languages][status.account_id].present? && status.language.present? && !crutches[:languages][status.account_id].include?(status.language) + return :filter if status.reblog? && status.reblog.blank? check_for_blocks = crutches[:active_mentions][status.id] || [] check_for_blocks.push(status.account_id) From afd44209891b0d5129d41cf471f259a1eb85df2f Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 16 Jan 2026 17:13:51 +0100 Subject: [PATCH 8/8] Disable rubocop rule disabled in `main` --- .rubocop_todo.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index a6e51d6aee..46f857fb12 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -21,11 +21,11 @@ Metrics/BlockNesting: # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/CyclomaticComplexity: - Max: 25 + Enabled: false # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/PerceivedComplexity: - Max: 27 + Enabled: false Rails/OutputSafety: Exclude: