mirror of
https://github.com/redmine/redmine.git
synced 2025-11-02 03:15:57 +01:00
Introduces issue webhooks (#29664):
* users can set up hooks for issue creation, update and deletion events, for any number of projects * hooks run in the context of the creating user, and only if the object in question is visible to that user * the actual HTTP call is done in ActiveJob * webhook calls are optionally signed the same way GitHub does Patch by Jens Krämer (user:jkraemer). git-svn-id: https://svn.redmine.org/redmine/trunk@24034 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
172
lib/webhook_endpoint_validator.rb
Normal file
172
lib/webhook_endpoint_validator.rb
Normal file
@@ -0,0 +1,172 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'uri'
|
||||
|
||||
class WebhookEndpointValidator < ActiveModel::EachValidator
|
||||
def validate_each(record, attribute, value)
|
||||
return if value.blank?
|
||||
|
||||
unless self.class.safe_webhook_uri?(value)
|
||||
record.errors.add attribute, :invalid
|
||||
end
|
||||
end
|
||||
|
||||
def self.safe_webhook_uri?(value)
|
||||
uri = value.is_a?(URI) ? value : URI.parse(value)
|
||||
return false if uri.nil?
|
||||
|
||||
return false unless valid_scheme?(uri.scheme)
|
||||
return false unless valid_host?(uri.host)
|
||||
return false unless valid_port?(uri.port)
|
||||
|
||||
true
|
||||
rescue
|
||||
Rails.logger.warn { "URI failed webhook safety checks: #{uri}" }
|
||||
false
|
||||
end
|
||||
|
||||
def self.valid_port?(port)
|
||||
!BAD_PORTS.include?(port)
|
||||
end
|
||||
|
||||
def self.valid_scheme?(scheme)
|
||||
%w[http https].include?(scheme)
|
||||
end
|
||||
|
||||
def self.blocked_hosts
|
||||
@blocked_hosts ||= begin
|
||||
ips = []
|
||||
wildcards = []
|
||||
hosts = []
|
||||
|
||||
Array(Redmine::Configuration['webhook_blocklist']).map(&:to_s).each do |block|
|
||||
# We try to parse the block as an IP address first...
|
||||
ips << IPAddr.new(block)
|
||||
rescue IPAddr::Error
|
||||
# If that failed, we assume it is a (wildcard) hostname
|
||||
if block.start_with?('*.')
|
||||
wildcards << Regexp.escape(block[2..])
|
||||
else
|
||||
hosts << Regexp.escape(block)
|
||||
end
|
||||
end
|
||||
|
||||
regex_parts = []
|
||||
regex_parts << "(?:#{hosts.join('|')})" if hosts.any?
|
||||
regex_parts << "(?:.*\\.)?(?:#{wildcards.join('|')})" if wildcards.any?
|
||||
|
||||
{
|
||||
ips: ips.freeze,
|
||||
host: regex_parts.any? ? /\A(?:#{regex_parts.join('|')})\z/i : nil
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
|
||||
def self.valid_host?(host)
|
||||
return false if host.blank?
|
||||
|
||||
return false if blocked_hosts[:host]&.match?(host)
|
||||
|
||||
Resolv.each_address(host) do |ip|
|
||||
ipaddr = IPAddr.new(ip)
|
||||
return false if ipaddr.link_local? || ipaddr.loopback?
|
||||
return false if IPAddr.new('224.0.0.0/24').include?(ipaddr) # multicast
|
||||
return false if blocked_hosts[:ips].any? { |net| net.include?(ipaddr) }
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
# A general port blacklist. Connections to these ports will not be allowed
|
||||
# unless the protocol overrides.
|
||||
#
|
||||
# This list is to be kept in sync with "bad ports" as defined in the
|
||||
# WHATWG Fetch standard at https://fetch.spec.whatwg.org/#port-blocking
|
||||
#
|
||||
# see also: https://github.com/mozilla/gecko-dev/blob/d55e89d48a8053ce45a74b0ec92c0ff6a9dcc43d/netwerk/base/nsIOService.cpp#L109-L199
|
||||
#
|
||||
BAD_PORTS = Set[
|
||||
1, # tcpmux
|
||||
7, # echo
|
||||
9, # discard
|
||||
11, # systat
|
||||
13, # daytime
|
||||
15, # netstat
|
||||
17, # qotd
|
||||
19, # chargen
|
||||
20, # ftp-data
|
||||
21, # ftp
|
||||
22, # ssh
|
||||
23, # telnet
|
||||
25, # smtp
|
||||
37, # time
|
||||
42, # name
|
||||
43, # nicname
|
||||
53, # domain
|
||||
69, # tftp
|
||||
77, # priv-rjs
|
||||
79, # finger
|
||||
87, # ttylink
|
||||
95, # supdup
|
||||
101, # hostriame
|
||||
102, # iso-tsap
|
||||
103, # gppitnp
|
||||
104, # acr-nema
|
||||
109, # pop2
|
||||
110, # pop3
|
||||
111, # sunrpc
|
||||
113, # auth
|
||||
115, # sftp
|
||||
117, # uucp-path
|
||||
119, # nntp
|
||||
123, # ntp
|
||||
135, # loc-srv / epmap
|
||||
137, # netbios
|
||||
139, # netbios
|
||||
143, # imap2
|
||||
161, # snmp
|
||||
179, # bgp
|
||||
389, # ldap
|
||||
427, # afp (alternate)
|
||||
465, # smtp (alternate)
|
||||
512, # print / exec
|
||||
513, # login
|
||||
514, # shell
|
||||
515, # printer
|
||||
526, # tempo
|
||||
530, # courier
|
||||
531, # chat
|
||||
532, # netnews
|
||||
540, # uucp
|
||||
548, # afp
|
||||
554, # rtsp
|
||||
556, # remotefs
|
||||
563, # nntp+ssl
|
||||
587, # smtp (outgoing)
|
||||
601, # syslog-conn
|
||||
636, # ldap+ssl
|
||||
989, # ftps-data
|
||||
990, # ftps
|
||||
993, # imap+ssl
|
||||
995, # pop3+ssl
|
||||
1719, # h323gatestat
|
||||
1720, # h323hostcall
|
||||
1723, # pptp
|
||||
2049, # nfs
|
||||
3659, # apple-sasl
|
||||
4045, # lockd
|
||||
4190, # sieve
|
||||
5060, # sip
|
||||
5061, # sips
|
||||
6000, # x11
|
||||
6566, # sane-port
|
||||
6665, # irc (alternate)
|
||||
6666, # irc (alternate)
|
||||
6667, # irc (default)
|
||||
6668, # irc (alternate)
|
||||
6669, # irc (alternate)
|
||||
6679, # osaut
|
||||
6697, # irc+tls
|
||||
10080 # amanda
|
||||
].freeze
|
||||
end
|
||||
Reference in New Issue
Block a user