Merge pull request #3354 from ClearlyClaire/glitch-soc/merge-4.4

Merge upstream changes up to 92be0fd12e into stable-4.4
This commit is contained in:
Claire
2026-01-19 19:30:24 +01:00
committed by GitHub
8 changed files with 23 additions and 15 deletions

View File

@@ -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
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowedVars, DefaultToNil.

View File

@@ -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 |

View File

@@ -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)

View File

@@ -21,14 +21,13 @@ class ActivityPub::Activity
class << self
def factory(json, account, **)
@json = json
klass&.new(json, account, **)
klass_for(json)&.new(json, account, **)
end
private
def klass
case @json['type']
def klass_for(json)
case json['type']
when 'Create'
ActivityPub::Activity::Create
when 'Announce'

View File

@@ -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)])

View File

@@ -500,6 +500,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)

View File

@@ -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?

View File

@@ -34,7 +34,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