Class: CC::Service::SafeWebhook

Inherits:
Object
  • Object
show all
Defined in:
lib/cc/service/safe_webhook.rb

Constant Summary collapse

InvalidWebhookURL =
Class.new(StandardError)
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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(url) ⇒ SafeWebhook

Returns a new instance of SafeWebhook.



25
26
27
# File 'lib/cc/service/safe_webhook.rb', line 25

def initialize(url)
  @url = url
end

Class Method Details

.getaddress(host) ⇒ Object



20
21
22
23
# File 'lib/cc/service/safe_webhook.rb', line 20

def self.getaddress(host)
  @resolv ||= Resolv::DNS.new
  @resolv.getaddress(host).to_s
end

Instance Method Details

#validate!(request) ⇒ Object

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 blog.fanout.io/2014/01/27/how-to-safely-invoke-webhooks/#ip-address-blacklisting



34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/cc/service/safe_webhook.rb', line 34

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