lib/bundler/audit/scanner.rb in bundler-audit-0.3.1 vs lib/bundler/audit/scanner.rb in bundler-audit-0.4.0
- old
+ new
@@ -1,10 +1,13 @@
require 'bundler'
require 'bundler/audit/database'
require 'bundler/lockfile_parser'
+require 'ipaddr'
+require 'resolv'
require 'set'
+require 'uri'
module Bundler
module Audit
class Scanner
@@ -57,41 +60,136 @@
# A result from the scan.
#
# @return [Enumerator]
# If no block is given, an Enumerator will be returned.
#
- def scan(options={})
- return enum_for(__method__,options) unless block_given?
+ def scan(options={},&block)
+ return enum_for(__method__,options) unless block
ignore = Set[]
ignore += options[:ignore] if options[:ignore]
+ scan_sources(options,&block)
+ scan_specs(options,&block)
+
+ return self
+ end
+
+ #
+ # Scans the gem sources in the lockfile.
+ #
+ # @param [Hash] options
+ # Additional options.
+ #
+ # @yield [result]
+ # The given block will be passed the results of the scan.
+ #
+ # @yieldparam [InsecureSource] result
+ # A result from the scan.
+ #
+ # @return [Enumerator]
+ # If no block is given, an Enumerator will be returned.
+ #
+ # @api semipublic
+ #
+ # @since 0.4.0
+ #
+ def scan_sources(options={})
+ return enum_for(__method__,options) unless block_given?
+
@lockfile.sources.map do |source|
case source
when Source::Git
case source.uri
when /^git:/, /^http:/
+ next if internal_host?(source.uri)
yield InsecureSource.new(source.uri)
end
when Source::Rubygems
source.remotes.each do |uri|
if uri.scheme == 'http'
yield InsecureSource.new(uri.to_s)
end
end
end
end
+ end
+ #
+ # Scans the gem sources in the lockfile.
+ #
+ # @param [Hash] options
+ # Additional options.
+ #
+ # @option options [Array<String>] :ignore
+ # The advisories to ignore.
+ #
+ # @yield [result]
+ # The given block will be passed the results of the scan.
+ #
+ # @yieldparam [UnpatchedGem] result
+ # A result from the scan.
+ #
+ # @return [Enumerator]
+ # If no block is given, an Enumerator will be returned.
+ #
+ # @api semipublic
+ #
+ # @since 0.4.0
+ #
+ def scan_specs(options={})
+ return enum_for(__method__,options) unless block_given?
+
+ ignore = Set[]
+ ignore += options[:ignore] if options[:ignore]
+
@lockfile.specs.each do |gem|
@database.check_gem(gem) do |advisory|
unless ignore.include?(advisory.id)
yield UnpatchedGem.new(gem,advisory)
end
end
end
+ end
- return self
+ private
+
+ #
+ # Determines whether a URI is internal.
+ #
+ # @param [String] uri
+ # The source URI.
+ #
+ # @return [Boolean]
+ #
+ def internal_host?(uri)
+ return unless host = URI.parse(uri).host
+ Resolv.getaddresses(host).all? { |ip| internal_ip?(ip) }
+ rescue URI::Error
+ false
end
+ # List of internal IP address ranges.
+ #
+ # @see https://tools.ietf.org/html/rfc1918#section-3
+ # @see https://tools.ietf.org/html/rfc4193#section-8
+ INTERNAL_SUBNETS = %w[
+ 10.0.0.0/8
+ 172.16.0.0/12
+ 192.168.0.0/16
+ fc00::/7
+ ].map(&IPAddr.method(:new))
+
+ #
+ # Determines whether an IP is internal.
+ #
+ # @param [String] ip
+ # The IPv4/IPv6 address.
+ #
+ # @return [Boolean]
+ #
+ def internal_ip?(ip)
+ INTERNAL_SUBNETS.any? { |subnet| subnet.include?(ip) }
+ end
end
end
end