From db0863255409bfb78aff5b43455a237228146c29 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 8 Apr 2025 09:18:56 +0200 Subject: [PATCH 01/12] Remove 4.1 support from SECURITY.md (#34384) --- SECURITY.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 43ab4454c..26c06e67f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -13,9 +13,8 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through ## Supported Versions -| Version | Supported | -| ------- | ---------------- | -| 4.3.x | Yes | -| 4.2.x | Yes | -| 4.1.x | Until 2025-04-08 | -| < 4.1 | No | +| Version | Supported | +| ------- | --------- | +| 4.3.x | Yes | +| 4.2.x | Yes | +| < 4.2 | No | From f1a6cca2e1ea102d597649d7636a3108f7caf666 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 5 May 2025 15:18:44 +0200 Subject: [PATCH 02/12] Change activity distribution error handling to skip retrying for deleted accounts (#33617) --- app/workers/activitypub/delivery_worker.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/workers/activitypub/delivery_worker.rb b/app/workers/activitypub/delivery_worker.rb index 376c237a9..6a7574200 100644 --- a/app/workers/activitypub/delivery_worker.rb +++ b/app/workers/activitypub/delivery_worker.rb @@ -62,7 +62,7 @@ class ActivityPub::DeliveryWorker light = Stoplight(@inbox_url) do request_pool.with(@host) do |http_client| build_request(http_client).perform do |response| - raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response) + raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response) || unsalvageable_authorization_failure?(response) @performed = true end @@ -74,6 +74,10 @@ class ActivityPub::DeliveryWorker .run end + def unsalvageable_authorization_failure?(response) + @source_account.suspended_permanently? && response.code == 401 + end + def failure_tracker @failure_tracker ||= DeliveryFailureTracker.new(@inbox_url) end From 0e026eafefd72c28a9940ad04b5eeba6d668d4ea Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 5 May 2025 15:20:45 +0200 Subject: [PATCH 03/12] Fix sign-up e-mail confirmation page reloading on error or redirect (#34548) --- app/javascript/packs/sign_up.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/javascript/packs/sign_up.js b/app/javascript/packs/sign_up.js index cf9c83777..2fa12bc01 100644 --- a/app/javascript/packs/sign_up.js +++ b/app/javascript/packs/sign_up.js @@ -5,8 +5,11 @@ import ready from '../mastodon/ready'; ready(() => { setInterval(() => { - axios.get('/api/v1/emails/check_confirmation').then((response) => { - if (response.data) { + axios.get('/api/v1/emails/check_confirmation', { + headers: { Accept: 'application/json' }, + withCredentials: true, + }).then((response) => { + if (response.status === 200 && response.data === true) { window.location = '/start'; } }).catch(error => { From d5a905f1c88d90974258d93e10d033fc978f3c88 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 5 May 2025 15:11:26 +0200 Subject: [PATCH 04/12] Fix incorrect redirect in response to unauthenticated API requests in limited federation mode (#34549) --- app/controllers/api/base_controller.rb | 7 +++++++ app/controllers/application_controller.rb | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index c764b4510..6e41c94fd 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -141,6 +141,13 @@ class Api::BaseController < ApplicationController end end + # Redefine `require_functional!` to properly output JSON instead of HTML redirects + def require_functional! + return if current_user.functional? + + require_user! + end + def render_empty render json: {}, status: 200 end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6ec93f824..48396b57d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -68,7 +68,23 @@ class ApplicationController < ActionController::Base end def require_functional! - redirect_to edit_user_registration_path unless current_user.functional? + return if current_user.functional? + + respond_to do |format| + format.any do + redirect_to edit_user_registration_path + end + + format.json do + if !current_user.confirmed? + render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403 + elsif !current_user.approved? + render json: { error: 'Your login is currently pending approval' }, status: 403 + elsif !current_user.functional? + render json: { error: 'Your login is currently disabled' }, status: 403 + end + end + end end def skip_csrf_meta_tags? From dfcd85a05cd37ee1c46ac1231e54b7de5ec0eace Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 5 May 2025 15:22:48 +0200 Subject: [PATCH 05/12] Add built-in context for interaction policies (#34574) --- app/helpers/context_helper.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/helpers/context_helper.rb b/app/helpers/context_helper.rb index 945ef9b91..05512d453 100644 --- a/app/helpers/context_helper.rb +++ b/app/helpers/context_helper.rb @@ -32,6 +32,13 @@ module ContextHelper 'messageFranking' => 'toot:messageFranking', 'messageType' => 'toot:messageType', 'cipherText' => 'toot:cipherText' }, suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' }, + interaction_policies: { + 'gts' => 'https://gotosocial.org/ns#', + 'interactionPolicy' => { '@id' => 'gts:interactionPolicy', '@type' => '@id' }, + 'canQuote' => { '@id' => 'gts:canQuote', '@type' => '@id' }, + 'automaticApproval' => { '@id' => 'gts:automaticApproval', '@type' => '@id' }, + 'manualApproval' => { '@id' => 'gts:manualApproval', '@type' => '@id' }, + }, }.freeze def full_context From 43311fd75355f3d5fdf2c22137a719dda8a21c8a Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 5 May 2025 15:13:08 +0200 Subject: [PATCH 06/12] Remove double-query for signed query strings (#34610) --- app/lib/http_signature_draft.rb | 5 ++--- app/lib/request.rb | 3 +-- app/services/activitypub/fetch_replies_service.rb | 15 +-------------- 3 files changed, 4 insertions(+), 19 deletions(-) diff --git a/app/lib/http_signature_draft.rb b/app/lib/http_signature_draft.rb index fc0d498b2..cb794b223 100644 --- a/app/lib/http_signature_draft.rb +++ b/app/lib/http_signature_draft.rb @@ -6,14 +6,13 @@ class HttpSignatureDraft REQUEST_TARGET = '(request-target)' - def initialize(keypair, key_id, full_path: true) + def initialize(keypair, key_id) @keypair = keypair @key_id = key_id - @full_path = full_path end def request_target(verb, url) - if url.query.nil? || !@full_path + if url.query.nil? "#{verb} #{url.path}" else "#{verb} #{url.path}?#{url.query}" diff --git a/app/lib/request.rb b/app/lib/request.rb index 330ab5956..d919bb903 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -75,7 +75,6 @@ class Request @url = Addressable::URI.parse(url).normalize @http_client = options.delete(:http_client) @allow_local = options.delete(:allow_local) - @full_path = !options.delete(:omit_query_string) @options = { follow: { max_hops: 3, @@ -102,7 +101,7 @@ class Request key_id = ActivityPub::TagManager.instance.key_uri_for(actor) keypair = sign_with.present? ? OpenSSL::PKey::RSA.new(sign_with) : actor.keypair - @signing = HttpSignatureDraft.new(keypair, key_id, full_path: @full_path) + @signing = HttpSignatureDraft.new(keypair, key_id) self end diff --git a/app/services/activitypub/fetch_replies_service.rb b/app/services/activitypub/fetch_replies_service.rb index 46cab6caf..0e0ba673b 100644 --- a/app/services/activitypub/fetch_replies_service.rb +++ b/app/services/activitypub/fetch_replies_service.rb @@ -37,20 +37,7 @@ class ActivityPub::FetchRepliesService < BaseService return unless @allow_synchronous_requests return if non_matching_uri_hosts?(@account.uri, collection_or_uri) - # NOTE: For backward compatibility reasons, Mastodon signs outgoing - # queries incorrectly by default. - # - # While this is relevant for all URLs with query strings, this is - # the only code path where this happens in practice. - # - # Therefore, retry with correct signatures if this fails. - begin - fetch_resource_without_id_validation(collection_or_uri, nil, true) - rescue Mastodon::UnexpectedResponseError => 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: { omit_query_string: false }) - end + fetch_resource_without_id_validation(collection_or_uri, nil, true) end def filtered_replies From 27453ce6115475d3e138cb80edae37ab8948d787 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 5 May 2025 15:01:16 +0200 Subject: [PATCH 07/12] Add warning for REDIS_NAMESPACE deprecation at startup (#34581) --- config/initializers/deprecations.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 config/initializers/deprecations.rb diff --git a/config/initializers/deprecations.rb b/config/initializers/deprecations.rb new file mode 100644 index 000000000..8c78e0064 --- /dev/null +++ b/config/initializers/deprecations.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +if ENV['REDIS_NAMESPACE'] + es_configured = ENV['ES_ENABLED'] == 'true' || ENV.fetch('ES_HOST', 'localhost') != 'localhost' || ENV.fetch('ES_PORT', '9200') != '9200' || ENV.fetch('ES_PASS', 'password') != 'password' + + warn <<~MESSAGE + WARNING: the REDIS_NAMESPACE environment variable is deprecated and will be removed in Mastodon 4.4.0. + + Please update to Mastodon 4.3 and see documentation at https://github.com/mastodon/redis_namespace_migration + MESSAGE + + warn <<~MESSAGE if es_configured && !ENV['ES_PREFIX'] + + In addition, as REDIS_NAMESPACE is being used as a prefix for Elasticsearch, please do not forget to set ES_PREFIX to "#{ENV.fetch('REDIS_NAMESPACE')}". + MESSAGE +end From 93e6fc9df7df8e125c0a25fb025439888d702a26 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 6 May 2025 15:02:13 +0200 Subject: [PATCH 08/12] Merge commit from fork * Check scheme in account and post links * Harden media attachments * Client-side mitigation * Client-side mitigation for media attachments --- .../mastodon/actions/importer/normalizer.js | 15 +++++++++++++++ .../activitypub/parser/media_attachment_parser.rb | 6 ++++-- app/lib/activitypub/parser/status_parser.rb | 5 ++++- app/lib/activitypub/tag_manager.rb | 3 ++- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index e6b361c0c..2ed3a7255 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -40,6 +40,10 @@ export function normalizeAccount(account) { account.moved = account.moved.id; } + if (!(account.url.startsWith('http://') || account.url.startsWith('https://'))) { + account.url = account.uri; + } + return account; } @@ -96,6 +100,17 @@ export function normalizeStatus(status, normalOldStatus) { normalStatus.contentHtml = emojify(normalStatus.content, emojiMap); normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap); normalStatus.hidden = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive; + + if (normalStatus.url && !(normalStatus.url.startsWith('http://') || normalStatus.url.startsWith('https://'))) { + normalStatus.url = null; + } + + normalStatus.url ||= normalStatus.uri; + + normalStatus.media_attachments.forEach(item => { + if (item.remote_url && !(item.remote_url.startsWith('http://') || item.remote_url.startsWith('https://'))) + item.remote_url = null; + }); } if (normalOldStatus) { diff --git a/app/lib/activitypub/parser/media_attachment_parser.rb b/app/lib/activitypub/parser/media_attachment_parser.rb index 56b8b23f8..bcbf92214 100644 --- a/app/lib/activitypub/parser/media_attachment_parser.rb +++ b/app/lib/activitypub/parser/media_attachment_parser.rb @@ -15,13 +15,15 @@ class ActivityPub::Parser::MediaAttachmentParser end def remote_url - Addressable::URI.parse(@json['url'])&.normalize&.to_s + url = Addressable::URI.parse(@json['url'])&.normalize&.to_s + url unless unsupported_uri_scheme?(url) rescue Addressable::URI::InvalidURIError nil end def thumbnail_remote_url - Addressable::URI.parse(@json['icon'].is_a?(Hash) ? @json['icon']['url'] : @json['icon'])&.normalize&.to_s + url = Addressable::URI.parse(@json['icon'].is_a?(Hash) ? @json['icon']['url'] : @json['icon'])&.normalize&.to_s + url unless unsupported_uri_scheme?(url) rescue Addressable::URI::InvalidURIError nil end diff --git a/app/lib/activitypub/parser/status_parser.rb b/app/lib/activitypub/parser/status_parser.rb index 6699dc21b..4c7dd37ad 100644 --- a/app/lib/activitypub/parser/status_parser.rb +++ b/app/lib/activitypub/parser/status_parser.rb @@ -27,7 +27,10 @@ class ActivityPub::Parser::StatusParser end def url - url_to_href(@object['url'], 'text/html') if @object['url'].present? + return if @object['url'].blank? + + url = url_to_href(@object['url'], 'text/html') + url unless unsupported_uri_scheme?(url) end def text diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb index 864328631..f286cc97f 100644 --- a/app/lib/activitypub/tag_manager.rb +++ b/app/lib/activitypub/tag_manager.rb @@ -4,6 +4,7 @@ require 'singleton' class ActivityPub::TagManager include Singleton + include JsonLdHelper include RoutingHelper CONTEXT = 'https://www.w3.org/ns/activitystreams' @@ -17,7 +18,7 @@ class ActivityPub::TagManager end def url_for(target) - return target.url if target.respond_to?(:local?) && !target.local? + return unsupported_uri_scheme?(target.url) ? nil : target.url if target.respond_to?(:local?) && !target.local? return unless target.respond_to?(:object_type) From ae4d621eeababa7e7f4ad941ff7bdb67b966815f Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 6 May 2025 08:49:43 +0200 Subject: [PATCH 09/12] Update dependency nokogiri --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 238e1bda9..c2d679518 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -469,7 +469,7 @@ GEM net-protocol net-ssh (7.1.0) nio4r (2.7.4) - nokogiri (1.18.3) + nokogiri (1.18.8) mini_portile2 (~> 2.8.2) racc (~> 1.4) nsa (0.3.0) From eac0852697807f7772c1716c33f199afc315ddb6 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 6 May 2025 08:50:16 +0200 Subject: [PATCH 10/12] Update dependency net-imap --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c2d679518..deafe1b60 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -215,7 +215,7 @@ GEM activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) - date (3.3.4) + date (3.4.1) debug_inspector (1.1.0) devise (4.9.2) bcrypt (~> 3.0) @@ -455,7 +455,7 @@ GEM uri net-http-persistent (4.0.2) connection_pool (~> 2.2) - net-imap (0.3.8) + net-imap (0.5.8) date net-protocol net-ldap (0.18.0) From 3406ac725b45385d3381ab96c3436491d7f00054 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 6 May 2025 08:48:14 +0200 Subject: [PATCH 11/12] Bump version to v4.2.21 --- CHANGELOG.md | 25 +++++++++++++++++++++++++ docker-compose.yml | 6 +++--- lib/mastodon/version.rb | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fd009f81..c583667fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ All notable changes to this project will be documented in this file. +## [4.2.21] - 2025-05-06 + +### Security + +- Update dependencies +- Check scheme on account, profile, and media URLs ([GHSA-x2rc-v5wx-g3m5](https://github.com/mastodon/mastodon/security/advisories/GHSA-x2rc-v5wx-g3m5)) + +### Added + +- Add warning for REDIS_NAMESPACE deprecation at startup (#34581 by @ClearlyClaire) +- Add built-in context for interaction policies (#34574 by @ClearlyClaire) + +### Changed + +- Change activity distribution error handling to skip retrying for deleted accounts (#33617 by @ClearlyClaire) + +### Removed + +- Remove double-query for signed query strings (#34610 by @ClearlyClaire) + +### Fixed + +- Fix incorrect redirect in response to unauthenticated API requests in limited federation mode (#34549 by @ClearlyClaire) +- Fix sign-up e-mail confirmation page reloading on error or redirect (#34548 by @ClearlyClaire) + ## [4.2.20] - 2025-04-02 ### Add diff --git a/docker-compose.yml b/docker-compose.yml index 84ec231d5..424f6822a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,7 +56,7 @@ services: web: build: . - image: ghcr.io/mastodon/mastodon:v4.2.20 + image: ghcr.io/mastodon/mastodon:v4.2.21 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.20 + image: ghcr.io/mastodon/mastodon:v4.2.21 restart: always env_file: .env.production command: node ./streaming @@ -95,7 +95,7 @@ services: sidekiq: build: . - image: ghcr.io/mastodon/mastodon:v4.2.20 + image: ghcr.io/mastodon/mastodon:v4.2.21 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 97a92ef74..6223a7595 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ module Mastodon end def patch - 20 + 21 end def default_prerelease From 7e2ea8d1fc079dfebeabe358522e936f26f41751 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 6 May 2025 15:22:50 +0200 Subject: [PATCH 12/12] Fix Javascript syntax issue --- app/javascript/mastodon/actions/importer/normalizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index 2ed3a7255..4466b1cad 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -105,7 +105,7 @@ export function normalizeStatus(status, normalOldStatus) { normalStatus.url = null; } - normalStatus.url ||= normalStatus.uri; + normalStatus.url = normalStatus.url || normalStatus.uri; normalStatus.media_attachments.forEach(item => { if (item.remote_url && !(item.remote_url.startsWith('http://') || item.remote_url.startsWith('https://')))