6c451d260f
Reviewed-on: #402 Co-authored-by: miteruzo <miteruzo@naver.com> Co-committed-by: miteruzo <miteruzo@naver.com>
56 行
1.9 KiB
Ruby
56 行
1.9 KiB
Ruby
require 'resolv'
|
|
require 'ipaddr'
|
|
require 'uri'
|
|
|
|
module Preview
|
|
class UrlSafety
|
|
class UnsafeUrl < StandardError; end
|
|
|
|
FORBIDDEN_NETWORKS = [
|
|
'0.0.0.0/8', '10.0.0.0/8', '100.64.0.0/10', '127.0.0.0/8',
|
|
'169.254.0.0/16', '172.16.0.0/12', '192.0.0.0/24',
|
|
'192.0.2.0/24', '192.168.0.0/16', '198.18.0.0/15',
|
|
'198.51.100.0/24', '203.0.113.0/24', '224.0.0.0/4',
|
|
'240.0.0.0/4', '::/128', '::1/128', 'fc00::/7', 'fe80::/10',
|
|
'ff00::/8', '2001:db8::/32', '::ffff:0:0/96'
|
|
].map { IPAddr.new(_1) }.freeze
|
|
|
|
def self.validate(raw_url)
|
|
value = raw_url.to_s.strip
|
|
if value.match?(/\A[a-z][a-z0-9+\-.]*:/i)
|
|
unless value.match?(/\Ahttps?:\/\//i)
|
|
raise UnsafeUrl, 'http または https の URL を指定してください.'
|
|
end
|
|
else
|
|
value = "http://#{ value }"
|
|
end
|
|
uri = URI.parse(value)
|
|
|
|
unless ['http', 'https'].include?(uri.scheme&.downcase) && uri.host.present?
|
|
raise UnsafeUrl, 'http または https の URL を指定してください.'
|
|
end
|
|
raise UnsafeUrl, 'userinfo つき URL は使用できません.' if uri.userinfo.present?
|
|
|
|
addresses = Resolv.getaddresses(uri.host)
|
|
raise UnsafeUrl, 'URL のホストを解決できません.' if addresses.empty?
|
|
|
|
parsed_addresses = addresses.map { IPAddr.new(_1) }
|
|
if parsed_addresses.any? { |address| forbidden?(address) }
|
|
raise UnsafeUrl, '安全でない接続先は使用できません.'
|
|
end
|
|
|
|
[uri, parsed_addresses.map(&:to_s)]
|
|
rescue Resolv::ResolvError
|
|
raise UnsafeUrl, 'URL のホストを解決できません.'
|
|
rescue URI::InvalidURIError, IPAddr::InvalidAddressError
|
|
raise UnsafeUrl, 'URL が不正です.'
|
|
end
|
|
|
|
def self.forbidden?(address)
|
|
FORBIDDEN_NETWORKS.any? { _1.include?(address) }
|
|
end
|
|
|
|
private_class_method :forbidden?
|
|
end
|
|
end
|