From a5641a9244f2289be1916953609f0b5184f85cb0 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 9 Aug 2024 16:48:05 +0200 Subject: [PATCH 01/27] Fix incorrect rate limit on PUT requests (#31356) --- config/initializers/rack_attack.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb index 6d8284e2b45b40..8125b335f95539 100644 --- a/config/initializers/rack_attack.rb +++ b/config/initializers/rack_attack.rb @@ -142,7 +142,7 @@ def paging_request? end throttle('throttle_password_change/account', limit: 10, period: 10.minutes) do |req| - req.warden_user_id if req.put? || (req.patch? && req.path_matches?('/auth')) + req.warden_user_id if (req.put? || req.patch?) && (req.path_matches?('/auth') || req.path_matches?('/auth/password')) end self.throttled_responder = lambda do |request| From fe92b241b2a6107286ed164a829837779b136740 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 2 Aug 2024 12:39:12 +0200 Subject: [PATCH 02/27] Fix status processing failing halfway when a remote post has a malformed `replies` attribute (#31246) --- app/lib/activitypub/activity/create.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 1581555bde8087..d93497521ada0a 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -332,13 +332,15 @@ def resolve_thread(status) def fetch_replies(status) collection = @object['replies'] - return if collection.nil? + return if collection.blank? replies = ActivityPub::FetchRepliesService.new.call(status, collection, allow_synchronous_requests: false, request_id: @options[:request_id]) return unless replies.nil? uri = value_or_id(collection) ActivityPub::FetchRepliesWorker.perform_async(status.id, uri, { 'request_id' => @options[:request_id] }) unless uri.nil? + rescue => e + Rails.logger.warn "Error fetching replies: #{e}" end def conversation_from_uri(uri) From 161aa0f8f614b502775e8b72943cbdd63cc61de5 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Tue, 23 Jul 2024 16:42:31 +0200 Subject: [PATCH 03/27] Select correct self link when parsing Webfinger response (#31110) --- app/lib/webfinger.rb | 17 +++++++- .../activitypub/fetch_remote_actor_service.rb | 5 +-- app/services/resolve_account_service.rb | 8 +--- .../requests/activitypub-webfinger.txt | 2 +- spec/fixtures/requests/webfinger.txt | 2 +- spec/lib/webfinger_spec.rb | 41 +++++++++++++++++++ .../fetch_remote_account_service_spec.rb | 10 ++--- .../fetch_remote_actor_service_spec.rb | 10 ++--- .../fetch_remote_key_service_spec.rb | 2 +- .../process_account_service_spec.rb | 2 +- 10 files changed, 73 insertions(+), 26 deletions(-) create mode 100644 spec/lib/webfinger_spec.rb diff --git a/app/lib/webfinger.rb b/app/lib/webfinger.rb index ae8a3b1eae0264..01a5dbc21d914b 100644 --- a/app/lib/webfinger.rb +++ b/app/lib/webfinger.rb @@ -6,6 +6,8 @@ class GoneError < Error; end class RedirectError < Error; end class Response + ACTIVITYPUB_READY_TYPE = ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].freeze + attr_reader :uri def initialize(uri, body) @@ -20,17 +22,28 @@ def subject end def link(rel, attribute) - links.dig(rel, attribute) + links.dig(rel, 0, attribute) + end + + def self_link_href + self_link.fetch('href') end private def links - @links ||= @json['links'].index_by { |link| link['rel'] } + @links ||= @json.fetch('links', []).group_by { |link| link['rel'] } + end + + def self_link + links.fetch('self', []).find do |link| + ACTIVITYPUB_READY_TYPE.include?(link['type']) + end end def validate_response! raise Webfinger::Error, "Missing subject in response for #{@uri}" if subject.blank? + raise Webfinger::Error, "Missing self link in response for #{@uri}" if self_link.blank? end end diff --git a/app/services/activitypub/fetch_remote_actor_service.rb b/app/services/activitypub/fetch_remote_actor_service.rb index 86a134bb4ed911..2c372c2ec3fd84 100644 --- a/app/services/activitypub/fetch_remote_actor_service.rb +++ b/app/services/activitypub/fetch_remote_actor_service.rb @@ -49,7 +49,7 @@ def check_webfinger! confirmed_username, confirmed_domain = split_acct(webfinger.subject) if @username.casecmp(confirmed_username).zero? && @domain.casecmp(confirmed_domain).zero? - raise Error, "Webfinger response for #{@username}@#{@domain} does not loop back to #{@uri}" if webfinger.link('self', 'href') != @uri + raise Error, "Webfinger response for #{@username}@#{@domain} does not loop back to #{@uri}" if webfinger.self_link_href != @uri return end @@ -58,8 +58,7 @@ def check_webfinger! @username, @domain = split_acct(webfinger.subject) raise Webfinger::RedirectError, "Too many webfinger redirects for URI #{@uri} (stopped at #{@username}@#{@domain})" unless confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero? - - raise Error, "Webfinger response for #{@username}@#{@domain} does not loop back to #{@uri}" if webfinger.link('self', 'href') != @uri + raise Error, "Webfinger response for #{@username}@#{@domain} does not loop back to #{@uri}" if webfinger.self_link_href != @uri rescue Webfinger::RedirectError => e raise Error, e.message rescue Webfinger::Error => e diff --git a/app/services/resolve_account_service.rb b/app/services/resolve_account_service.rb index 6204fefd6ff790..842c0040a2e881 100644 --- a/app/services/resolve_account_service.rb +++ b/app/services/resolve_account_service.rb @@ -104,8 +104,6 @@ def split_acct(acct) end def fetch_account! - return unless activitypub_ready? - with_redis_lock("resolve:#{@username}@#{@domain}") do @account = ActivityPub::FetchRemoteAccountService.new.call(actor_url, suppress_errors: @options[:suppress_errors]) end @@ -120,12 +118,8 @@ def webfinger_update_due? @options[:skip_cache] || @account.nil? || @account.possibly_stale? end - def activitypub_ready? - ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(@webfinger.link('self', 'type')) - end - def actor_url - @actor_url ||= @webfinger.link('self', 'href') + @actor_url ||= @webfinger.self_link_href end def gone_from_origin? diff --git a/spec/fixtures/requests/activitypub-webfinger.txt b/spec/fixtures/requests/activitypub-webfinger.txt index 465066d84e1032..733b1693dc8807 100644 --- a/spec/fixtures/requests/activitypub-webfinger.txt +++ b/spec/fixtures/requests/activitypub-webfinger.txt @@ -4,4 +4,4 @@ Content-Type: application/jrd+json; charset=utf-8 X-Content-Type-Options: nosniff Date: Sun, 17 Sep 2017 06:22:50 GMT -{"subject":"acct:foo@ap.example.com","aliases":["https://ap.example.com/@foo","https://ap.example.com/users/foo"],"links":[{"rel":"http://webfinger.net/rel/profile-page","type":"text/html","href":"https://ap.example.com/@foo"},{"rel":"http://schemas.google.com/g/2010#updates-from","type":"application/atom+xml","href":"https://ap.example.com/users/foo.atom"},{"rel":"self","type":"application/activity+json","href":"https://ap.example.com/users/foo"},{"rel":"salmon","href":"https://ap.example.com/api/salmon/1"},{"rel":"magic-public-key","href":"data:application/magic-public-key,RSA.u3L4vnpNLzVH31MeWI394F0wKeJFsLDAsNXGeOu0QF2x-h1zLWZw_agqD2R3JPU9_kaDJGPIV2Sn5zLyUA9S6swCCMOtn7BBR9g9sucgXJmUFB0tACH2QSgHywMAybGfmSb3LsEMNKsGJ9VsvYoh8lDET6X4Pyw-ZJU0_OLo_41q9w-OrGtlsTm_PuPIeXnxa6BLqnDaxC-4IcjG_FiPahNCTINl_1F_TgSSDZ4Taf4U9XFEIFw8wmgploELozzIzKq-t8nhQYkgAkt64euWpva3qL5KD1mTIZQEP-LZvh3s2WHrLi3fhbdRuwQ2c0KkJA2oSTFPDpqqbPGZ3QvuHQ==.AQAB"},{"rel":"http://ostatus.org/schema/1.0/subscribe","template":"https://ap.example.com/authorize_follow?acct={uri}"}]} \ No newline at end of file +{"subject":"acct:foo@ap.example.com","aliases":["https://ap.example.com/@foo","https://ap.example.com/users/foo"],"links":[{"rel":"http://webfinger.net/rel/profile-page","type":"text/html","href":"https://ap.example.com/@foo"},{"rel":"http://schemas.google.com/g/2010#updates-from","type":"application/atom+xml","href":"https://ap.example.com/users/foo.atom"},{"rel":"self","type":"application/html","href":"https://ap.example.com/users/foo.html"},{"rel":"self","type":"application/activity+json","href":"https://ap.example.com/users/foo"},{"rel":"self","type":"application/json","href":"https://ap.example.com/users/foo.json"},{"rel":"salmon","href":"https://ap.example.com/api/salmon/1"},{"rel":"magic-public-key","href":"data:application/magic-public-key,RSA.u3L4vnpNLzVH31MeWI394F0wKeJFsLDAsNXGeOu0QF2x-h1zLWZw_agqD2R3JPU9_kaDJGPIV2Sn5zLyUA9S6swCCMOtn7BBR9g9sucgXJmUFB0tACH2QSgHywMAybGfmSb3LsEMNKsGJ9VsvYoh8lDET6X4Pyw-ZJU0_OLo_41q9w-OrGtlsTm_PuPIeXnxa6BLqnDaxC-4IcjG_FiPahNCTINl_1F_TgSSDZ4Taf4U9XFEIFw8wmgploELozzIzKq-t8nhQYkgAkt64euWpva3qL5KD1mTIZQEP-LZvh3s2WHrLi3fhbdRuwQ2c0KkJA2oSTFPDpqqbPGZ3QvuHQ==.AQAB"},{"rel":"http://ostatus.org/schema/1.0/subscribe","template":"https://ap.example.com/authorize_follow?acct={uri}"}]} \ No newline at end of file diff --git a/spec/fixtures/requests/webfinger.txt b/spec/fixtures/requests/webfinger.txt index f337ecae6f1501..fce821bddbf5c8 100644 --- a/spec/fixtures/requests/webfinger.txt +++ b/spec/fixtures/requests/webfinger.txt @@ -8,4 +8,4 @@ Access-Control-Allow-Origin: * Vary: Accept-Encoding,Cookie Strict-Transport-Security: max-age=31536000; includeSubdomains; -{"subject":"acct:gargron@quitter.no","aliases":["https:\/\/quitter.no\/user\/7477","https:\/\/quitter.no\/gargron","https:\/\/quitter.no\/index.php\/user\/7477","https:\/\/quitter.no\/index.php\/gargron"],"links":[{"rel":"http:\/\/webfinger.net\/rel\/profile-page","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/gmpg.org\/xfn\/11","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"describedby","type":"application\/rdf+xml","href":"https:\/\/quitter.no\/gargron\/foaf"},{"rel":"http:\/\/apinamespace.org\/atom","type":"application\/atomsvc+xml","href":"https:\/\/quitter.no\/api\/statusnet\/app\/service\/gargron.xml"},{"rel":"http:\/\/apinamespace.org\/twitter","href":"https:\/\/quitter.no\/api\/"},{"rel":"http:\/\/specs.openid.net\/auth\/2.0\/provider","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/schemas.google.com\/g\/2010#updates-from","type":"application\/atom+xml","href":"https:\/\/quitter.no\/api\/statuses\/user_timeline\/7477.atom"},{"rel":"magic-public-key","href":"data:application\/magic-public-key,RSA.1ZBkHTavLvxH3FzlKv4O6WtlILKRFfNami3_Rcu8EuogtXSYiS-bB6hElZfUCSHbC4uLemOA34PEhz__CDMozax1iI_t8dzjDnh1x0iFSup7pSfW9iXk_WU3Dm74yWWW2jildY41vWgrEstuQ1dJ8vVFfSJ9T_tO4c-T9y8vDI8=.AQAB"},{"rel":"salmon","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-replies","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-mention","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/ostatus.org\/schema\/1.0\/subscribe","template":"https:\/\/quitter.no\/main\/ostatussub?profile={uri}"}]} +{"subject":"acct:gargron@quitter.no","aliases":["https:\/\/quitter.no\/user\/7477","https:\/\/quitter.no\/gargron","https:\/\/quitter.no\/index.php\/user\/7477","https:\/\/quitter.no\/index.php\/gargron"],"links":[{"rel":"http:\/\/webfinger.net\/rel\/profile-page","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/gmpg.org\/xfn\/11","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"describedby","type":"application\/rdf+xml","href":"https:\/\/quitter.no\/gargron\/foaf"},{"rel":"self","type":"application/activity+json","href":"https://ap.example.com/users/foo"},{"rel":"http:\/\/apinamespace.org\/atom","type":"application\/atomsvc+xml","href":"https:\/\/quitter.no\/api\/statusnet\/app\/service\/gargron.xml"},{"rel":"http:\/\/apinamespace.org\/twitter","href":"https:\/\/quitter.no\/api\/"},{"rel":"http:\/\/specs.openid.net\/auth\/2.0\/provider","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/schemas.google.com\/g\/2010#updates-from","type":"application\/atom+xml","href":"https:\/\/quitter.no\/api\/statuses\/user_timeline\/7477.atom"},{"rel":"magic-public-key","href":"data:application\/magic-public-key,RSA.1ZBkHTavLvxH3FzlKv4O6WtlILKRFfNami3_Rcu8EuogtXSYiS-bB6hElZfUCSHbC4uLemOA34PEhz__CDMozax1iI_t8dzjDnh1x0iFSup7pSfW9iXk_WU3Dm74yWWW2jildY41vWgrEstuQ1dJ8vVFfSJ9T_tO4c-T9y8vDI8=.AQAB"},{"rel":"salmon","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-replies","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-mention","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/ostatus.org\/schema\/1.0\/subscribe","template":"https:\/\/quitter.no\/main\/ostatussub?profile={uri}"}]} diff --git a/spec/lib/webfinger_spec.rb b/spec/lib/webfinger_spec.rb new file mode 100644 index 00000000000000..5015deac7ffd87 --- /dev/null +++ b/spec/lib/webfinger_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Webfinger do + describe 'self link' do + context 'when self link is specified with type application/activity+json' do + let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } } + + it 'correctly parses the response' do + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + + response = described_class.new('acct:alice@example.com').perform + + expect(response.self_link_href).to eq 'https://example.com/alice' + end + end + + context 'when self link is specified with type application/ld+json' do + let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' }] } } + + it 'correctly parses the response' do + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + + response = described_class.new('acct:alice@example.com').perform + + expect(response.self_link_href).to eq 'https://example.com/alice' + end + end + + context 'when self link is specified with incorrect type' do + let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/json"' }] } } + + it 'raises an error' do + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + + expect { described_class.new('acct:alice@example.com').perform }.to raise_error(Webfinger::Error) + end + end + end +end diff --git a/spec/services/activitypub/fetch_remote_account_service_spec.rb b/spec/services/activitypub/fetch_remote_account_service_spec.rb index 42badde0517980..a69a43f5291ca1 100644 --- a/spec/services/activitypub/fetch_remote_account_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_account_service_spec.rb @@ -39,7 +39,7 @@ end context 'when the account does not have a inbox' do - let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } } + let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } } before do actor[:inbox] = nil @@ -64,7 +64,7 @@ end context 'when URI and WebFinger share the same host' do - let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } } + let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } } before do stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) @@ -90,7 +90,7 @@ end context 'when WebFinger presents different domain than URI' do - let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/alice' }] } } + let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } } before do stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) @@ -122,7 +122,7 @@ end context 'when WebFinger returns a different URI' do - let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/bob' }] } } + let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/bob', type: 'application/activity+json' }] } } before do stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) @@ -145,7 +145,7 @@ end context 'when WebFinger returns a different URI after a redirection' do - let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/bob' }] } } + let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/bob', type: 'application/activity+json' }] } } before do stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) diff --git a/spec/services/activitypub/fetch_remote_actor_service_spec.rb b/spec/services/activitypub/fetch_remote_actor_service_spec.rb index 6d264b7b825276..c92705130b9c87 100644 --- a/spec/services/activitypub/fetch_remote_actor_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_actor_service_spec.rb @@ -39,7 +39,7 @@ end context 'when the account does not have a inbox' do - let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } } + let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } } before do actor[:inbox] = nil @@ -64,7 +64,7 @@ end context 'when URI and WebFinger share the same host' do - let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } } + let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } } before do stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) @@ -90,7 +90,7 @@ end context 'when WebFinger presents different domain than URI' do - let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/alice' }] } } + let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } } before do stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) @@ -122,7 +122,7 @@ end context 'when WebFinger returns a different URI' do - let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/bob' }] } } + let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/bob', type: 'application/activity+json' }] } } before do stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) @@ -145,7 +145,7 @@ end context 'when WebFinger returns a different URI after a redirection' do - let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/bob' }] } } + let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/bob', type: 'application/activity+json' }] } } before do stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) diff --git a/spec/services/activitypub/fetch_remote_key_service_spec.rb b/spec/services/activitypub/fetch_remote_key_service_spec.rb index 478778cc9f5762..011f12915725b1 100644 --- a/spec/services/activitypub/fetch_remote_key_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_key_service_spec.rb @@ -5,7 +5,7 @@ RSpec.describe ActivityPub::FetchRemoteKeyService, type: :service do subject { described_class.new } - let(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } } + let(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } } let(:public_key_pem) do <<~TEXT diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index 60214d2ed839c1..a3a084d23e99d9 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -217,7 +217,7 @@ }.with_indifferent_access webfinger = { subject: "acct:user#{i}@foo.test", - links: [{ rel: 'self', href: "https://foo.test/users/#{i}" }], + links: [{ rel: 'self', href: "https://foo.test/users/#{i}", type: 'application/activity+json' }], }.with_indifferent_access stub_request(:get, "https://foo.test/users/#{i}").to_return(status: 200, body: actor_json.to_json, headers: { 'Content-Type': 'application/activity+json' }) stub_request(:get, "https://foo.test/users/#{i}/featured").to_return(status: 200, body: featured_json.to_json, headers: { 'Content-Type': 'application/activity+json' }) From 13bab9426569fd7f4c17213777cf46ca38d32769 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 22 Jul 2024 10:56:05 +0200 Subject: [PATCH 04/27] Fix duplicate `orderedItems` in user archive's `outbox.json` (#31099) --- app/lib/activitypub/adapter.rb | 2 +- app/services/backup_service.rb | 6 +++--- spec/lib/activitypub/adapter_spec.rb | 10 +++++----- spec/services/backup_service_spec.rb | 1 + 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb index 098b6296fb053e..5b9437eb8dad40 100644 --- a/app/lib/activitypub/adapter.rb +++ b/app/lib/activitypub/adapter.rb @@ -20,6 +20,6 @@ def serializable_hash(options = nil) serialized_hash = serialized_hash.select { |k, _| options[:fields].include?(k) } if options[:fields] serialized_hash = self.class.transform_key_casing!(serialized_hash, instance_options) - { '@context' => serialized_context(named_contexts, context_extensions) }.merge(serialized_hash) + { '@context': serialized_context(named_contexts, context_extensions) }.merge(serialized_hash) end end diff --git a/app/services/backup_service.rb b/app/services/backup_service.rb index b3282c409b41dc..5bfa69f32ad3bd 100644 --- a/app/services/backup_service.rb +++ b/app/services/backup_service.rb @@ -19,8 +19,8 @@ def call(backup) def build_outbox_json!(file) skeleton = serialize(collection_presenter, ActivityPub::CollectionSerializer) - skeleton['@context'] = full_context - skeleton['orderedItems'] = ['!PLACEHOLDER!'] + skeleton[:@context] = full_context + skeleton[:orderedItems] = ['!PLACEHOLDER!'] skeleton = Oj.dump(skeleton) prepend, append = skeleton.split('"!PLACEHOLDER!"') add_comma = false @@ -33,7 +33,7 @@ def build_outbox_json!(file) file.write(statuses.map do |status| item = serialize_payload(ActivityPub::ActivityPresenter.from_status(status), ActivityPub::ActivitySerializer) - item.delete('@context') + item.delete(:@context) unless item[:type] == 'Announce' || item[:object][:attachment].blank? item[:object][:attachment].each do |attachment| diff --git a/spec/lib/activitypub/adapter_spec.rb b/spec/lib/activitypub/adapter_spec.rb index f9f8b8dce0d81d..bd63ebb9c87657 100644 --- a/spec/lib/activitypub/adapter_spec.rb +++ b/spec/lib/activitypub/adapter_spec.rb @@ -59,7 +59,7 @@ def virtual_object let(:serializer_class) { TestWithBasicContextSerializer } it 'renders a basic @context' do - expect(subject).to include({ '@context' => 'https://www.w3.org/ns/activitystreams' }) + expect(subject).to include({ '@context': 'https://www.w3.org/ns/activitystreams' }) end end @@ -67,7 +67,7 @@ def virtual_object let(:serializer_class) { TestWithNamedContextSerializer } it 'renders a @context with both items' do - expect(subject).to include({ '@context' => ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] }) + expect(subject).to include({ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] }) end end @@ -75,7 +75,7 @@ def virtual_object let(:serializer_class) { TestWithNestedNamedContextSerializer } it 'renders a @context with both items' do - expect(subject).to include({ '@context' => ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] }) + expect(subject).to include({ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] }) end end @@ -83,7 +83,7 @@ def virtual_object let(:serializer_class) { TestWithContextExtensionSerializer } it 'renders a @context with the extension' do - expect(subject).to include({ '@context' => ['https://www.w3.org/ns/activitystreams', { 'sensitive' => 'as:sensitive' }] }) + expect(subject).to include({ '@context': ['https://www.w3.org/ns/activitystreams', { 'sensitive' => 'as:sensitive' }] }) end end @@ -91,7 +91,7 @@ def virtual_object let(:serializer_class) { TestWithNestedContextExtensionSerializer } it 'renders a @context with both extensions' do - expect(subject).to include({ '@context' => ['https://www.w3.org/ns/activitystreams', { 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', 'sensitive' => 'as:sensitive' }] }) + expect(subject).to include({ '@context': ['https://www.w3.org/ns/activitystreams', { 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', 'sensitive' => 'as:sensitive' }] }) end end end diff --git a/spec/services/backup_service_spec.rb b/spec/services/backup_service_spec.rb index 1eb789d1f5837f..befff0b530e5d0 100644 --- a/spec/services/backup_service_spec.rb +++ b/spec/services/backup_service_spec.rb @@ -60,6 +60,7 @@ def expect_outbox_export aggregate_failures do expect(body.scan('@context').count).to eq 1 + expect(body.scan('orderedItems').count).to eq 1 expect(json['@context']).to_not be_nil expect(json['type']).to eq 'OrderedCollection' expect(json['totalItems']).to eq 2 From 29c35ef4f9e14c2b1faa76e74e331cea7a9ad7be Mon Sep 17 00:00:00 2001 From: Django Date: Mon, 12 Aug 2024 05:10:05 -0600 Subject: [PATCH 05/27] Add support for incoming tag (#31375) --- lib/sanitize_ext/sanitize_config.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sanitize_ext/sanitize_config.rb b/lib/sanitize_ext/sanitize_config.rb index bdb4c56a085c0f..520e6cba9ea760 100644 --- a/lib/sanitize_ext/sanitize_config.rb +++ b/lib/sanitize_ext/sanitize_config.rb @@ -65,7 +65,7 @@ module Config end MASTODON_STRICT ||= freeze_config( - elements: %w(p br span a del pre blockquote code b strong u i em ul ol li), + elements: %w(p br span a del s pre blockquote code b strong u i em ul ol li), attributes: { 'a' => %w(href rel class translate), From 0a345ad5e109c98f7115912c359175d258e39025 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 7 Aug 2024 08:52:10 +0200 Subject: [PATCH 06/27] Fix logic of block/mute bypass for mentions from moderators (#31271) --- app/services/notify_service.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index 759d6e393730ca..2001fa6dace702 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -86,7 +86,8 @@ def response_to_recipient? end def from_staff? - @notification.from_account.local? && @notification.from_account.user.present? && @notification.from_account.user_role&.overrides?(@recipient.user_role) + sender = @notification.from_account + sender.local? && sender.user.present? && sender.user_role&.overrides?(@recipient.user_role) && @sender.user_role&.highlighted? && sender.user_role&.can?(*UserRole::Flags::CATEGORIES[:moderation]) end def optional_non_following_and_direct? From a8039dda13dc4782bcf3d39eb9583f535c08797c Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 1 Aug 2024 22:50:36 +0200 Subject: [PATCH 07/27] Fix click event handling when clicking outside of an open dropdown menu (#31251) --- app/javascript/mastodon/components/dropdown_menu.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/mastodon/components/dropdown_menu.jsx b/app/javascript/mastodon/components/dropdown_menu.jsx index fd66310e856ee6..2457692cd8675d 100644 --- a/app/javascript/mastodon/components/dropdown_menu.jsx +++ b/app/javascript/mastodon/components/dropdown_menu.jsx @@ -40,6 +40,7 @@ class DropdownMenu extends PureComponent { if (this.node && !this.node.contains(e.target)) { this.props.onClose(); e.stopPropagation(); + e.preventDefault(); } }; From 5cb36daa0f3c6d8de7b69a9f300163b5fa0a2949 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 16 May 2024 15:01:01 +0200 Subject: [PATCH 08/27] Fix Web UI trying to save user settings when logged out (#30324) --- app/javascript/mastodon/actions/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/actions/settings.js b/app/javascript/mastodon/actions/settings.js index 3685b0684e0b83..fbd89f9d4b0b52 100644 --- a/app/javascript/mastodon/actions/settings.js +++ b/app/javascript/mastodon/actions/settings.js @@ -20,7 +20,7 @@ export function changeSetting(path, value) { } const debouncedSave = debounce((dispatch, getState) => { - if (getState().getIn(['settings', 'saved'])) { + if (getState().getIn(['settings', 'saved']) || !getState().getIn(['meta', 'me'])) { return; } From 0fc738a32361f9d6317db9150d08cbb577dc8eed Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 6 May 2024 14:47:19 +0200 Subject: [PATCH 09/27] Fix hashtag matching pattern matching some link anchors (#30190) --- app/models/tag.rb | 2 +- spec/models/tag_spec.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/tag.rb b/app/models/tag.rb index 8fab98fb5c0107..c61079ba2cd0f9 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -35,7 +35,7 @@ class Tag < ApplicationRecord HASHTAG_LAST_SEQUENCE = '([[:word:]_]*[[:alpha:]][[:word:]_]*)' HASHTAG_NAME_PAT = "#{HASHTAG_FIRST_SEQUENCE}|#{HASHTAG_LAST_SEQUENCE}" - HASHTAG_RE = %r{(? Date: Thu, 25 Jul 2024 16:24:19 +0200 Subject: [PATCH 10/27] =?UTF-8?q?Fix=20=C3=9F=20bug=20in=20regexp=20for=20?= =?UTF-8?q?mentions=20and=20tags=20(#31122)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/account.rb | 2 +- app/models/tag.rb | 2 +- spec/models/account_spec.rb | 8 ++++++++ spec/models/tag_spec.rb | 8 ++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/models/account.rb b/app/models/account.rb index a25ebc4aaf15d3..17882db9a34251 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -66,7 +66,7 @@ class Account < ApplicationRecord BACKGROUND_REFRESH_INTERVAL = 1.week.freeze USERNAME_RE = /[a-z0-9_]+([a-z0-9_.-]+[a-z0-9_]+)?/i - MENTION_RE = %r{(? Date: Fri, 7 Jun 2024 08:39:53 -0400 Subject: [PATCH 11/27] Restore `verbose` option to media remove cli (#30536) --- lib/mastodon/cli/media.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/mastodon/cli/media.rb b/lib/mastodon/cli/media.rb index 5879c532e80a99..61132646192c11 100644 --- a/lib/mastodon/cli/media.rb +++ b/lib/mastodon/cli/media.rb @@ -13,6 +13,7 @@ class Media < Base option :remove_headers, type: :boolean, default: false option :include_follows, type: :boolean, default: false option :concurrency, type: :numeric, default: 5, aliases: [:c] + option :verbose, type: :boolean, default: false, aliases: [:v] option :dry_run, type: :boolean, default: false desc 'remove', 'Remove remote media files, headers or avatars' long_desc <<-DESC From a2c7f7f690f903f33d62b6dd81cfe5e66010016d Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 7 Jun 2024 19:42:43 +0200 Subject: [PATCH 12/27] Fix division by zero on some video/GIF files (#30600) --- app/lib/video_metadata_extractor.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/lib/video_metadata_extractor.rb b/app/lib/video_metadata_extractor.rb index df5409375f16ef..2155766251e35d 100644 --- a/app/lib/video_metadata_extractor.rb +++ b/app/lib/video_metadata_extractor.rb @@ -41,8 +41,8 @@ def parse_metadata @colorspace = video_stream[:pix_fmt] @width = video_stream[:width] @height = video_stream[:height] - @frame_rate = video_stream[:avg_frame_rate] == '0/0' ? nil : Rational(video_stream[:avg_frame_rate]) - @r_frame_rate = video_stream[:r_frame_rate] == '0/0' ? nil : Rational(video_stream[:r_frame_rate]) + @frame_rate = parse_framerate(video_stream[:avg_frame_rate]) + @r_frame_rate = parse_framerate(video_stream[:r_frame_rate]) # For some video streams the frame_rate reported by `ffprobe` will be 0/0, but for these streams we # should use `r_frame_rate` instead. Video screencast generated by Gnome Screencast have this issue. @frame_rate ||= @r_frame_rate @@ -55,4 +55,10 @@ def parse_metadata @invalid = true if @metadata.key?(:error) end + + def parse_framerate(raw) + Rational(raw) + rescue ZeroDivisionError + nil + end end From d1854798c9ddbfbe9602789d284146056e5cf1e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= <842+raucao@users.noreply.github.com> Date: Fri, 3 May 2024 11:22:48 +0200 Subject: [PATCH 13/27] Fix local account search on LDAP login being case-sensitive (#30113) Co-authored-by: Claire --- app/models/concerns/ldap_authenticable.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/concerns/ldap_authenticable.rb b/app/models/concerns/ldap_authenticable.rb index 775df081764d96..1a46b4e80e38a8 100644 --- a/app/models/concerns/ldap_authenticable.rb +++ b/app/models/concerns/ldap_authenticable.rb @@ -22,7 +22,7 @@ def ldap_get_user(attributes = {}) safe_username = safe_username.gsub(keys, replacement) end - resource = joins(:account).find_by(accounts: { username: safe_username }) + resource = joins(:account).merge(Account.where(Account.arel_table[:username].lower.eq safe_username.downcase)).take if resource.blank? resource = new(email: attributes[Devise.ldap_mail.to_sym].first, agreement: true, account_attributes: { username: safe_username }, admin: false, external: true, confirmed_at: Time.now.utc) From c06436eb916d3e8c0fa9f1700655425ed4d198b8 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 16 Apr 2024 19:30:32 +0200 Subject: [PATCH 14/27] Fix development environment admin account not being auto-approved (#29958) --- db/seeds/04_admin.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/db/seeds/04_admin.rb b/db/seeds/04_admin.rb index c9b0369c9f3281..887b4a22130376 100644 --- a/db/seeds/04_admin.rb +++ b/db/seeds/04_admin.rb @@ -7,5 +7,7 @@ admin = Account.where(username: 'admin').first_or_initialize(username: 'admin') admin.save(validate: false) - User.where(email: "admin@#{domain}").first_or_initialize(email: "admin@#{domain}", password: 'mastodonadmin', password_confirmation: 'mastodonadmin', confirmed_at: Time.now.utc, role: UserRole.find_by(name: 'Owner'), account: admin, agreement: true, approved: true).save! + user = User.where(email: "admin@#{domain}").first_or_initialize(email: "admin@#{domain}", password: 'mastodonadmin', password_confirmation: 'mastodonadmin', confirmed_at: Time.now.utc, role: UserRole.find_by(name: 'Owner'), account: admin, agreement: true, approved: true) + user.save! + user.approve! end From e1be281e3d2c1bcb46327ff8452b0860382649b2 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 6 Feb 2024 10:35:36 +0100 Subject: [PATCH 15/27] Fix report reason selector in moderation interface not unselecting rules when changing category (#29026) --- .../mastodon/components/admin/ReportReasonSelector.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/components/admin/ReportReasonSelector.jsx b/app/javascript/mastodon/components/admin/ReportReasonSelector.jsx index ecce92b309429d..90f4334a6e1ad2 100644 --- a/app/javascript/mastodon/components/admin/ReportReasonSelector.jsx +++ b/app/javascript/mastodon/components/admin/ReportReasonSelector.jsx @@ -124,7 +124,7 @@ class ReportReasonSelector extends PureComponent { api().put(`/api/v1/admin/reports/${id}`, { category, - rule_ids, + rule_ids: category === 'violation' ? rule_ids : [], }).catch(err => { console.error(err); }); From 297ad9aeb810de34a6d1ec7ed9d8daf0232d7c7b Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 6 Feb 2024 10:35:27 +0100 Subject: [PATCH 16/27] Fix already-invalid reports failing to resolve (#29027) --- app/models/report.rb | 6 ++---- spec/models/report_spec.rb | 13 +++++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/models/report.rb b/app/models/report.rb index eaf662d1e29441..d0f19e12340927 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -44,9 +44,9 @@ class Report < ApplicationRecord delegate :local?, to: :account validates :comment, length: { maximum: 1_000 }, if: :local? - validates :rule_ids, absence: true, unless: :violation? + validates :rule_ids, absence: true, if: -> { (category_changed? || rule_ids_changed?) && !violation? } - validate :validate_rule_ids + validate :validate_rule_ids, if: -> { (category_changed? || rule_ids_changed?) && violation? } # entries here need to be kept in sync with the front-end: # - app/javascript/mastodon/features/notifications/components/report.jsx @@ -154,8 +154,6 @@ def set_uri end def validate_rule_ids - return unless violation? - errors.add(:rule_ids, I18n.t('reports.errors.invalid_rules')) unless rules.size == rule_ids&.size end diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb index 0093dcd8de9496..830f2f6085fd0c 100644 --- a/spec/models/report_spec.rb +++ b/spec/models/report_spec.rb @@ -133,5 +133,18 @@ report = Fabricate.build(:report, account: remote_account, comment: Faker::Lorem.characters(number: 1001)) expect(report.valid?).to be true end + + it 'is invalid if it references invalid rules' do + report = Fabricate.build(:report, category: :violation, rule_ids: [-1]) + expect(report.valid?).to be false + expect(report).to model_have_error_on_field(:rule_ids) + end + + it 'is invalid if it references rules but category is not "violation"' do + rule = Fabricate(:rule) + report = Fabricate.build(:report, category: :spam, rule_ids: rule.id) + expect(report.valid?).to be false + expect(report).to model_have_error_on_field(:rule_ids) + end end end From 6fcb1f5799e89d1acb81cb13bea4c12ea7f67b9c Mon Sep 17 00:00:00 2001 From: Michael Stanclift Date: Tue, 2 Jan 2024 03:09:54 -0600 Subject: [PATCH 17/27] Fix OCR when using S3/CDN for assets (#28551) --- .../mastodon/features/ui/components/focal_point_modal.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/ui/components/focal_point_modal.jsx b/app/javascript/mastodon/features/ui/components/focal_point_modal.jsx index 0c2e3901ea5e6e..8ce3733b7d2352 100644 --- a/app/javascript/mastodon/features/ui/components/focal_point_modal.jsx +++ b/app/javascript/mastodon/features/ui/components/focal_point_modal.jsx @@ -221,7 +221,7 @@ class FocalPointModal extends ImmutablePureComponent { const worker = createWorker({ workerPath: tesseractWorkerPath, corePath: tesseractCorePath, - langPath: `${assetHost}/ocr/lang-data/`, + langPath: `${assetHost}/ocr/lang-data`, logger: ({ status, progress }) => { if (status === 'recognizing text') { this.setState({ ocrStatus: 'detecting', progress }); From 9d2e59bb45f67be0ab5a63e152ae0941ae1e3093 Mon Sep 17 00:00:00 2001 From: Jonathan de Jong Date: Tue, 5 Dec 2023 14:59:15 +0100 Subject: [PATCH 18/27] Fix error when encountering malformed Tag objects from Kbin (#28235) --- app/services/activitypub/process_status_update_service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb index ec983510b91414..c4a5f5115b69b3 100644 --- a/app/services/activitypub/process_status_update_service.rb +++ b/app/services/activitypub/process_status_update_service.rb @@ -172,9 +172,9 @@ def update_metadata! as_array(@json['tag']).each do |tag| if equals_or_includes?(tag['type'], 'Hashtag') - @raw_tags << tag['name'] + @raw_tags << tag['name'] if tag['name'].present? elsif equals_or_includes?(tag['type'], 'Mention') - @raw_mentions << tag['href'] + @raw_mentions << tag['href'] if tag['href'].present? elsif equals_or_includes?(tag['type'], 'Emoji') @raw_emojis << tag end From 49820ecefabf4e11888f90ccc8bbbba2f4c4d249 Mon Sep 17 00:00:00 2001 From: June Date: Mon, 27 Nov 2023 11:06:48 +0100 Subject: [PATCH 19/27] Fix not all legal images showing in file picker when uploading custom emoji (#28076) --- app/views/admin/custom_emojis/new.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/custom_emojis/new.html.haml b/app/views/admin/custom_emojis/new.html.haml index 95996dec86164d..a03676b001c9e3 100644 --- a/app/views/admin/custom_emojis/new.html.haml +++ b/app/views/admin/custom_emojis/new.html.haml @@ -7,7 +7,7 @@ .fields-group = f.input :shortcode, wrapper: :with_label, label: t('admin.custom_emojis.shortcode'), hint: t('admin.custom_emojis.shortcode_hint') .fields-group - = f.input :image, wrapper: :with_label, input_html: { accept: CustomEmoji::IMAGE_MIME_TYPES.join(' ') }, hint: t('admin.custom_emojis.image_hint', size: number_to_human_size(CustomEmoji::LIMIT)) + = f.input :image, wrapper: :with_label, input_html: { accept: CustomEmoji::IMAGE_MIME_TYPES.join(',') }, hint: t('admin.custom_emojis.image_hint', size: number_to_human_size(CustomEmoji::LIMIT)) .actions = f.button :button, t('admin.custom_emojis.upload'), type: :submit From 86f15cef660145d86d9574e0831b4fd200928808 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 24 Nov 2023 10:27:54 +0100 Subject: [PATCH 20/27] Change search popout to not list unusable search options when logged out (#27918) --- .../mastodon/features/compose/components/search.jsx | 11 ++++++++--- app/javascript/mastodon/locales/en.json | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/search.jsx b/app/javascript/mastodon/features/compose/components/search.jsx index 7e1d8b76094d03..56a4292c445ce5 100644 --- a/app/javascript/mastodon/features/compose/components/search.jsx +++ b/app/javascript/mastodon/features/compose/components/search.jsx @@ -274,6 +274,7 @@ class Search extends PureComponent { } _calculateOptions (value) { + const { signedIn } = this.context.identity; const trimmedValue = value.trim(); const options = []; @@ -298,7 +299,7 @@ class Search extends PureComponent { const couldBeStatusSearch = searchEnabled; - if (couldBeStatusSearch) { + if (couldBeStatusSearch && signedIn) { options.push({ key: 'status-search', label: {trimmedValue} }} />, action: this.handleStatusSearch }); } @@ -375,7 +376,7 @@ class Search extends PureComponent {

- {searchEnabled ? ( + {searchEnabled && signedIn ? (
{this.defaultOptions.map(({ key, label, action }, i) => (
) : (
- + {searchEnabled ? ( + + ) : ( + + )}
)} diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 4399b999513363..10d9cf3a219088 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -591,6 +591,7 @@ "search.quick_action.status_search": "Posts matching {x}", "search.search_or_paste": "Search or paste URL", "search_popout.full_text_search_disabled_message": "Not available on {domain}.", + "search_popout.full_text_search_logged_out_message": "Only available when logged in.", "search_popout.language_code": "ISO language code", "search_popout.options": "Search options", "search_popout.quick_actions": "Quick actions", From 8fe1cefe4c820855f04ada2780f914b279eb04c6 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Fri, 27 Oct 2023 11:36:22 +0900 Subject: [PATCH 21/27] Handle featured collections without items (#27581) --- .../activitypub/fetch_featured_collection_service.rb | 2 ++ .../fetch_featured_collection_service_spec.rb | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/app/services/activitypub/fetch_featured_collection_service.rb b/app/services/activitypub/fetch_featured_collection_service.rb index d5d52c3cdecbb9..89c3a1b6c03261 100644 --- a/app/services/activitypub/fetch_featured_collection_service.rb +++ b/app/services/activitypub/fetch_featured_collection_service.rb @@ -37,6 +37,8 @@ def fetch_collection(collection_or_uri) end def process_items(items) + return if items.nil? + process_note_items(items) if @options[:note] process_hashtag_items(items) if @options[:hashtag] end diff --git a/spec/services/activitypub/fetch_featured_collection_service_spec.rb b/spec/services/activitypub/fetch_featured_collection_service_spec.rb index 583212c3756031..237fc7123e9ae4 100644 --- a/spec/services/activitypub/fetch_featured_collection_service_spec.rb +++ b/spec/services/activitypub/fetch_featured_collection_service_spec.rb @@ -42,12 +42,22 @@ } end + let(:featured_with_null) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'https://example.com/account/collections/featured', + totalItems: 0, + type: 'OrderedCollection', + } + end + let(:items) do [ 'https://example.com/account/pinned/known', # known status_json_pinned_unknown_inlined, # unknown inlined 'https://example.com/account/pinned/unknown-unreachable', # unknown unreachable 'https://example.com/account/pinned/unknown-reachable', # unknown reachable + 'https://example.com/account/collections/featured', # featured with null ] end @@ -66,6 +76,7 @@ stub_request(:get, 'https://example.com/account/pinned/unknown-inlined').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_inlined), headers: { 'Content-Type': 'application/activity+json' }) stub_request(:get, 'https://example.com/account/pinned/unknown-unreachable').to_return(status: 404) stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_reachable), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/account/collections/featured').to_return(status: 200, body: Oj.dump(featured_with_null), headers: { 'Content-Type': 'application/activity+json' }) subject.call(actor, note: true, hashtag: false) end From 63ad8254ff8cdabbbd666f0112fd929122b1acd9 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 23 Jul 2024 02:51:57 -0400 Subject: [PATCH 22/27] Fix `mastodon:stats` decoration of stats rake task (#31104) --- lib/tasks/statistics.rake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/tasks/statistics.rake b/lib/tasks/statistics.rake index dde7890f6b445d..82840f4fdc842e 100644 --- a/lib/tasks/statistics.rake +++ b/lib/tasks/statistics.rake @@ -9,11 +9,13 @@ namespace :mastodon do [ ['App Libraries', 'app/lib'], %w(Presenters app/presenters), + %w(Policies app/policies), + %w(Serializers app/serializers), %w(Services app/services), %w(Validators app/validators), %w(Workers app/workers), ].each do |name, dir| - STATS_DIRECTORIES << [name, Rails.root.join(dir)] + STATS_DIRECTORIES << [name, dir] end end end From a6522938422603990a9392d65307905a80960e43 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 14 Aug 2024 16:15:44 +0200 Subject: [PATCH 23/27] Update dependenxy rexml --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 34958703a9ff06..436f670a69f925 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -604,8 +604,8 @@ GEM responders (3.1.0) actionpack (>= 5.2) railties (>= 5.2) - rexml (3.2.8) - strscan (>= 3.0.9) + rexml (3.3.5) + strscan rotp (6.3.0) rouge (4.1.2) rpam2 (4.0.2) @@ -731,7 +731,7 @@ GEM redlock (~> 1.0) strong_migrations (0.8.0) activerecord (>= 5.2) - strscan (3.0.9) + strscan (3.1.0) swd (1.3.0) activesupport (>= 3) attr_required (>= 0.0.5) From a02ff33f0ef2026d776b40dcdbb81ce9785e7443 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 14 Aug 2024 15:42:50 +0200 Subject: [PATCH 24/27] Bump version to v4.2.11 --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ docker-compose.yml | 6 +++--- lib/mastodon/version.rb | 2 +- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e57d5f23b092d8..b0c05d0d3a020a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,39 @@ All notable changes to this project will be documented in this file. +## |4.2.11] - 2024-08-16 + +### Added + +- Add support for incoming `` tag ([mediaformat](https://github.com/mastodon/mastodon/pull/31375)) + +### Changed + +- Change logic of block/mute bypass for mentions from moderators to only apply to visible roles with moderation powers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31271)) + +### Fixed + +- Fix incorrect rate limit on PUT requests ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31356)) +- Fix presence of `ß` in adjacent word preventing mention and hashtag matching ([adamniedzielski](https://github.com/mastodon/mastodon/pull/31122)) +- Fix processing of webfinger responses with multiple `self` links ([adamniedzielski](https://github.com/mastodon/mastodon/pull/31110)) +- Fix duplicate `orderedItems` in user archive's `outbox.json` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31099)) +- Fix click event handling when clicking outside of an open dropdown menu ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31251)) +- Fix status processing failing halfway when a remote post has a malformed `replies` attribute ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31246)) +- Fix `--verbose` option of `tootctl media remove`, which was previously erroneously removed ([mjankowski](https://github.com/mastodon/mastodon/pull/30536)) +- Fix division by zero on some video/GIF files ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30600)) +- Fix Web UI trying to save user settings despite being logged out ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30324)) +- Fix hashtag regexp matching some link anchors ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30190)) +- Fix local account search on LDAP login being case-sensitive ([raucao](https://github.com/mastodon/mastodon/pull/30113)) +- Fix development environment admin account not being auto-approved ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29958)) +- Fix report reason selector in moderation interface not unselecting rules when changing category ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29026)) +- Fix already-invalid reports failing to resolve ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29027)) +- Fix OCR when using S3/CDN for assets ([vmstan](https://github.com/mastodon/mastodon/pull/28551)) +- Fix error when encountering malformed `Tag` objects from Kbin ([ShadowJonathan](https://github.com/mastodon/mastodon/pull/28235)) +- Fix not all allowed image formats showing in file picker when uploading custom emoji ([june128](https://github.com/mastodon/mastodon/pull/28076)) +- Fix search popout listing unusable search options when logged out ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27918)) +- Fix processing of featured collections lacking an `items` attribute ([tribela](https://github.com/mastodon/mastodon/pull/27581)) +- Fix `mastodon:stats` decoration of stats rake task ([mjankowski](https://github.com/mastodon/mastodon/pull/31104)) + ## [4.2.10] - 2024-07-04 ### Security diff --git a/docker-compose.yml b/docker-compose.yml index d61aa05582806c..a599f01e6e5a97 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,7 +56,7 @@ services: web: build: . - image: ghcr.io/mastodon/mastodon:v4.2.10 + image: ghcr.io/mastodon/mastodon:v4.2.11 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.10 + image: ghcr.io/mastodon/mastodon:v4.2.11 restart: always env_file: .env.production command: node ./streaming @@ -95,7 +95,7 @@ services: sidekiq: build: . - image: ghcr.io/mastodon/mastodon:v4.2.10 + image: ghcr.io/mastodon/mastodon:v4.2.11 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 77e85a16528046..52e1ea540d5693 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ def minor end def patch - 10 + 11 end def default_prerelease From d675803f0768eb7479226acd370147c7e3c7f4a3 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 19 Aug 2024 09:52:32 +0200 Subject: [PATCH 25/27] Fix broken notifications for mentions from local moderators in 4.2.11 (#31484) --- app/services/notify_service.rb | 2 +- spec/services/notify_service_spec.rb | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index 2001fa6dace702..515248979ae076 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -87,7 +87,7 @@ def response_to_recipient? def from_staff? sender = @notification.from_account - sender.local? && sender.user.present? && sender.user_role&.overrides?(@recipient.user_role) && @sender.user_role&.highlighted? && sender.user_role&.can?(*UserRole::Flags::CATEGORIES[:moderation]) + sender.local? && sender.user.present? && sender.user_role&.overrides?(@recipient.user_role) && sender.user_role&.highlighted? && sender.user_role&.can?(*UserRole::Flags::CATEGORIES[:moderation].map(&:to_sym)) end def optional_non_following_and_direct? diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb index c2664e79c23bb5..568fb1cdfdb6ec 100644 --- a/spec/services/notify_service_spec.rb +++ b/spec/services/notify_service_spec.rb @@ -18,6 +18,17 @@ expect { subject }.to_not change(Notification, :count) end + context 'when the sender is a local moderator' do + let(:sender) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account } + let(:type) { :mention } + let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender)) } + + it 'does notify when the sender is blocked' do + recipient.block!(sender) + expect { subject }.to change(Notification, :count).by(1) + end + end + it 'does not notify when sender is muted with hide_notifications' do recipient.mute!(sender, notifications: true) expect { subject }.to_not change(Notification, :count) From f9a929ed5c71e0aaf42c124e96d499d81507c2ac Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 19 Aug 2024 11:13:45 +0200 Subject: [PATCH 26/27] Bump version to v4.2.12 (#31491) --- CHANGELOG.md | 8 +++++++- docker-compose.yml | 6 +++--- lib/mastodon/version.rb | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0c05d0d3a020a..b2229f9fdcbfa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,13 @@ All notable changes to this project will be documented in this file. -## |4.2.11] - 2024-08-16 +## [4.2.12] - 2024-08-19 + +### Fixed + +- Fix broken notifications for mentions from local moderators ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31484)) + +## [4.2.11] - 2024-08-16 ### Added diff --git a/docker-compose.yml b/docker-compose.yml index a599f01e6e5a97..d4d5e89febfa9b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,7 +56,7 @@ services: web: build: . - image: ghcr.io/mastodon/mastodon:v4.2.11 + image: ghcr.io/mastodon/mastodon:v4.2.12 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.11 + image: ghcr.io/mastodon/mastodon:v4.2.12 restart: always env_file: .env.production command: node ./streaming @@ -95,7 +95,7 @@ services: sidekiq: build: . - image: ghcr.io/mastodon/mastodon:v4.2.11 + image: ghcr.io/mastodon/mastodon:v4.2.12 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 52e1ea540d5693..f0e0554c3f26c4 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ def minor end def patch - 11 + 12 end def default_prerelease From 751f2283c0602e46b801ef9489132dbc53060e68 Mon Sep 17 00:00:00 2001 From: ray03j Date: Wed, 21 Aug 2024 08:50:51 +0900 Subject: [PATCH 27/27] [add] pr-auto-approve --- .github/workflows/pr-auto-approve.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/pr-auto-approve.yml diff --git a/.github/workflows/pr-auto-approve.yml b/.github/workflows/pr-auto-approve.yml new file mode 100644 index 00000000000000..e46af2d48e86d2 --- /dev/null +++ b/.github/workflows/pr-auto-approve.yml @@ -0,0 +1,27 @@ +name: Auto Approve +# このGitHub Actionsを起動するイベントの種類 +on: + # Pull Request関係のイベント + pull_request: + types: + - opened + - reopened + - synchronize + - ready_for_review # Draft Pull RequestからDraftが外れたら起動 +jobs: + approve: + # このJobを実行する条件。以下の全てを満たす場合 + # + Pull Requestの作成者が、リポジトリ所有者と等しい + # + Pull Requestが、Draft Pull Requestではない + if: | + github.event.pull_request.user.login == github.repository_owner + && ! github.event.pull_request.draft + # このJobを実行する時に使うOS + # 今回は何でもいいが、GitHub Actionsでは指定が必須 + runs-on: ubuntu-latest + # 内部で使用されるGitHub APIの権限を上書き + permissions: + pull-requests: write + steps: + # Pull RequestをApproveする + - uses: hmarr/auto-approve-action@v3