Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion data/ruby-advisory-db
Submodule ruby-advisory-db updated 143 files
2 changes: 1 addition & 1 deletion data/ruby-advisory-db.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2017-06-13 16:51:56 UTC
2019-08-22 12:28:25 UTC
56 changes: 38 additions & 18 deletions lib/bundler/audit/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,35 +29,24 @@ class CLI < ::Thor
default_task :check
map '--version' => :version

desc 'check', 'Checks the Gemfile.lock for insecure dependencies'
desc 'check [lockfile ...]', 'Checks specified lockfiles (the default is Gemfile.lock) for insecure dependencies'
method_option :quiet, :type => :boolean, :aliases => '-q'
method_option :verbose, :type => :boolean, :aliases => '-v'
method_option :ignore, :type => :array, :aliases => '-i'
method_option :update, :type => :boolean, :aliases => '-u'

def check
def check(*lockfiles)
update if options[:update]

scanner = Scanner.new
lockfiles << 'Gemfile.lock' if lockfiles.empty?
scanner = Scanner.new(Dir.pwd, :ignore => options.ignore)
vulnerable = false

scanner.scan(:ignore => options.ignore) do |result|
vulnerable = true

case result
when Scanner::InsecureSource
print_warning "Insecure Source URI found: #{result.source}"
when Scanner::UnpatchedGem
print_advisory result.gem, result.advisory
end
lockfiles.each do |lockfile|
vulnerable = check_lockfile(lockfile, scanner) || vulnerable
end

if vulnerable
say "Vulnerabilities found!", :red
exit 1
else
say("No vulnerabilities found", :green) unless options.quiet?
end
exit 1 if vulnerable
end

desc 'update', 'Updates the ruby-advisory-db'
Expand Down Expand Up @@ -90,6 +79,32 @@ def version

protected

def check_lockfile(lockfile, scanner)
print_header(lockfile)

vulnerable = false

scanner.scan(lockfile) do |result|
vulnerable = true

case result
when Scanner::InsecureSource
print_warning "Insecure Source URI found: #{result.source}"
when Scanner::UnpatchedGem
print_advisory result.gem, result.advisory
end
end

if vulnerable
say("Vulnerabilities found!", :red)
else
say("No vulnerabilities found", :green) unless options.quiet?
end
say

vulnerable
end

def say(message="", color=nil)
color = nil unless $stdout.tty?
super(message.to_s, color)
Expand All @@ -99,6 +114,11 @@ def print_warning(message)
say message, :yellow
end

def print_header(lockfile)
say lockfile, :bold
say
end

def print_advisory(gem, advisory)
say "Name: ", :red
say gem.name
Expand Down
47 changes: 20 additions & 27 deletions lib/bundler/audit/scanner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,19 @@ class Scanner
# Project root directory
attr_reader :root

# The parsed `Gemfile.lock` from the project
#
# @return [Bundler::LockfileParser]
attr_reader :lockfile

#
# Initializes a scanner.
#
# @param [String] root
# The path to the project root.
#
# @param [String] gemfile_lock
# @param [String] lockfile
# Alternative name for the `Gemfile.lock` file.
#
def initialize(root=Dir.pwd,gemfile_lock='Gemfile.lock')
def initialize(root=Dir.pwd, options={})
@root = File.expand_path(root)
@ignore = (options[:ignore] || Set.new).to_set
@database = Database.new
@lockfile = LockfileParser.new(
File.read(File.join(@root,gemfile_lock))
)
end

#
Expand All @@ -65,14 +58,11 @@ def initialize(root=Dir.pwd,gemfile_lock='Gemfile.lock')
# @return [Enumerator]
# If no block is given, an Enumerator will be returned.
#
def scan(options={},&block)
return enum_for(__method__,options) unless block

ignore = Set[]
ignore += options[:ignore] if options[:ignore]
def scan(lockfile='Gemfile.lock', &block)
return enum_for(__method__, lockfile) unless block

scan_sources(options,&block)
scan_specs(options,&block)
scan_sources(lockfile, &block)
scan_specs(lockfile, &block)

return self
end
Expand All @@ -96,10 +86,10 @@ def scan(options={},&block)
#
# @since 0.4.0
#
def scan_sources(options={})
return enum_for(__method__,options) unless block_given?
def scan_sources(lockfile='Gemfile.lock')
return enum_for(__method__, lockfile) unless block_given?

@lockfile.sources.map do |source|
gems(lockfile).sources.map do |source|
case source
when Source::Git
case source.uri
Expand Down Expand Up @@ -140,15 +130,12 @@ def scan_sources(options={})
#
# @since 0.4.0
#
def scan_specs(options={})
return enum_for(__method__,options) unless block_given?
def scan_specs(lockfile='Gemfile.lock')
return enum_for(__method__, lockfile) unless block_given?

ignore = Set[]
ignore += options[:ignore] if options[:ignore]

@lockfile.specs.each do |gem|
gems(lockfile).specs.each do |gem|
@database.check_gem(gem) do |advisory|
is_ignored = ignore.intersect?(advisory.identifiers.to_set)
is_ignored = @ignore.intersect?(advisory.identifiers.to_set)
next if is_ignored

yield UnpatchedGem.new(gem,advisory)
Expand Down Expand Up @@ -208,6 +195,12 @@ def internal_host?(host)
def internal_ip?(ip)
INTERNAL_SUBNETS.any? { |subnet| subnet.include?(ip) }
end

def gems(lockfile)
LockfileParser.new(
File.read(File.join(root, lockfile))
)
end
end
end
end
21 changes: 20 additions & 1 deletion spec/integration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,25 @@
end
end

context "when auditing multiple bundles" do
let(:bundle) { 'unpatched_gems' }
let(:directory) { File.join('spec','bundle',bundle) }
let(:command) do
File.expand_path(File.join(File.dirname(__FILE__),'..','bin','bundler-audit check Gemfile.lock ../insecure_sources/Gemfile.lock'))
end

subject do
Dir.chdir(directory) { sh(command, :fail => true) }
end

it "should print advisory information for multiple lockfiles" do
output = subject
expect(output).to include('Gemfile.lock')
expect(output).to include('../insecure_sources/Gemfile.lock')
expect(output.scan("Vulnerabilities found!").size).to eq(2)
end
end

context "when auditing a bundle with ignored gems" do
let(:bundle) { 'unpatched_gems' }
let(:directory) { File.join('spec','bundle',bundle) }
Expand Down Expand Up @@ -75,7 +94,7 @@
end

it "should print nothing when everything is fine" do
expect(subject.strip).to eq("No vulnerabilities found")
expect(subject.strip).to match("No vulnerabilities found")
end
end

Expand Down
2 changes: 1 addition & 1 deletion spec/scanner_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
end

context "when the :ignore option is given" do
subject { scanner.scan(:ignore => ['OSVDB-89025']) }
let(:scanner) { described_class.new(directory, :ignore => ['OSVDB-89025']) }

it "should ignore the specified advisories" do
ids = subject.map { |result| result.advisory.id }
Expand Down