Module: Coercive::URI
- Defined in:
- lib/coercive/uri.rb
Constant Summary collapse
- PRIVATE_IP_RANGES =
The IP ranges below are considered private and by default not permitted by the ‘uri` coercion function. To allow connecting to local services (in development, for example) users can set the `allow_private_ip` option, which ignores if the URI resolves to a public address or not.
[ IPAddr.new("0.0.0.0/8"), # Broadcasting to the current network. RFC 1700. IPAddr.new("10.0.0.0/8"), # Local private network. RFC 1918. IPAddr.new("127.0.0.0/8"), # Loopback addresses to the localhost. RFC 990. IPAddr.new("169.254.0.0/16"), # link-local addresses between two hosts on a single link. RFC 3927. IPAddr.new("172.16.0.0/12"), # Local private network. RFC 1918. IPAddr.new("192.168.0.0/16"), # Local private network. RFC 1918. IPAddr.new("198.18.0.0/15"), # Testing of inter-network communications between two separate subnets. RFC 2544. IPAddr.new("198.51.100.0/24"), # Assigned as "TEST-NET-2" in RFC 5737. IPAddr.new("203.0.113.0/24"), # Assigned as "TEST-NET-3" in RFC 5737. IPAddr.new("240.0.0.0/4"), # Reserved for future use, as specified by RFC 6890 IPAddr.new("::1/128"), # Loopback addresses to the localhost. RFC 5156. IPAddr.new("2001:20::/28"), # Non-routed IPv6 addresses used for Cryptographic Hash Identifiers. RFC 7343. IPAddr.new("fc00::/7"), # Unique Local Addresses (ULAs). RFC 1918. IPAddr.new("fe80::/10"), # link-local addresses between two hosts on a single link. RFC 3927. ].freeze
Class Method Summary collapse
-
.coerce_fn(string_coerce_fn, schema_fn: nil, require_path: false, require_port: false, require_user: false, require_password: false, allow_private_ip: false) ⇒ Object
Public DSL: Return a coercion function to coerce input to a URI.
-
.ip_from_bytes(bytes) ⇒ Object
Internal: Return an IPAddr built from the given address bytes.
-
.resolvable_public_ip?(uri) ⇒ Boolean
Internal: Return true if the given URI is resolvable to a non-private IP.
Class Method Details
.coerce_fn(string_coerce_fn, schema_fn: nil, require_path: false, require_port: false, require_user: false, require_password: false, allow_private_ip: false) ⇒ Object
Public DSL: Return a coercion function to coerce input to a URI. Used when declaring an attribute. See documentation for attr_coerce_fns.
string_coerce_fn - the string coerce function used to coerce the URI schema_fn - the optional function used to coerce the schema require_path - set true to make the URI path a required element require_port - set true to make the URI port a required element require_user - set true to make the URI user a required element require_password - set true to make the URI password a required element
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/coercive/uri.rb', line 36 def self.coerce_fn(string_coerce_fn, schema_fn: nil, require_path: false, require_port: false, require_user: false, require_password: false, allow_private_ip: false) ->(input) do uri = begin ::URI.parse(string_coerce_fn.call(input)) rescue ::URI::InvalidURIError fail Coercive::Error.new("not_valid") end fail Coercive::Error.new("no_host") unless uri.host fail Coercive::Error.new("not_resolvable") unless allow_private_ip || resolvable_public_ip?(uri) fail Coercive::Error.new("no_path") if require_path && uri.path.empty? fail Coercive::Error.new("no_port") if require_port && !uri.port fail Coercive::Error.new("no_user") if require_user && !uri.user fail Coercive::Error.new("no_password") if require_password && !uri.password if schema_fn begin schema_fn.call(uri.scheme) rescue Coercive::Error fail Coercive::Error.new("unsupported_schema") end end uri.to_s end end |
.ip_from_bytes(bytes) ⇒ Object
Internal: Return an IPAddr built from the given address bytes.
bytes - the binary-encoded String returned by Socket.gethostbyname.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/coercive/uri.rb', line 85 def self.ip_from_bytes(bytes) octets = bytes.unpack("C*") string = if octets.length == 4 # IPv4 octets.join(".") else # IPv6 octets.map { |i| "%02x" % i }.each_slice(2).map(&:join).join(":") end IPAddr.new(string) rescue IPAddr::InvalidAddressError nil end |
.resolvable_public_ip?(uri) ⇒ Boolean
Internal: Return true if the given URI is resolvable to a non-private IP.
uri - the URI to check.
68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/coercive/uri.rb', line 68 def self.resolvable_public_ip?(uri) begin _, _, _, *resolved_addresses = Socket.gethostbyname(uri.host) rescue SocketError return false end resolved_addresses.none? do |bytes| ip = ip_from_bytes(bytes) ip.nil? || PRIVATE_IP_RANGES.any? { |range| range.include?(ip) } end end |