From 3ece6e25e6771c17bf79774564e6999003499f46 Mon Sep 17 00:00:00 2001 From: Georg Gadinger Date: Sun, 7 Jul 2019 18:13:19 +0200 Subject: Update fuubar dependency to 2.4.1 (#11248) See also: thekompanee/fuubar#111 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Gemfile.lock') diff --git a/Gemfile.lock b/Gemfile.lock index 6043a2fa9..69f788957 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -231,7 +231,7 @@ GEM fugit (1.1.6) et-orbi (~> 1.1, >= 1.1.6) raabro (~> 1.1) - fuubar (2.4.0) + fuubar (2.4.1) rspec-core (~> 3.0) ruby-progressbar (~> 1.4) get_process_mem (0.2.3) -- cgit From f935004a7492b2be4979a344efcf1cf60fbc46bb Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2019 18:26:12 +0900 Subject: Bump tzinfo-data from 1.2019.1 to 1.2019.2 (#11258) Bumps [tzinfo-data](https://github.com/tzinfo/tzinfo-data) from 1.2019.1 to 1.2019.2. - [Release notes](https://github.com/tzinfo/tzinfo-data/releases) - [Commits](https://github.com/tzinfo/tzinfo-data/compare/v1.2019.1...v1.2019.2) Signed-off-by: dependabot-preview[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Gemfile.lock') diff --git a/Gemfile.lock b/Gemfile.lock index 69f788957..a3c14f16a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -612,7 +612,7 @@ GEM unf (~> 0.1.0) tzinfo (1.2.5) thread_safe (~> 0.1) - tzinfo-data (1.2019.1) + tzinfo-data (1.2019.2) tzinfo (>= 1.0.0) unf (0.1.4) unf_ext -- cgit From 01ee35f14ad443d27ea16f17e194cf1a871a73c0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2019 18:26:41 +0900 Subject: Bump aws-sdk-s3 from 1.43.0 to 1.45.0 (#11262) Bumps [aws-sdk-s3](https://github.com/aws/aws-sdk-ruby) from 1.43.0 to 1.45.0. - [Release notes](https://github.com/aws/aws-sdk-ruby/releases) - [Changelog](https://github.com/aws/aws-sdk-ruby/blob/master/gems/aws-sdk-s3/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-ruby/compare/v1.43.0...v1.45.0) Signed-off-by: dependabot-preview[bot] --- Gemfile | 2 +- Gemfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'Gemfile.lock') diff --git a/Gemfile b/Gemfile index 2fcb603c3..4ad055dae 100644 --- a/Gemfile +++ b/Gemfile @@ -15,7 +15,7 @@ gem 'makara', '~> 0.4' gem 'pghero', '~> 2.2' gem 'dotenv-rails', '~> 2.7' -gem 'aws-sdk-s3', '~> 1.43', require: false +gem 'aws-sdk-s3', '~> 1.45', require: false gem 'fog-core', '<= 2.1.0' gem 'fog-openstack', '~> 0.3', require: false gem 'paperclip', '~> 6.0' diff --git a/Gemfile.lock b/Gemfile.lock index a3c14f16a..8061d7ec4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -76,17 +76,17 @@ GEM av (0.9.0) cocaine (~> 0.5.3) aws-eventstream (1.0.3) - aws-partitions (1.177.0) - aws-sdk-core (3.56.0) + aws-partitions (1.184.0) + aws-sdk-core (3.59.0) aws-eventstream (~> 1.0, >= 1.0.2) aws-partitions (~> 1.0) aws-sigv4 (~> 1.1) jmespath (~> 1.0) - aws-sdk-kms (1.22.0) - aws-sdk-core (~> 3, >= 3.56.0) + aws-sdk-kms (1.23.0) + aws-sdk-core (~> 3, >= 3.58.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.43.0) - aws-sdk-core (~> 3, >= 3.56.0) + aws-sdk-s3 (1.45.0) + aws-sdk-core (~> 3, >= 3.58.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.1) aws-sigv4 (1.1.0) @@ -647,7 +647,7 @@ DEPENDENCIES active_record_query_trace (~> 1.6) addressable (~> 2.6) annotate (~> 2.7) - aws-sdk-s3 (~> 1.43) + aws-sdk-s3 (~> 1.45) better_errors (~> 2.5) binding_of_caller (~> 0.7) blurhash (~> 0.1) -- cgit From d76ca8964ce77c465e3b92b9e80db5cadf0631d6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2019 18:28:33 +0900 Subject: Bump simplecov from 0.16.1 to 0.17.0 (#11260) Bumps [simplecov](https://github.com/colszowka/simplecov) from 0.16.1 to 0.17.0. - [Release notes](https://github.com/colszowka/simplecov/releases) - [Changelog](https://github.com/colszowka/simplecov/blob/master/CHANGELOG.md) - [Commits](https://github.com/colszowka/simplecov/compare/v0.16.1...v0.17.0) Signed-off-by: dependabot-preview[bot] --- Gemfile | 2 +- Gemfile.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'Gemfile.lock') diff --git a/Gemfile b/Gemfile index 4ad055dae..613515628 100644 --- a/Gemfile +++ b/Gemfile @@ -114,7 +114,7 @@ group :test do gem 'microformats', '~> 4.1' gem 'rails-controller-testing', '~> 1.0' gem 'rspec-sidekiq', '~> 3.0' - gem 'simplecov', '~> 0.16', require: false + gem 'simplecov', '~> 0.17', require: false gem 'webmock', '~> 3.6' gem 'parallel_tests', '~> 2.29' end diff --git a/Gemfile.lock b/Gemfile.lock index 8061d7ec4..34de2aaf9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -183,7 +183,7 @@ GEM devise (>= 4.0.0) rpam2 (~> 4.0) diff-lcs (1.3) - docile (1.3.0) + docile (1.3.2) domain_name (0.5.20180417) unf (>= 0.0.5, < 1.0.0) doorkeeper (5.1.0) @@ -289,7 +289,7 @@ GEM iso-639 (0.2.8) jaro_winkler (1.5.3) jmespath (1.4.0) - json (2.1.0) + json (2.2.0) json-ld (3.0.2) multi_json (~> 1.12) rdf (>= 2.2.8, < 4.0) @@ -565,7 +565,7 @@ GEM simple_form (4.1.0) actionpack (>= 5.0) activemodel (>= 5.0) - simplecov (0.16.1) + simplecov (0.17.0) docile (~> 1.1) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) @@ -747,7 +747,7 @@ DEPENDENCIES sidekiq-unique-jobs (~> 6.0) simple-navigation (~> 4.0) simple_form (~> 4.1) - simplecov (~> 0.16) + simplecov (~> 0.17) sprockets-rails (~> 3.2) stackprof stoplight (~> 2.1.3) -- cgit From f14776475d285bb7d2831a20192c1611c733f410 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2019 18:29:00 +0900 Subject: Bump faker from 1.9.3 to 1.9.6 (#11259) Bumps [faker](https://github.com/stympy/faker) from 1.9.3 to 1.9.6. - [Release notes](https://github.com/stympy/faker/releases) - [Changelog](https://github.com/stympy/faker/blob/master/CHANGELOG.md) - [Commits](https://github.com/stympy/faker/compare/v1.9.3...1.9.6) Signed-off-by: dependabot-preview[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Gemfile.lock') diff --git a/Gemfile.lock b/Gemfile.lock index 34de2aaf9..340bbcdd8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -208,7 +208,7 @@ GEM tzinfo excon (0.62.0) fabrication (2.20.2) - faker (1.9.3) + faker (1.9.6) i18n (>= 0.7) faraday (0.15.0) multipart-post (>= 1.2, < 3) -- cgit From 6ff67be0f6e79ec403e08c69717ee8c89451c70e Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 13 Jul 2019 16:45:50 +0200 Subject: Add a spam check (#11217) * Add a spam check * Use Nilsimsa to generate locality-sensitive hashes and compare using Levenshtein distance * Add more tests * Add exemption when the message is a reply to something that mentions the sender * Use Nilsimsa Compare Value instead of Levenshtein distance * Use MD5 for messages shorter than 10 characters * Add message to automated report, do not add non-public statuses to automated report, add trust level to accounts and make unsilencing raise the trust level to prevent repeated spam checks on that account * Expire spam check data after 3 months * Add support for local statuses, reduce expiration to 1 week, always create a report * Add content warnings to the spam check and exempt empty statuses * Change Nilsimsa threshold to 95 and make sure removed statuses are removed from the spam check * Add all matched statuses into automatic report --- Gemfile | 1 + Gemfile.lock | 8 + app/lib/activitypub/activity/create.rb | 13 ++ app/lib/spam_check.rb | 169 +++++++++++++++++++++ app/models/account.rb | 18 ++- app/services/remove_status_service.rb | 5 + config/locales/en.yml | 2 + .../20190701022101_add_trust_level_to_accounts.rb | 5 + db/schema.rb | 1 + spec/lib/spam_check_spec.rb | 160 +++++++++++++++++++ 10 files changed, 377 insertions(+), 5 deletions(-) create mode 100644 app/lib/spam_check.rb create mode 100644 db/migrate/20190701022101_add_trust_level_to_accounts.rb create mode 100644 spec/lib/spam_check_spec.rb (limited to 'Gemfile.lock') diff --git a/Gemfile b/Gemfile index 613515628..15334678b 100644 --- a/Gemfile +++ b/Gemfile @@ -58,6 +58,7 @@ gem 'idn-ruby', require: 'idn' gem 'kaminari', '~> 1.1' gem 'link_header', '~> 0.0' gem 'mime-types', '~> 3.2', require: 'mime/types/columnar' +gem 'nilsimsa', git: 'https://github.com/witgo/nilsimsa', ref: 'fd184883048b922b176939f851338d0a4971a532' gem 'nokogiri', '~> 1.10' gem 'nsa', '~> 0.2' gem 'oj', '~> 3.7' diff --git a/Gemfile.lock b/Gemfile.lock index 340bbcdd8..c3198b7d9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,6 +12,13 @@ GIT specs: http_parser.rb (0.6.1) +GIT + remote: https://github.com/witgo/nilsimsa + revision: fd184883048b922b176939f851338d0a4971a532 + ref: fd184883048b922b176939f851338d0a4971a532 + specs: + nilsimsa (1.1.2) + GEM remote: https://rubygems.org/ specs: @@ -704,6 +711,7 @@ DEPENDENCIES microformats (~> 4.1) mime-types (~> 3.2) net-ldap (~> 0.10) + nilsimsa! nokogiri (~> 1.10) nsa (~> 0.2) oj (~> 3.7) diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 5849c20d7..56c24680a 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -41,6 +41,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity resolve_thread(@status) fetch_replies(@status) + check_for_spam distribute(@status) forward_for_reply if @status.distributable? end @@ -406,6 +407,18 @@ class ActivityPub::Activity::Create < ActivityPub::Activity Account.local.where(username: local_usernames).exists? end + def check_for_spam + spam_check = SpamCheck.new(@status) + + return if spam_check.skip? + + if spam_check.spam? + spam_check.flag! + else + spam_check.remember! + end + end + def forward_for_reply return unless @json['signature'].present? && reply_to_local? ActivityPub::RawDistributionWorker.perform_async(Oj.dump(@json), replied_to_status.account_id, [@account.preferred_inbox_url]) diff --git a/app/lib/spam_check.rb b/app/lib/spam_check.rb new file mode 100644 index 000000000..923d48a02 --- /dev/null +++ b/app/lib/spam_check.rb @@ -0,0 +1,169 @@ +# frozen_string_literal: true + +class SpamCheck + include Redisable + include ActionView::Helpers::TextHelper + + NILSIMSA_COMPARE_THRESHOLD = 95 + NILSIMSA_MIN_SIZE = 10 + EXPIRE_SET_AFTER = 1.week.seconds + + def initialize(status) + @account = status.account + @status = status + end + + def skip? + already_flagged? || trusted? || no_unsolicited_mentions? || solicited_reply? + end + + def spam? + if insufficient_data? + false + elsif nilsimsa? + any_other_digest?('nilsimsa') { |_, other_digest| nilsimsa_compare_value(digest, other_digest) >= NILSIMSA_COMPARE_THRESHOLD } + else + any_other_digest?('md5') { |_, other_digest| other_digest == digest } + end + end + + def flag! + auto_silence_account! + auto_report_status! + end + + def remember! + # The scores in sorted sets don't actually have enough bits to hold an exact + # value of our snowflake IDs, so we use it only for its ordering property. To + # get the correct status ID back, we have to save it in the string value + + redis.zadd(redis_key, @status.id, digest_with_algorithm) + redis.zremrangebyrank(redis_key, '0', '-10') + redis.expire(redis_key, EXPIRE_SET_AFTER) + end + + def reset! + redis.del(redis_key) + end + + def hashable_text + return @hashable_text if defined?(@hashable_text) + + @hashable_text = @status.text + @hashable_text = remove_mentions(@hashable_text) + @hashable_text = strip_tags(@hashable_text) unless @status.local? + @hashable_text = normalize_unicode(@status.spoiler_text + ' ' + @hashable_text) + @hashable_text = remove_whitespace(@hashable_text) + end + + def insufficient_data? + hashable_text.blank? + end + + def digest + @digest ||= begin + if nilsimsa? + Nilsimsa.new(hashable_text).hexdigest + else + Digest::MD5.hexdigest(hashable_text) + end + end + end + + def digest_with_algorithm + if nilsimsa? + ['nilsimsa', digest, @status.id].join(':') + else + ['md5', digest, @status.id].join(':') + end + end + + private + + def remove_mentions(text) + return text.gsub(Account::MENTION_RE, '') if @status.local? + + Nokogiri::HTML.fragment(text).tap do |html| + mentions = @status.mentions.map { |mention| ActivityPub::TagManager.instance.url_for(mention.account) } + + html.traverse do |element| + element.unlink if element.name == 'a' && mentions.include?(element['href']) + end + end.to_s + end + + def normalize_unicode(text) + text.unicode_normalize(:nfkc).downcase + end + + def remove_whitespace(text) + text.gsub(/\s+/, ' ').strip + end + + def auto_silence_account! + @account.silence! + end + + def auto_report_status! + status_ids = Status.where(visibility: %i(public unlisted)).where(id: matching_status_ids).pluck(:id) + [@status.id] if @status.distributable? + ReportService.new.call(Account.representative, @account, status_ids: status_ids, comment: I18n.t('spam_check.spam_detected_and_silenced')) + end + + def already_flagged? + @account.silenced? + end + + def trusted? + @account.trust_level > Account::TRUST_LEVELS[:untrusted] + end + + def no_unsolicited_mentions? + @status.mentions.all? { |mention| mention.silent? || (!@account.local? && !mention.account.local?) || mention.account.following?(@account) } + end + + def solicited_reply? + !@status.thread.nil? && @status.thread.mentions.where(account: @account).exists? + end + + def nilsimsa_compare_value(first, second) + first = [first].pack('H*') + second = [second].pack('H*') + bits = 0 + + 0.upto(31) do |i| + bits += Nilsimsa::POPC[255 & (first[i].ord ^ second[i].ord)].ord + end + + 128 - bits # -128 <= Nilsimsa Compare Value <= 128 + end + + def nilsimsa? + hashable_text.size > NILSIMSA_MIN_SIZE + end + + def other_digests + redis.zrange(redis_key, 0, -1) + end + + def any_other_digest?(filter_algorithm) + other_digests.any? do |record| + algorithm, other_digest, status_id = record.split(':') + + next unless algorithm == filter_algorithm + + yield algorithm, other_digest, status_id + end + end + + def matching_status_ids + if nilsimsa? + other_digests.select { |record| record.start_with?('nilsimsa') && nilsimsa_compare_value(digest, record.split(':')[1]) >= NILSIMSA_COMPARE_THRESHOLD }.map { |record| record.split(':')[2] }.compact + else + other_digests.select { |record| record.start_with?('md5') && record.split(':')[1] == digest }.map { |record| record.split(':')[2] }.compact + end + end + + def redis_key + @redis_key ||= "spam_check:#{@account.id}" + end +end diff --git a/app/models/account.rb b/app/models/account.rb index d6772eb98..a22b7fd7c 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -45,6 +45,7 @@ # also_known_as :string is an Array # silenced_at :datetime # suspended_at :datetime +# trust_level :integer # class Account < ApplicationRecord @@ -62,6 +63,11 @@ class Account < ApplicationRecord include AccountCounters include DomainNormalizable + TRUST_LEVELS = { + untrusted: 0, + trusted: 1, + }.freeze + enum protocol: [:ostatus, :activitypub] validates :username, presence: true @@ -163,6 +169,10 @@ class Account < ApplicationRecord last_webfingered_at.nil? || last_webfingered_at <= 1.day.ago end + def trust_level + self[:trust_level] || 0 + end + def refresh! ResolveAccountService.new.call(acct) unless local? end @@ -171,21 +181,19 @@ class Account < ApplicationRecord silenced_at.present? end - def silence!(date = nil) - date ||= Time.now.utc + def silence!(date = Time.now.utc) update!(silenced_at: date) end def unsilence! - update!(silenced_at: nil) + update!(silenced_at: nil, trust_level: trust_level == TRUST_LEVELS[:untrusted] ? TRUST_LEVELS[:trusted] : trust_level) end def suspended? suspended_at.present? end - def suspend!(date = nil) - date ||= Time.now.utc + def suspend!(date = Time.now.utc) transaction do user&.disable! if local? update!(suspended_at: date) diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb index 6311971ff..a69fce8b8 100644 --- a/app/services/remove_status_service.rb +++ b/app/services/remove_status_service.rb @@ -23,6 +23,7 @@ class RemoveStatusService < BaseService remove_from_hashtags remove_from_public remove_from_media if status.media_attachments.any? + remove_from_spam_check @status.destroy! else @@ -142,6 +143,10 @@ class RemoveStatusService < BaseService redis.publish('timeline:public:local:media', @payload) if @status.local? end + def remove_from_spam_check + redis.zremrangebyscore("spam_check:#{@status.account_id}", @status.id, @status.id) + end + def lock_options { redis: Redis.current, key: "distribute:#{@status.id}" } end diff --git a/config/locales/en.yml b/config/locales/en.yml index 00b7d1dbe..89251ad40 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -875,6 +875,8 @@ en: profile: Profile relationships: Follows and followers two_factor_authentication: Two-factor Auth + spam_check: + spam_detected_and_silenced: This is an automated report. Spam has been detected and the sender has been silenced automatically. If this is a mistake, please unsilence the account. statuses: attached: description: 'Attached: %{attached}' diff --git a/db/migrate/20190701022101_add_trust_level_to_accounts.rb b/db/migrate/20190701022101_add_trust_level_to_accounts.rb new file mode 100644 index 000000000..917486d2e --- /dev/null +++ b/db/migrate/20190701022101_add_trust_level_to_accounts.rb @@ -0,0 +1,5 @@ +class AddTrustLevelToAccounts < ActiveRecord::Migration[5.2] + def change + add_column :accounts, :trust_level, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 2e38fb1f2..c7b6b9be6 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -148,6 +148,7 @@ ActiveRecord::Schema.define(version: 2019_07_06_233204) do t.string "also_known_as", array: true t.datetime "silenced_at" t.datetime "suspended_at" + t.integer "trust_level" t.index "(((setweight(to_tsvector('simple'::regconfig, (display_name)::text), 'A'::\"char\") || setweight(to_tsvector('simple'::regconfig, (username)::text), 'B'::\"char\")) || setweight(to_tsvector('simple'::regconfig, (COALESCE(domain, ''::character varying))::text), 'C'::\"char\")))", name: "search_index", using: :gin t.index "lower((username)::text), lower((domain)::text)", name: "index_accounts_on_username_and_domain_lower", unique: true t.index ["moved_to_account_id"], name: "index_accounts_on_moved_to_account_id" diff --git a/spec/lib/spam_check_spec.rb b/spec/lib/spam_check_spec.rb new file mode 100644 index 000000000..c722dc642 --- /dev/null +++ b/spec/lib/spam_check_spec.rb @@ -0,0 +1,160 @@ +require 'rails_helper' + +RSpec.describe SpamCheck do + let!(:sender) { Fabricate(:account) } + let!(:alice) { Fabricate(:account, username: 'alice') } + let!(:bob) { Fabricate(:account, username: 'bob') } + + def status_with_html(text, options = {}) + status = PostStatusService.new.call(sender, { text: text }.merge(options)) + status.update_columns(text: Formatter.instance.format(status), local: false) + status + end + + describe '#hashable_text' do + it 'removes mentions from HTML for remote statuses' do + status = status_with_html('@alice Hello') + expect(described_class.new(status).hashable_text).to eq 'hello' + end + + it 'removes mentions from text for local statuses' do + status = PostStatusService.new.call(alice, text: "Hey @#{sender.username}, how are you?") + expect(described_class.new(status).hashable_text).to eq 'hey , how are you?' + end + end + + describe '#insufficient_data?' do + it 'returns true when there is no text' do + status = status_with_html('@alice') + expect(described_class.new(status).insufficient_data?).to be true + end + + it 'returns false when there is text' do + status = status_with_html('@alice h') + expect(described_class.new(status).insufficient_data?).to be false + end + end + + describe '#digest' do + it 'returns a string' do + status = status_with_html('@alice Hello world') + expect(described_class.new(status).digest).to be_a String + end + end + + describe '#spam?' do + it 'returns false for a unique status' do + status = status_with_html('@alice Hello') + expect(described_class.new(status).spam?).to be false + end + + it 'returns false for different statuses to the same recipient' do + status1 = status_with_html('@alice Hello') + described_class.new(status1).remember! + status2 = status_with_html('@alice Are you available to talk?') + expect(described_class.new(status2).spam?).to be false + end + + it 'returns false for statuses with different content warnings' do + status1 = status_with_html('@alice Are you available to talk?') + described_class.new(status1).remember! + status2 = status_with_html('@alice Are you available to talk?', spoiler_text: 'This is a completely different matter than what I was talking about previously, I swear!') + expect(described_class.new(status2).spam?).to be false + end + + it 'returns false for different statuses to different recipients' do + status1 = status_with_html('@alice How is it going?') + described_class.new(status1).remember! + status2 = status_with_html('@bob Are you okay?') + expect(described_class.new(status2).spam?).to be false + end + + it 'returns false for very short different statuses to different recipients' do + status1 = status_with_html('@alice 🙄') + described_class.new(status1).remember! + status2 = status_with_html('@bob Huh?') + expect(described_class.new(status2).spam?).to be false + end + + it 'returns false for statuses with no text' do + status1 = status_with_html('@alice') + described_class.new(status1).remember! + status2 = status_with_html('@bob') + expect(described_class.new(status2).spam?).to be false + end + + it 'returns true for duplicate statuses to the same recipient' do + status1 = status_with_html('@alice Hello') + described_class.new(status1).remember! + status2 = status_with_html('@alice Hello') + expect(described_class.new(status2).spam?).to be true + end + + it 'returns true for duplicate statuses to different recipients' do + status1 = status_with_html('@alice Hello') + described_class.new(status1).remember! + status2 = status_with_html('@bob Hello') + expect(described_class.new(status2).spam?).to be true + end + + it 'returns true for nearly identical statuses with random numbers' do + source_text = 'Sodium, atomic number 11, was first isolated by Humphry Davy in 1807. A chemical component of salt, he named it Na in honor of the saltiest region on earth, North America.' + status1 = status_with_html('@alice ' + source_text + ' 1234') + described_class.new(status1).remember! + status2 = status_with_html('@bob ' + source_text + ' 9568') + expect(described_class.new(status2).spam?).to be true + end + end + + describe '#skip?' do + it 'returns true when the sender is already silenced' do + status = status_with_html('@alice Hello') + sender.silence! + expect(described_class.new(status).skip?).to be true + end + + it 'returns true when the mentioned person follows the sender' do + status = status_with_html('@alice Hello') + alice.follow!(sender) + expect(described_class.new(status).skip?).to be true + end + + it 'returns false when even one mentioned person doesn\'t follow the sender' do + status = status_with_html('@alice @bob Hello') + alice.follow!(sender) + expect(described_class.new(status).skip?).to be false + end + + it 'returns true when the sender is replying to a status that mentions the sender' do + parent = PostStatusService.new.call(alice, text: "Hey @#{sender.username}, how are you?") + status = status_with_html('@alice @bob Hello', thread: parent) + expect(described_class.new(status).skip?).to be true + end + end + + describe '#remember!' do + pending + end + + describe '#flag!' do + let!(:status1) { status_with_html('@alice General Kenobi you are a bold one') } + let!(:status2) { status_with_html('@alice @bob General Kenobi, you are a bold one') } + + before do + described_class.new(status1).remember! + described_class.new(status2).flag! + end + + it 'silences the account' do + expect(sender.silenced?).to be true + end + + it 'creates a report about the account' do + expect(sender.targeted_reports.unresolved.count).to eq 1 + end + + it 'attaches both matching statuses to the report' do + expect(sender.targeted_reports.first.status_ids).to include(status1.id, status2.id) + end + end +end -- cgit From 6af0c955e1ab15f768d4310607177f9f628eb0ad Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 13 Jul 2019 23:52:08 +0900 Subject: Bump rubocop-rails from 2.0.1 to 2.2.0 (#11257) Bumps [rubocop-rails](https://github.com/rubocop-hq/rubocop-rails) from 2.0.1 to 2.2.0. - [Release notes](https://github.com/rubocop-hq/rubocop-rails/releases) - [Changelog](https://github.com/rubocop-hq/rubocop-rails/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop-hq/rubocop-rails/compare/v2.0.1...v2.2.0) Signed-off-by: dependabot-preview[bot] --- Gemfile | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Gemfile.lock') diff --git a/Gemfile b/Gemfile index 15334678b..42208085c 100644 --- a/Gemfile +++ b/Gemfile @@ -130,7 +130,7 @@ group :development do gem 'letter_opener_web', '~> 1.3' gem 'memory_profiler' gem 'rubocop', '~> 0.72', require: false - gem 'rubocop-rails', '~> 2.0', require: false + gem 'rubocop-rails', '~> 2.2', require: false gem 'brakeman', '~> 4.5', require: false gem 'bundler-audit', '~> 0.6', require: false diff --git a/Gemfile.lock b/Gemfile.lock index c3198b7d9..4036a05c1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -538,9 +538,9 @@ GEM rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 1.7) - rubocop-rails (2.0.1) + rubocop-rails (2.2.0) rack (>= 1.1) - rubocop (>= 0.70.0) + rubocop (>= 0.72.0) ruby-progressbar (1.10.1) ruby-saml (1.9.0) nokogiri (>= 1.5.10) @@ -747,7 +747,7 @@ DEPENDENCIES rspec-rails (~> 3.8) rspec-sidekiq (~> 3.0) rubocop (~> 0.72) - rubocop-rails (~> 2.0) + rubocop-rails (~> 2.2) sanitize (~> 5.0) sidekiq (~> 5.2) sidekiq-bulk (~> 0.2.0) -- cgit From 7cc98eba2825072b2d83c1c7fffef2eaaf2473bb Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2019 01:06:14 +0200 Subject: Bump puma from 3.12.1 to 4.0.1 (#11306) Bumps [puma](https://github.com/puma/puma) from 3.12.1 to 4.0.1. - [Release notes](https://github.com/puma/puma/releases) - [Changelog](https://github.com/puma/puma/blob/master/History.md) - [Commits](https://github.com/puma/puma/compare/v3.12.1...v4.0.1) Signed-off-by: dependabot-preview[bot] --- Gemfile | 2 +- Gemfile.lock | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'Gemfile.lock') diff --git a/Gemfile b/Gemfile index 42208085c..d82673324 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,7 @@ ruby '>= 2.4.0', '< 2.7.0' gem 'pkg-config', '~> 1.3' -gem 'puma', '~> 3.12' +gem 'puma', '~> 4.0' gem 'rails', '~> 5.2.3' gem 'thor', '~> 0.20' diff --git a/Gemfile.lock b/Gemfile.lock index 4036a05c1..6ff6dac73 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -428,7 +428,8 @@ GEM pry-rails (0.3.9) pry (>= 0.10.4) public_suffix (3.1.1) - puma (3.12.1) + puma (4.0.1) + nio4r (~> 2.0) pundit (2.0.1) activesupport (>= 3.0.0) raabro (1.1.6) @@ -731,7 +732,7 @@ DEPENDENCIES private_address_check (~> 0.5) pry-byebug (~> 3.7) pry-rails (~> 0.3) - puma (~> 3.12) + puma (~> 4.0) pundit (~> 2.0) rack-attack (~> 6.0) rack-cors (~> 1.0) -- cgit From cbd7748961e2fcbb10d3fe1a549fba5c323fe395 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2019 01:06:39 +0200 Subject: Bump rubocop-rails from 2.2.0 to 2.2.1 (#11308) Bumps [rubocop-rails](https://github.com/rubocop-hq/rubocop-rails) from 2.2.0 to 2.2.1. - [Release notes](https://github.com/rubocop-hq/rubocop-rails/releases) - [Changelog](https://github.com/rubocop-hq/rubocop-rails/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop-hq/rubocop-rails/compare/v2.2.0...v2.2.1) Signed-off-by: dependabot-preview[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Gemfile.lock') diff --git a/Gemfile.lock b/Gemfile.lock index 6ff6dac73..e013c4ec0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -539,7 +539,7 @@ GEM rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 1.7) - rubocop-rails (2.2.0) + rubocop-rails (2.2.1) rack (>= 1.1) rubocop (>= 0.72.0) ruby-progressbar (1.10.1) -- cgit From 3a6fe657ba30c56677c271432338a7002191d772 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2019 01:07:02 +0200 Subject: Bump rack-attack from 6.0.0 to 6.1.0 (#11313) Bumps [rack-attack](https://github.com/kickstarter/rack-attack) from 6.0.0 to 6.1.0. - [Release notes](https://github.com/kickstarter/rack-attack/releases) - [Changelog](https://github.com/kickstarter/rack-attack/blob/master/CHANGELOG.md) - [Commits](https://github.com/kickstarter/rack-attack/compare/v6.0.0...v6.1.0) Signed-off-by: dependabot-preview[bot] --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'Gemfile.lock') diff --git a/Gemfile b/Gemfile index d82673324..d27e3560c 100644 --- a/Gemfile +++ b/Gemfile @@ -67,7 +67,7 @@ gem 'ox', '~> 2.11' gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c' gem 'pundit', '~> 2.0' gem 'premailer-rails' -gem 'rack-attack', '~> 6.0' +gem 'rack-attack', '~> 6.1' gem 'rack-cors', '~> 1.0', require: 'rack/cors' gem 'rails-i18n', '~> 5.1' gem 'rails-settings-cached', '~> 0.6' diff --git a/Gemfile.lock b/Gemfile.lock index e013c4ec0..7be1679b3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -434,7 +434,7 @@ GEM activesupport (>= 3.0.0) raabro (1.1.6) rack (2.0.7) - rack-attack (6.0.0) + rack-attack (6.1.0) rack (>= 1.0, < 3) rack-cors (1.0.3) rack-protection (2.0.5) @@ -734,7 +734,7 @@ DEPENDENCIES pry-rails (~> 0.3) puma (~> 4.0) pundit (~> 2.0) - rack-attack (~> 6.0) + rack-attack (~> 6.1) rack-cors (~> 1.0) rails (~> 5.2.3) rails-controller-testing (~> 1.0) -- cgit From 6867a0beb5e1d48eba6d8962f5b0a0e17ba09ba8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 20 Jul 2019 01:08:02 +0900 Subject: Bump strong_migrations from 0.4.0 to 0.4.1 (#11307) Bumps [strong_migrations](https://github.com/ankane/strong_migrations) from 0.4.0 to 0.4.1. - [Release notes](https://github.com/ankane/strong_migrations/releases) - [Changelog](https://github.com/ankane/strong_migrations/blob/master/CHANGELOG.md) - [Commits](https://github.com/ankane/strong_migrations/compare/v0.4.0...v0.4.1) Signed-off-by: dependabot-preview[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Gemfile.lock') diff --git a/Gemfile.lock b/Gemfile.lock index 7be1679b3..340537ac9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -593,7 +593,7 @@ GEM stoplight (2.1.3) streamio-ffmpeg (3.0.2) multi_json (~> 1.8) - strong_migrations (0.4.0) + strong_migrations (0.4.1) activerecord (>= 5) temple (0.8.1) terminal-table (1.8.0) -- cgit