Compare commits

..

10 Commits

11 changed files with 81 additions and 17 deletions

View File

@@ -20,7 +20,7 @@ jobs:
# Only tag with latest when ran against the latest stable branch
# This needs to be updated after each minor version release
flavor: |
latest=${{ startsWith(github.ref, 'refs/tags/v4.4.') }}
latest=false
tags: |
type=pep440,pattern={{raw}}
type=pep440,pattern=v{{major}}.{{minor}}
@@ -37,7 +37,7 @@ jobs:
# Only tag with latest when ran against the latest stable branch
# This needs to be updated after each minor version release
flavor: |
latest=${{ startsWith(github.ref, 'refs/tags/v4.4.') }}
latest=false
tags: |
type=pep440,pattern={{raw}}
type=pep440,pattern=v{{major}}.{{minor}}

View File

@@ -2,6 +2,21 @@
All notable changes to this project will be documented in this file.
## [4.4.11] - 2026-01-07
### Security
- Fix SSRF protection bypass ([GHSA](https://github.com/mastodon/mastodon/security/advisories/GHSA-xfrj-c749-jxxq))
- Fix missing ownership check in severed relationships controller ([GHSA](https://github.com/mastodon/mastodon/security/advisories/GHSA-ww85-x9cp-5v24))
### Changed
- Change HTTP Signature verification status from 401 to 503 on temporary failure to get remote actor (#37221 by @ClearlyClaire)
### Fixed
- Fix mentions of domain-blocked users being processed (#37257 by @ClearlyClaire)
## [4.4.10] - 2025-12-08
### Security

View File

@@ -70,10 +70,13 @@ module SignatureVerification
rescue Mastodon::SignatureVerificationError => e
fail_with! e.message
rescue *Mastodon::HTTP_CONNECTION_ERRORS => e
@signature_verification_failure_code ||= 503
fail_with! "Failed to fetch remote data: #{e.message}"
rescue Mastodon::UnexpectedResponseError
@signature_verification_failure_code ||= 503
fail_with! 'Failed to fetch remote data (got unexpected reply from server)'
rescue Stoplight::Error::RedLight
@signature_verification_failure_code ||= 503
fail_with! 'Fetching attempt skipped because of recent connection failure'
end

View File

@@ -26,7 +26,7 @@ class SeveredRelationshipsController < ApplicationController
private
def set_event
@event = AccountRelationshipSeveranceEvent.find(params[:id])
@event = AccountRelationshipSeveranceEvent.where(account: current_account).find(params[:id])
end
def following_data

View File

@@ -1,9 +1,7 @@
# frozen_string_literal: true
module PrivateAddressCheck
module_function
CIDR_LIST = [
IP4_CIDR_LIST = [
IPAddr.new('0.0.0.0/8'), # Current network (only valid as source address)
IPAddr.new('100.64.0.0/10'), # Shared Address Space
IPAddr.new('172.16.0.0/12'), # Private network
@@ -16,6 +14,9 @@ module PrivateAddressCheck
IPAddr.new('224.0.0.0/4'), # IP multicast (former Class D network)
IPAddr.new('240.0.0.0/4'), # Reserved (former Class E network)
IPAddr.new('255.255.255.255'), # Broadcast
].freeze
CIDR_LIST = (IP4_CIDR_LIST + IP4_CIDR_LIST.map(&:ipv4_mapped) + [
IPAddr.new('64:ff9b::/96'), # IPv4/IPv6 translation (RFC 6052)
IPAddr.new('100::/64'), # Discard prefix (RFC 6666)
IPAddr.new('2001::/32'), # Teredo tunneling
@@ -25,7 +26,9 @@ module PrivateAddressCheck
IPAddr.new('2002::/16'), # 6to4
IPAddr.new('fc00::/7'), # Unique local address
IPAddr.new('ff00::/8'), # Multicast
].freeze
]).freeze
module_function
def private_address?(address)
address.private? || address.loopback? || address.link_local? || CIDR_LIST.any? { |cidr| cidr.include?(address) }

View File

@@ -71,7 +71,7 @@ class ProcessMentionsService < BaseService
# Make sure we never mention blocked accounts
unless @current_mentions.empty?
mentioned_domains = @current_mentions.filter_map { |m| m.account.domain }.uniq
blocked_domains = Set.new(mentioned_domains.empty? ? [] : AccountDomainBlock.where(account_id: @status.account_id, domain: mentioned_domains))
blocked_domains = Set.new(mentioned_domains.empty? ? [] : AccountDomainBlock.where(account_id: @status.account_id, domain: mentioned_domains).pluck(:domain))
mentioned_account_ids = @current_mentions.map(&:account_id)
blocked_account_ids = Set.new(@status.account.block_relationships.where(target_account_id: mentioned_account_ids).pluck(:target_account_id))

View File

@@ -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/glitch-soc/mastodon:v4.4.10
image: ghcr.io/glitch-soc/mastodon:v4.4.11
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/glitch-soc/mastodon-streaming:v4.4.10
image: ghcr.io/glitch-soc/mastodon-streaming:v4.4.11
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/glitch-soc/mastodon:v4.4.10
image: ghcr.io/glitch-soc/mastodon:v4.4.11
restart: always
env_file: .env.production
command: bundle exec sidekiq

View File

@@ -13,7 +13,7 @@ module Mastodon
end
def patch
10
11
end
def default_prerelease

View File

@@ -0,0 +1,20 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe PrivateAddressCheck do
describe 'private_address?' do
it 'returns true for private addresses' do
# rubocop:disable RSpec/ExpectActual
expect(
[
'192.168.1.7',
'0.0.0.0',
'127.0.0.1',
'::ffff:0.0.0.1',
]
).to all satisfy('return true') { |addr| described_class.private_address?(IPAddr.new(addr)) }
# rubocop:enable RSpec/ExpectActual
end
end
end

View File

@@ -3,9 +3,10 @@
require 'rails_helper'
RSpec.describe 'Severed Relationships' do
let(:account_rs_event) { Fabricate :account_relationship_severance_event }
let(:account_rs_event) { Fabricate(:account_relationship_severance_event) }
let(:user) { account_rs_event.account.user }
before { sign_in Fabricate(:user) }
before { sign_in user }
describe 'GET /severed_relationships/:id/following' do
it 'returns a CSV file with correct data' do
@@ -22,6 +23,17 @@ RSpec.describe 'Severed Relationships' do
expect(response.body)
.to include('Account address')
end
context 'when the user is not the subject of the event' do
let(:user) { Fabricate(:user) }
it 'returns a 404' do
get following_severed_relationship_path(account_rs_event, format: :csv)
expect(response)
.to have_http_status(404)
end
end
end
describe 'GET /severed_relationships/:id/followers' do
@@ -39,5 +51,16 @@ RSpec.describe 'Severed Relationships' do
expect(response.body)
.to include('Account address')
end
context 'when the user is not the subject of the event' do
let(:user) { Fabricate(:user) }
it 'returns a 404' do
get followers_severed_relationship_path(account_rs_event, format: :csv)
expect(response)
.to have_http_status(404)
end
end
end
end

View File

@@ -8,9 +8,9 @@ RSpec.describe ProcessMentionsService do
let(:account) { Fabricate(:account, username: 'alice') }
context 'when mentions contain blocked accounts' do
let(:non_blocked_account) { Fabricate(:account) }
let(:individually_blocked_account) { Fabricate(:account) }
let(:domain_blocked_account) { Fabricate(:account, domain: 'evil.com') }
let!(:non_blocked_account) { Fabricate(:account) }
let!(:individually_blocked_account) { Fabricate(:account) }
let!(:domain_blocked_account) { Fabricate(:account, domain: 'evil.com', protocol: :activitypub) }
let(:status) { Fabricate(:status, account: account, text: "Hello @#{non_blocked_account.acct} @#{individually_blocked_account.acct} @#{domain_blocked_account.acct}", visibility: :public) }
before do