Skip to content

Commit

Permalink
Merge tag 'v4.1.17' into update/imastodon-v4.1.18
Browse files Browse the repository at this point in the history
  • Loading branch information
takayamaki committed Jul 6, 2024
2 parents aa28051 + 1cad857 commit 012e3fe
Show file tree
Hide file tree
Showing 46 changed files with 485 additions and 156 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/test-ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,5 @@ jobs:

- name: Load database schema
run: './bin/rails db:create db:schema:load db:seed'

- run: bin/rspec
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,39 @@ Changelog

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

## [4.1.17] - 2024-05-30

### Security

- Update dependencies
- Fix private mention filtering ([GHSA-5fq7-3p3j-9vrf](https://github.com/mastodon/mastodon/security/advisories/GHSA-5fq7-3p3j-9vrf))
- Fix password change endpoint not being rate-limited ([GHSA-q3rg-xx5v-4mxh](https://github.com/mastodon/mastodon/security/advisories/GHSA-q3rg-xx5v-4mxh))
- Add hardening around rate-limit bypass ([GHSA-c2r5-cfqr-c553](https://github.com/mastodon/mastodon/security/advisories/GHSA-c2r5-cfqr-c553))

### Added

- Add fallback redirection when getting a webfinger query `WEB_DOMAIN@WEB_DOMAIN` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28592))
- Add `digest` attribute to `Admin::DomainBlock` entity in REST API ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/29092))

### Removed

- Remove superfluous application-level caching in some controllers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29862))

### Fixed

