debian-mirror-gitlab/app/validators/url_validator.rb

99 lines
2.8 KiB
Ruby
Raw Normal View History

2018-11-08 19:23:39 +05:30
# frozen_string_literal: true
2015-12-23 02:04:40 +05:30
# UrlValidator
#
# Custom validator for URLs.
#
# By default, only URLs for the HTTP(S) protocols will be considered valid.
# Provide a `:protocols` option to configure accepted protocols.
#
# Example:
#
# class User < ActiveRecord::Base
# validates :personal_url, url: true
#
# validates :ftp_url, url: { protocols: %w(ftp) }
#
# validates :git_url, url: { protocols: %w(http https ssh git) }
# end
#
2018-11-08 19:23:39 +05:30
# 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.
#
# The available options are:
# - protocols: Allowed protocols. Default: http and https
# - allow_localhost: Allow urls pointing to localhost. Default: true
# - allow_local_network: Allow urls pointing to private network addresses. Default: true
# - ports: Allowed ports. Default: all.
# - enforce_user: Validate user format. Default: false
2019-02-15 15:39:39 +05:30
# - enforce_sanitization: Validate that there are no html/css/js tags. Default: false
2018-11-08 19:23:39 +05:30
#
# Example:
# class User < ActiveRecord::Base
# validates :personal_url, url: { allow_localhost: false, allow_local_network: false}
#
# validates :web_url, url: { ports: [80, 443] }
# end
2015-12-23 02:04:40 +05:30
class UrlValidator < ActiveModel::EachValidator
2018-11-08 19:23:39 +05:30
DEFAULT_PROTOCOLS = %w(http https).freeze
attr_reader :record
2015-12-23 02:04:40 +05:30
def validate_each(record, attribute, value)
2018-11-08 19:23:39 +05:30
@record = record
2018-11-29 20:51:05 +05:30
unless value.present?
2018-11-08 19:23:39 +05:30
record.errors.add(attribute, 'must be a valid URL')
2018-11-29 20:51:05 +05:30
return
2015-12-23 02:04:40 +05:30
end
2018-11-08 19:23:39 +05:30
2018-11-29 20:51:05 +05:30
value = strip_value!(record, attribute, value)
2018-11-08 19:23:39 +05:30
Gitlab::UrlBlocker.validate!(value, blocker_args)
rescue Gitlab::UrlBlocker::BlockedUrlError => e
record.errors.add(attribute, "is blocked: #{e.message}")
2015-12-23 02:04:40 +05:30
end
private
2018-11-29 20:51:05 +05:30
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
2015-12-23 02:04:40 +05:30
def default_options
2018-11-08 19:23:39 +05:30
# By default the validator doesn't block any url based on the ip address
{
protocols: DEFAULT_PROTOCOLS,
ports: [],
allow_localhost: true,
allow_local_network: true,
2019-02-15 15:39:39 +05:30
ascii_only: false,
enforce_user: false,
enforce_sanitization: false
2018-11-08 19:23:39 +05:30
}
2015-12-23 02:04:40 +05:30
end
2018-11-08 19:23:39 +05:30
def current_options
options = self.options.map do |option, value|
[option, value.is_a?(Proc) ? value.call(record) : value]
end.to_h
2016-06-02 11:05:42 +05:30
2018-11-08 19:23:39 +05:30
default_options.merge(options)
end
def blocker_args
current_options.slice(*default_options.keys).tap do |args|
if allow_setting_local_requests?
args[:allow_localhost] = args[:allow_local_network] = true
end
end
end
2015-12-23 02:04:40 +05:30
2018-11-08 19:23:39 +05:30
def allow_setting_local_requests?
ApplicationSetting.current&.allow_local_requests_from_hooks_and_services?
2015-12-23 02:04:40 +05:30
end
end