Compare commits

...

5 Commits

Author SHA1 Message Date
Eashwar Ranganathan
5fbca019ad
Merge 624f06e756 into fbe9728f36 2025-05-06 15:05:47 +00:00
Claire
fbe9728f36
Bump version to v4.3.8 (#34626)
Some checks are pending
Check i18n / check-i18n (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (ruby) (push) Waiting to run
Check formatting / lint (push) Waiting to run
JavaScript Linting / lint (push) Waiting to run
Ruby Linting / lint (push) Waiting to run
JavaScript Testing / test (push) Waiting to run
Historical data migration test / test (14-alpine) (push) Waiting to run
Historical data migration test / test (15-alpine) (push) Waiting to run
Historical data migration test / test (16-alpine) (push) Waiting to run
Historical data migration test / test (17-alpine) (push) Waiting to run
Ruby Testing / build (production) (push) Waiting to run
Ruby Testing / build (test) (push) Waiting to run
Ruby Testing / test (.ruby-version) (push) Blocked by required conditions
Ruby Testing / test (3.2) (push) Blocked by required conditions
Ruby Testing / test (3.3) (push) Blocked by required conditions
Ruby Testing / Libvips tests (.ruby-version) (push) Blocked by required conditions
Ruby Testing / Libvips tests (3.2) (push) Blocked by required conditions
Ruby Testing / Libvips tests (3.3) (push) Blocked by required conditions
Ruby Testing / End to End testing (.ruby-version) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.2) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.3) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:8.10.2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, opensearchproject/opensearch:2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.2, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.3, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
2025-05-06 14:17:07 +00:00
Claire
3bbf3e9709
Fix code style issue (#34624) 2025-05-06 13:35:54 +00:00
Claire
79931bf3ae
Merge commit from fork
* Check scheme in account and post links

* Harden media attachments

* Client-side mitigation

* Client-side mitigation for media attachments
2025-05-06 15:02:13 +02:00
Eashwar Ranganathan
624f06e756
Make tootctl aware of new 'require approval' for email domains 2025-05-04 15:34:03 -04:00
12 changed files with 143 additions and 17 deletions

View File

@ -538,7 +538,7 @@ and provided thanks to the work of the following contributors:
* [Drew Schuster](mailto:dtschust@gmail.com) * [Drew Schuster](mailto:dtschust@gmail.com)
* [Dryusdan](mailto:dryusdan@dryusdan.fr) * [Dryusdan](mailto:dryusdan@dryusdan.fr)
* [Eai](mailto:eai@mizle.net) * [Eai](mailto:eai@mizle.net)
* [Eashwar Ranganathan](mailto:eranganathan@lyft.com) * [Eashwar Ranganathan](mailto:eashwar@eashwar.com)
* [Ed Knutson](mailto:knutsoned@gmail.com) * [Ed Knutson](mailto:knutsoned@gmail.com)
* [Elizabeth Martín Campos](mailto:me@elizabeth.sh) * [Elizabeth Martín Campos](mailto:me@elizabeth.sh)
* [Elizabeth Myers](mailto:elizabeth@interlinked.me) * [Elizabeth Myers](mailto:elizabeth@interlinked.me)

View File

@ -2,9 +2,34 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [4.3.8] - 2025-05-06
### Security
- Update dependencies
- Check scheme on account, profile, and media URLs ([GHSA-x2rc-v5wx-g3m5](https://github.com/mastodon/mastodon/security/advisories/GHSA-x2rc-v5wx-g3m5))
### Added
- Add warning for REDIS_NAMESPACE deprecation at startup (#34581 by @ClearlyClaire)
- Add built-in context for interaction policies (#34574 by @ClearlyClaire)
### Changed
- Change activity distribution error handling to skip retrying for deleted accounts (#33617 by @ClearlyClaire)
### Removed
- Remove double-query for signed query strings (#34610 by @ClearlyClaire)
### Fixed
- Fix incorrect redirect in response to unauthenticated API requests in limited federation mode (#34549 by @ClearlyClaire)
- Fix sign-up e-mail confirmation page reloading on error or redirect (#34548 by @ClearlyClaire)
## [4.3.7] - 2025-04-02 ## [4.3.7] - 2025-04-02
### Add ### Added
- Add delay to profile updates to debounce them (#34137 by @ClearlyClaire) - Add delay to profile updates to debounce them (#34137 by @ClearlyClaire)
- Add support for paginating partial collections in `SynchronizeFollowersService` (#34272 and #34277 by @ClearlyClaire) - Add support for paginating partial collections in `SynchronizeFollowersService` (#34272 and #34277 by @ClearlyClaire)

View File

@ -77,6 +77,17 @@ export function normalizeStatus(status, normalOldStatus) {
normalStatus.contentHtml = emojify(normalStatus.content, emojiMap); normalStatus.contentHtml = emojify(normalStatus.content, emojiMap);
normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap); normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap);
normalStatus.hidden = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive; normalStatus.hidden = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive;
if (normalStatus.url && !(normalStatus.url.startsWith('http://') || normalStatus.url.startsWith('https://'))) {
normalStatus.url = null;
}
normalStatus.url ||= normalStatus.uri;
normalStatus.media_attachments.forEach(item => {
if (item.remote_url && !(item.remote_url.startsWith('http://') || item.remote_url.startsWith('https://')))
item.remote_url = null;
});
} }
if (normalOldStatus) { if (normalOldStatus) {

View File

@ -144,5 +144,10 @@ export function createAccountFromServerJSON(serverJSON: ApiAccountJSON) {
), ),
note_emojified: emojify(accountJSON.note, emojiMap), note_emojified: emojify(accountJSON.note, emojiMap),
note_plain: unescapeHTML(accountJSON.note), note_plain: unescapeHTML(accountJSON.note),
url:
accountJSON.url.startsWith('http://') ||
accountJSON.url.startsWith('https://')
? accountJSON.url
: accountJSON.uri,
}); });
} }

View File

@ -15,13 +15,15 @@ class ActivityPub::Parser::MediaAttachmentParser
end end
def remote_url def remote_url
Addressable::URI.parse(@json['url'])&.normalize&.to_s url = Addressable::URI.parse(@json['url'])&.normalize&.to_s
url unless unsupported_uri_scheme?(url)
rescue Addressable::URI::InvalidURIError rescue Addressable::URI::InvalidURIError
nil nil
end end
def thumbnail_remote_url def thumbnail_remote_url
Addressable::URI.parse(@json['icon'].is_a?(Hash) ? @json['icon']['url'] : @json['icon'])&.normalize&.to_s url = Addressable::URI.parse(@json['icon'].is_a?(Hash) ? @json['icon']['url'] : @json['icon'])&.normalize&.to_s
url unless unsupported_uri_scheme?(url)
rescue Addressable::URI::InvalidURIError rescue Addressable::URI::InvalidURIError
nil nil
end end

View File

@ -29,7 +29,10 @@ class ActivityPub::Parser::StatusParser
end end
def url def url
url_to_href(@object['url'], 'text/html') if @object['url'].present? return if @object['url'].blank?
url = url_to_href(@object['url'], 'text/html')
url unless unsupported_uri_scheme?(url)
end end
def text def text

View File

@ -4,6 +4,7 @@ require 'singleton'
class ActivityPub::TagManager class ActivityPub::TagManager
include Singleton include Singleton
include JsonLdHelper
include RoutingHelper include RoutingHelper
CONTEXT = 'https://www.w3.org/ns/activitystreams' CONTEXT = 'https://www.w3.org/ns/activitystreams'
@ -17,7 +18,7 @@ class ActivityPub::TagManager
end end
def url_for(target) def url_for(target)
return target.url if target.respond_to?(:local?) && !target.local? return unsupported_uri_scheme?(target.url) ? nil : target.url if target.respond_to?(:local?) && !target.local?
return unless target.respond_to?(:object_type) return unless target.respond_to?(:object_type)

View File

@ -59,7 +59,7 @@ services:
web: web:
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes # You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
# build: . # build: .
image: ghcr.io/mastodon/mastodon:v4.3.7 image: ghcr.io/mastodon/mastodon:v4.3.8
restart: always restart: always
env_file: .env.production env_file: .env.production
command: bundle exec puma -C config/puma.rb command: bundle exec puma -C config/puma.rb
@ -83,7 +83,7 @@ services:
# build: # build:
# dockerfile: ./streaming/Dockerfile # dockerfile: ./streaming/Dockerfile
# context: . # context: .
image: ghcr.io/mastodon/mastodon-streaming:v4.3.7 image: ghcr.io/mastodon/mastodon-streaming:v4.3.8
restart: always restart: always
env_file: .env.production env_file: .env.production
command: node ./streaming/index.js command: node ./streaming/index.js
@ -102,7 +102,7 @@ services:
sidekiq: sidekiq:
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes # You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
# build: . # build: .
image: ghcr.io/mastodon/mastodon:v4.3.7 image: ghcr.io/mastodon/mastodon:v4.3.8
restart: always restart: always
env_file: .env.production env_file: .env.production
command: bundle exec sidekiq command: bundle exec sidekiq

View File

@ -5,9 +5,33 @@ require_relative 'base'
module Mastodon::CLI module Mastodon::CLI
class EmailDomainBlocks < Base class EmailDomainBlocks < Base
option :only_blocked, type: :boolean, defaut: false
option :only_with_approval, type: :boolean, default: false
desc 'list', 'List blocked e-mail domains' desc 'list', 'List blocked e-mail domains'
long_desc <<-LONG_DESC
By default this command lists all domains in the email domain block list
and their associated MX records (if included).
If the --only-blocked option is provided, this command will list only email
domains that are fully blocked from signup.
If the --only-with-approval option is provided, this command will list only
email domains that are allowed to be used but require manual approval.
The --only-blocked and --only-with-approval options are mutually exclusive.
LONG_DESC
def list def list
EmailDomainBlock.parents.find_each do |parent| fail_with_message 'Cannot specify both --only-blocked and --only-with-approval' if options[:only_blocked] && options[:only_with_approval]
base_query = EmailDomainBlock.where(parent_id: nil)
if options[:only_blocked]
base_query = base_query.where(allow_with_approval: false)
elsif options[:only_with_approval]
base_query = base_query.where(allow_with_approval: true)
end
base_query.find_each do |parent|
say(parent.domain.to_s, :white) say(parent.domain.to_s, :white)
shell.indent do shell.indent do
@ -19,6 +43,7 @@ module Mastodon::CLI
end end
option :with_dns_records, type: :boolean option :with_dns_records, type: :boolean
option :allow_with_approval, type: :boolean, defaut: false
desc 'add DOMAIN...', 'Block e-mail domain(s)' desc 'add DOMAIN...', 'Block e-mail domain(s)'
long_desc <<-LONG_DESC long_desc <<-LONG_DESC
Blocking an e-mail domain prevents users from signing up Blocking an e-mail domain prevents users from signing up
@ -30,6 +55,9 @@ module Mastodon::CLI
This can be helpful if you are blocking an e-mail server that has many This can be helpful if you are blocking an e-mail server that has many
different domains pointing to it as it allows you to essentially block different domains pointing to it as it allows you to essentially block
it at the root. it at the root.
When the --allow-with-approval option is set, the email domains provided will
have to be manually approved for signup.
LONG_DESC LONG_DESC
def add(*domains) def add(*domains)
fail_with_message 'No domain(s) given' if domains.empty? fail_with_message 'No domain(s) given' if domains.empty?
@ -47,19 +75,18 @@ module Mastodon::CLI
other_domains = [] other_domains = []
other_domains = DomainResource.new(domain).mx if options[:with_dns_records] other_domains = DomainResource.new(domain).mx if options[:with_dns_records]
email_domain_block = EmailDomainBlock.new(domain: domain, other_domains: other_domains) email_domain_block = EmailDomainBlock.new(domain: domain, other_domains: other_domains, allow_with_approval: options[:allow_with_approval])
email_domain_block.save! email_domain_block.save!
processed += 1 processed += 1
(email_domain_block.other_domains || []).uniq.each do |hostname| (email_domain_block.other_domains || []).uniq.each do |hostname|
another_email_domain_block = EmailDomainBlock.new(domain: hostname, parent: email_domain_block)
if EmailDomainBlock.exists?(domain: hostname) if EmailDomainBlock.exists?(domain: hostname)
say("#{hostname} is already blocked.", :yellow) say("#{hostname} is already blocked.", :yellow)
skipped += 1 skipped += 1
next next
end end
another_email_domain_block = EmailDomainBlock.new(domain: hostname, parent: email_domain_block, allow_with_approval: options[:allow_with_approval])
another_email_domain_block.save! another_email_domain_block.save!
processed += 1 processed += 1
end end

View File

@ -17,7 +17,7 @@ module Mastodon
end end
def default_prerelease def default_prerelease
'alpha.4' 'alpha.5'
end end
def prerelease def prerelease

View File

@ -15,17 +15,62 @@ RSpec.describe Mastodon::CLI::EmailDomainBlocks do
describe '#list' do describe '#list' do
let(:action) { :list } let(:action) { :list }
context 'with both --only-blocked and --only-with-approval' do
let(:options) { { only_blocked: true, only_with_approval: true } }
it 'warns about usage and exits' do
expect { subject }
.to raise_error(Thor::Error, 'Cannot specify both --only-blocked and --only-with-approval')
end
end
context 'with email domain block records' do context 'with email domain block records' do
let!(:parent_block) { Fabricate(:email_domain_block) } let!(:parent_block) { Fabricate(:email_domain_block) }
let!(:child_block) { Fabricate(:email_domain_block, parent: parent_block) } let!(:child_block) { Fabricate(:email_domain_block, parent: parent_block) }
let!(:parent_allow_block) { Fabricate(:email_domain_block, allow_with_approval: true) }
let!(:child_allow_block) { Fabricate(:email_domain_block, parent: parent_allow_block, allow_with_approval: true) }
it 'lists the blocks' do it 'lists all the blocks by default' do
expect { subject } expect { subject }
.to output_results( .to output_results(
parent_block.domain, parent_block.domain,
child_block.domain child_block.domain,
parent_allow_block.domain,
child_allow_block.domain
) )
end end
context 'with the --only-blocked flag set' do
let(:options) { { only_blocked: true } }
it 'lists only blocked domains' do
expect { subject }
.to output_results(
parent_block.domain,
child_block.domain
)
.and not_output_results(
parent_allow_block.domain,
child_allow_block.domain
)
end
end
context 'with the --only-with-approval flag set' do
let(:options) { { only_with_approval: true } }
it 'lists only manually approvable domains' do
expect { subject }
.to output_results(
parent_allow_block.domain,
child_allow_block.domain
)
.and not_output_results(
parent_block.domain,
child_block.domain
)
end
end
end end
end end
@ -56,6 +101,7 @@ RSpec.describe Mastodon::CLI::EmailDomainBlocks do
context 'when no blocks exist' do context 'when no blocks exist' do
let(:domain) { 'host.example' } let(:domain) { 'host.example' }
let(:arguments) { [domain] } let(:arguments) { [domain] }
let(:options) { { allow_with_approval: false } }
it 'adds a new block' do it 'adds a new block' do
expect { subject } expect { subject }
@ -67,7 +113,7 @@ RSpec.describe Mastodon::CLI::EmailDomainBlocks do
context 'with --with-dns-records true' do context 'with --with-dns-records true' do
let(:domain) { 'host.example' } let(:domain) { 'host.example' }
let(:arguments) { [domain] } let(:arguments) { [domain] }
let(:options) { { with_dns_records: true } } let(:options) { { allow_with_approval: false, with_dns_records: true } }
before do before do
configure_mx(domain: domain, exchange: 'other.host') configure_mx(domain: domain, exchange: 'other.host')

View File

@ -6,4 +6,10 @@ module CommandLineHelpers
include(*) include(*)
).to_stdout ).to_stdout
end end
def not_output_results(*)
output(
not_include(*)
).to_stdout
end
end end