- Fix leaking Elasticsearch connections in Sidekiq processes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30450))
- Fix language of remote posts not being recognized when using unusual casing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30403))
- Fix off-by-one in `tootctl media` commands ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30306))
- Fix removal of allowed domains (in `LIMITED_FEDERATION_MODE`) not being recorded in the audit log ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/30125))
- Fix not being able to block a subdomain of an already-blocked domain through the API ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30119))
- Fix `Idempotency-Key` being ignored when scheduling a post ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30084))
- Fix crash when supplying the `FFMPEG_BINARY` environment variable ([timothyjrogers](https://github.com/mastodon/mastodon/pull/30022))
- Fix improper email address validation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29838))
- Fix results/query in `api/v1/featured_tags/suggestions` ([mjankowski](https://github.com/mastodon/mastodon/pull/29597))
- Fix unblocking internationalized domain names under certain conditions ([tribela](https://github.com/mastodon/mastodon/pull/29530))
- Fix admin account created by `mastodon:setup` not being auto-approved ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29379))
- Fix reference to non-existent var in CLI maintenance command ([mjankowski](https://github.com/mastodon/mastodon/pull/28363))

## [4.1.16] - 2024-02-23

### Added
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,4 @@ gem 'concurrent-ruby', require: false
gem 'connection_pool', require: false
gem 'xorcist', '~> 1.1'
gem 'cocoon', '~> 1.2'
gem 'mail', '~> 2.8'
11 changes: 7 additions & 4 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ GEM
jmespath (1.6.2)
json (2.6.3)
json-canonicalization (0.3.0)
json-jwt (1.15.3)
json-jwt (1.15.3.1)
activesupport (>= 4.2)
aes_key_wrap
bindata
Expand Down Expand Up @@ -424,7 +424,7 @@ GEM
net-protocol
net-ssh (7.0.1)
nio4r (2.5.9)
nokogiri (1.16.2)
nokogiri (1.16.5)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
nsa (0.2.8)
Expand Down Expand Up @@ -491,7 +491,7 @@ GEM
pry-rails (0.3.9)
pry (>= 0.10.4)
public_suffix (5.0.1)
puma (5.6.7)
puma (5.6.8)
nio4r (~> 2.0)
pundit (2.3.0)
activesupport (>= 3.0.0)
Expand Down Expand Up @@ -565,7 +565,8 @@ GEM
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
rexml (3.2.5)
rexml (3.2.8)
strscan (>= 3.0.9)
rotp (6.2.0)
rpam2 (4.0.2)
rqrcode (2.1.2)
Expand Down Expand Up @@ -679,6 +680,7 @@ GEM
redlock (~> 1.0)
strong_migrations (0.7.9)
activerecord (>= 5)
strscan (3.1.0)
swd (1.3.0)
activesupport (>= 3)
attr_required (>= 0.0.5)
Expand Down Expand Up @@ -816,6 +818,7 @@ DEPENDENCIES
letter_opener_web (~> 2.0)
link_header (~> 0.0)
lograge (~> 0.12)
mail (~> 2.8)
makara (~> 0.5)
mario-redis-lock (~> 1.2)
memory_profiler
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/admin/domain_allows_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ def create
def destroy
authorize @domain_allow, :destroy?
UnallowDomainService.new.call(@domain_allow)
log_action :destroy, @domain_allow

redirect_to admin_instances_path, notice: I18n.t('admin.domain_allows.destroyed_msg')
end

Expand Down
9 changes: 7 additions & 2 deletions app/controllers/api/v1/admin/domain_blocks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ class Api::V1::Admin::DomainBlocksController < Api::BaseController
def create
authorize :domain_block, :create?

@domain_block = DomainBlock.new(resource_params)
existing_domain_block = resource_params[:domain].present? ? DomainBlock.rule_for(resource_params[:domain]) : nil
return render json: existing_domain_block, serializer: REST::Admin::ExistingDomainBlockErrorSerializer, status: 422 if existing_domain_block.present?
return render json: existing_domain_block, serializer: REST::Admin::ExistingDomainBlockErrorSerializer, status: 422 if conflicts_with_existing_block?(@domain_block, existing_domain_block)

@domain_block = DomainBlock.create!(resource_params)
@domain_block.save!
DomainBlockWorker.perform_async(@domain_block.id)
log_action :create, @domain_block
render json: @domain_block, serializer: REST::Admin::DomainBlockSerializer
Expand Down Expand Up @@ -55,6 +56,10 @@ def destroy

private

def conflicts_with_existing_block?(domain_block, existing_domain_block)
existing_domain_block.present? && (existing_domain_block.domain == TagManager.instance.normalize_domain(domain_block.domain) || !domain_block.stricter_than?(existing_domain_block))
end

def set_domain_blocks
@domain_blocks = filtered_domain_blocks.order(id: :desc).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ def index
private

def set_recently_used_tags
@recently_used_tags = Tag.recently_used(current_account).where.not(id: current_account.featured_tags).limit(10)
@recently_used_tags = Tag.recently_used(current_account).where.not(id: featured_tag_ids).limit(10)
end

def featured_tag_ids
current_account.featured_tags.pluck(:tag_id)
end
end
24 changes: 7 additions & 17 deletions app/controllers/concerns/cache_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,19 @@ def set_cache_headers
response.headers['Vary'] = public_fetch_mode? ? 'Accept' : 'Accept, Signature'
end

# TODO: Rename this method, as it does not perform any caching anymore.
def cache_collection(raw, klass)
return raw unless klass.respond_to?(:with_includes)
return raw unless klass.respond_to?(:preload_cacheable_associations)

raw = raw.cache_ids.to_a if raw.is_a?(ActiveRecord::Relation)
return [] if raw.empty?
records = raw.to_a

cached_keys_with_value = Rails.cache.read_multi(*raw).transform_keys(&:id)
uncached_ids = raw.map(&:id) - cached_keys_with_value.keys
klass.preload_cacheable_associations(records)

klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!)

unless uncached_ids.empty?
uncached = klass.where(id: uncached_ids).with_includes.index_by(&:id)

uncached.each_value do |item|
Rails.cache.write(item, item)
end
end

raw.filter_map { |item| cached_keys_with_value[item.id] || uncached[item.id] }
records
end

# TODO: Rename this method, as it does not perform any caching anymore.
def cache_collection_paginated_by_id(raw, klass, limit, options)
cache_collection raw.cache_ids.to_a_paginated_by_id(limit, options), klass
cache_collection raw.to_a_paginated_by_id(limit, options), klass
end
end
2 changes: 1 addition & 1 deletion app/controllers/well_known/webfinger_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def show
def set_account
username = username_from_resource
@account = begin
if username == Rails.configuration.x.local_domain
if username == Rails.configuration.x.local_domain || username == Rails.configuration.x.web_domain
Account.representative
else
Account.find_local!(username)
Expand Down
11 changes: 9 additions & 2 deletions app/lib/activitypub/parser/status_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
class ActivityPub::Parser::StatusParser
include JsonLdHelper

NORMALIZED_LOCALE_NAMES = LanguagesHelper::SUPPORTED_LOCALES.keys.index_by(&:downcase).freeze

# @param [Hash] json
# @param [Hash] magic_values
# @option magic_values [String] :followers_collection
Expand Down Expand Up @@ -86,6 +88,13 @@ def visibility
end

def language
lang = raw_language_code
lang.presence && NORMALIZED_LOCALE_NAMES.fetch(lang.downcase.to_sym, lang)
end

private

def raw_language_code
if content_language_map?
@object['contentMap'].keys.first
elsif name_language_map?
Expand All @@ -95,8 +104,6 @@ def language
end
end

private

def audience_to
as_array(@object['to'] || @json['to']).map { |x| value_or_id(x) }
end
Expand Down
2 changes: 1 addition & 1 deletion app/lib/video_metadata_extractor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def valid?
private

def ffmpeg_command_output
command = Terrapin::CommandLine.new('ffprobe', '-i :path -print_format :format -show_format -show_streams -show_error -loglevel :loglevel')
command = Terrapin::CommandLine.new(Rails.configuration.x.ffprobe_binary, '-i :path -print_format :format -show_format -show_streams -show_error -loglevel :loglevel')
command.run(path: @path, format: 'json', loglevel: 'fatal')
end

Expand Down
6 changes: 5 additions & 1 deletion app/models/concerns/account_interactions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ def unmute_conversation!(conversation)
end

def unblock_domain!(other_domain)
block = domain_blocks.find_by(domain: other_domain)
block = domain_blocks.find_by(domain: normalized_domain(other_domain))
block&.destroy
end

Expand Down Expand Up @@ -299,4 +299,8 @@ def local_followers_hash
def remove_potential_friendship(other_account)
PotentialFriendshipTracker.remove(id, other_account.id)
end

def normalized_domain(domain)
TagManager.instance.normalize_domain(domain)
end
end
4 changes: 4 additions & 0 deletions app/models/concerns/cacheable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ def with_includes
includes(@cache_associated)
end

def preload_cacheable_associations(records)
ActiveRecord::Associations::Preloader.new.preload(records, @cache_associated)
end

def cache_ids
select(:id, :updated_at)
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/feed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def from_redis(limit, max_id, since_id, min_id)
unhydrated = redis.zrangebyscore(key, "(#{min_id}", "(#{max_id}", limit: [0, limit], with_scores: true).map(&:first).map(&:to_i)
end

Status.where(id: unhydrated).cache_ids
Status.where(id: unhydrated)
end

def key
Expand Down
2 changes: 1 addition & 1 deletion app/models/public_feed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def get(limit, max_id = nil, since_id = nil, min_id = nil)
scope.merge!(media_only_scope) if media_only?
scope.merge!(language_scope) if account&.chosen_languages.present?

scope.cache_ids.to_a_paginated_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id)
scope.to_a_paginated_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id)
end

private
Expand Down
32 changes: 0 additions & 32 deletions app/models/status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -345,38 +345,6 @@ def pins_map(status_ids, account_id)
StatusPin.select('status_id').where(status_id: status_ids).where(account_id: account_id).each_with_object({}) { |p, h| h[p.status_id] = true }
end

def reload_stale_associations!(cached_items)
account_ids = []

cached_items.each do |item|
account_ids << item.account_id
account_ids << item.reblog.account_id if item.reblog?
end

account_ids.uniq!

status_ids = cached_items.map { |item| item.reblog? ? item.reblog_of_id : item.id }.uniq

return if account_ids.empty?

accounts = Account.where(id: account_ids).includes(:account_stat, :user).index_by(&:id)

status_stats = StatusStat.where(status_id: status_ids).index_by(&:status_id)

cached_items.each do |item|
item.account = accounts[item.account_id]
item.reblog.account = accounts[item.reblog.account_id] if item.reblog?

if item.reblog?
status_stat = status_stats[item.reblog.id]
item.reblog.status_stat = status_stat if status_stat.present?
else
status_stat = status_stats[item.id]
item.status_stat = status_stat if status_stat.present?
end
end
end

def from_text(text)
return [] if text.blank?

Expand Down
2 changes: 1 addition & 1 deletion app/models/tag_feed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def get(limit, max_id = nil, since_id = nil, min_id = nil)
scope.merge!(account_filters_scope) if account?
scope.merge!(media_only_scope) if media_only?

scope.cache_ids.to_a_paginated_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id)
scope.to_a_paginated_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id)
end

