From fdca24ba56276b3d0e6bf0b31152de02c6162f75 Mon Sep 17 00:00:00 2001 From: KMY Date: Mon, 10 Feb 2025 12:44:42 +0900 Subject: [PATCH 01/31] Bump version to 17.0 --- docker-compose.yml | 6 +++--- lib/mastodon/version.rb | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 61deb51ab6..47eb36698f 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: kmyblue:17.0-dev + image: kmyblue:17.0 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: kmyblue-streaming:17.0-dev + image: kmyblue-streaming:17.0 restart: always env_file: .env.production command: node ./streaming/index.js @@ -101,7 +101,7 @@ services: sidekiq: build: . - image: kmyblue:17.0-dev + image: kmyblue:17.0 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index f09f32ab30..f45b4a4305 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -18,8 +18,8 @@ module Mastodon def kmyblue_flag # 'LTS' - 'dev' - # nil + # 'dev' + nil end def major From e97f8d1b59bb20bb5b28713a7b470b9c5dd4c1e0 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 3 Feb 2025 14:43:45 +0100 Subject: [PATCH 02/31] Fix emoji rewrite adding unnecessary curft to the DOM for most emoji (#33818) --- .../features/emoji/__tests__/emoji-test.js | 36 +++++++++---------- .../mastodon/features/emoji/emoji.js | 32 ++++++++--------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js b/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js index 022c9baaf7..35804de82a 100644 --- a/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js +++ b/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js @@ -22,23 +22,23 @@ describe('emoji', () => { it('does unicode', () => { expect(emojify('\uD83D\uDC69\u200D\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66')).toEqual( - 'πŸ‘©β€πŸ‘©β€πŸ‘¦β€πŸ‘¦'); + 'πŸ‘©β€πŸ‘©β€πŸ‘¦β€πŸ‘¦'); expect(emojify('πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘§')).toEqual( - 'πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘§'); - expect(emojify('πŸ‘©β€πŸ‘©β€πŸ‘¦')).toEqual('πŸ‘©β€πŸ‘©β€πŸ‘¦'); + 'πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘§'); + expect(emojify('πŸ‘©β€πŸ‘©β€πŸ‘¦')).toEqual('πŸ‘©β€πŸ‘©β€πŸ‘¦'); expect(emojify('\u2757')).toEqual( - '❗'); + '❗'); }); it('does multiple unicode', () => { expect(emojify('\u2757 #\uFE0F\u20E3')).toEqual( - '❗ #️⃣'); + '❗ #️⃣'); expect(emojify('\u2757#\uFE0F\u20E3')).toEqual( - '❗#️⃣'); + '❗#️⃣'); expect(emojify('\u2757 #\uFE0F\u20E3 \u2757')).toEqual( - '❗ #️⃣ ❗'); + '❗ #️⃣ ❗'); expect(emojify('foo \u2757 #\uFE0F\u20E3 bar')).toEqual( - 'foo ❗ #️⃣ bar'); + 'foo ❗ #️⃣ bar'); }); it('ignores unicode inside of tags', () => { @@ -46,16 +46,16 @@ describe('emoji', () => { }); it('does multiple emoji properly (issue 5188)', () => { - expect(emojify('πŸ‘ŒπŸŒˆπŸ’•')).toEqual('πŸ‘ŒπŸŒˆπŸ’•'); - expect(emojify('πŸ‘Œ 🌈 πŸ’•')).toEqual('πŸ‘Œ 🌈 πŸ’•'); + expect(emojify('πŸ‘ŒπŸŒˆπŸ’•')).toEqual('πŸ‘ŒπŸŒˆπŸ’•'); + expect(emojify('πŸ‘Œ 🌈 πŸ’•')).toEqual('πŸ‘Œ 🌈 πŸ’•'); }); it('does an emoji that has no shortcode', () => { - expect(emojify('πŸ‘β€πŸ—¨')).toEqual('πŸ‘β€πŸ—¨'); + expect(emojify('πŸ‘β€πŸ—¨')).toEqual('πŸ‘β€πŸ—¨'); }); it('does an emoji whose filename is irregular', () => { - expect(emojify('↙️')).toEqual('↙️'); + expect(emojify('↙️')).toEqual('↙️'); }); it('avoid emojifying on invisible text', () => { @@ -67,11 +67,11 @@ describe('emoji', () => { it('avoid emojifying on invisible text with nested tags', () => { expect(emojify('πŸ˜‡')) - .toEqual('πŸ˜‡'); + .toEqual('πŸ˜‡'); expect(emojify('πŸ˜‡')) - .toEqual('πŸ˜‡'); + .toEqual('πŸ˜‡'); expect(emojify('πŸ˜‡')) - .toEqual('πŸ˜‡'); + .toEqual('πŸ˜‡'); }); it('does not emojify emojis with textual presentation VS15 character', () => { @@ -81,17 +81,17 @@ describe('emoji', () => { it('does a simple emoji properly', () => { expect(emojify('♀♂')) - .toEqual('♀♂'); + .toEqual('♀♂'); }); it('does an emoji containing ZWJ properly', () => { expect(emojify('πŸ’‚β€β™€οΈπŸ’‚β€β™‚οΈ')) - .toEqual('πŸ’‚\u200Dβ™€οΈπŸ’‚\u200D♂️'); + .toEqual('πŸ’‚\u200Dβ™€οΈπŸ’‚\u200D♂️'); }); it('keeps ordering as expected (issue fixed by PR 20677)', () => { expect(emojify('

πŸ’• #foo test: foo.

')) - .toEqual('

πŸ’• #foo test: foo.

'); + .toEqual('

πŸ’• #foo test: foo.

