From 72b1af137e993db3f9d2833b631be2525c041199 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 17 Jan 2025 10:12:59 +0100 Subject: [PATCH 01/11] 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 0c6ca026bb..7a1440ed15 100644 --- a/app/workers/activitypub/delivery_worker.rb +++ b/app/workers/activitypub/delivery_worker.rb @@ -62,7 +62,7 @@ class ActivityPub::DeliveryWorker stoplight_wrapper.run 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 @@ -70,6 +70,10 @@ class ActivityPub::DeliveryWorker end end + def unsalvageable_authorization_failure?(response) + @source_account.permanently_unavailable? && response.code == 401 + end + def stoplight_wrapper Stoplight(@inbox_url) .with_threshold(STOPLIGHT_FAILURE_THRESHOLD) From 698e4fdef2ec37db229c668ca85f9ecfcabc9abe Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Apr 2025 11:00:54 +0200 Subject: [PATCH 02/11] Fix sign-up e-mail confirmation page reloading on error or redirect (#34548) --- app/javascript/entrypoints/sign_up.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/javascript/entrypoints/sign_up.ts b/app/javascript/entrypoints/sign_up.ts index 880738fcb7..87100be56d 100644 --- a/app/javascript/entrypoints/sign_up.ts +++ b/app/javascript/entrypoints/sign_up.ts @@ -4,9 +4,12 @@ import axios from 'axios'; import ready from '../mastodon/ready'; async function checkConfirmation() { - const response = await axios.get('/api/v1/emails/check_confirmation'); + const response = await axios.get('/api/v1/emails/check_confirmation', { + headers: { Accept: 'application/json' }, + withCredentials: true, + }); - if (response.data) { + if (response.status === 200 && response.data === true) { window.location.href = '/start'; } } From a9f8b1ad965151601dc3d27701368f132caab8f8 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 5 May 2025 15:11:26 +0200 Subject: [PATCH 03/11] 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 0980e0ebbc..b10c2f5737 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -72,6 +72,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 62e3355ae6..081b435811 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -71,7 +71,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 86a8aa5e5c0fd718f83a8f820354315d0983b61e Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 5 May 2025 15:12:07 +0200 Subject: [PATCH 04/11] 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 18bb088b48..6662aaaa92 100644 --- a/app/helpers/context_helper.rb +++ b/app/helpers/context_helper.rb @@ -25,6 +25,13 @@ module ContextHelper voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' }, suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' }, attribution_domains: { 'toot' => 'http://joinmastodon.org/ns#', 'attributionDomains' => { '@id' => 'toot:attributionDomains', '@type' => '@id' } }, + 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 e6a6c26c36c6ccc9892104dedb034f3c4d16f46b Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 5 May 2025 15:13:08 +0200 Subject: [PATCH 05/11] 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 fc0d498b29..cb794b223a 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 03c27c7cea..10a3680484 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 46cab6caf9..0e0ba673be 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 ec2023233d3e7cae1aba5aa1bdce0e6d72437101 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 5 May 2025 15:01:16 +0200 Subject: [PATCH 06/11] 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 0000000000..e0ad54d8c3 --- /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 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 6d46225718f54d4d6f88190e92ace8da3ebc67f3 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 6 May 2025 15:02:13 +0200 Subject: [PATCH 07/11] 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 | 11 +++++++++++ app/javascript/mastodon/models/account.ts | 1 + app/lib/activitypub/parser/media_attachment_parser.rb | 6 ++++-- app/lib/activitypub/parser/status_parser.rb | 5 ++++- app/lib/activitypub/tag_manager.rb | 3 ++- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index c09a3f442c..3a3bff0617 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -80,6 +80,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/javascript/mastodon/models/account.ts b/app/javascript/mastodon/models/account.ts index 8e8e3b0e8d..2841665b64 100644 --- a/app/javascript/mastodon/models/account.ts +++ b/app/javascript/mastodon/models/account.ts @@ -150,5 +150,6 @@ export function createAccountFromServerJSON(serverJSON: ApiAccountJSON) { ), note_emojified: emojify(accountJSON.note, emojiMap), note_plain: unescapeHTML(accountJSON.note), + url: accountJSON.url.startsWith('http://') || accountJSON.url.startsWith('https://') ? accountJSON.url : accountJSON.uri, }); } diff --git a/app/lib/activitypub/parser/media_attachment_parser.rb b/app/lib/activitypub/parser/media_attachment_parser.rb index 56b8b23f84..bcbf92214f 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 2940aea44b..554f2931de 100644 --- a/app/lib/activitypub/parser/status_parser.rb +++ b/app/lib/activitypub/parser/status_parser.rb @@ -28,7 +28,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 23b44be372..b244f26800 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 e14bf631b5063ce144b179d0189dffa76a30800f Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 6 May 2025 08:26:43 +0200 Subject: [PATCH 08/11] Update dependency nokogiri --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index cdd6b15dcf..650a5b1f9a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -458,7 +458,7 @@ GEM net-smtp (0.5.1) net-protocol nio4r (2.7.3) - nokogiri (1.18.3) + nokogiri (1.18.8) mini_portile2 (~> 2.8.2) racc (~> 1.4) oj (3.16.6) From 5ef82d793749463bfc8e2c0abec4c9c7cd0f3259 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 6 May 2025 08:27:36 +0200 Subject: [PATCH 09/11] Update dependency net-imap --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 650a5b1f9a..130296745a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -190,7 +190,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 (1.9.2) irb (~> 1.10) reline (>= 0.3.8) @@ -447,7 +447,7 @@ GEM uri net-http-persistent (4.0.2) connection_pool (~> 2.2) - net-imap (0.4.19) + net-imap (0.5.8) date net-protocol net-ldap (0.19.0) From 30e25ff7fc7fc286ee71e9df43c6cc9cbe3e3f2c Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 6 May 2025 08:39:08 +0200 Subject: [PATCH 10/11] Bump version to v4.3.8 --- CHANGELOG.md | 27 ++++++++++++++++++++++++++- docker-compose.yml | 6 +++--- lib/mastodon/version.rb | 2 +- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f849f6f11..ea985b236f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,34 @@ All notable changes to this project will be documented in this file. +## [4.3.8] - 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.3.7] - 2025-04-02 -### Add +### Added - Add delay to profile updates to debounce them (#34137 by @ClearlyClaire) - Add support for paginating partial collections in `SynchronizeFollowersService` (#34272 and #34277 by @ClearlyClaire) diff --git a/docker-compose.yml b/docker-compose.yml index 7b8cccab58..e42afa5464 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -59,7 +59,7 @@ services: web: # You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes # build: . - image: ghcr.io/mastodon/mastodon:v4.3.7 + image: ghcr.io/mastodon/mastodon:v4.3.8 restart: always env_file: .env.production command: bundle exec puma -C config/puma.rb @@ -83,7 +83,7 @@ services: # build: # dockerfile: ./streaming/Dockerfile # context: . - image: ghcr.io/mastodon/mastodon-streaming:v4.3.7 + image: ghcr.io/mastodon/mastodon-streaming:v4.3.8 restart: always env_file: .env.production command: node ./streaming/index.js @@ -102,7 +102,7 @@ services: sidekiq: # You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes # build: . - image: ghcr.io/mastodon/mastodon:v4.3.7 + image: ghcr.io/mastodon/mastodon:v4.3.8 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 1503c0e44e..3059788d53 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ module Mastodon end def patch - 7 + 8 end def default_prerelease From e6591bf322c7e47a8420a588d52a44a585c10b54 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 6 May 2025 15:08:57 +0200 Subject: [PATCH 11/11] Fix code style issue --- app/javascript/mastodon/models/account.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/models/account.ts b/app/javascript/mastodon/models/account.ts index 2841665b64..4c89a0c30d 100644 --- a/app/javascript/mastodon/models/account.ts +++ b/app/javascript/mastodon/models/account.ts @@ -150,6 +150,10 @@ export function createAccountFromServerJSON(serverJSON: ApiAccountJSON) { ), note_emojified: emojify(accountJSON.note, emojiMap), note_plain: unescapeHTML(accountJSON.note), - url: accountJSON.url.startsWith('http://') || accountJSON.url.startsWith('https://') ? accountJSON.url : accountJSON.uri, + url: + accountJSON.url.startsWith('http://') || + accountJSON.url.startsWith('https://') + ? accountJSON.url + : accountJSON.uri, }); }