mirror of
https://gitlab.com/gitlab-org/gitlab-foss.git
synced 2025-07-25 16:03:48 +00:00
105 lines
2.8 KiB
Ruby
105 lines
2.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Gitlab
|
|
module Search
|
|
class AbuseDetection
|
|
include ActiveModel::Validations
|
|
include AbuseValidators
|
|
|
|
MAX_PIPE_SYNTAX_FILTERS = 5
|
|
ABUSIVE_TERM_SIZE = 100
|
|
ALLOWED_CHARS_REGEX = %r{\A[[:alnum:]_\-\+\/\.!]+\z}
|
|
|
|
ALLOWED_SCOPES = %w[
|
|
blobs
|
|
code
|
|
commits
|
|
epics
|
|
issues
|
|
merge_requests
|
|
milestones
|
|
notes
|
|
projects
|
|
snippet_titles
|
|
users
|
|
wiki_blobs
|
|
].freeze
|
|
|
|
READABLE_PARAMS = %i[
|
|
group_id
|
|
project_id
|
|
project_ref
|
|
query_string
|
|
repository_ref
|
|
scope
|
|
].freeze
|
|
|
|
STOP_WORDS = %w[
|
|
a an and are as at be but by for if in into is it no not of on or such that the their then there these they this
|
|
to was will with
|
|
].freeze
|
|
|
|
validates :project_id, :group_id,
|
|
numericality: { only_integer: true, message: "abusive ID detected" }, allow_blank: true
|
|
|
|
validates :scope, inclusion: { in: ALLOWED_SCOPES, message: 'abusive scope detected' }, allow_blank: true
|
|
|
|
validates :repository_ref, :project_ref,
|
|
format: { with: ALLOWED_CHARS_REGEX, message: "abusive characters detected" }, allow_blank: true
|
|
|
|
validates :query_string,
|
|
exclusion: { in: STOP_WORDS, message: 'stopword only abusive search detected' }, allow_blank: true
|
|
|
|
validates :query_string,
|
|
length: { minimum: Params::MIN_TERM_LENGTH, message: 'abusive tiny search detected' },
|
|
unless: :skip_tiny_search_validation?, allow_blank: true
|
|
|
|
validates :query_string,
|
|
no_abusive_term_length: { maximum: ABUSIVE_TERM_SIZE, maximum_for_url: ABUSIVE_TERM_SIZE * 2 }
|
|
|
|
validates :query_string, :repository_ref, :project_ref, no_abusive_coercion_from_string: true
|
|
|
|
attr_reader(*READABLE_PARAMS, :raw_params)
|
|
|
|
def initialize(params)
|
|
@raw_params = {}
|
|
READABLE_PARAMS.each do |p|
|
|
instance_variable_set(:"@#{p}", params[p])
|
|
@raw_params[p] = params[p]
|
|
end
|
|
end
|
|
|
|
def abusive_pipes?
|
|
pipes = query_string.to_s.split('|')
|
|
errors.add(:query_string, 'too many pipe syntax filters') if pipes.length > MAX_PIPE_SYNTAX_FILTERS
|
|
pipes.each do |q|
|
|
self.class.new(raw_params.merge(query_string: q)).tap do |p|
|
|
p.validate
|
|
|
|
p.errors.messages_for(:query_string).each do |msg|
|
|
next if errors.added?(:query_string, msg)
|
|
|
|
errors.add(:query_string, msg)
|
|
end
|
|
end
|
|
end
|
|
errors.any?
|
|
end
|
|
|
|
private
|
|
|
|
def skip_tiny_search_validation?
|
|
wildcard_search? || stop_word_search?
|
|
end
|
|
|
|
def wildcard_search?
|
|
query_string == '*'
|
|
end
|
|
|
|
def stop_word_search?
|
|
STOP_WORDS.include? query_string
|
|
end
|
|
end
|
|
end
|
|
end
|