diff --git a/CHANGELOG.md b/CHANGELOG.md index 38c64fda3e6dc0..51f5470d819240 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,23 @@ Changelog All notable changes to this project will be documented in this file. +## [4.1.20] - 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 + +### 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.1.19] - 2024-08-16 ### Fixed diff --git a/Gemfile.lock b/Gemfile.lock index 67f6e6eb3604d8..4e739061d5afef 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -226,7 +226,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.95.0) fabrication (2.30.0) @@ -274,7 +274,7 @@ GEM fog-json (>= 1.0) ipaddress (>= 0.8) formatador (0.3.0) - fugit (1.7.1) + fugit (1.7.2) et-orbi (~> 1, >= 1.2.7) raabro (~> 1.4) fuubar (2.5.1) @@ -426,7 +426,7 @@ GEM net-protocol net-ssh (7.0.1) nio4r (2.5.9) - nokogiri (1.16.6) + nokogiri (1.16.7) mini_portile2 (~> 2.8.2) racc (~> 1.4) nsa (0.2.8) @@ -445,9 +445,9 @@ GEM omniauth-rails_csrf_protection (0.1.2) actionpack (>= 4.2) omniauth (>= 1.3.1) - omniauth-saml (1.10.3) + omniauth-saml (1.10.5) omniauth (~> 1.3, >= 1.3.2) - ruby-saml (~> 1.9) + ruby-saml (~> 1.17) openid_connect (1.4.2) activemodel attr_required (>= 1.0.0) @@ -493,7 +493,7 @@ GEM pry-rails (0.3.9) pry (>= 0.10.4) public_suffix (5.0.1) - puma (5.6.8) + puma (5.6.9) nio4r (~> 2.0) pundit (2.3.0) activesupport (>= 3.0.0) @@ -567,8 +567,7 @@ GEM responders (3.0.1) actionpack (>= 5.0) railties (>= 5.0) - rexml (3.2.8) - strscan (>= 3.0.9) + rexml (3.3.7) rotp (6.2.0) rpam2 (4.0.2) rqrcode (2.1.2) @@ -622,8 +621,8 @@ GEM rubocop (~> 1.33) rubocop-capybara (~> 2.17) ruby-progressbar (1.11.0) - ruby-saml (1.13.0) - nokogiri (>= 1.10.5) + ruby-saml (1.17.0) + nokogiri (>= 1.13.10) rexml ruby2_keywords (0.0.5) rufus-scheduler (3.8.2) @@ -683,7 +682,6 @@ 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) diff --git a/app/controllers/activitypub/replies_controller.rb b/app/controllers/activitypub/replies_controller.rb index 8e0f9de2eeb78e..a2850dbffe040a 100644 --- a/app/controllers/activitypub/replies_controller.rb +++ b/app/controllers/activitypub/replies_controller.rb @@ -13,7 +13,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 dff052ffaadca8..4b2ca0998dceb2 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/feed_manager.rb b/app/lib/feed_manager.rb index 8d7540e0f7220d..baa460f7423b53 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -535,8 +535,8 @@ def build_crutches(receiver_id, statuses) arr = crutches[:active_mentions][s.id] || [] arr.concat([s.account_id]) - if s.reblog? - arr.concat([s.reblog.account_id]) + 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 f45cacfcd73f3c..5af72fe31b509a 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 63680a1b9ec8a5..6f3a9e8883dd97 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -61,8 +61,8 @@ class Account < ApplicationRecord trust_level ) - 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/docker-compose.yml b/docker-compose.yml index 0e2f0a120c9ace..6d887d2de41433 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,7 +56,7 @@ services: web: build: . - image: ghcr.io/mastodon/mastodon:v4.1.19 + image: ghcr.io/mastodon/mastodon:v4.1.20 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.1.19 + image: ghcr.io/mastodon/mastodon:v4.1.20 restart: always env_file: .env.production command: node ./streaming @@ -95,7 +95,7 @@ services: sidekiq: build: . - image: ghcr.io/mastodon/mastodon:v4.1.19 + image: ghcr.io/mastodon/mastodon:v4.1.20 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index e0ce45eb527983..3d7424f410c553 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ def minor end def patch - 19 + 20 end def flags diff --git a/spec/lib/activitypub/linked_data_signature_spec.rb b/spec/lib/activitypub/linked_data_signature_spec.rb index 8b7e18c8867095..3f28fd90c45528 100644 --- a/spec/lib/activitypub/linked_data_signature_spec.rb +++ b/spec/lib/activitypub/linked_data_signature_spec.rb @@ -97,16 +97,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