private
Expand Down
3 changes: 3 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ class User < ApplicationRecord
validates :invite_request, presence: true, on: :create, if: :invite_text_required?

validates :locale, inclusion: I18n.available_locales.map(&:to_s), if: :locale?

validates :email, presence: true, email_address: true

validates_with BlacklistedEmailValidator, if: -> { ENV['EMAIL_DOMAIN_LISTS_APPLY_AFTER_CONFIRMATION'] == 'true' || !confirmed? }
validates_with EmailMxValidator, if: :validate_email_dns?
validates :agreement, acceptance: { allow_nil: false, accept: [true, 'true', '1'] }, on: :create
Expand Down
6 changes: 5 additions & 1 deletion app/serializers/rest/admin/domain_block_serializer.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
# frozen_string_literal: true

class REST::Admin::DomainBlockSerializer < ActiveModel::Serializer
attributes :id, :domain, :created_at, :severity,
attributes :id, :domain, :digest, :created_at, :severity,
:reject_media, :reject_reports,
:private_comment, :public_comment, :obfuscate

def id
object.id.to_s
end

def digest
object.domain_digest
end
end
17 changes: 9 additions & 8 deletions app/services/notify_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,17 @@ def response_to_recipient?
LEFT JOIN mentions m ON m.silent = FALSE AND m.account_id = :sender_id AND m.status_id = s.id
WHERE s.id = :id
UNION ALL
SELECT s.id, s.in_reply_to_id, m.id, st.path || s.id, st.depth + 1
FROM ancestors st
JOIN statuses s ON s.id = st.in_reply_to_id
LEFT JOIN mentions m ON m.silent = FALSE AND m.account_id = :sender_id AND m.status_id = s.id
WHERE st.mention_id IS NULL AND NOT s.id = ANY(path) AND st.depth < :depth_limit
SELECT s.id, s.in_reply_to_id, m.id, ancestors.path || s.id, ancestors.depth + 1
FROM ancestors
JOIN statuses s ON s.id = ancestors.in_reply_to_id
/* early exit if we already have a mention matching our requirements */
LEFT JOIN mentions m ON m.silent = FALSE AND m.account_id = :sender_id AND m.status_id = s.id AND s.account_id = :recipient_id
WHERE ancestors.mention_id IS NULL AND NOT s.id = ANY(path) AND ancestors.depth < :depth_limit
)
SELECT COUNT(*)
FROM ancestors st
JOIN statuses s ON s.id = st.id
WHERE st.mention_id IS NOT NULL AND s.visibility = 3
FROM ancestors
JOIN statuses s ON s.id = ancestors.id
WHERE ancestors.mention_id IS NOT NULL AND s.account_id = :recipient_id AND s.visibility = 3
SQL
end

Expand Down
Loading

0 comments on commit 012e3fe

Please sign in to comment.