diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ad88c5107..249596dc05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,48 @@ All notable changes to this project will be documented in this file. +## [4.4.6] - 2025-10-13 + +### Security + +- Update dependencies `rack` and `uri` +- Fix streaming server connection not being closed on user suspension (by @ThisIsMissEm, [GHSA-r2fh-jr9c-9pxh](https://github.com/mastodon/mastodon/security/advisories/GHSA-r2fh-jr9c-9pxh)) +- Fix password change through admin CLI not invalidating existing sessions and access tokens (by @ThisIsMissEm, [GHSA-f3q3-rmf7-9655](https://github.com/mastodon/mastodon/security/advisories/GHSA-f3q3-rmf7-9655)) +- Fix streaming server allowing access to public timelines even without the `read` or `read:statuses` OAuth scopes (by @ThisIsMissEm, [GHSA-7gwh-mw97-qjgp](https://github.com/mastodon/mastodon/security/advisories/GHSA-7gwh-mw97-qjgp)) + +### Added + +- Add support for processing quotes of deleted posts signaled through a `Tombstone` (#36381 by @ClearlyClaire) + +### Fixed + +- Fix quote post state sometimes not being updated through streaming server (#36408 by @ClearlyClaire) +- Fix inconsistent “pending tags” count on admin dashboard (#36404 by @mjankowski) +- Fix JSON payload being potentially mutated when processing interaction policies (#36392 by @ClearlyClaire) +- Fix quotes not being displayed in email notifications (#36379 by @diondiondion) +- Fix redirect to external object when URL is missing or malformed (#36347 by @ClearlyClaire) +- Fix quotes not being displayed in the featured carousel (#36335 by @diondiondion) + +## [4.4.5] - 2025-09-23 + +### Security + +- Update dependencies + +### Added + +- Add support for `has:quote` in search (#36217 by @ClearlyClaire) + +### Changed + +- Change quoted posts from silenced accounts to use a click-through rather than being hidden (#36166 and #36167 by @ClearlyClaire) + +### Fixed + +- Fix processing of out-of-order `Update` as implicit updates (#36190 by @ClearlyClaire) +- Fix getting `Create` and `Update` out of order (#36176 by @ClearlyClaire) +- Fix quotes with Content Warnings but no text being shown without Content Warnings (#36150 by @ClearlyClaire) + ## [4.4.4] - 2025-09-16 ### Security diff --git a/app/models/concerns/account/suspensions.rb b/app/models/concerns/account/suspensions.rb index c981fb5a29..4c9ca593ad 100644 --- a/app/models/concerns/account/suspensions.rb +++ b/app/models/concerns/account/suspensions.rb @@ -32,6 +32,10 @@ module Account::Suspensions update!(suspended_at: date, suspension_origin: origin) create_canonical_email_block! if block_email end + + # This terminates all connections for the given account with the streaming + # server: + redis.publish("timeline:system:#{id}", Oj.dump(event: :kill)) if local? end def unsuspend! diff --git a/docker-compose.yml b/docker-compose.yml index d0ae54ed0c..83ff4336d0 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/glitch-soc/mastodon:v4.4.4 + image: ghcr.io/glitch-soc/mastodon:v4.4.6 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.4 + image: ghcr.io/glitch-soc/mastodon-streaming:v4.4.6 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.4 + image: ghcr.io/glitch-soc/mastodon:v4.4.6 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 043f22b28e..7fca8e8c82 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -17,7 +17,7 @@ module Mastodon end def default_prerelease - 'alpha.2' + 'alpha.3' end def prerelease diff --git a/spec/system/streaming/streaming_spec.rb b/spec/system/streaming/streaming_spec.rb index 7370033890..f5d3ba1142 100644 --- a/spec/system/streaming/streaming_spec.rb +++ b/spec/system/streaming/streaming_spec.rb @@ -98,4 +98,28 @@ RSpec.describe 'Streaming', :inline_jobs, :streaming do expect(streaming_client.open?).to be(false) end end + + context 'with a suspended user account' do + before do + user.account.suspend! + end + + it 'receives an 401 unauthorized error when trying to connect' do + streaming_client.connect + + expect(streaming_client.status).to eq(401) + expect(streaming_client.open?).to be(false) + end + end + + context 'when the user account is suspended whilst connected' do + it 'terminates the connection for the user' do + streaming_client.connect + + user.account.suspend! + + expect(streaming_client.wait_for(:closed).code).to be(1000) + expect(streaming_client.open?).to be(false) + end + end end diff --git a/streaming/index.js b/streaming/index.js index 291c912ddd..492491f04f 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -351,7 +351,7 @@ const startServer = async () => { * @returns {Promise} */ const accountFromToken = async (token, req) => { - const result = await pgPool.query('SELECT oauth_access_tokens.id, oauth_access_tokens.resource_owner_id, users.account_id, users.chosen_languages, oauth_access_tokens.scopes FROM oauth_access_tokens INNER JOIN users ON oauth_access_tokens.resource_owner_id = users.id WHERE oauth_access_tokens.token = $1 AND oauth_access_tokens.revoked_at IS NULL AND users.disabled IS FALSE LIMIT 1', [token]); + const result = await pgPool.query('SELECT oauth_access_tokens.id, oauth_access_tokens.resource_owner_id, users.account_id, users.chosen_languages, oauth_access_tokens.scopes FROM oauth_access_tokens INNER JOIN users ON oauth_access_tokens.resource_owner_id = users.id INNER JOIN accounts ON accounts.id = users.account_id WHERE oauth_access_tokens.token = $1 AND oauth_access_tokens.revoked_at IS NULL AND users.disabled IS FALSE AND accounts.suspended_at IS NULL LIMIT 1', [token]); if (result.rows.length === 0) { throw new AuthenticationError('Invalid access token');