mirror of
https://github.com/mastodon/mastodon.git
synced 2025-09-06 09:51:24 +00:00
Fix banned text being able to be circumvented via unicode (#35978)
This commit is contained in:
parent
6e09dd10a7
commit
7fc884ba00
|
@ -32,26 +32,30 @@ class Antispam
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def local_preflight_check!(status)
|
def initialize(status)
|
||||||
return unless considered_spam?(status)
|
@status = status
|
||||||
|
end
|
||||||
|
|
||||||
report_if_needed!(status.account)
|
def local_preflight_check!
|
||||||
|
return unless considered_spam?
|
||||||
|
|
||||||
raise SilentlyDrop, status
|
report_if_needed!
|
||||||
|
|
||||||
|
raise SilentlyDrop, @status
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def considered_spam?(status)
|
def considered_spam?
|
||||||
(all_time_suspicious?(status) || recent_suspicious?(status)) && suspicious_reply_or_mention?(status)
|
(all_time_suspicious? || recent_suspicious?) && suspicious_reply_or_mention?
|
||||||
end
|
end
|
||||||
|
|
||||||
def all_time_suspicious?(status)
|
def all_time_suspicious?
|
||||||
all_time_spammy_texts.any? { |spammy_text| status.text.include?(spammy_text) }
|
all_time_spammy_texts.any? { |spammy_text| status_text.include?(spammy_text) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def recent_suspicious?(status)
|
def recent_suspicious?
|
||||||
status.account.created_at >= ACCOUNT_AGE_EXEMPTION.ago && spammy_texts.any? { |spammy_text| status.text.include?(spammy_text) }
|
@status.account.created_at >= ACCOUNT_AGE_EXEMPTION.ago && spammy_texts.any? { |spammy_text| status_text.include?(spammy_text) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def spammy_texts
|
def spammy_texts
|
||||||
|
@ -62,25 +66,26 @@ class Antispam
|
||||||
redis.smembers('antispam:all_time_spammy_texts')
|
redis.smembers('antispam:all_time_spammy_texts')
|
||||||
end
|
end
|
||||||
|
|
||||||
def suspicious_reply_or_mention?(status)
|
def suspicious_reply_or_mention?
|
||||||
parent = status.thread
|
account_ids = ([@status.in_reply_to_account_id] + @status.mentions.map(&:account_id)).uniq
|
||||||
return true if parent.present? && !Follow.exists?(account_id: parent.account_id, target_account: status.account_id)
|
!Follow.exists?(account_id: account_ids, target_account_id: @status.account.id)
|
||||||
|
|
||||||
account_ids = status.mentions.map(&:account_id).uniq
|
|
||||||
!Follow.exists?(account_id: account_ids, target_account_id: status.account.id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def report_if_needed!(account)
|
def report_if_needed!
|
||||||
return if system_reports.unresolved.exists?(target_account: account)
|
return if system_reports.unresolved.exists?(target_account: @status.account)
|
||||||
|
|
||||||
system_reports.create!(
|
system_reports.create!(
|
||||||
category: :spam,
|
category: :spam,
|
||||||
comment: 'Account automatically reported for posting a banned URL',
|
comment: 'Account automatically reported for posting a banned URL',
|
||||||
target_account: account
|
target_account: @status.account
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def system_reports
|
def system_reports
|
||||||
Account.representative.reports
|
Account.representative.reports
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def status_text
|
||||||
|
@status_text ||= @status.text.unicode_normalize(:nfkc).downcase
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -39,8 +39,6 @@ class PostStatusService < BaseService
|
||||||
@in_reply_to = @options[:thread]
|
@in_reply_to = @options[:thread]
|
||||||
@quoted_status = @options[:quoted_status]
|
@quoted_status = @options[:quoted_status]
|
||||||
|
|
||||||
@antispam = Antispam.new
|
|
||||||
|
|
||||||
return idempotency_duplicate if idempotency_given? && idempotency_duplicate?
|
return idempotency_duplicate if idempotency_given? && idempotency_duplicate?
|
||||||
|
|
||||||
validate_media!
|
validate_media!
|
||||||
|
@ -82,7 +80,9 @@ class PostStatusService < BaseService
|
||||||
process_mentions_service.call(@status, save_records: false)
|
process_mentions_service.call(@status, save_records: false)
|
||||||
safeguard_mentions!(@status)
|
safeguard_mentions!(@status)
|
||||||
attach_quote!(@status)
|
attach_quote!(@status)
|
||||||
@antispam.local_preflight_check!(@status)
|
|
||||||
|
antispam = Antispam.new(@status)
|
||||||
|
antispam.local_preflight_check!
|
||||||
|
|
||||||
# The following transaction block is needed to wrap the UPDATEs to
|
# The following transaction block is needed to wrap the UPDATEs to
|
||||||
# the media attachments when the status is created
|
# the media attachments when the status is created
|
||||||
|
@ -113,7 +113,9 @@ class PostStatusService < BaseService
|
||||||
|
|
||||||
def schedule_status!
|
def schedule_status!
|
||||||
status_for_validation = @account.statuses.build(status_attributes)
|
status_for_validation = @account.statuses.build(status_attributes)
|
||||||
@antispam.local_preflight_check!(status_for_validation)
|
|
||||||
|
antispam = Antispam.new(status_for_validation)
|
||||||
|
antispam.local_preflight_check!
|
||||||
|
|
||||||
if status_for_validation.valid?
|
if status_for_validation.valid?
|
||||||
# Marking the status as destroyed is necessary to prevent the status from being
|
# Marking the status as destroyed is necessary to prevent the status from being
|
||||||
|
|
|
@ -4,7 +4,7 @@ require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Antispam do
|
RSpec.describe Antispam do
|
||||||
describe '#local_preflight_check!' do
|
describe '#local_preflight_check!' do
|
||||||
subject { described_class.new.local_preflight_check!(status) }
|
subject { described_class.new(status).local_preflight_check! }
|
||||||
|
|
||||||
let(:status) { Fabricate :status }
|
let(:status) { Fabricate :status }
|
||||||
|
|
||||||
|
@ -39,6 +39,30 @@ RSpec.describe Antispam do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when status matches unicode variants' do
|
||||||
|
let(:status) { Fabricate :status, text: 'I use https://𝐛𝐚𝐧𝐧𝐞𝐝.𝐞𝐱𝐚𝐦𝐩𝐥𝐞 urls in my text' }
|
||||||
|
|
||||||
|
it 'raises error and reports' do
|
||||||
|
expect { subject }
|
||||||
|
.to raise_error(described_class::SilentlyDrop)
|
||||||
|
.and change(spam_reports, :count).by(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when report already exists' do
|
||||||
|
before { Fabricate :report, account: Account.representative, target_account: status.account }
|
||||||
|
|
||||||
|
it 'raises error and does not report' do
|
||||||
|
expect { subject }
|
||||||
|
.to raise_error(described_class::SilentlyDrop)
|
||||||
|
.and not_change(spam_reports, :count)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def spam_reports
|
||||||
|
Account.representative.reports.where(target_account: status.account).spam
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when status does not match' do
|
context 'when status does not match' do
|
||||||
it { is_expected.to be_nil }
|
it { is_expected.to be_nil }
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user