e5e1c907c0
This MR adds new application setting to network section `allow_local_requests_from_system_hooks`. Prior to this change system hooks were allowed to do local network requests by default and we are adding an ability for admins to control it.
112 lines
3.7 KiB
Ruby
112 lines
3.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# AddressableUrlValidator
|
|
#
|
|
# Custom validator for URLs. This is a stricter version of UrlValidator - it also checks
|
|
# for using the right protocol, but it actually parses the URL checking for any syntax errors.
|
|
# The regex is also different from `URI` as we use `Addressable::URI` here.
|
|
#
|
|
# By default, only URLs for the HTTP(S) schemes will be considered valid.
|
|
# Provide a `:schemes` option to configure accepted schemes.
|
|
#
|
|
# Example:
|
|
#
|
|
# class User < ActiveRecord::Base
|
|
# validates :personal_url, addressable_url: true
|
|
#
|
|
# validates :ftp_url, addressable_url: { schemes: %w(ftp) }
|
|
#
|
|
# validates :git_url, addressable_url: { schemes: %w(http https ssh git) }
|
|
# end
|
|
#
|
|
# This validator can also block urls pointing to localhost or the local network to
|
|
# protect against Server-side Request Forgery (SSRF), or check for the right port.
|
|
#
|
|
# Configuration options:
|
|
# * <tt>message</tt> - A custom error message (default is: "must be a valid URL").
|
|
# * <tt>schemes</tt> - Array of URI schemes. Default: +['http', 'https']+
|
|
# * <tt>allow_localhost</tt> - Allow urls pointing to +localhost+. Default: +true+
|
|
# * <tt>allow_local_network</tt> - Allow urls pointing to private network addresses. Default: +true+
|
|
# * <tt>allow_blank</tt> - Allow urls to be +blank+. Default: +false+
|
|
# * <tt>allow_nil</tt> - Allow urls to be +nil+. Default: +false+
|
|
# * <tt>ports</tt> - Allowed ports. Default: +all+.
|
|
# * <tt>enforce_user</tt> - Validate user format. Default: +false+
|
|
# * <tt>enforce_sanitization</tt> - Validate that there are no html/css/js tags. Default: +false+
|
|
#
|
|
# Example:
|
|
# class User < ActiveRecord::Base
|
|
# validates :personal_url, addressable_url: { allow_localhost: false, allow_local_network: false}
|
|
#
|
|
# validates :web_url, addressable_url: { ports: [80, 443] }
|
|
# end
|
|
class AddressableUrlValidator < ActiveModel::EachValidator
|
|
attr_reader :record
|
|
|
|
BLOCKER_VALIDATE_OPTIONS = {
|
|
schemes: %w(http https),
|
|
ports: [],
|
|
allow_localhost: true,
|
|
allow_local_network: true,
|
|
ascii_only: false,
|
|
enforce_user: false,
|
|
enforce_sanitization: false
|
|
}.freeze
|
|
|
|
DEFAULT_OPTIONS = BLOCKER_VALIDATE_OPTIONS.merge({
|
|
message: 'must be a valid URL'
|
|
}).freeze
|
|
|
|
def initialize(options)
|
|
options.reverse_merge!(DEFAULT_OPTIONS)
|
|
|
|
super(options)
|
|
end
|
|
|
|
def validate_each(record, attribute, value)
|
|
@record = record
|
|
|
|
unless value.present?
|
|
record.errors.add(attribute, options.fetch(:message))
|
|
return
|
|
end
|
|
|
|
value = strip_value!(record, attribute, value)
|
|
|
|
Gitlab::UrlBlocker.validate!(value, blocker_args)
|
|
rescue Gitlab::UrlBlocker::BlockedUrlError => e
|
|
record.errors.add(attribute, "is blocked: #{e.message}")
|
|
end
|
|
|
|
private
|
|
|
|
def strip_value!(record, attribute, value)
|
|
new_value = value.strip
|
|
return value if new_value == value
|
|
|
|
record.public_send("#{attribute}=", new_value) # rubocop:disable GitlabSecurity/PublicSend
|
|
end
|
|
|
|
def current_options
|
|
options.map do |option, value|
|
|
[option, value.is_a?(Proc) ? value.call(record) : value]
|
|
end.to_h
|
|
end
|
|
|
|
def blocker_args
|
|
current_options.slice(*BLOCKER_VALIDATE_OPTIONS.keys).tap do |args|
|
|
if self.class.allow_setting_local_requests?
|
|
args[:allow_localhost] = args[:allow_local_network] = true
|
|
end
|
|
end
|
|
end
|
|
|
|
def self.allow_setting_local_requests?
|
|
# We cannot use Gitlab::CurrentSettings as ApplicationSetting itself
|
|
# uses UrlValidator to validate urls. This ends up in a cycle
|
|
# when Gitlab::CurrentSettings creates an ApplicationSetting which then
|
|
# calls this validator.
|
|
#
|
|
# See https://gitlab.com/gitlab-org/gitlab-ee/issues/9833
|
|
ApplicationSetting.current&.allow_local_requests_from_web_hooks_and_services?
|
|
end
|
|
end
|