diff --git a/.browserslistrc b/.browserslistrc index d5734664c..54dd3aaf3 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -4,6 +4,4 @@ not IE 11 not dead [development] -last 1 chrome version -last 1 firefox version -last 1 safari version +supports es6-module diff --git a/SECURITY.md b/SECURITY.md index 12f50ed88..62e23f736 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -14,7 +14,7 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through | ------- | ------------------ | | 3.5.x | Yes | | 3.4.x | Yes | -| 3.3.x | Yes | +| 3.3.x | No | | < 3.3 | No | [bug-bounty]: https://app.intigriti.com/programs/mastodon/mastodonio/detail diff --git a/app/controllers/activitypub/base_controller.rb b/app/controllers/activitypub/base_controller.rb index 196d85a32..b8a7e0ab9 100644 --- a/app/controllers/activitypub/base_controller.rb +++ b/app/controllers/activitypub/base_controller.rb @@ -2,6 +2,7 @@ class ActivityPub::BaseController < Api::BaseController skip_before_action :require_authenticated_user! + skip_before_action :require_not_suspended! skip_around_action :set_locale private diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index d96285b44..2e393fbb6 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -11,6 +11,7 @@ class Api::BaseController < ApplicationController skip_before_action :require_functional!, unless: :whitelist_mode? before_action :require_authenticated_user!, if: :disallow_unauthenticated_api_access? + before_action :require_not_suspended! before_action :set_cache_headers protect_from_forgery with: :null_session @@ -97,6 +98,10 @@ class Api::BaseController < ApplicationController render json: { error: 'This method requires an authenticated user' }, status: 401 unless current_user end + def require_not_suspended! + render json: { error: 'Your login is currently disabled' }, status: 403 if current_user&.account&.suspended? + end + def require_user! if !current_user render json: { error: 'This method requires an authenticated user' }, status: 422 diff --git a/app/controllers/auth/confirmations_controller.rb b/app/controllers/auth/confirmations_controller.rb index 17ad56fa8..0817a905c 100644 --- a/app/controllers/auth/confirmations_controller.rb +++ b/app/controllers/auth/confirmations_controller.rb @@ -89,7 +89,7 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController def after_confirmation_path_for(_resource_name, user) if user.created_by_application && truthy_param?(:redirect_to_app) - user.created_by_application.redirect_uri + user.created_by_application.confirmation_redirect_uri else super end diff --git a/app/lib/application_extension.rb b/app/lib/application_extension.rb index a1fea6430..d61ec0e6e 100644 --- a/app/lib/application_extension.rb +++ b/app/lib/application_extension.rb @@ -12,4 +12,8 @@ module ApplicationExtension def most_recently_used_access_token @most_recently_used_access_token ||= access_tokens.where.not(last_used_at: nil).order(last_used_at: :desc).first end + + def confirmation_redirect_uri + redirect_uri.lines.first.strip + end end diff --git a/app/models/account_stat.rb b/app/models/account_stat.rb index b49827267..a5d71a5b8 100644 --- a/app/models/account_stat.rb +++ b/app/models/account_stat.rb @@ -20,4 +20,16 @@ class AccountStat < ApplicationRecord belongs_to :account, inverse_of: :account_stat update_index('accounts', :account) + + def following_count + [attributes['following_count'], 0].max + end + + def followers_count + [attributes['followers_count'], 0].max + end + + def statuses_count + [attributes['statuses_count'], 0].max + end end diff --git a/app/models/admin/status_batch_action.rb b/app/models/admin/status_batch_action.rb index 631af183c..7bf6fa6da 100644 --- a/app/models/admin/status_batch_action.rb +++ b/app/models/admin/status_batch_action.rb @@ -103,7 +103,7 @@ class Admin::StatusBatchAction def handle_report! @report = Report.new(report_params) unless with_report? - @report.status_ids = (@report.status_ids + status_ids.map(&:to_i)).uniq + @report.status_ids = (@report.status_ids + allowed_status_ids).uniq @report.save! @report_id = @report.id @@ -135,4 +135,8 @@ class Admin::StatusBatchAction def report_params { account: current_account, target_account: target_account } end + + def allowed_status_ids + AccountStatusesFilter.new(@report.target_account, current_account).results.with_discarded.where(id: status_ids).pluck(:id) + end end diff --git a/app/models/status_stat.rb b/app/models/status_stat.rb index 024c467e7..437861d1c 100644 --- a/app/models/status_stat.rb +++ b/app/models/status_stat.rb @@ -17,6 +17,18 @@ class StatusStat < ApplicationRecord after_commit :reset_parent_cache + def replies_count + [attributes['replies_count'], 0].max + end + + def reblogs_count + [attributes['reblogs_count'], 0].max + end + + def favourites_count + [attributes['favourites_count'], 0].max + end + private def reset_parent_cache diff --git a/app/services/appeal_service.rb b/app/services/appeal_service.rb index 1397c50f5..cef9be05f 100644 --- a/app/services/appeal_service.rb +++ b/app/services/appeal_service.rb @@ -14,7 +14,8 @@ class AppealService < BaseService private def create_appeal! - @appeal = @strike.create_appeal!( + @appeal = Appeal.create!( + strike: @strike, text: @text, account: @strike.target_account ) diff --git a/app/services/approve_appeal_service.rb b/app/services/approve_appeal_service.rb index 37a08b46e..96aaaa7d0 100644 --- a/app/services/approve_appeal_service.rb +++ b/app/services/approve_appeal_service.rb @@ -52,8 +52,9 @@ class ApproveAppealService < BaseService end def undo_mark_statuses_as_sensitive! + representative_account = Account.representative @strike.statuses.includes(:media_attachments).each do |status| - UpdateStatusService.new.call(status, @current_account.id, sensitive: false) if status.with_media? + UpdateStatusService.new.call(status, representative_account.id, sensitive: false) if status.with_media? end end diff --git a/app/services/report_service.rb b/app/services/report_service.rb index 9d784c341..d251bb33f 100644 --- a/app/services/report_service.rb +++ b/app/services/report_service.rb @@ -57,7 +57,7 @@ class ReportService < BaseService end def reported_status_ids - @target_account.statuses.with_discarded.find(Array(@status_ids)).pluck(:id) + AccountStatusesFilter.new(@target_account, @source_account).results.with_discarded.find(Array(@status_ids)).pluck(:id) end def payload diff --git a/app/services/unfollow_service.rb b/app/services/unfollow_service.rb index 151f3674f..d83a60e4e 100644 --- a/app/services/unfollow_service.rb +++ b/app/services/unfollow_service.rb @@ -2,6 +2,8 @@ class UnfollowService < BaseService include Payloadable + include Redisable + include Lockable # Unfollow and notify the remote user # @param [Account] source_account Where to unfollow from @@ -13,7 +15,9 @@ class UnfollowService < BaseService @target_account = target_account @options = options - unfollow! || undo_follow_request! + with_lock("relationship:#{[source_account.id, target_account.id].sort.join(':')}") do + unfollow! || undo_follow_request! + end end private diff --git a/app/services/vote_service.rb b/app/services/vote_service.rb index ccd04dbfc..114ec285c 100644 --- a/app/services/vote_service.rb +++ b/app/services/vote_service.rb @@ -7,6 +7,8 @@ class VoteService < BaseService include Lockable def call(account, poll, choices) + return if choices.empty? + authorize_with account, poll, :vote? @account = account diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index f78db8653..84b649f5c 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -128,6 +128,13 @@ Doorkeeper.configure do # force_ssl_in_redirect_uri false + # Specify what redirect URI's you want to block during Application creation. + # Any redirect URI is whitelisted by default. + # + # You can use this option in order to forbid URI's with 'javascript' scheme + # for example. + forbid_redirect_uri { |uri| %w[data vbscript javascript].include?(uri.scheme.to_s.downcase) } + # Specify what grant flows are enabled in array of Strings. The valid # strings and the flows they enable are: #