Merge commit 'd27eb181f6ab419d1745a1fe9b94094be17a618f' into glitch-soc/merge-upstream
Conflicts: - `spec/requests/api/v2/instance_spec.rb`: Conflict due to glitch-soc having a different default site name. Updated the tests as upstream did, keeping glitch-soc's default name.main-rebase-security-fix
commit
15f6d2d038
|
@ -11,6 +11,6 @@ linters:
|
|||
MiddleDot:
|
||||
enabled: true
|
||||
LineLength:
|
||||
max: 320
|
||||
max: 300
|
||||
ViewLength:
|
||||
max: 200 # Override default value of 100 inherited from rubocop
|
||||
|
|
|
@ -39,7 +39,7 @@ Layout/FirstHashElementIndentation:
|
|||
# Reason: Currently disabled in .rubocop_todo.yml
|
||||
# https://docs.rubocop.org/rubocop/cops_layout.html#layoutlinelength
|
||||
Layout/LineLength:
|
||||
Max: 320 # Default of 120 causes a duplicate entry in generated todo file
|
||||
Max: 300 # Default of 120 causes a duplicate entry in generated todo file
|
||||
|
||||
## Disable most Metrics/*Length cops
|
||||
# Reason: those are often triggered and force significant refactors when this happend
|
||||
|
|
3
Gemfile
3
Gemfile
|
@ -69,7 +69,6 @@ gem 'nsa'
|
|||
gem 'oj', '~> 3.14'
|
||||
gem 'ox', '~> 2.14'
|
||||
gem 'parslet'
|
||||
gem 'posix-spawn'
|
||||
gem 'public_suffix', '~> 5.0'
|
||||
gem 'pundit', '~> 2.3'
|
||||
gem 'premailer-rails'
|
||||
|
@ -89,7 +88,7 @@ gem 'sidekiq-unique-jobs', '~> 7.1'
|
|||
gem 'sidekiq-bulk', '~> 0.2.0'
|
||||
gem 'simple-navigation', '~> 4.4'
|
||||
gem 'simple_form', '~> 5.2'
|
||||
gem 'stoplight', '~> 3.0.1'
|
||||
gem 'stoplight', '~> 4.1'
|
||||
gem 'strong_migrations', '1.8.0'
|
||||
gem 'tty-prompt', '~> 0.23', require: false
|
||||
gem 'twitter-text', '~> 3.1.0'
|
||||
|
|
|
@ -245,7 +245,7 @@ GEM
|
|||
tzinfo
|
||||
excon (0.110.0)
|
||||
fabrication (2.31.0)
|
||||
faker (3.3.0)
|
||||
faker (3.3.1)
|
||||
i18n (>= 1.8.11, < 2)
|
||||
faraday (1.10.3)
|
||||
faraday-em_http (~> 1.0)
|
||||
|
@ -509,7 +509,6 @@ GEM
|
|||
pg (1.5.6)
|
||||
pghero (3.4.1)
|
||||
activerecord (>= 6)
|
||||
posix-spawn (0.3.15)
|
||||
premailer (1.23.0)
|
||||
addressable
|
||||
css_parser (>= 1.12.0)
|
||||
|
@ -733,7 +732,7 @@ GEM
|
|||
smart_properties (1.17.0)
|
||||
stackprof (0.2.26)
|
||||
statsd-ruby (1.5.0)
|
||||
stoplight (3.0.2)
|
||||
stoplight (4.1.0)
|
||||
redlock (~> 1.0)
|
||||
stringio (3.1.0)
|
||||
strong_migrations (1.8.0)
|
||||
|
@ -899,7 +898,6 @@ DEPENDENCIES
|
|||
parslet
|
||||
pg (~> 1.5)
|
||||
pghero
|
||||
posix-spawn
|
||||
premailer-rails
|
||||
private_address_check (~> 0.5)
|
||||
propshaft
|
||||
|
@ -941,7 +939,7 @@ DEPENDENCIES
|
|||
simplecov (~> 0.22)
|
||||
simplecov-lcov (~> 0.8)
|
||||
stackprof
|
||||
stoplight (~> 3.0.1)
|
||||
stoplight (~> 4.1)
|
||||
strong_migrations (= 1.8.0)
|
||||
test-prof
|
||||
thor (~> 1.2)
|
||||
|
|
|
@ -66,7 +66,7 @@ module SignatureVerification
|
|||
compare_signed_string = build_signed_string(include_query_string: false)
|
||||
return actor unless verify_signature(actor, signature, compare_signed_string).nil?
|
||||
|
||||
actor = stoplight_wrap_request { actor_refresh_key!(actor) }
|
||||
actor = stoplight_wrapper.run { actor_refresh_key!(actor) }
|
||||
|
||||
raise SignatureVerificationError, "Could not refresh public key #{signature_params['keyId']}" if actor.nil?
|
||||
|
||||
|
@ -226,10 +226,10 @@ module SignatureVerification
|
|||
end
|
||||
|
||||
if key_id.start_with?('acct:')
|
||||
stoplight_wrap_request { ResolveAccountService.new.call(key_id.delete_prefix('acct:'), suppress_errors: false) }
|
||||
stoplight_wrapper.run { ResolveAccountService.new.call(key_id.delete_prefix('acct:'), suppress_errors: false) }
|
||||
elsif !ActivityPub::TagManager.instance.local_uri?(key_id)
|
||||
account = ActivityPub::TagManager.instance.uri_to_actor(key_id)
|
||||
account ||= stoplight_wrap_request { ActivityPub::FetchRemoteKeyService.new.call(key_id, suppress_errors: false) }
|
||||
account ||= stoplight_wrapper.run { ActivityPub::FetchRemoteKeyService.new.call(key_id, suppress_errors: false) }
|
||||
account
|
||||
end
|
||||
rescue Mastodon::PrivateNetworkAddressError => e
|
||||
|
@ -238,12 +238,11 @@ module SignatureVerification
|
|||
raise SignatureVerificationError, e.message
|
||||
end
|
||||
|
||||
def stoplight_wrap_request(&block)
|
||||
Stoplight("source:#{request.remote_ip}", &block)
|
||||
def stoplight_wrapper
|
||||
Stoplight("source:#{request.remote_ip}")
|
||||
.with_threshold(1)
|
||||
.with_cool_off_time(5.minutes.seconds)
|
||||
.with_error_handler { |error, handle| error.is_a?(HTTP::Error) || error.is_a?(OpenSSL::SSL::SSLError) ? handle.call(error) : raise(error) }
|
||||
.run
|
||||
end
|
||||
|
||||
def actor_refresh_key!(actor)
|
||||
|
|
|
@ -10104,9 +10104,10 @@ noscript {
|
|||
}
|
||||
|
||||
.filtered-notifications-banner__badge {
|
||||
background-color: $highlight-text-color;
|
||||
background: $ui-button-background-color;
|
||||
border-radius: 4px;
|
||||
padding: 1px 6px;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -491,10 +491,10 @@ class FeedManager
|
|||
# @param [List] list
|
||||
# @return [Boolean]
|
||||
def filter_from_list?(status, list)
|
||||
if status.reply? && status.in_reply_to_account_id != status.account_id
|
||||
should_filter = status.in_reply_to_account_id != list.account_id
|
||||
should_filter &&= !list.show_followed?
|
||||
should_filter &&= !(list.show_list? && ListAccount.exists?(list_id: list.id, account_id: status.in_reply_to_account_id))
|
||||
if status.reply? && status.in_reply_to_account_id != status.account_id # Status is a reply to account other than status account
|
||||
should_filter = status.in_reply_to_account_id != list.account_id # Status replies to account id other than list account
|
||||
should_filter &&= !list.show_followed? # List show_followed? is false
|
||||
should_filter &&= !(list.show_list? && ListAccount.exists?(list_id: list.id, account_id: status.in_reply_to_account_id)) # If show_list? true, check for a ListAccount with list and reply to account
|
||||
|
||||
return !!should_filter
|
||||
end
|
||||
|
@ -509,7 +509,11 @@ class FeedManager
|
|||
# @param [Hash] crutches
|
||||
# @return [Boolean]
|
||||
def filter_from_tags?(status, receiver_id, crutches)
|
||||
receiver_id == status.account_id || ((crutches[:active_mentions][status.id] || []) + [status.account_id]).any? { |target_account_id| crutches[:blocking][target_account_id] || crutches[:muting][target_account_id] } || crutches[:blocked_by][status.account_id] || crutches[:domain_blocking][status.account.domain]
|
||||
receiver_id == status.account_id || # Receiver is status account?
|
||||
((crutches[:active_mentions][status.id] || []) + [status.account_id]) # For mentioned accounts or status account:
|
||||
.any? { |target_account_id| crutches[:blocking][target_account_id] || crutches[:muting][target_account_id] } || # - Target account is muted or blocked?
|
||||
crutches[:blocked_by][status.account_id] || # Blocked by status account?
|
||||
crutches[:domain_blocking][status.account.domain] # Blocking domain of status account?
|
||||
end
|
||||
|
||||
# Adds a status to an account's feed, returning true if a status was
|
||||
|
|
|
@ -15,4 +15,6 @@ class UserIp < ApplicationRecord
|
|||
self.primary_key = :user_id
|
||||
|
||||
belongs_to :user
|
||||
|
||||
scope :by_latest_used, -> { order(used_at: :desc) }
|
||||
end
|
||||
|
|
|
@ -54,6 +54,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer
|
|||
|
||||
accounts: {
|
||||
max_featured_tags: FeaturedTag::LIMIT,
|
||||
max_pinned_statuses: StatusPinValidator::PIN_LIMIT,
|
||||
},
|
||||
|
||||
statuses: {
|
||||
|
|
|
@ -10,7 +10,7 @@ class BulkImportRowService
|
|||
when :following, :blocking, :muting, :lists
|
||||
target_acct = @data['acct']
|
||||
target_domain = domain(target_acct)
|
||||
@target_account = stoplight_wrap_request(target_domain) { ResolveAccountService.new.call(target_acct, { check_delivery_availability: true }) }
|
||||
@target_account = stoplight_wrapper(target_domain).run { ResolveAccountService.new.call(target_acct, { check_delivery_availability: true }) }
|
||||
return false if @target_account.nil?
|
||||
when :bookmarks
|
||||
target_uri = @data['uri']
|
||||
|
@ -18,7 +18,7 @@ class BulkImportRowService
|
|||
@target_status = ActivityPub::TagManager.instance.uri_to_resource(target_uri, Status)
|
||||
return false if @target_status.nil? && ActivityPub::TagManager.instance.local_uri?(target_uri)
|
||||
|
||||
@target_status ||= stoplight_wrap_request(target_domain) { ActivityPub::FetchRemoteStatusService.new.call(target_uri) }
|
||||
@target_status ||= stoplight_wrapper(target_domain).run { ActivityPub::FetchRemoteStatusService.new.call(target_uri) }
|
||||
return false if @target_status.nil?
|
||||
end
|
||||
|
||||
|
@ -51,16 +51,15 @@ class BulkImportRowService
|
|||
TagManager.instance.local_domain?(domain) ? nil : TagManager.instance.normalize_domain(domain)
|
||||
end
|
||||
|
||||
def stoplight_wrap_request(domain, &block)
|
||||
def stoplight_wrapper(domain)
|
||||
if domain.present?
|
||||
Stoplight("source:#{domain}", &block)
|
||||
Stoplight("source:#{domain}")
|
||||
.with_fallback { nil }
|
||||
.with_threshold(1)
|
||||
.with_cool_off_time(5.minutes.seconds)
|
||||
.with_error_handler { |error, handle| error.is_a?(HTTP::Error) || error.is_a?(OpenSSL::SSL::SSLError) ? handle.call(error) : raise(error) }
|
||||
.run
|
||||
else
|
||||
yield
|
||||
Stoplight('domain-blank')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -62,13 +62,7 @@
|
|||
%td
|
||||
%time.formatted{ datetime: account.created_at.iso8601, title: l(account.created_at) }= l account.created_at
|
||||
%td
|
||||
- recent_ips = account.user.ips.order(used_at: :desc).to_a
|
||||
- recent_ips.each_with_index do |recent_ip, i|
|
||||
%tr
|
||||
- if i.zero?
|
||||
%th{ rowspan: recent_ips.size }= t('admin.accounts.most_recent_ip')
|
||||
%td= recent_ip.ip
|
||||
%td= table_link_to 'search', t('admin.accounts.search_same_ip'), admin_accounts_path(ip: recent_ip.ip)
|
||||
= render partial: 'admin/accounts/user_ip', collection: account.user.ips.by_latest_used
|
||||
%tr
|
||||
%th= t('admin.accounts.most_recent_activity')
|
||||
%td
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
%tr
|
||||
- if user_ip_iteration.first?
|
||||
%th{ rowspan: user_ip_iteration.size }= t('admin.accounts.most_recent_ip')
|
||||
%td= user_ip.ip
|
||||
%td= table_link_to 'search', t('admin.accounts.search_same_ip'), admin_accounts_path(ip: user_ip.ip)
|
|
@ -59,7 +59,7 @@ class ActivityPub::DeliveryWorker
|
|||
end
|
||||
|
||||
def perform_request
|
||||
light = Stoplight(@inbox_url) do
|
||||
stoplight_wrapper.run do
|
||||
request_pool.with(@host) do |http_client|
|
||||
build_request(http_client).perform do |response|
|
||||
raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response)
|
||||
|
@ -68,10 +68,12 @@ class ActivityPub::DeliveryWorker
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
light.with_threshold(STOPLIGHT_FAILURE_THRESHOLD)
|
||||
def stoplight_wrapper
|
||||
Stoplight(@inbox_url)
|
||||
.with_threshold(STOPLIGHT_FAILURE_THRESHOLD)
|
||||
.with_cool_off_time(STOPLIGHT_COOLDOWN)
|
||||
.run
|
||||
end
|
||||
|
||||
def failure_tracker
|
||||
|
|
|
@ -11,7 +11,7 @@ class Import::RelationshipWorker
|
|||
def perform(account_id, target_account_uri, relationship, options)
|
||||
from_account = Account.find(account_id)
|
||||
target_domain = domain(target_account_uri)
|
||||
target_account = stoplight_wrap_request(target_domain) { ResolveAccountService.new.call(target_account_uri, { check_delivery_availability: true }) }
|
||||
target_account = stoplight_wrapper(target_domain).run { ResolveAccountService.new.call(target_account_uri, { check_delivery_availability: true }) }
|
||||
options.symbolize_keys!
|
||||
|
||||
return if target_account.nil?
|
||||
|
@ -43,16 +43,15 @@ class Import::RelationshipWorker
|
|||
TagManager.instance.local_domain?(domain) ? nil : TagManager.instance.normalize_domain(domain)
|
||||
end
|
||||
|
||||
def stoplight_wrap_request(domain, &block)
|
||||
def stoplight_wrapper(domain)
|
||||
if domain.present?
|
||||
Stoplight("source:#{domain}", &block)
|
||||
Stoplight("source:#{domain}")
|
||||
.with_fallback { nil }
|
||||
.with_threshold(1)
|
||||
.with_cool_off_time(5.minutes.seconds)
|
||||
.with_error_handler { |error, handle| error.is_a?(HTTP::Error) || error.is_a?(OpenSSL::SSL::SSLError) ? handle.call(error) : raise(error) }
|
||||
.run
|
||||
else
|
||||
yield
|
||||
Stoplight('domain-blank')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
require 'stoplight'
|
||||
|
||||
Rails.application.reloader.to_prepare do
|
||||
Stoplight::Light.default_data_store = Stoplight::DataStore::Redis.new(RedisConfiguration.new.connection)
|
||||
Stoplight::Light.default_notifiers = [Stoplight::Notifier::Logger.new(Rails.logger)]
|
||||
Stoplight.default_data_store = Stoplight::DataStore::Redis.new(RedisConfiguration.new.connection)
|
||||
Stoplight.default_notifiers = [Stoplight::Notifier::Logger.new(Rails.logger)]
|
||||
end
|
||||
|
|
|
@ -84,13 +84,11 @@ module Paperclip
|
|||
# Don't go through Stoplight if we don't have anything object-storage-oriented to do
|
||||
return super if @queued_for_delete.empty? && @queued_for_write.empty? && !dirty?
|
||||
|
||||
Stoplight('object-storage') { super }.with_threshold(STOPLIGHT_THRESHOLD).with_cool_off_time(STOPLIGHT_COOLDOWN).with_error_handler do |error, handle|
|
||||
if error.is_a?(Seahorse::Client::NetworkingError)
|
||||
handle.call(error)
|
||||
else
|
||||
raise error
|
||||
end
|
||||
end.run
|
||||
Stoplight('object-storage')
|
||||
.with_threshold(STOPLIGHT_THRESHOLD)
|
||||
.with_cool_off_time(STOPLIGHT_COOLDOWN)
|
||||
.with_error_handler { |error, handle| error.is_a?(Seahorse::Client::NetworkingError) ? handle.call(error) : raise(error) }
|
||||
.run { super }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,13 +22,15 @@ describe 'Public' do
|
|||
get '/api/v1/timelines/public', headers: headers, params: params
|
||||
end
|
||||
|
||||
let!(:private_status) { Fabricate(:status, visibility: :private) } # rubocop:disable RSpec/LetSetup
|
||||
let!(:local_status) { Fabricate(:status, account: Fabricate.build(:account, domain: nil)) }
|
||||
let!(:remote_status) { Fabricate(:status, account: Fabricate.build(:account, domain: 'example.com')) }
|
||||
let!(:media_status) { Fabricate(:status, media_attachments: [Fabricate.build(:media_attachment)]) }
|
||||
|
||||
let(:params) { {} }
|
||||
|
||||
before do
|
||||
Fabricate(:status, visibility: :private)
|
||||
end
|
||||
|
||||
context 'when the instance allows public preview' do
|
||||
let(:expected_statuses) { [local_status, remote_status, media_status] }
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ describe 'Instances' do
|
|||
expect(body_as_json)
|
||||
.to be_present
|
||||
.and include(title: 'Mastodon Glitch Edition')
|
||||
.and include_configuration_limits
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -31,7 +32,26 @@ describe 'Instances' do
|
|||
expect(body_as_json)
|
||||
.to be_present
|
||||
.and include(title: 'Mastodon Glitch Edition')
|
||||
end
|
||||
.and include_configuration_limits
|
||||
end
|
||||
end
|
||||
|
||||
def include_configuration_limits
|
||||
include(
|
||||
configuration: include(
|
||||
accounts: include(
|
||||
max_featured_tags: FeaturedTag::LIMIT,
|
||||
max_pinned_statuses: StatusPinValidator::PIN_LIMIT
|
||||
),
|
||||
statuses: include(
|
||||
max_characters: StatusLengthValidator::MAX_CHARS,
|
||||
max_media_attachments: 4 # TODO, move to constant somewhere
|
||||
),
|
||||
polls: include(
|
||||
max_options: PollValidator::MAX_OPTIONS
|
||||
)
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,11 +10,22 @@ describe REST::InstanceSerializer do
|
|||
it 'returns recent usage data' do
|
||||
expect(serialization['usage']).to eq({ 'users' => { 'active_month' => 0 } })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'configuration' do
|
||||
it 'returns the VAPID public key' do
|
||||
expect(serialization['configuration']['vapid']).to eq({
|
||||
'public_key' => Rails.configuration.x.vapid_public_key,
|
||||
})
|
||||
end
|
||||
|
||||
it 'returns the max pinned statuses limit' do
|
||||
expect(serialization.deep_symbolize_keys)
|
||||
.to include(
|
||||
configuration: include(
|
||||
accounts: include(max_pinned_statuses: StatusPinValidator::PIN_LIMIT)
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue