Sha256: c5f56072b83f3913521106f1d45ed2078e5dd80b980b57457548079c45d482d1
Contents?: true
Size: 1.93 KB
Versions: 1
Compression:
Stored size: 1.93 KB
Contents
require "ipaddr" require "resolv" module CC class Service class SafeWebhook InvalidWebhookURL = Class.new(StandardError) # https://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces # https://en.wikipedia.org/wiki/Private_network#Private_IPv6_addresses PRIVATE_ADDRESS_SUBNETS = [ IPAddr.new("10.0.0.0/8"), IPAddr.new("172.16.0.0/12"), IPAddr.new("192.168.0.0/16"), IPAddr.new("fd00::/8"), IPAddr.new("127.0.0.1"), IPAddr.new("0:0:0:0:0:0:0:1"), ].freeze def self.getaddress(host) @resolv ||= Resolv::DNS.new @resolv.getaddress(host).to_s end def initialize(url) @url = url end # Resolve the Host to an IP address, validate that it doesn't point to # anything internal, then alter the request to be for the IP directly with # an explicit Host header given. # # See http://blog.fanout.io/2014/01/27/how-to-safely-invoke-webhooks/#ip-address-blacklisting def validate!(request) uri = URI.parse(url) address = self.class.getaddress(uri.host) if internal?(address) raise_invalid("resolves to a private IP address") end alter_request(request, uri, address) rescue URI::InvalidURIError, Resolv::ResolvError, Resolv::ResolvTimeout => ex raise_invalid(ex.message) end private attr_reader :url def internal?(address) ip_addr = IPAddr.new(address) PRIVATE_ADDRESS_SUBNETS.any? do |subnet| subnet === ip_addr end end def alter_request(request, uri, address) address_uri = uri.dup address_uri.host = address request.url(address_uri.to_s) request.headers.update(Host: uri.host) end def raise_invalid(message) raise InvalidWebhookURL, "The Webhook URL #{url} is invalid: #{message}" end end end end
Version data entries
1 entries across 1 versions & 1 rubygems
Version | Path |
---|---|
codeclimate-services-1.8.0 | lib/cc/service/safe_webhook.rb |