diff --git a/.bundler-audit.yml b/.bundler-audit.yml index 0671df390fed81..c867b1abf040b2 100644 --- a/.bundler-audit.yml +++ b/.bundler-audit.yml @@ -4,3 +4,7 @@ ignore: # We have rate-limits on authentication endpoints in place (including second # factor verification) since Mastodon v3.2.0 - CVE-2024-0227 + # devise-two-factor advisory about generated secrets being weaker than expected + # We call `generate_otp_secret` ourselves with a requested length of 32 characters, + # which exceeds the recommended remediation of 26 characters, so we're safe + - CVE-2024-8796 diff --git a/CHANGELOG.md b/CHANGELOG.md index b2229f9fdcbfa7..f3e364320bd1b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,27 @@ All notable changes to this project will be documented in this file. +## [4.2.13] - 2024-09-30 + +### Security + +- Fix ReDoS vulnerability on some Ruby versions ([GHSA-jpxp-r43f-rhvx](https://github.com/mastodon/mastodon/security/advisories/GHSA-jpxp-r43f-rhvx)) +- Update dependencies + +### Added + +- Add “A Mastodon update is available.” message on admin dashboard for non-bugfix updates (#32106 by @ClearlyClaire) + +### Changed + +- Change Mastodon to issue correct HTTP signatures by default (#31994 by @ClearlyClaire) + +### Fixed + +- Fix replies collection being cached improperly +- Fix security context sometimes not being added in LD-Signed activities (#31871 by @ClearlyClaire) +- Fix error when encountering reblog of deleted post in feed rebuild (#32001 by @ClearlyClaire) + ## [4.2.12] - 2024-08-19 ### Fixed diff --git a/Gemfile.lock b/Gemfile.lock index b78a018963c93f..c2d92990e58c31 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -201,7 +201,7 @@ GEM climate_control (0.2.0) cocoon (1.2.15) color_diff (0.1) - concurrent-ruby (1.2.3) + concurrent-ruby (1.3.4) connection_pool (2.4.1) cose (1.3.0) cbor (~> 0.5.9) @@ -256,7 +256,7 @@ GEM multi_json encryptor (3.0.0) erubi (1.12.0) - et-orbi (1.2.7) + et-orbi (1.2.11) tzinfo excon (0.100.0) fabrication (2.30.0) @@ -306,8 +306,8 @@ GEM fog-json (>= 1.0) ipaddress (>= 0.8) formatador (0.3.0) - fugit (1.8.1) - et-orbi (~> 1, >= 1.2.7) + fugit (1.11.1) + et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) fuubar (2.5.1) rspec-core (~> 3.0) @@ -469,7 +469,7 @@ GEM net-protocol net-ssh (7.1.0) nio4r (2.7.3) - nokogiri (1.16.6) + nokogiri (1.16.7) mini_portile2 (~> 2.8.2) racc (~> 1.4) nsa (0.3.0) @@ -478,16 +478,16 @@ GEM sidekiq (>= 3.5) statsd-ruby (~> 1.4, >= 1.4.0) oj (3.16.1) - omniauth (2.1.1) + omniauth (2.1.2) hashie (>= 3.4.6) rack (>= 2.2.3) rack-protection omniauth-rails_csrf_protection (1.0.1) actionpack (>= 4.2) omniauth (~> 2.0) - omniauth-saml (2.1.0) - omniauth (~> 2.0) - ruby-saml (~> 1.12) + omniauth-saml (2.1.2) + omniauth (~> 2.1) + ruby-saml (~> 1.17) omniauth_openid_connect (0.6.1) omniauth (>= 1.9, < 3) openid_connect (~> 1.1) @@ -527,12 +527,12 @@ GEM premailer (~> 1.7, >= 1.7.9) private_address_check (0.5.0) public_suffix (5.0.3) - puma (6.4.2) + puma (6.4.3) nio4r (~> 2.0) pundit (2.3.0) activesupport (>= 3.0.0) raabro (1.4.0) - racc (1.7.3) + racc (1.8.1) rack (2.2.9) rack-attack (6.7.0) rack (>= 1.0, < 4) @@ -544,7 +544,7 @@ GEM httpclient json-jwt (>= 1.11.0) rack (>= 2.1.0) - rack-protection (3.0.5) + rack-protection (3.0.6) rack rack-proxy (0.7.6) rack @@ -604,8 +604,7 @@ GEM responders (3.1.0) actionpack (>= 5.2) railties (>= 5.2) - rexml (3.3.5) - strscan + rexml (3.3.7) rotp (6.3.0) rouge (4.1.2) rpam2 (4.0.2) @@ -667,7 +666,7 @@ GEM rubocop-factory_bot (~> 2.22) ruby-prof (1.6.3) ruby-progressbar (1.13.0) - ruby-saml (1.15.0) + ruby-saml (1.17.0) nokogiri (>= 1.13.10) rexml ruby2_keywords (0.0.5) @@ -736,7 +735,6 @@ GEM redlock (~> 1.0) strong_migrations (0.8.0) activerecord (>= 5.2) - strscan (3.1.0) swd (1.3.0) activesupport (>= 3) attr_required (>= 0.0.5) diff --git a/app/controllers/activitypub/replies_controller.rb b/app/controllers/activitypub/replies_controller.rb index c38ff89d1c4215..25f095cfa5653f 100644 --- a/app/controllers/activitypub/replies_controller.rb +++ b/app/controllers/activitypub/replies_controller.rb @@ -14,7 +14,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController before_action :set_replies def index - expires_in 0, public: public_fetch_mode? + expires_in 0, public: @status.distributable? && public_fetch_mode? render json: replies_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', skip_activities: true end diff --git a/app/lib/activitypub/linked_data_signature.rb b/app/lib/activitypub/linked_data_signature.rb index 9459fdd8b76972..c42313b05ee975 100644 --- a/app/lib/activitypub/linked_data_signature.rb +++ b/app/lib/activitypub/linked_data_signature.rb @@ -4,6 +4,7 @@ class ActivityPub::LinkedDataSignature include JsonLdHelper CONTEXT = 'https://w3id.org/identity/v1' + SIGNATURE_CONTEXT = 'https://w3id.org/security/v1' def initialize(json) @json = json.with_indifferent_access @@ -46,7 +47,13 @@ def sign!(creator, sign_with: nil) signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), to_be_signed)) - @json.merge('signature' => options.merge('signatureValue' => signature)) + # Mastodon's context is either an array or a single URL + context_with_security = Array(@json['@context']) + context_with_security << 'https://w3id.org/security/v1' + context_with_security.uniq! + context_with_security = context_with_security.first if context_with_security.size == 1 + + @json.merge('signature' => options.merge('signatureValue' => signature), '@context' => context_with_security) end private diff --git a/app/lib/admin/system_check/software_version_check.rb b/app/lib/admin/system_check/software_version_check.rb index e142feddf0d000..e5cacfe354bbaf 100644 --- a/app/lib/admin/system_check/software_version_check.rb +++ b/app/lib/admin/system_check/software_version_check.rb @@ -14,14 +14,16 @@ def pass? def message if software_updates.any?(&:urgent?) Admin::SystemCheck::Message.new(:software_version_critical_check, nil, admin_software_updates_path, true) - else + elsif software_updates.any?(&:patch_type?) Admin::SystemCheck::Message.new(:software_version_patch_check, nil, admin_software_updates_path) + else + Admin::SystemCheck::Message.new(:software_version_check, nil, admin_software_updates_path) end end private def software_updates - @software_updates ||= SoftwareUpdate.pending_to_a.filter { |update| update.urgent? || update.patch_type? } + @software_updates ||= SoftwareUpdate.pending_to_a end end diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index 8b7f208115d2b3..32236f20d90d53 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -557,7 +557,7 @@ def build_crutches(receiver_id, statuses) arr = crutches[:active_mentions][s.id] || [] arr.push(s.account_id) - if s.reblog? + if s.reblog? && s.reblog.present? arr.push(s.reblog.account_id) arr.concat(crutches[:active_mentions][s.reblog_of_id] || []) end diff --git a/app/lib/request.rb b/app/lib/request.rb index 8d4120868d4feb..4f3f3ff43c6361 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -77,7 +77,7 @@ def initialize(verb, url, **options) @url = Addressable::URI.parse(url).normalize @http_client = options.delete(:http_client) @allow_local = options.delete(:allow_local) - @full_path = options.delete(:with_query_string) + @full_path = !options.delete(:omit_query_string) @options = options.merge(socket_class: use_proxy? || @allow_local ? ProxySocket : Socket) @options = @options.merge(timeout_class: PerOperationWithDeadline, timeout_options: TIMEOUT) @options = @options.merge(proxy_url) if use_proxy? diff --git a/app/models/account.rb b/app/models/account.rb index 17882db9a34251..5d6e8864d5fde9 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -65,8 +65,8 @@ class Account < ApplicationRecord BACKGROUND_REFRESH_INTERVAL = 1.week.freeze - USERNAME_RE = /[a-z0-9_]+([a-z0-9_.-]+[a-z0-9_]+)?/i - MENTION_RE = %r{(? e raise unless e.response && e.response.code == 401 && Addressable::URI.parse(collection_or_uri).query.present? - fetch_resource_without_id_validation(collection_or_uri, nil, true, request_options: { with_query_string: true }) + fetch_resource_without_id_validation(collection_or_uri, nil, true, request_options: { omit_query_string: false }) end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 7db5e1bad12a8d..e14ca08fcd88c2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -858,6 +858,9 @@ en: message_html: You haven't defined any server rules. sidekiq_process_check: message_html: No Sidekiq process running for the %{value} queue(s). Please review your Sidekiq configuration + software_version_check: + action: See available updates + message_html: A Mastodon update is available. software_version_critical_check: action: See available updates message_html: A critical Mastodon update is available, please update as quickly as possible. diff --git a/docker-compose.yml b/docker-compose.yml index d4d5e89febfa9b..2645c9eebe92ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,7 +56,7 @@ services: web: build: . - image: ghcr.io/mastodon/mastodon:v4.2.12 + image: ghcr.io/mastodon/mastodon:v4.2.13 restart: always env_file: .env.production command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000" @@ -77,7 +77,7 @@ services: streaming: build: . - image: ghcr.io/mastodon/mastodon:v4.2.12 + image: ghcr.io/mastodon/mastodon:v4.2.13 restart: always env_file: .env.production command: node ./streaming @@ -95,7 +95,7 @@ services: sidekiq: build: . - image: ghcr.io/mastodon/mastodon:v4.2.12 + image: ghcr.io/mastodon/mastodon:v4.2.13 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index f0e0554c3f26c4..da6bca7b3ea07e 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ def minor end def patch - 12 + 13 end def default_prerelease diff --git a/spec/lib/activitypub/linked_data_signature_spec.rb b/spec/lib/activitypub/linked_data_signature_spec.rb index e821cee6b33816..8a867790a0665f 100644 --- a/spec/lib/activitypub/linked_data_signature_spec.rb +++ b/spec/lib/activitypub/linked_data_signature_spec.rb @@ -99,16 +99,11 @@ describe '#sign!' do subject { described_class.new(raw_json).sign!(sender) } - it 'returns a hash' do + it 'returns a hash with a signature, the expected context, and the signature can be verified', :aggregate_failures do expect(subject).to be_a Hash - end - - it 'contains signature' do expect(subject['signature']).to be_a Hash expect(subject['signature']['signatureValue']).to be_present - end - - it 'can be verified again' do + expect(Array(subject['@context'])).to include('https://w3id.org/security/v1') expect(described_class.new(subject).verify_actor!).to eq sender end end diff --git a/spec/lib/admin/system_check/software_version_check_spec.rb b/spec/lib/admin/system_check/software_version_check_spec.rb index de4335fc519fc2..cc4c80e7a4d7b2 100644 --- a/spec/lib/admin/system_check/software_version_check_spec.rb +++ b/spec/lib/admin/system_check/software_version_check_spec.rb @@ -51,8 +51,8 @@ Fabricate(:software_update, version: '99.99.99', type: 'major', urgent: false) end - it 'returns true' do - expect(check.pass?).to be true + it 'returns false' do + expect(check.pass?).to be false end end