Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/services/spam')
-rw-r--r--app/services/spam/akismet_service.rb4
-rw-r--r--app/services/spam/spam_action_service.rb21
-rw-r--r--app/services/spam/spam_constants.rb22
-rw-r--r--app/services/spam/spam_verdict_service.rb76
4 files changed, 109 insertions, 14 deletions
diff --git a/app/services/spam/akismet_service.rb b/app/services/spam/akismet_service.rb
index ab35fb8700f..e11a1dbdd96 100644
--- a/app/services/spam/akismet_service.rb
+++ b/app/services/spam/akismet_service.rb
@@ -27,7 +27,7 @@ module Spam
is_spam, is_blatant = akismet_client.check(options[:ip_address], options[:user_agent], params)
is_spam || is_blatant
rescue => e
- Rails.logger.error("Unable to connect to Akismet: #{e}, skipping check") # rubocop:disable Gitlab/RailsLogger
+ Gitlab::AppLogger.error("Unable to connect to Akismet: #{e}, skipping check")
false
end
end
@@ -67,7 +67,7 @@ module Spam
akismet_client.public_send(type, options[:ip_address], options[:user_agent], params) # rubocop:disable GitlabSecurity/PublicSend
true
rescue => e
- Rails.logger.error("Unable to connect to Akismet: #{e}, skipping!") # rubocop:disable Gitlab/RailsLogger
+ Gitlab::AppLogger.error("Unable to connect to Akismet: #{e}, skipping!")
false
end
end
diff --git a/app/services/spam/spam_action_service.rb b/app/services/spam/spam_action_service.rb
index f0a4aff4443..b745b67f566 100644
--- a/app/services/spam/spam_action_service.rb
+++ b/app/services/spam/spam_action_service.rb
@@ -7,9 +7,11 @@ module Spam
attr_accessor :target, :request, :options
attr_reader :spam_log
- def initialize(spammable:, request:)
+ def initialize(spammable:, request:, user:, context: {})
@target = spammable
@request = request
+ @user = user
+ @context = context
@options = {}
if @request
@@ -22,7 +24,7 @@ module Spam
end
end
- def execute(api: false, recaptcha_verified:, spam_log_id:, user:)
+ def execute(api: false, recaptcha_verified:, spam_log_id:)
if recaptcha_verified
# If it's a request which is already verified through reCAPTCHA,
# update the spam log accordingly.
@@ -40,6 +42,8 @@ module Spam
private
+ attr_reader :user, :context
+
def allowlisted?(user)
user.respond_to?(:gitlab_employee) && user.gitlab_employee?
end
@@ -49,7 +53,8 @@ module Spam
# ask the SpamVerdictService what to do with the target.
spam_verdict_service.execute.tap do |result|
case result
- when REQUIRE_RECAPTCHA
+ when CONDITIONAL_ALLOW
+ # at the moment, this means "ask for reCAPTCHA"
create_spam_log(api)
break if target.allow_possible_spam?
@@ -74,7 +79,7 @@ module Spam
description: target.spam_description,
source_ip: options[:ip_address],
user_agent: options[:user_agent],
- noteable_type: target.class.to_s,
+ noteable_type: notable_type,
via_api: api
}
)
@@ -84,8 +89,14 @@ module Spam
def spam_verdict_service
SpamVerdictService.new(target: target,
+ user: user,
request: @request,
- options: options)
+ options: options,
+ context: context.merge(target_type: notable_type))
+ end
+
+ def notable_type
+ @notable_type ||= target.class.to_s
end
end
end
diff --git a/app/services/spam/spam_constants.rb b/app/services/spam/spam_constants.rb
index 085bac684c4..2a16cfae78b 100644
--- a/app/services/spam/spam_constants.rb
+++ b/app/services/spam/spam_constants.rb
@@ -2,8 +2,24 @@
module Spam
module SpamConstants
- REQUIRE_RECAPTCHA = :recaptcha
- DISALLOW = :disallow
- ALLOW = :allow
+ CONDITIONAL_ALLOW = "conditional_allow"
+ DISALLOW = "disallow"
+ ALLOW = "allow"
+ BLOCK_USER = "block"
+
+ SUPPORTED_VERDICTS = {
+ BLOCK_USER => {
+ priority: 1
+ },
+ DISALLOW => {
+ priority: 2
+ },
+ CONDITIONAL_ALLOW => {
+ priority: 3
+ },
+ ALLOW => {
+ priority: 4
+ }
+ }.freeze
end
end
diff --git a/app/services/spam/spam_verdict_service.rb b/app/services/spam/spam_verdict_service.rb
index 2b4d5f4a984..68f1135ae28 100644
--- a/app/services/spam/spam_verdict_service.rb
+++ b/app/services/spam/spam_verdict_service.rb
@@ -5,22 +5,90 @@ module Spam
include AkismetMethods
include SpamConstants
- def initialize(target:, request:, options:)
+ def initialize(user:, target:, request:, options:, context: {})
@target = target
@request = request
+ @user = user
@options = options
+ @verdict_params = assemble_verdict_params(context)
end
def execute
+ external_spam_check_result = spam_verdict
+ akismet_result = akismet_verdict
+
+ # filter out anything we don't recognise, including nils.
+ valid_results = [external_spam_check_result, akismet_result].compact.select { |r| SUPPORTED_VERDICTS.key?(r) }
+ # Treat nils - such as service unavailable - as ALLOW
+ return ALLOW unless valid_results.any?
+
+ # Favour the most restrictive result.
+ valid_results.min_by { |v| SUPPORTED_VERDICTS[v][:priority] }
+ end
+
+ private
+
+ attr_reader :user, :target, :request, :options, :verdict_params
+
+ def akismet_verdict
if akismet.spam?
- Gitlab::Recaptcha.enabled? ? REQUIRE_RECAPTCHA : DISALLOW
+ Gitlab::Recaptcha.enabled? ? CONDITIONAL_ALLOW : DISALLOW
else
ALLOW
end
end
- private
+ def spam_verdict
+ return unless Gitlab::CurrentSettings.spam_check_endpoint_enabled
+ return if endpoint_url.blank?
+
+ begin
+ result = Gitlab::HTTP.post(endpoint_url, body: verdict_params.to_json, headers: { 'Content-Type' => 'application/json' })
+ return unless result
+
+ json_result = Gitlab::Json.parse(result).with_indifferent_access
+ # @TODO metrics/logging
+ # Expecting:
+ # error: (string or nil)
+ # result: (string or nil)
+ verdict = json_result[:verdict]
+ return unless SUPPORTED_VERDICTS.include?(verdict)
- attr_reader :target, :request, :options
+ # @TODO log if json_result[:error]
+
+ json_result[:verdict]
+ rescue *Gitlab::HTTP::HTTP_ERRORS => e
+ # @TODO: log error via try_post https://gitlab.com/gitlab-org/gitlab/-/issues/219223
+ Gitlab::ErrorTracking.log_exception(e)
+ return
+ rescue
+ # @TODO log
+ ALLOW
+ end
+ end
+
+ def assemble_verdict_params(context)
+ return {} unless endpoint_url.present?
+
+ project = target.try(:project)
+
+ context.merge({
+ target: {
+ title: target.spam_title,
+ description: target.spam_description,
+ type: target.class.to_s
+ },
+ user: {
+ created_at: user.created_at,
+ email: user.email,
+ username: user.username
+ },
+ user_in_project: user.authorized_project?(project)
+ })
+ end
+
+ def endpoint_url
+ @endpoint_url ||= Gitlab::CurrentSettings.current_application_settings.spam_check_endpoint_url
+ end
end
end