'); }); }); }); diff --git a/app/javascript/mastodon/features/emoji/emoji.js b/app/javascript/mastodon/features/emoji/emoji.js index 1f469aced7..66dcd89488 100644 --- a/app/javascript/mastodon/features/emoji/emoji.js +++ b/app/javascript/mastodon/features/emoji/emoji.js @@ -97,30 +97,30 @@ const emojifyTextNode = (node, customEmojis) => { const { filename, shortCode } = unicodeMapping[unicode_emoji]; const title = shortCode ? `:${shortCode}:` : ''; - replacement = document.createElement('picture'); - const isSystemTheme = !!document.body?.classList.contains('theme-system'); - if(isSystemTheme) { - let source = document.createElement('source'); - source.setAttribute('media', '(prefers-color-scheme: dark)'); - source.setAttribute('srcset', `${assetHost}/emoji/${emojiFilename(filename, "dark")}.svg`); - replacement.appendChild(source); - } + const theme = (isSystemTheme || document.body?.classList.contains('theme-mastodon-light')) ? 'light' : 'dark'; - let img = document.createElement('img'); + const imageFilename = emojiFilename(filename, theme); + + const img = document.createElement('img'); img.setAttribute('draggable', 'false'); img.setAttribute('class', 'emojione'); img.setAttribute('alt', unicode_emoji); img.setAttribute('title', title); + img.setAttribute('src', `${assetHost}/emoji/${imageFilename}.svg`); - let theme = "light"; + if (isSystemTheme && imageFilename !== emojiFilename(filename, 'dark')) { + replacement = document.createElement('picture'); - if(!isSystemTheme && !document.body?.classList.contains('theme-mastodon-light')) - theme = "dark"; - - img.setAttribute('src', `${assetHost}/emoji/${emojiFilename(filename, theme)}.svg`); - replacement.appendChild(img); + const source = document.createElement('source'); + source.setAttribute('media', '(prefers-color-scheme: dark)'); + source.setAttribute('srcset', `${assetHost}/emoji/${emojiFilename(filename, 'dark')}.svg`); + replacement.appendChild(source); + replacement.appendChild(img); + } else { + replacement = img; + } } // Add the processed-up-to-now string and the emoji replacement @@ -135,7 +135,7 @@ const emojifyTextNode = (node, customEmojis) => { }; const emojifyNode = (node, customEmojis) => { - for (const child of node.childNodes) { + for (const child of Array.from(node.childNodes)) { switch(child.nodeType) { case Node.TEXT_NODE: emojifyTextNode(child, customEmojis); From 5fb4ae8edfe3ea4ee7ea3b3cb231fe286c2fe71f Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 5 Feb 2025 15:02:39 +0100 Subject: [PATCH 03/31] Optimize timeline generation (#33839) --- app/lib/feed_manager.rb | 83 +++++++++++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 23 deletions(-) diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index df4fc17908..895d45ce69 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -42,7 +42,7 @@ class FeedManager when :home filter_from_home(status, receiver.id, build_crutches(receiver.id, [status]), :home) when :list - (filter_from_list?(status, receiver) ? :filter : nil) || filter_from_home(status, receiver.account_id, build_crutches(receiver.account_id, [status]), :list, stl_home: stl_home) + (filter_from_list?(status, receiver) ? :filter : nil) || filter_from_home(status, receiver.account_id, build_crutches(receiver.account_id, [status], list: list), :list, stl_home: stl_home) when :mentions filter_from_mentions?(status, receiver.id) ? :filter : nil when :tags @@ -136,7 +136,7 @@ class FeedManager timeline_key = key(:home, into_account.id) aggregate = into_account.user&.aggregates_reblogs? - query = from_account.statuses.list_eligible_visibility.includes(:preloadable_poll, :media_attachments, reblog: :account).limit(FeedManager::MAX_ITEMS / 4) + query = from_account.statuses.list_eligible_visibility.includes(reblog: :account).limit(FeedManager::MAX_ITEMS / 4) if redis.zcard(timeline_key) >= FeedManager::MAX_ITEMS / 4 oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i @@ -164,7 +164,7 @@ class FeedManager timeline_key = key(:list, list.id) aggregate = list.account.user&.aggregates_reblogs? - query = from_account.statuses.list_eligible_visibility.includes(:preloadable_poll, :media_attachments, reblog: :account).limit(FeedManager::MAX_ITEMS / 4) + query = from_account.statuses.list_eligible_visibility.includes(reblog: :account).limit(FeedManager::MAX_ITEMS / 4) if redis.zcard(timeline_key) >= FeedManager::MAX_ITEMS / 4 oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i @@ -172,10 +172,10 @@ class FeedManager end statuses = query.to_a - crutches = build_crutches(list.account_id, statuses) + crutches = build_crutches(list.account_id, statuses, list: list) statuses.each do |status| - next if filter_from_home(status, list.account_id, crutches) || filter_from_list?(status, list) + next if filter_from_home(status, list.account_id, crutches, :list) add_to_feed(:list, list.id, status, aggregate_reblogs: aggregate) end @@ -309,23 +309,32 @@ class FeedManager limit = FeedManager::MAX_ITEMS / 2 aggregate = account.user&.aggregates_reblogs? timeline_key = key(:home, account.id) + over_limit = false account.statuses.limit(limit).each do |status| add_to_feed(:home, account.id, status, aggregate_reblogs: aggregate) end account.following.includes(:account_stat).reorder(nil).find_each do |target_account| - if redis.zcard(timeline_key) >= limit + query = target_account.statuses.list_eligible_visibility.includes(reblog: :account).limit(limit) + + over_limit ||= redis.zcard(timeline_key) >= limit + if over_limit oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i - last_status_score = Mastodon::Snowflake.id_at(target_account.last_status_at) + last_status_score = Mastodon::Snowflake.id_at(target_account.last_status_at, with_random: false) # If the feed is full and this account has not posted more recently # than the last item on the feed, then we can skip the whole account # because none of its statuses would stay on the feed anyway next if last_status_score < oldest_home_score + + # No need to get older statuses + query = query.where(id: oldest_home_score...) end - statuses = target_account.statuses.list_eligible_visibility.includes(:preloadable_poll, :media_attachments, :account, reblog: :account).limit(limit) + statuses = query.to_a + next if statuses.empty? + crutches = build_crutches(account.id, statuses) statuses.each do |status| @@ -345,23 +354,32 @@ class FeedManager limit = FeedManager::MAX_ITEMS / 2 aggregate = list.account.user&.aggregates_reblogs? timeline_key = key(:list, list.id) + over_limit = false list.active_accounts.includes(:account_stat).reorder(nil).find_each do |target_account| - if redis.zcard(timeline_key) >= limit + query = target_account.statuses.list_eligible_visibility.includes(reblog: :account).limit(limit) + + over_limit ||= redis.zcard(timeline_key) >= limit + if over_limit oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i - last_status_score = Mastodon::Snowflake.id_at(target_account.last_status_at) + last_status_score = Mastodon::Snowflake.id_at(target_account.last_status_at, with_random: false) # If the feed is full and this account has not posted more recently # than the last item on the feed, then we can skip the whole account # because none of its statuses would stay on the feed anyway next if last_status_score < oldest_home_score + + # No need to get older statuses + query = query.where(id: oldest_home_score...) end - statuses = target_account.statuses.list_eligible_visibility.includes(:preloadable_poll, :media_attachments, :account, reblog: :account).limit(limit) - crutches = build_crutches(list.account_id, statuses) + statuses = query.to_a + next if statuses.empty? + + crutches = build_crutches(list.account_id, statuses, list: list) statuses.each do |status| - next if filter_from_home(status, list.account_id, crutches) || filter_from_list?(status, list) + next if filter_from_home(status, list.account_id, crutches, :list) add_to_feed(:list, list.id, status, aggregate_reblogs: aggregate) end @@ -632,8 +650,9 @@ class FeedManager # are going to be checked by the filtering methods # @param [Integer] receiver_id # @param [Array] statuses + # @param [List] list # @return [Hash] - def build_crutches(receiver_id, statuses) # rubocop:disable Metrics/AbcSize + def build_crutches(receiver_id, statuses, list: nil) crutches = {} crutches[:active_mentions] = crutches_active_mentions(statuses) @@ -650,25 +669,43 @@ class FeedManager arr end - lists = List.where(account_id: receiver_id, exclusive: true) - antennas = Antenna.where(list: lists, insert_feeds: true) - - replied_accounts = statuses.filter_map(&:in_reply_to_account_id) - replied_accounts += statuses.filter { |status| status.limited_visibility? && status.thread.present? }.map { |status| status.thread.account_id } - - crutches[:following] = Follow.where(account_id: receiver_id, target_account_id: replied_accounts).pluck(:target_account_id).index_with(true) + crutches[:following] = crutches_following(receiver_id, statuses, list) crutches[:languages] = Follow.where(account_id: receiver_id, target_account_id: statuses.map(&:account_id)).pluck(:target_account_id, :languages).to_h crutches[:hiding_reblogs] = Follow.where(account_id: receiver_id, target_account_id: statuses.filter_map { |s| s.account_id if s.reblog? }, show_reblogs: false).pluck(:target_account_id).index_with(true) crutches[:blocking] = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true) crutches[:muting] = Mute.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true) crutches[:domain_blocking] = AccountDomainBlock.where(account_id: receiver_id, domain: statuses.flat_map { |s| [s.account.domain, s.reblog&.account&.domain] }.compact).pluck(:domain).index_with(true) crutches[:blocked_by] = Block.where(target_account_id: receiver_id, account_id: statuses.map { |s| [s.account_id, s.reblog&.account_id] }.flatten.compact).pluck(:account_id).index_with(true) - crutches[:exclusive_list_users] = ListAccount.where(list: lists, account_id: statuses.map(&:account_id)).pluck(:account_id).index_with(true) - crutches[:exclusive_antenna_users] = AntennaAccount.where(antenna: antennas, account_id: statuses.map(&:account_id)).pluck(:account_id).index_with(true) + crutches[:exclusive_list_users] = crutches_exclusive_list_users(receiver_id, statuses) if list.blank? + crutches[:exclusive_antenna_users] = crutches_exclusive_antenna_users(receiver_id, statuses) crutches end + def crutches_exclusive_list_users(recipient_id, statuses) + lists = List.where(account_id: recipient_id, exclusive: true) + ListAccount.where(list: lists, account_id: statuses.map(&:account_id)).pluck(:account_id).index_with(true) + end + + def crutches_exclusive_antenna_users(recipient_id, statuses) + lists = List.where(account_id: recipient_id, exclusive: true) + antennas = Antenna.where(list: lists, insert_feeds: true) + AntennaAccount.where(antenna: antennas, account_id: statuses.map(&:account_id)).pluck(:account_id).index_with(true) + end + + def crutches_following(recipient_id, statuses, list) + if list.blank? || list.show_followed? + replied_accounts = statuses.filter_map(&:in_reply_to_account_id) + replied_accounts += statuses.filter { |status| status.limited_visibility? && status.thread.present? }.map { |status| status.thread.account_id } + + Follow.where(account_id: recipient_id, target_account_id: replied_accounts).pluck(:target_account_id).index_with(true) + elsif list.show_list? + ListAccount.where(list_id: list.id, account_id: statuses.filter_map(&:in_reply_to_account_id)).pluck(:account_id).index_with(true) + else + {} + end + end + def crutches_active_mentions(statuses) Mention .active From d4f0b0120723d5037a9cf579543b0aa8cf4b3939 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 5 Feb 2025 16:08:12 +0100 Subject: [PATCH 04/31] Fix filtering for lists (#33842) --- app/lib/feed_manager.rb | 2 +- spec/lib/feed_manager_spec.rb | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index 895d45ce69..1a558cacbe 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -42,7 +42,7 @@ class FeedManager when :home filter_from_home(status, receiver.id, build_crutches(receiver.id, [status]), :home) when :list - (filter_from_list?(status, receiver) ? :filter : nil) || filter_from_home(status, receiver.account_id, build_crutches(receiver.account_id, [status], list: list), :list, stl_home: stl_home) + (filter_from_list?(status, receiver) ? :filter : nil) || filter_from_home(status, receiver.account_id, build_crutches(receiver.account_id, [status], list: receiver), :list, stl_home: stl_home) when :mentions filter_from_mentions?(status, receiver.id) ? :filter : nil when :tags diff --git a/spec/lib/feed_manager_spec.rb b/spec/lib/feed_manager_spec.rb index ad7913c758..48defa1f83 100644 --- a/spec/lib/feed_manager_spec.rb +++ b/spec/lib/feed_manager_spec.rb @@ -233,6 +233,28 @@ RSpec.describe FeedManager do end end + context 'with list feed' do + let(:list) { Fabricate(:list, account: bob) } + + before do + bob.follow!(alice) + list.list_accounts.create!(account: alice) + end + + it "returns false for followee's status" do + status = Fabricate(:status, text: 'Hello world', account: alice) + + expect(subject.filter?(:list, status, list)).to be false + end + + it 'returns false for reblog by followee' do + status = Fabricate(:status, text: 'Hello world', account: jeff) + reblog = Fabricate(:status, reblog: status, account: alice) + + expect(subject.filter?(:list, reblog, list)).to be false + end + end + context 'with mentions feed' do it 'returns true for status that mentions blocked account' do bob.block!(jeff) From 4fa550d8816aa2989c2efff100aaf74a72f6db02 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 12 Feb 2025 16:34:56 +0100 Subject: [PATCH 05/31] Fix handling of duplicate mentions in incoming status `Update` (#33911) --- .../activitypub/process_status_update_service.rb | 2 +- spec/lib/activitypub/activity/create_spec.rb | 14 ++++++++++---- .../process_status_update_service_spec.rb | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb index 1be39aaedf..10224f4d7e 100644 --- a/app/services/activitypub/process_status_update_service.rb +++ b/app/services/activitypub/process_status_update_service.rb @@ -296,7 +296,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService nil end - @status.mentions.upsert_all(currently_mentioned_account_ids.map { |id| { account_id: id, silent: false } }, unique_by: %w(status_id account_id)) + @status.mentions.upsert_all(currently_mentioned_account_ids.uniq.map { |id| { account_id: id, silent: false } }, unique_by: %w(status_id account_id)) # If previous mentions are no longer contained in the text, convert them # to silent mentions, since withdrawing access from someone who already diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb index f5528f13a7..108111c06b 100644 --- a/spec/lib/activitypub/activity/create_spec.rb +++ b/spec/lib/activitypub/activity/create_spec.rb @@ -48,10 +48,16 @@ RSpec.describe ActivityPub::Activity::Create do content: '@bob lorem ipsum', published: 1.hour.ago.utc.iso8601, updated: 1.hour.ago.utc.iso8601, - tag: { - type: 'Mention', - href: ActivityPub::TagManager.instance.uri_for(follower), - }, + tag: [ + { + type: 'Mention', + href: ActivityPub::TagManager.instance.uri_for(follower), + }, + { + type: 'Mention', + href: ActivityPub::TagManager.instance.uri_for(follower), + }, + ], } end diff --git a/spec/services/activitypub/process_status_update_service_spec.rb b/spec/services/activitypub/process_status_update_service_spec.rb index 49d496e295..6d008840be 100644 --- a/spec/services/activitypub/process_status_update_service_spec.rb +++ b/spec/services/activitypub/process_status_update_service_spec.rb @@ -12,6 +12,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do [ { type: 'Hashtag', name: 'hoge' }, { type: 'Mention', href: ActivityPub::TagManager.instance.uri_for(alice) }, + { type: 'Mention', href: ActivityPub::TagManager.instance.uri_for(alice) }, { type: 'Mention', href: bogus_mention }, ] end From 689d431dc6b8825eec65195770a120c837fefd34 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Feb 2025 09:48:43 +0100 Subject: [PATCH 06/31] chore(deps): update dependency ruby-vips to v2.2.3 (#33853) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8547e4fba1..2529435115 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -748,7 +748,7 @@ GEM ruby-saml (1.17.0) nokogiri (>= 1.13.10) rexml - ruby-vips (2.2.2) + ruby-vips (2.2.3) ffi (~> 1.12) logger rubyzip (2.4.1) From 4da06664a68dca9fc62d30c2d0c5cc269b5e794b Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 12 Feb 2025 11:31:42 +0100 Subject: [PATCH 07/31] Update dependency net-imap to 0.5.6 (#33901) --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2529435115..cfca6f8ab1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -416,7 +416,7 @@ GEM mutex_m (0.3.0) net-http (0.6.0) uri - net-imap (0.5.5) + net-imap (0.5.6) date net-protocol net-ldap (0.19.0) @@ -424,7 +424,7 @@ GEM net-protocol net-protocol (0.2.2) timeout - net-smtp (0.5.0) + net-smtp (0.5.1) net-protocol nio4r (2.7.4) nokogiri (1.18.2) From 7afe02b36bf1a6acb0cf44e333e8b3a80cb37632 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:37:16 +0100 Subject: [PATCH 08/31] chore(deps): update dependency rack to v2.2.11 (#33900) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index cfca6f8ab1..e7f61119b4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -597,7 +597,7 @@ GEM activesupport (>= 3.0.0) raabro (1.4.0) racc (1.8.1) - rack (2.2.10) + rack (2.2.11) rack-attack (6.7.0) rack (>= 1.0, < 4) rack-cors (2.0.2) From 9ed1cb3c298eac010023fad7664d14511a2eae74 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 19 Feb 2025 09:17:40 +0100 Subject: [PATCH 09/31] chore(deps): update dependency nokogiri to v1.18.3 [security] (#33961) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index e7f61119b4..7c92629114 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -427,7 +427,7 @@ GEM net-smtp (0.5.1) net-protocol nio4r (2.7.4) - nokogiri (1.18.2) + nokogiri (1.18.3) mini_portile2 (~> 2.8.2) racc (~> 1.4) oj (3.16.9) From 6515244a16368cb94d50b674b0fa63daf5e28644 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 25 Feb 2025 15:37:44 +0100 Subject: [PATCH 10/31] Fix `GET /api/v2/notifications/:id` and `POST /api/v2/notifications/:id/dismiss` for ungrouped notifications (#33990) --- .../api/v2/notifications_controller.rb | 4 +-- app/models/concerns/notification/groups.rb | 4 +++ spec/requests/api/v2/notifications_spec.rb | 25 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v2/notifications_controller.rb b/app/controllers/api/v2/notifications_controller.rb index cc38b95114..848c361cfc 100644 --- a/app/controllers/api/v2/notifications_controller.rb +++ b/app/controllers/api/v2/notifications_controller.rb @@ -46,7 +46,7 @@ class Api::V2::NotificationsController < Api::BaseController end def show - @notification = current_account.notifications.without_suspended.find_by!(group_key: params[:group_key]) + @notification = current_account.notifications.without_suspended.by_group_key(params[:group_key]).take! presenter = GroupedNotificationsPresenter.new(NotificationGroup.from_notifications([@notification])) render json: presenter, serializer: REST::DedupNotificationGroupSerializer end @@ -57,7 +57,7 @@ class Api::V2::NotificationsController < Api::BaseController end def dismiss - current_account.notifications.where(group_key: params[:group_key]).destroy_all + current_account.notifications.by_group_key(params[:group_key]).destroy_all render_empty end diff --git a/app/models/concerns/notification/groups.rb b/app/models/concerns/notification/groups.rb index e064df8502..c678ff9f50 100644 --- a/app/models/concerns/notification/groups.rb +++ b/app/models/concerns/notification/groups.rb @@ -7,6 +7,10 @@ module Notification::Groups GROUPABLE_NOTIFICATION_TYPES = %i(favourite reblog follow emoji_reaction).freeze MAXIMUM_GROUP_SPAN_HOURS = 12 + included do + scope :by_group_key, ->(group_key) { group_key&.start_with?('ungrouped-') ? where(id: group_key.delete_prefix('ungrouped-')) : where(group_key: group_key) } + end + def set_group_key! return if filtered? || GROUPABLE_NOTIFICATION_TYPES.exclude?(type) diff --git a/spec/requests/api/v2/notifications_spec.rb b/spec/requests/api/v2/notifications_spec.rb index b2f6d71b51..a7608e1419 100644 --- a/spec/requests/api/v2/notifications_spec.rb +++ b/spec/requests/api/v2/notifications_spec.rb @@ -365,6 +365,18 @@ RSpec.describe 'Notifications' do .to start_with('application/json') end + context 'with an ungrouped notification' do + let(:notification) { Fabricate(:notification, account: user.account, type: :favourite) } + + it 'returns http success' do + get "/api/v2/notifications/ungrouped-#{notification.id}", headers: headers + + expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + end + end + context 'when notification belongs to someone else' do let(:notification) { Fabricate(:notification, group_key: 'foobar') } @@ -396,6 +408,19 @@ RSpec.describe 'Notifications' do expect { notification.reload }.to raise_error(ActiveRecord::RecordNotFound) end + context 'with an ungrouped notification' do + let(:notification) { Fabricate(:notification, account: user.account, type: :favourite) } + + it 'destroys the notification' do + post "/api/v2/notifications/ungrouped-#{notification.id}/dismiss", headers: headers + + expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect { notification.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end + context 'when notification belongs to someone else' do let(:notification) { Fabricate(:notification, group_key: 'foobar') } From e09adc6314e00ea4d0cda1c939df542f39b8660e Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 27 Feb 2025 15:44:35 +0100 Subject: [PATCH 11/31] Merge commit from fork * Add rate-limit on `/auth/setup` * Remove useless test --- config/initializers/rack_attack.rb | 10 +++++++++- spec/requests/auth/setup_spec.rb | 11 ----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb index b4eaab1daa..f558ee5fe0 100644 --- a/config/initializers/rack_attack.rb +++ b/config/initializers/rack_attack.rb @@ -122,7 +122,7 @@ class Rack::Attack end throttle('throttle_email_confirmations/ip', limit: 25, period: 5.minutes) do |req| - req.throttleable_remote_ip if req.post? && (req.path_matches?('/auth/confirmation') || req.path == '/api/v1/emails/confirmations') + req.throttleable_remote_ip if (req.post? && (req.path_matches?('/auth/confirmation') || req.path == '/api/v1/emails/confirmations')) || ((req.put? || req.patch?) && req.path_matches?('/auth/setup')) end throttle('throttle_email_confirmations/email', limit: 5, period: 30.minutes) do |req| @@ -133,6 +133,14 @@ class Rack::Attack end end + throttle('throttle_auth_setup/email', limit: 5, period: 10.minutes) do |req| + req.params.dig('user', 'email').presence if (req.put? || req.patch?) && req.path_matches?('/auth/setup') + end + + throttle('throttle_auth_setup/account', limit: 5, period: 10.minutes) do |req| + req.warden_user_id if (req.put? || req.patch?) && req.path_matches?('/auth/setup') + end + throttle('throttle_login_attempts/ip', limit: 25, period: 5.minutes) do |req| req.throttleable_remote_ip if req.post? && req.path_matches?('/auth/sign_in') end diff --git a/spec/requests/auth/setup_spec.rb b/spec/requests/auth/setup_spec.rb index 72413e1740..fa3c196805 100644 --- a/spec/requests/auth/setup_spec.rb +++ b/spec/requests/auth/setup_spec.rb @@ -24,15 +24,4 @@ RSpec.describe 'Auth Setup' do end end end - - describe 'PUT /auth/setup' do - before { sign_in Fabricate(:user, confirmed_at: nil) } - - it 'gracefully handles invalid nested params' do - put '/auth/setup?user=invalid' - - expect(response) - .to have_http_status(400) - end - end end From 55e31110e381be9775555b098be9caefeb769e54 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Thu, 27 Feb 2025 15:49:57 +0100 Subject: [PATCH 12/31] Merge commit from fork * Fix domain blocks/rationales being visible to unapproved/unconfirmed users * Fix domain blocks/rationales being visible to suspended users Co-authored-by: Claire * Allow moved users to view domain blocks * Add authorization specs for `/api/v1/instance/domain_blocks` spec * Fix tests * Fix incorrect test setup --------- Co-authored-by: Claire --- .../v1/instances/domain_blocks_controller.rb | 4 +- .../api/v1/instances/domain_blocks_spec.rb | 99 +++++++++++++++++-- 2 files changed, 94 insertions(+), 9 deletions(-) diff --git a/app/controllers/api/v1/instances/domain_blocks_controller.rb b/app/controllers/api/v1/instances/domain_blocks_controller.rb index 7ec94312f4..bf96fbaaa8 100644 --- a/app/controllers/api/v1/instances/domain_blocks_controller.rb +++ b/app/controllers/api/v1/instances/domain_blocks_controller.rb @@ -31,7 +31,7 @@ class Api::V1::Instances::DomainBlocksController < Api::V1::Instances::BaseContr end def show_domain_blocks_to_user? - Setting.show_domain_blocks == 'users' && user_signed_in? + Setting.show_domain_blocks == 'users' && user_signed_in? && current_user.functional_or_moved? end def set_domain_blocks @@ -47,6 +47,6 @@ class Api::V1::Instances::DomainBlocksController < Api::V1::Instances::BaseContr end def show_rationale_for_user? - Setting.show_domain_blocks_rationale == 'users' && user_signed_in? + Setting.show_domain_blocks_rationale == 'users' && user_signed_in? && current_user.functional_or_moved? end end diff --git a/spec/requests/api/v1/instances/domain_blocks_spec.rb b/spec/requests/api/v1/instances/domain_blocks_spec.rb index d0707d784c..b48298d6bc 100644 --- a/spec/requests/api/v1/instances/domain_blocks_spec.rb +++ b/spec/requests/api/v1/instances/domain_blocks_spec.rb @@ -9,9 +9,10 @@ RSpec.describe 'Domain Blocks' do let(:headers) { { Authorization: "Bearer #{token.token}" } } describe 'GET /api/v1/instance/domain_blocks' do - before do - Fabricate(:domain_block) - end + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id).token } + + before { Fabricate(:domain_block) } context 'with domain blocks set to all' do before { Setting.show_domain_blocks = 'all' } @@ -45,11 +46,95 @@ RSpec.describe 'Domain Blocks' do context 'with domain blocks set to users' do before { Setting.show_domain_blocks = 'users' } - it 'returns http not found' do - get api_v1_instance_domain_blocks_path + context 'without authentication token' do + it 'returns http not found' do + get api_v1_instance_domain_blocks_path - expect(response) - .to have_http_status(404) + expect(response) + .to have_http_status(404) + end + end + + context 'with authentication token' do + context 'with unapproved user' do + before { user.update(approved: false) } + + it 'returns http not found' do + get api_v1_instance_domain_blocks_path, headers: { 'Authorization' => "Bearer #{token}" } + + expect(response) + .to have_http_status(404) + end + end + + context 'with unconfirmed user' do + before { user.update(confirmed_at: nil) } + + it 'returns http not found' do + get api_v1_instance_domain_blocks_path, headers: { 'Authorization' => "Bearer #{token}" } + + expect(response) + .to have_http_status(404) + end + end + + context 'with disabled user' do + before { user.update(disabled: true) } + + it 'returns http not found' do + get api_v1_instance_domain_blocks_path, headers: { 'Authorization' => "Bearer #{token}" } + + expect(response) + .to have_http_status(404) + end + end + + context 'with suspended user' do + before { user.account.update(suspended_at: Time.zone.now) } + + it 'returns http not found' do + get api_v1_instance_domain_blocks_path, headers: { 'Authorization' => "Bearer #{token}" } + + expect(response) + .to have_http_status(403) + end + end + + context 'with moved user' do + before { user.account.update(moved_to_account_id: Fabricate(:account).id) } + + it 'returns http success' do + get api_v1_instance_domain_blocks_path, headers: { 'Authorization' => "Bearer #{token}" } + + expect(response) + .to have_http_status(200) + + expect(response.content_type) + .to start_with('application/json') + + expect(response.parsed_body) + .to be_present + .and(be_an(Array)) + .and(have_attributes(size: 1)) + end + end + + context 'with normal user' do + it 'returns http success' do + get api_v1_instance_domain_blocks_path, headers: { 'Authorization' => "Bearer #{token}" } + + expect(response) + .to have_http_status(200) + + expect(response.content_type) + .to start_with('application/json') + + expect(response.parsed_body) + .to be_present + .and(be_an(Array)) + .and(have_attributes(size: 1)) + end + end end end From 8f25192072040fe7ed8b86493133f74a51e2f0ae Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 27 Feb 2025 11:12:58 +0100 Subject: [PATCH 13/31] Change HTML sanitization to remove unusable and unused `embed` tag (#34021) --- lib/sanitize_ext/sanitize_config.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/sanitize_ext/sanitize_config.rb b/lib/sanitize_ext/sanitize_config.rb index 7815d9ed52..410a58b07e 100644 --- a/lib/sanitize_ext/sanitize_config.rb +++ b/lib/sanitize_ext/sanitize_config.rb @@ -155,18 +155,16 @@ class Sanitize ) MASTODON_OEMBED = freeze_config( - elements: %w(audio embed iframe source video), + elements: %w(audio iframe source video), attributes: { 'audio' => %w(controls), - 'embed' => %w(height src type width), 'iframe' => %w(allowfullscreen frameborder height scrolling src width), 'source' => %w(src type), 'video' => %w(controls height loop width), }, protocols: { - 'embed' => { 'src' => HTTP_PROTOCOLS }, 'iframe' => { 'src' => HTTP_PROTOCOLS }, 'source' => { 'src' => HTTP_PROTOCOLS }, }, From d1208a2cf5651e5b8ad4e8c856a73e4202660fe2 Mon Sep 17 00:00:00 2001 From: KMY Date: Fri, 28 Feb 2025 09:54:45 +0900 Subject: [PATCH 14/31] Bump version to 17.1 --- docker-compose.yml | 6 +++--- lib/mastodon/version.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 47eb36698f..dbbb430acd 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: kmyblue:17.0 + image: kmyblue:17.1 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: kmyblue-streaming:17.0 + image: kmyblue-streaming:17.1 restart: always env_file: .env.production command: node ./streaming/index.js @@ -101,7 +101,7 @@ services: sidekiq: build: . - image: kmyblue:17.0 + image: kmyblue:17.1 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index f45b4a4305..688ad8b158 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ module Mastodon end def kmyblue_minor - 0 + 1 end def kmyblue_flag From d5ba371a5efa189896b009bc515f9666af8af71c Mon Sep 17 00:00:00 2001 From: KMY Date: Fri, 28 Feb 2025 13:08:04 +0900 Subject: [PATCH 15/31] Fix test --- spec/requests/api/v1/instances/domain_blocks_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/requests/api/v1/instances/domain_blocks_spec.rb b/spec/requests/api/v1/instances/domain_blocks_spec.rb index b48298d6bc..40b79c9691 100644 --- a/spec/requests/api/v1/instances/domain_blocks_spec.rb +++ b/spec/requests/api/v1/instances/domain_blocks_spec.rb @@ -4,9 +4,9 @@ require 'rails_helper' RSpec.describe 'Domain Blocks' do let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes).token } let(:scopes) { 'read' } - let(:headers) { { Authorization: "Bearer #{token.token}" } } + let(:headers) { { Authorization: "Bearer #{token}" } } describe 'GET /api/v1/instance/domain_blocks' do let(:user) { Fabricate(:user) } From a4c43dcf1879f79828a7f6303bef3428d40b3e31 Mon Sep 17 00:00:00 2001 From: KMY Date: Fri, 28 Feb 2025 15:15:39 +0900 Subject: [PATCH 16/31] =?UTF-8?q?Fix:=20NG=E3=83=AF=E3=83=BC=E3=83=89?= =?UTF-8?q?=E8=A8=AD=E5=AE=9A=E7=94=BB=E9=9D=A2=E3=81=AE=E3=82=A8=E3=83=A9?= =?UTF-8?q?=E3=83=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/ng_words/keywords_controller.rb | 4 ++++ app/controllers/admin/ng_words_controller.rb | 12 +++++++++++- app/controllers/system_css_controller.rb | 8 -------- app/views/custom_css/show_system.css.erb | 6 ------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/controllers/admin/ng_words/keywords_controller.rb b/app/controllers/admin/ng_words/keywords_controller.rb index 9af38fab7b..10969204e8 100644 --- a/app/controllers/admin/ng_words/keywords_controller.rb +++ b/app/controllers/admin/ng_words/keywords_controller.rb @@ -21,6 +21,10 @@ module Admin false end + def avoid_save? + true + end + private def after_update_redirect_path diff --git a/app/controllers/admin/ng_words_controller.rb b/app/controllers/admin/ng_words_controller.rb index f052843475..f0d7142500 100644 --- a/app/controllers/admin/ng_words_controller.rb +++ b/app/controllers/admin/ng_words_controller.rb @@ -13,6 +13,12 @@ module Admin return unless validate + if avoid_save? + flash[:notice] = I18n.t('generic.changes_saved_msg') + redirect_to after_update_redirect_path + return + end + @admin_settings = Form::AdminSettings.new(settings_params) if @admin_settings.save @@ -33,6 +39,10 @@ module Admin admin_ng_words_path end + def avoid_save? + false + end + private def settings_params @@ -40,7 +50,7 @@ module Admin end def settings_params_test - params.require(:form_admin_settings)[:ng_words_test] + params.expect(form_admin_settings: [ng_words_test: [keywords: [], strangers: [], temporary_ids: []]])['ng_words_test'] end end end diff --git a/app/controllers/system_css_controller.rb b/app/controllers/system_css_controller.rb index a19728bbfd..dd90491894 100644 --- a/app/controllers/system_css_controller.rb +++ b/app/controllers/system_css_controller.rb @@ -1,16 +1,8 @@ # frozen_string_literal: true class SystemCssController < ActionController::Base # rubocop:disable Rails/ApplicationController - before_action :set_user_roles - def show expires_in 3.minutes, public: true render content_type: 'text/css' end - - private - - def set_user_roles - @user_roles = UserRole.providing_styles - end end diff --git a/app/views/custom_css/show_system.css.erb b/app/views/custom_css/show_system.css.erb index 72f0281aa1..e69de29bb2 100644 --- a/app/views/custom_css/show_system.css.erb +++ b/app/views/custom_css/show_system.css.erb @@ -1,6 +0,0 @@ -<%- @user_roles.each do |role| %> -.user-role-<%= role.id %> { - --user-role-accent: <%= role.color %>; -} - -<%- end %> From ac26f5f48ad5256a626d7719672dffb21350f111 Mon Sep 17 00:00:00 2001 From: KMY Date: Fri, 28 Feb 2025 15:18:11 +0900 Subject: [PATCH 17/31] Fix system css --- app/views/system_css/show.css.erb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/views/system_css/show.css.erb b/app/views/system_css/show.css.erb index 72f0281aa1..e69de29bb2 100644 --- a/app/views/system_css/show.css.erb +++ b/app/views/system_css/show.css.erb @@ -1,6 +0,0 @@ -<%- @user_roles.each do |role| %> -.user-role-<%= role.id %> { - --user-role-accent: <%= role.color %>; -} - -<%- end %> From 3cba3a6af36b99fe5e17eb2fcf4b3d02a554b3e4 Mon Sep 17 00:00:00 2001 From: KMY Date: Fri, 28 Feb 2025 16:51:15 +0900 Subject: [PATCH 18/31] Fix regexp --- app/controllers/admin/ng_words_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/ng_words_controller.rb b/app/controllers/admin/ng_words_controller.rb index f0d7142500..9e437f8c8b 100644 --- a/app/controllers/admin/ng_words_controller.rb +++ b/app/controllers/admin/ng_words_controller.rb @@ -50,7 +50,7 @@ module Admin end def settings_params_test - params.expect(form_admin_settings: [ng_words_test: [keywords: [], strangers: [], temporary_ids: []]])['ng_words_test'] + params.expect(form_admin_settings: [ng_words_test: [keywords: [], regexps: [], strangers: [], temporary_ids: []]])['ng_words_test'] end end end From 9d8f5fd45dea1eba40f03116f32ec2ee9dc5b6f1 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 5 Mar 2025 09:43:31 +0100 Subject: [PATCH 19/31] Fix preview cards under Content Warnings not being shown in detailed statuses (#34068) --- .../mastodon/features/status/components/detailed_status.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/status/components/detailed_status.tsx b/app/javascript/mastodon/features/status/components/detailed_status.tsx index 1540f50065..c8f082156e 100644 --- a/app/javascript/mastodon/features/status/components/detailed_status.tsx +++ b/app/javascript/mastodon/features/status/components/detailed_status.tsx @@ -237,7 +237,7 @@ export const DetailedStatus: React.FC<{ ); } From 6e81109b6607185e91ed89f68997431374b4e9d4 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 21 Feb 2025 08:48:31 +0100 Subject: [PATCH 20/31] Fix streaming server not filtering unknown-language posts from public timelines (#33774) --- streaming/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/streaming/index.js b/streaming/index.js index 4b6817d8fd..ff3ef1c7ba 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -689,7 +689,7 @@ const startServer = async () => { // filtering of statuses: // Filter based on language: - if (Array.isArray(req.chosenLanguages) && payload.language !== null && req.chosenLanguages.indexOf(payload.language) === -1) { + if (Array.isArray(req.chosenLanguages) && req.chosenLanguages.indexOf(payload.language) === -1) { log.debug(`Message ${payload.id} filtered by language (${payload.language})`); return; } From 5ef848aa9c0453ae96b46a636af6861aad24f96f Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Thu, 6 Mar 2025 11:44:27 +0100 Subject: [PATCH 21/31] Fix processing errors for some HEIF images from iOS 18 (#34086) --- Dockerfile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4e1bb24ff8..588b100107 100644 --- a/Dockerfile +++ b/Dockerfile @@ -96,6 +96,9 @@ RUN \ # Set /opt/mastodon as working directory WORKDIR /opt/mastodon +# Add backport repository for some specific packages where we need the latest version +RUN echo 'deb http://deb.debian.org/debian bookworm-backports main' >> /etc/apt/sources.list + # hadolint ignore=DL3008,DL3005 RUN \ # Mount Apt cache and lib directories from Docker buildx caches @@ -165,7 +168,7 @@ RUN \ libexif-dev \ libexpat1-dev \ libgirepository1.0-dev \ - libheif-dev \ + libheif-dev/bookworm-backports \ libimagequant-dev \ libjpeg62-turbo-dev \ liblcms2-dev \ @@ -348,7 +351,7 @@ RUN \ # libvips components libcgif0 \ libexif12 \ - libheif1 \ + libheif1/bookworm-backports \ libimagequant0 \ libjpeg62-turbo \ liblcms2-2 \ From 090f9eaf1543470c633c145c475a1bb63f5c84a1 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 6 Mar 2025 11:00:33 +0100 Subject: [PATCH 22/31] Change hashtag suggestion to prefer personal history capitalization (#34070) --- app/javascript/mastodon/reducers/compose.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index 1f0011448f..9b69663c45 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -342,12 +342,26 @@ const expiresInFromExpiresAt = expires_at => { const mergeLocalHashtagResults = (suggestions, prefix, tagHistory) => { prefix = prefix.toLowerCase(); + if (suggestions.length < 4) { const localTags = tagHistory.filter(tag => tag.toLowerCase().startsWith(prefix) && !suggestions.some(suggestion => suggestion.type === 'hashtag' && suggestion.name.toLowerCase() === tag.toLowerCase())); - return suggestions.concat(localTags.slice(0, 4 - suggestions.length).toJS().map(tag => ({ type: 'hashtag', name: tag }))); - } else { - return suggestions; + suggestions = suggestions.concat(localTags.slice(0, 4 - suggestions.length).toJS().map(tag => ({ type: 'hashtag', name: tag }))); } + + // Prefer capitalization from personal history, unless personal history is all lower-case + const fixSuggestionCapitalization = (suggestion) => { + if (suggestion.type !== 'hashtag') + return suggestion; + + const tagFromHistory = tagHistory.find((tag) => tag.localeCompare(suggestion.name, undefined, { sensitivity: 'accent' }) === 0); + + if (!tagFromHistory || tagFromHistory.toLowerCase() === tagFromHistory) + return suggestion; + + return { ...suggestion, name: tagFromHistory }; + }; + + return suggestions.map(fixSuggestionCapitalization); }; const normalizeSuggestions = (state, { accounts, emojis, tags, token }) => { From 0beef81affd74774ab6421bbe3e75e3eefb963ac Mon Sep 17 00:00:00 2001 From: KMY Date: Mon, 10 Mar 2025 19:26:43 +0900 Subject: [PATCH 23/31] Bump version to 17.2 --- docker-compose.yml | 6 +++--- lib/mastodon/version.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index dbbb430acd..7281ef595c 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: kmyblue:17.1 + image: kmyblue:17.2 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: kmyblue-streaming:17.1 + image: kmyblue-streaming:17.2 restart: always env_file: .env.production command: node ./streaming/index.js @@ -101,7 +101,7 @@ services: sidekiq: build: . - image: kmyblue:17.1 + image: kmyblue:17.2 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 688ad8b158..dd6571b5c9 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ module Mastodon end def kmyblue_minor - 1 + 2 end def kmyblue_flag From f0b525ccd8e9093aef2eee320267340f3f23213b Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 10 Mar 2025 15:27:43 +0100 Subject: [PATCH 24/31] Fix Stoplight errors when using `REDIS_NAMESPACE` (#34126) --- lib/redis/namespace_extensions.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/redis/namespace_extensions.rb b/lib/redis/namespace_extensions.rb index 9af59c296e..2be738b04d 100644 --- a/lib/redis/namespace_extensions.rb +++ b/lib/redis/namespace_extensions.rb @@ -5,6 +5,10 @@ class Redis def exists?(...) call_with_namespace('exists?', ...) end + + def with + yield self + end end end From d2962a525636ae20a425b7813f240319cf494538 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 09:41:25 +0100 Subject: [PATCH 25/31] chore(deps): update dependency rack to v2.2.13 [security] (#34135) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7c92629114..a51775b48a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -597,7 +597,7 @@ GEM activesupport (>= 3.0.0) raabro (1.4.0) racc (1.8.1) - rack (2.2.11) + rack (2.2.13) rack-attack (6.7.0) rack (>= 1.0, < 4) rack-cors (2.0.2) From f045fba749e5c05a8a37f29c6c4e9e71129c4fd4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Mar 2025 09:38:44 +0100 Subject: [PATCH 26/31] Update dependency omniauth-saml to v2.2.3 [SECURITY] (#34156) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index a51775b48a..dbb677cf8a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -444,9 +444,9 @@ GEM omniauth-rails_csrf_protection (1.0.2) actionpack (>= 4.2) omniauth (~> 2.0) - omniauth-saml (2.2.1) + omniauth-saml (2.2.3) omniauth (~> 2.1) - ruby-saml (~> 1.17) + ruby-saml (~> 1.18) omniauth_openid_connect (0.6.1) omniauth (>= 1.9, < 3) openid_connect (~> 1.1) @@ -745,7 +745,7 @@ GEM rubocop-rspec (~> 3, >= 3.0.1) ruby-prof (1.7.1) ruby-progressbar (1.13.0) - ruby-saml (1.17.0) + ruby-saml (1.18.0) nokogiri (>= 1.13.10) rexml ruby-vips (2.2.3) From 8445fa183d931da9e4e7940d74128d81629af577 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 13 Mar 2025 15:27:20 +0100 Subject: [PATCH 27/31] Bump version to v4.3.6 (#34167) --- CHANGELOG.md | 59 +++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 6 ++--- lib/mastodon/version.rb | 4 +-- 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef6a87ebb9..c7b0f64146 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,65 @@ All notable changes to this project will be documented in this file. +## [4.3.6] - 2025-03-13 + +### Security + +- Update dependency `omniauth-saml` +- Update dependency `rack` + +### Fixed + +- Fix Stoplight errors when using `REDIS_NAMESPACE` (#34126 by @ClearlyClaire) + +## [4.3.5] - 2025-03-10 + +### Changed + +- Change hashtag suggestion to prefer personal history capitalization (#34070 by @ClearlyClaire) + +### Fixed + +- Fix processing errors for some HEIF images from iOS 18 (#34086 by @renchap) +- Fix streaming server not filtering unknown-language posts from public timelines (#33774 by @ClearlyClaire) +- Fix preview cards under Content Warnings not being shown in detailed statuses (#34068 by @ClearlyClaire) +- Fix username and display name being hidden on narrow screens in moderation interface (#33064 by @ClearlyClaire) + +## [4.3.4] - 2025-02-27 + +### Security + +- Update dependencies +- Change HTML sanitization to remove unusable and unused `embed` tag (#34021 by @ClearlyClaire, [GHSA-mq2m-hr29-8gqf](https://github.com/mastodon/mastodon/security/advisories/GHSA-mq2m-hr29-8gqf)) +- Fix rate-limit on sign-up email verification ([GHSA-v39f-c9jj-8w7h](https://github.com/mastodon/mastodon/security/advisories/GHSA-v39f-c9jj-8w7h)) +- Fix improper disclosure of domain blocks to unverified users ([GHSA-94h4-fj37-c825](https://github.com/mastodon/mastodon/security/advisories/GHSA-94h4-fj37-c825)) + +### Changed + +- Change preview cards to be shown when Content Warnings are expanded (#33827 by @ClearlyClaire) +- Change warnings against changing encryption secrets to be even more noticeable (#33631 by @ClearlyClaire) +- Change `mastodon:setup` to prevent overwriting already-configured servers (#33603, #33616, and #33684 by @ClearlyClaire and @mjankowski) +- Change notifications from moderators to not be filtered (#32974 and #33654 by @ClearlyClaire and @mjankowski) + +### Fixed + +- Fix `GET /api/v2/notifications/:id` and `POST /api/v2/notifications/:id/dismiss` for ungrouped notifications (#33990 by @ClearlyClaire) +- Fix issue with some versions of libvips on some systems (#33853 by @kleisauke) +- Fix handling of duplicate mentions in incoming status `Update` (#33911 by @ClearlyClaire) +- Fix inefficiencies in timeline generation (#33839 and #33842 by @ClearlyClaire) +- Fix emoji rewrite adding unnecessary curft to the DOM for most emoji (#33818 by @ClearlyClaire) +- Fix `tootctl feeds build` not building list timelines (#33783 by @ClearlyClaire) +- Fix flaky test in `/api/v2/notifications` tests (#33773 by @ClearlyClaire) +- Fix incorrect signature after HTTP redirect (#33757 and #33769 by @ClearlyClaire) +- Fix polls not being validated on edition (#33755 by @ClearlyClaire) +- Fix media preview height in compose form when 3 or more images are attached (#33571 by @ClearlyClaire) +- Fix preview card sizing in β€œAuthor attribution” in profile settings (#33482 by @ClearlyClaire) +- Fix processing of incoming notifications for unfilterable types (#33429 by @ClearlyClaire) +- Fix featured tags for remote accounts not being kept up to date (#33372, #33406, and #33425 by @ClearlyClaire and @mjankowski) +- Fix notification polling showing a loading bar in web UI (#32960 by @Gargron) +- Fix accounts table long display name (#29316 by @WebCoder49) +- Fix exclusive lists interfering with notifications (#28162 by @ShadowJonathan) + ## [4.3.3] - 2025-01-16 ### Security diff --git a/docker-compose.yml b/docker-compose.yml index 7281ef595c..56c734318e 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: kmyblue:17.2 + image: kmyblue:17.3 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: kmyblue-streaming:17.2 + image: kmyblue-streaming:17.3 restart: always env_file: .env.production command: node ./streaming/index.js @@ -101,7 +101,7 @@ services: sidekiq: build: . - image: kmyblue:17.2 + image: kmyblue:17.3 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index dd6571b5c9..d523a1ef84 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ module Mastodon end def kmyblue_minor - 2 + 3 end def kmyblue_flag @@ -35,7 +35,7 @@ module Mastodon end def default_prerelease - 'alpha.2' + 'alpha.4' end def prerelease From 8bd585a0facbafdfe554ff03009133fd57254a6b Mon Sep 17 00:00:00 2001 From: KMY Date: Fri, 14 Mar 2025 08:28:19 +0900 Subject: [PATCH 28/31] Fix bundler-audit --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index dbb677cf8a..c11a970bc2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -845,7 +845,7 @@ GEM unf_ext unf_ext (0.0.9.1) unicode-display_width (2.6.0) - uri (1.0.2) + uri (1.0.3) useragent (0.16.11) validate_email (0.1.6) activemodel (>= 3.0) From 4dd07dfa1651c61abee2b27785c8cbaa882a1fda Mon Sep 17 00:00:00 2001 From: KMY Date: Fri, 14 Mar 2025 09:15:46 +0900 Subject: [PATCH 29/31] Fix test --- spec/system/report_interface_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/system/report_interface_spec.rb b/spec/system/report_interface_spec.rb index 6a90aa5bc6..1c61774594 100644 --- a/spec/system/report_interface_spec.rb +++ b/spec/system/report_interface_spec.rb @@ -18,6 +18,7 @@ RSpec.describe 'report interface', :attachment_processing, :js, :streaming do before do as_a_logged_in_admin visit admin_report_path(report) + page.driver.browser.manage.window.resize_to(1600, 1050) end it 'displays the report interface, including the javascript bits' do From 4c49ac2a074467e657e417e35e45e1cfee8942e5 Mon Sep 17 00:00:00 2001 From: KMY Date: Fri, 14 Mar 2025 09:32:35 +0900 Subject: [PATCH 30/31] Test --- lib/redis/namespace_extensions.rb | 4 ---- spec/system/report_interface_spec.rb | 1 - 2 files changed, 5 deletions(-) diff --git a/lib/redis/namespace_extensions.rb b/lib/redis/namespace_extensions.rb index 2be738b04d..9af59c296e 100644 --- a/lib/redis/namespace_extensions.rb +++ b/lib/redis/namespace_extensions.rb @@ -5,10 +5,6 @@ class Redis def exists?(...) call_with_namespace('exists?', ...) end - - def with - yield self - end end end diff --git a/spec/system/report_interface_spec.rb b/spec/system/report_interface_spec.rb index 1c61774594..6a90aa5bc6 100644 --- a/spec/system/report_interface_spec.rb +++ b/spec/system/report_interface_spec.rb @@ -18,7 +18,6 @@ RSpec.describe 'report interface', :attachment_processing, :js, :streaming do before do as_a_logged_in_admin visit admin_report_path(report) - page.driver.browser.manage.window.resize_to(1600, 1050) end it 'displays the report interface, including the javascript bits' do From 4a5bf16e73f43fd67d3b8e297b909389e426da17 Mon Sep 17 00:00:00 2001 From: KMY Date: Fri, 14 Mar 2025 09:37:59 +0900 Subject: [PATCH 31/31] Test --- lib/redis/namespace_extensions.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/redis/namespace_extensions.rb b/lib/redis/namespace_extensions.rb index 9af59c296e..2be738b04d 100644 --- a/lib/redis/namespace_extensions.rb +++ b/lib/redis/namespace_extensions.rb @@ -5,6 +5,10 @@ class Redis def exists?(...) call_with_namespace('exists?', ...) end + + def with + yield self + end end end