# # Copyright (c) 2013-2020 Hal Brodigan (postmodern.mod3 at gmail.com) # # bundler-audit is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # bundler-audit is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with bundler-audit. If not, see . # require 'bundler/audit/scanner' require 'bundler/audit/version' require 'bundler/audit/cli/formats' require 'thor' require 'bundler' module Bundler module Audit class CLI < ::Thor default_task :check map '--version' => :version desc 'check [DIR]', 'Checks the 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' method_option :database, :type => :string, :aliases => '-D', :default => Database::USER_PATH method_option :format, :type => :string, :default => 'text', :aliases => '-F' method_option :gemfile_lock, :type => :string, :aliases => '-G', :default => 'Gemfile.lock' method_option :output, :type => :string, :aliases => '-o' def check(dir=Dir.pwd) unless File.directory?(dir) say "No such file or directory: #{dir}", :red exit 1 end begin extend Formats.load(options[:format]) rescue Formats::FormatNotFound say "Unknown format: #{options[:format]}", :red exit 1 end if !Database.exists? download(options[:database]) elsif options[:update] update(options[:database]) end database = Database.new(options[:database]) scanner = begin Scanner.new(dir,options[:gemfile_lock],database) rescue Bundler::GemfileLockNotFound => exception say exception.message, :red exit 1 end report = scanner.report(:ignore => options.ignore) output = if options[:output] then File.new(options[:output],'w') else $stdout end print_report(report,output) output.close if options[:output] exit(1) if report.vulnerable? end desc 'stats', 'Prints ruby-advisory-db stats' method_option :quiet, :type => :boolean, :aliases => '-q' def stats(path=Database.path) database = Database.new(path) puts "ruby-advisory-db:" puts " advisories:\t#{database.size} advisories" puts " last updated:\t#{database.last_updated_at}" end desc 'download', 'Downloads ruby-advisory-db' method_option :quiet, :type => :boolean, :aliases => '-q' def download(path=Database.path) if Database.exists?(path) say "Database already exists", :yellow return end say("Download ruby-advisory-db ...") unless options.quiet? begin Database.download(path: path, quiet: options.quiet?) rescue Database::DownloadFailed => error say error.message, :red exit 1 end stats(path) unless options.quiet? end desc 'update', 'Updates the ruby-advisory-db' method_option :quiet, :type => :boolean, :aliases => '-q' def update(path=Database.path) unless Database.exists?(path) download(path) return end say("Updating ruby-advisory-db ...") unless options.quiet? database = Database.new(path) case database.update!(quiet: options.quiet?) when true say("Updated ruby-advisory-db", :green) unless options.quiet? when false say "Failed updating ruby-advisory-db!", :red exit 1 when nil unless Bundler.git_present? say "Git is not installed!", :red exit 1 end say "Skipping update", :yellow end stats(path) unless options.quiet? end desc 'version', 'Prints the bundler-audit version' def version database = Database.new puts "#{File.basename($0)} #{VERSION} (advisories: #{database.size})" end protected # # @abstract # def print_report(report) raise(NotImplementedError,"#{self.class}##{__method__} not defined") end def say(message="", color=nil) color = nil unless $stdout.tty? super(message.to_s, color) end end end end