From 0d0da22a0f4047784ca2124f1555645bfab826a0 Mon Sep 17 00:00:00 2001 From: Ian Anderson <ianderson@gitlab.com> Date: Wed, 12 Jul 2023 08:08:31 +0000 Subject: [PATCH] Check non-public issues for spam --- app/models/concerns/spammable.rb | 6 +-- app/models/issue.rb | 20 ++------ app/services/spam/spam_verdict_service.rb | 2 +- spec/models/concerns/spammable_spec.rb | 26 +++------- spec/models/issue_spec.rb | 48 ++++++++++++++++--- .../spam/spam_verdict_service_spec.rb | 19 +++++++- 6 files changed, 76 insertions(+), 45 deletions(-) diff --git a/app/models/concerns/spammable.rb b/app/models/concerns/spammable.rb index 6550c5a94a0a..5986f8f5b5fb 100644 --- a/app/models/concerns/spammable.rb +++ b/app/models/concerns/spammable.rb @@ -138,7 +138,7 @@ def spammable_text result.reject(&:blank?).join("\n") end - # Override in Spammable if further checks are necessary + # Override in included class if further checks are necessary def check_for_spam?(*) spammable_attribute_changed? end @@ -153,8 +153,8 @@ def check_for_spam(user:, action:, extra_features: {}) end end - # Override in Spammable if differs - def allow_possible_spam? + # Override in included class if you want to allow possible spam under specific circumstances + def allow_possible_spam?(*) Gitlab::CurrentSettings.allow_possible_spam end end diff --git a/app/models/issue.rb b/app/models/issue.rb index 453c451cebf1..f94ebf30de3e 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -584,16 +584,12 @@ def visible_to_user?(user = nil) user, project.external_authorization_classification_label) end - def check_for_spam?(user:) - # content created via support bots is always checked for spam, EVEN if - # the issue is not publicly visible and/or confidential - return true if user.support_bot? && spammable_attribute_changed? + # Always enforce spam check for support bot but allow for other users when issue is not publicly visible + def allow_possible_spam?(user) + return true if Gitlab::CurrentSettings.allow_possible_spam + return false if user.support_bot? - # Only check for spam on issues which are publicly visible (and thus indexed in search engines) - return false unless publicly_visible? - - # Only check for spam if certain attributes have changed - spammable_attribute_changed? + !publicly_visible? end def supports_recaptcha? @@ -826,12 +822,6 @@ def persist_pg_full_text_search_vector(search_vector) Issues::SearchData.upsert({ project_id: project_id, issue_id: id, search_vector: search_vector }, unique_by: %i(project_id issue_id)) end - def spammable_attribute_changed? - # NOTE: We need to check them for spam when issues are made non-confidential, because spam - # may have been added while they were confidential and thus not being checked for spam. - super || confidential_changed?(from: true, to: false) - end - def ensure_metrics! Issue::Metrics.record!(self) end diff --git a/app/services/spam/spam_verdict_service.rb b/app/services/spam/spam_verdict_service.rb index 160ebb81b397..e0a6d58b9040 100644 --- a/app/services/spam/spam_verdict_service.rb +++ b/app/services/spam/spam_verdict_service.rb @@ -85,7 +85,7 @@ def override_via_allow_possible_spam?(verdict:) # than the override verdict's priority value), then we don't need to override it. return false if SUPPORTED_VERDICTS[verdict][:priority] > SUPPORTED_VERDICTS[OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM][:priority] - target.allow_possible_spam? || user.allow_possible_spam? + target.allow_possible_spam?(user) || user.allow_possible_spam? end def spamcheck_client diff --git a/spec/models/concerns/spammable_spec.rb b/spec/models/concerns/spammable_spec.rb index 8a2fa6675e5d..7ef0473aea8b 100644 --- a/spec/models/concerns/spammable_spec.rb +++ b/spec/models/concerns/spammable_spec.rb @@ -71,8 +71,8 @@ expect(issue.check_for_spam?(user: issue.author)).to eq(true) end - it 'returns false for other visibility levels' do - expect(issue.check_for_spam?(user: issue.author)).to eq(false) + it 'returns true for other visibility levels' do + expect(issue.check_for_spam?(user: issue.author)).to eq(true) end end @@ -82,7 +82,7 @@ end context 'when the model is spam' do - where(model: [:issue, :merge_request, :snippet, :spammable_model]) + where(model: [:issue, :merge_request, :note, :snippet, :spammable_model]) with_them do subject do @@ -94,21 +94,7 @@ it 'has an error related to spam on the model' do expect(subject.errors.messages[:base]) - .to match_array /Your #{subject.class.model_name.human.downcase} has been recognized as spam./ - end - end - - context 'when the spammable model is a Note' do - subject do - Note.new.tap do |m| - m.spam! - m.invalidate_if_spam - end - end - - it 'has an error related to spam on the model' do - expect(subject.errors.messages[:base]) - .to match_array /Your comment has been recognized as spam./ + .to match_array /Your #{subject.spammable_entity_type} has been recognized as spam./ end end end @@ -293,7 +279,9 @@ def invalidate_if_spam(is_spam: false, needs_recaptcha: false) end describe '#allow_possible_spam?' do - subject { issue.allow_possible_spam? } + let_it_be(:user) { build(:user) } + + subject { spammable_model.allow_possible_spam?(user) } context 'when the `allow_possible_spam` application setting is turned off' do it { is_expected.to eq(false) } diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index ecf9e4480eb1..7cf3981969c6 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -1559,6 +1559,42 @@ end end + describe '#allow_possible_spam?' do + let_it_be(:issue) { build(:issue) } + + subject { issue.allow_possible_spam?(issue.author) } + + context 'when the `allow_possible_spam` application setting is turned off' do + context 'when the issue is private' do + it { is_expected.to eq(true) } + + context 'when the user is the support bot' do + before do + allow(issue.author).to receive(:support_bot?).and_return(true) + end + + it { is_expected.to eq(false) } + end + end + + context 'when the issue is public' do + before do + allow(issue).to receive(:publicly_visible?).and_return(true) + end + + it { is_expected.to eq(false) } + end + end + + context 'when the `allow_possible_spam` application setting is turned on' do + before do + stub_application_setting(allow_possible_spam: true) + end + + it { is_expected.to eq(true) } + end + end + describe '#check_for_spam?' do let_it_be(:support_bot) { ::User.support_bot } @@ -1568,24 +1604,24 @@ false | Gitlab::VisibilityLevel::PUBLIC | false | { description: 'new' } | true false | Gitlab::VisibilityLevel::PUBLIC | false | { title: 'new' } | true # confidential to non-confidential - false | Gitlab::VisibilityLevel::PUBLIC | true | { confidential: false } | true + false | Gitlab::VisibilityLevel::PUBLIC | true | { confidential: false } | false # non-confidential to confidential false | Gitlab::VisibilityLevel::PUBLIC | false | { confidential: true } | false # spammable attributes changing on confidential - false | Gitlab::VisibilityLevel::PUBLIC | true | { description: 'new' } | false + false | Gitlab::VisibilityLevel::PUBLIC | true | { description: 'new' } | true # spammable attributes changing while changing to confidential - false | Gitlab::VisibilityLevel::PUBLIC | false | { title: 'new', confidential: true } | false + false | Gitlab::VisibilityLevel::PUBLIC | false | { title: 'new', confidential: true } | true # spammable attribute not changing false | Gitlab::VisibilityLevel::PUBLIC | false | { description: 'original description' } | false # non-spammable attribute changing false | Gitlab::VisibilityLevel::PUBLIC | false | { weight: 3 } | false # spammable attributes changing on non-public - false | Gitlab::VisibilityLevel::INTERNAL | false | { description: 'new' } | false - false | Gitlab::VisibilityLevel::PRIVATE | false | { description: 'new' } | false + false | Gitlab::VisibilityLevel::INTERNAL | false | { description: 'new' } | true + false | Gitlab::VisibilityLevel::PRIVATE | false | { description: 'new' } | true ### support-bot cases # confidential to non-confidential - true | Gitlab::VisibilityLevel::PUBLIC | true | { confidential: false } | true + true | Gitlab::VisibilityLevel::PUBLIC | true | { confidential: false } | false # non-confidential to confidential true | Gitlab::VisibilityLevel::PUBLIC | false | { confidential: true } | false # spammable attributes changing on confidential diff --git a/spec/services/spam/spam_verdict_service_spec.rb b/spec/services/spam/spam_verdict_service_spec.rb index bf6273048071..70f43d82ead0 100644 --- a/spec/services/spam/spam_verdict_service_spec.rb +++ b/spec/services/spam/spam_verdict_service_spec.rb @@ -403,7 +403,24 @@ describe 'issue' do let(:target) { issue } - it_behaves_like 'execute spam verdict service' + context 'when issue is publicly visible' do + before do + allow(issue).to receive(:publicly_visible?).and_return(true) + end + + it_behaves_like 'execute spam verdict service' + end + + context 'when issue is not publicly visible' do + before do + allow(issue).to receive(:publicly_visible?).and_return(false) + allow(service).to receive(:get_spamcheck_verdict).and_return(BLOCK_USER) + end + + it 'overrides and renders the override verdict' do + expect(service.execute).to eq OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM + end + end end describe 'snippet' do -- GitLab