diff --git a/.bundler-audit.yml b/.bundler-audit.yml new file mode 100644 index 0000000000..c867b1abf0 --- /dev/null +++ b/.bundler-audit.yml @@ -0,0 +1,10 @@ +--- +ignore: + # devise-two-factor advisory about brute-forcing TOTP + # 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/Gemfile.lock b/Gemfile.lock index 738ca0fd7e..2fd7c3ad2b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -290,8 +290,10 @@ GEM ruby-progressbar (~> 1.4) globalid (1.2.1) activesupport (>= 6.1) - google-protobuf (3.25.4) - googleapis-common-protos-types (1.15.0) + google-protobuf (4.28.2) + bigdecimal + rake (>= 13) + googleapis-common-protos-types (1.16.0) google-protobuf (>= 3.18, < 5.a) haml (6.3.0) temple (>= 0.8.2) @@ -607,7 +609,7 @@ GEM psych (5.1.2) stringio public_suffix (6.0.1) - puma (6.4.2) + puma (6.4.3) nio4r (~> 2.0) pundit (2.4.0) activesupport (>= 3.0.0) @@ -892,7 +894,7 @@ GEM rack-proxy (>= 0.6.1) railties (>= 5.2) semantic_range (>= 2.3.0) - webrick (1.8.1) + webrick (1.8.2) websocket (1.2.11) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) diff --git a/app/controllers/activitypub/replies_controller.rb b/app/controllers/activitypub/replies_controller.rb index 11aac48c9c..0a19275d38 100644 --- a/app/controllers/activitypub/replies_controller.rb +++ b/app/controllers/activitypub/replies_controller.rb @@ -12,7 +12,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 9459fdd8b7..c42313b05e 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 @@ class ActivityPub::LinkedDataSignature 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 e142feddf0..e5cacfe354 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 @@ class Admin::SystemCheck::SoftwareVersionCheck < Admin::SystemCheck::BaseCheck 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 12f2b9a20f..4ad166fcd3 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -598,7 +598,7 @@ class FeedManager 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 ab42e82300..d7da9fe63c 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -77,7 +77,7 @@ class Request @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 24ce72d235..694c9000eb 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -71,8 +71,8 @@ class Account < ApplicationRecord DEFAULT_FIELDS_SIZE = 6 INSTANCE_ACTOR_ID = -99 - 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 20f45bebc9..e67796211a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1136,6 +1136,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/lib/mastodon/version.rb b/lib/mastodon/version.rb index fd6eb9325e..1e7c4183d3 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -9,7 +9,7 @@ module Mastodon end def kmyblue_minor - 1 + 2 end def kmyblue_flag diff --git a/spec/lib/activitypub/linked_data_signature_spec.rb b/spec/lib/activitypub/linked_data_signature_spec.rb index 1af45673c0..b1a8dc5c49 100644 --- a/spec/lib/activitypub/linked_data_signature_spec.rb +++ b/spec/lib/activitypub/linked_data_signature_spec.rb @@ -95,16 +95,11 @@ RSpec.describe ActivityPub::LinkedDataSignature do 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 de4335fc51..cc4c80e7a4 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 @@ describe Admin::SystemCheck::SoftwareVersionCheck do 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