bundler-audit 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug-report.md +44 -0
  3. data/.github/workflows/ruby.yml +14 -2
  4. data/.rubocop.yml +83 -0
  5. data/COPYING.txt +4 -4
  6. data/ChangeLog.md +25 -0
  7. data/Gemfile +7 -3
  8. data/README.md +14 -10
  9. data/Rakefile +7 -3
  10. data/bundler-audit.gemspec +3 -4
  11. data/lib/bundler/audit/advisory.rb +18 -2
  12. data/lib/bundler/audit/cli/formats/json.rb +16 -2
  13. data/lib/bundler/audit/cli/formats/junit.rb +127 -0
  14. data/lib/bundler/audit/cli/formats/text.rb +11 -7
  15. data/lib/bundler/audit/cli/formats.rb +7 -3
  16. data/lib/bundler/audit/cli.rb +32 -15
  17. data/lib/bundler/audit/configuration.rb +7 -4
  18. data/lib/bundler/audit/database.rb +20 -4
  19. data/lib/bundler/audit/results/insecure_source.rb +4 -1
  20. data/lib/bundler/audit/results/unpatched_gem.rb +6 -2
  21. data/lib/bundler/audit/results.rb +1 -1
  22. data/lib/bundler/audit/scanner.rb +8 -2
  23. data/lib/bundler/audit/task.rb +20 -5
  24. data/lib/bundler/audit/version.rb +2 -2
  25. data/lib/bundler/audit.rb +1 -1
  26. data/spec/advisory_spec.rb +9 -1
  27. data/spec/bundle/insecure_sources/Gemfile.lock +69 -71
  28. data/spec/bundle/secure/Gemfile.lock +51 -53
  29. data/spec/cli/formats/json_spec.rb +1 -0
  30. data/spec/cli/formats/junit_spec.rb +284 -0
  31. data/spec/cli/formats/text_spec.rb +87 -17
  32. data/spec/cli_spec.rb +57 -17
  33. data/spec/database_spec.rb +25 -1
  34. data/spec/fixtures/advisory/CVE-2020-1234.yml +1 -0
  35. data/spec/fixtures/lib/bundler/audit/cli/formats/bad.rb +0 -2
  36. data/spec/fixtures/lib/bundler/audit/cli/formats/good.rb +0 -2
  37. data/spec/results/unpatched_gem_spec.rb +2 -2
  38. data/spec/scanner_spec.rb +25 -1
  39. data/spec/spec_helper.rb +5 -1
  40. metadata +10 -6
@@ -12,7 +12,7 @@
12
12
  # GNU General Public License for more details.
13
13
  #
14
14
  # You should have received a copy of the GNU General Public License
15
- # along with bundler-audit. If not, see <http://www.gnu.org/licenses/>.
15
+ # along with bundler-audit. If not, see <https://www.gnu.org/licenses/>.
16
16
  #
17
17
 
18
18
  require 'thor'
@@ -21,6 +21,9 @@ module Bundler
21
21
  module Audit
22
22
  class CLI < ::Thor
23
23
  module Formats
24
+ #
25
+ # The plain-text output format.
26
+ #
24
27
  module Text
25
28
  #
26
29
  # Prints any findings as plain-text.
@@ -78,10 +81,12 @@ module Bundler
78
81
 
79
82
  say "Criticality: ", :red
80
83
  case advisory.criticality
81
- when :low then say "Low"
82
- when :medium then say "Medium", :yellow
83
- when :high then say "High", [:red, :bold]
84
- else say "Unknown"
84
+ when :none then say "None"
85
+ when :low then say "Low"
86
+ when :medium then say "Medium", :yellow
87
+ when :high then say "High", [:red, :bold]
88
+ when :critical then say "Critical", [:red, :bold]
89
+ else say "Unknown"
85
90
  end
86
91
 
87
92
  say "URL: ", :red
@@ -91,7 +96,7 @@ module Bundler
91
96
  say "Description:", :red
92
97
  say
93
98
 
94
- print_wrapped advisory.description, :indent => 2
99
+ print_wrapped advisory.description, indent: 2
95
100
  say
96
101
  else
97
102
  say "Title: ", :red
@@ -108,7 +113,6 @@ module Bundler
108
113
 
109
114
  say
110
115
  end
111
-
112
116
  end
113
117
 
114
118
  Formats.register :text, Text
@@ -12,7 +12,7 @@
12
12
  # GNU General Public License for more details.
13
13
  #
14
14
  # You should have received a copy of the GNU General Public License
15
- # along with bundler-audit. If not, see <http://www.gnu.org/licenses/>.
15
+ # along with bundler-audit. If not, see <https://www.gnu.org/licenses/>.
16
16
  #
17
17
 
18
18
  require 'thor'
@@ -126,15 +126,19 @@ module Bundler
126
126
  #
127
127
  def self.load(name)
128
128
  name = name.to_s
129
+ path = File.join(DIR,File.basename(name))
129
130
 
130
131
  begin
131
- require File.join(DIR,File.basename(name))
132
+ require path
132
133
  rescue LoadError
133
134
  raise(FormatNotFound,"could not load format #{name.inspect}")
134
135
  end
135
136
 
136
- return self[name] || \
137
+ unless (format = self[name])
137
138
  raise(FormatNotFound,"unknown format #{name.inspect}")
139
+ end
140
+
141
+ return format
138
142
  end
139
143
  end
140
144
  end
@@ -12,7 +12,7 @@
12
12
  # GNU General Public License for more details.
13
13
  #
14
14
  # You should have received a copy of the GNU General Public License
15
- # along with bundler-audit. If not, see <http://www.gnu.org/licenses/>.
15
+ # along with bundler-audit. If not, see <https://www.gnu.org/licenses/>.
16
16
  #
17
17
 
18
18
  require 'bundler/audit/scanner'
@@ -25,21 +25,26 @@ require 'bundler'
25
25
 
26
26
  module Bundler
27
27
  module Audit
28
+ #
29
+ # The `bundle-audit` command.
30
+ #
28
31
  class CLI < ::Thor
29
32
 
30
33
  default_task :check
31
34
  map '--version' => :version
32
35
 
33
36
  desc 'check [DIR]', 'Checks the Gemfile.lock for insecure dependencies'
34
- method_option :quiet, :type => :boolean, :aliases => '-q'
35
- method_option :verbose, :type => :boolean, :aliases => '-v'
36
- method_option :ignore, :type => :array, :aliases => '-i'
37
- method_option :update, :type => :boolean, :aliases => '-u'
38
- method_option :database, :type => :string, :aliases => '-D', :default => Database::USER_PATH
39
- method_option :format, :type => :string, :default => 'text',
40
- :aliases => '-F'
41
- method_option :gemfile_lock, :type => :string, :aliases => '-G', :default => 'Gemfile.lock'
42
- method_option :output, :type => :string, :aliases => '-o'
37
+ method_option :quiet, type: :boolean, aliases: '-q'
38
+ method_option :verbose, type: :boolean, aliases: '-v'
39
+ method_option :ignore, type: :array, aliases: '-i'
40
+ method_option :update, type: :boolean, aliases: '-u'
41
+ method_option :database, type: :string, aliases: '-D',
42
+ default: Database::USER_PATH
43
+ method_option :format, type: :string, default: 'text', aliases: '-F'
44
+ method_option :config, type: :string, aliases: '-c', default: '.bundler-audit.yml'
45
+ method_option :gemfile_lock, type: :string, aliases: '-G',
46
+ default: 'Gemfile.lock'
47
+ method_option :output, type: :string, aliases: '-o'
43
48
 
44
49
  def check(dir=Dir.pwd)
45
50
  unless File.directory?(dir)
@@ -62,12 +67,13 @@ module Bundler
62
67
 
63
68
  database = Database.new(options[:database])
64
69
  scanner = begin
65
- Scanner.new(dir,options[:gemfile_lock],database)
70
+ Scanner.new(dir,options[:gemfile_lock],database, options[:config])
66
71
  rescue Bundler::GemfileLockNotFound => exception
67
72
  say exception.message, :red
68
73
  exit 1
69
74
  end
70
- report = scanner.report(:ignore => options.ignore)
75
+
76
+ report = scanner.report(ignore: options.ignore)
71
77
 
72
78
  output = if options[:output] then File.new(options[:output],'w')
73
79
  else $stdout
@@ -81,7 +87,7 @@ module Bundler
81
87
  end
82
88
 
83
89
  desc 'stats', 'Prints ruby-advisory-db stats'
84
- method_option :quiet, :type => :boolean, :aliases => '-q'
90
+ method_option :quiet, type: :boolean, aliases: '-q'
85
91
 
86
92
  def stats(path=Database.path)
87
93
  database = Database.new(path)
@@ -89,10 +95,14 @@ module Bundler
89
95
  puts "ruby-advisory-db:"
90
96
  puts " advisories:\t#{database.size} advisories"
91
97
  puts " last updated:\t#{database.last_updated_at}"
98
+
99
+ if (commit_id = database.commit_id)
100
+ puts " commit:\t#{commit_id}"
101
+ end
92
102
  end
93
103
 
94
104
  desc 'download', 'Downloads ruby-advisory-db'
95
- method_option :quiet, :type => :boolean, :aliases => '-q'
105
+ method_option :quiet, type: :boolean, aliases: '-q'
96
106
 
97
107
  def download(path=Database.path)
98
108
  if Database.exists?(path)
@@ -113,7 +123,7 @@ module Bundler
113
123
  end
114
124
 
115
125
  desc 'update', 'Updates the ruby-advisory-db'
116
- method_option :quiet, :type => :boolean, :aliases => '-q'
126
+ method_option :quiet, type: :boolean, aliases: '-q'
117
127
 
118
128
  def update(path=Database.path)
119
129
  unless Database.exists?(path)
@@ -150,6 +160,13 @@ module Bundler
150
160
 
151
161
  protected
152
162
 
163
+ #
164
+ # @note Silence deprecation warnings from Thor.
165
+ #
166
+ def self.exit_on_failure?
167
+ true
168
+ end
169
+
153
170
  #
154
171
  # @abstract
155
172
  #
@@ -12,7 +12,7 @@
12
12
  # GNU General Public License for more details.
13
13
  #
14
14
  # You should have received a copy of the GNU General Public License
15
- # along with bundler-audit. If not, see <http://www.gnu.org/licenses/>.
15
+ # along with bundler-audit. If not, see <https://www.gnu.org/licenses/>.
16
16
  #
17
17
 
18
18
  require 'yaml'
@@ -26,14 +26,17 @@ module Bundler
26
26
  # @since 0.8.0
27
27
  #
28
28
  class Configuration
29
- class InvalidConfigurationError < StandardError; end
30
- class FileNotFound < StandardError; end
29
+ class InvalidConfigurationError < StandardError
30
+ end
31
+
32
+ class FileNotFound < StandardError
33
+ end
31
34
 
32
35
  #
33
36
  # A constructor method for loading configuration from a YAML file.
34
37
  #
35
38
  # @param [String] file_path
36
- # Path to the yaml file holding the configuration.
39
+ # Path to the YAML file holding the configuration.
37
40
  #
38
41
  # @raise [FileNotFound]
39
42
  # Will raise a file not found error when the path to the
@@ -12,7 +12,7 @@
12
12
  # GNU General Public License for more details.
13
13
  #
14
14
  # You should have received a copy of the GNU General Public License
15
- # along with bundler-audit. If not, see <http://www.gnu.org/licenses/>.
15
+ # along with bundler-audit. If not, see <https://www.gnu.org/licenses/>.
16
16
  #
17
17
 
18
18
  require 'bundler/audit/advisory'
@@ -82,7 +82,7 @@ module Bundler
82
82
  # The given path of the database to check.
83
83
  #
84
84
  # @return [Boolean]
85
- #
85
+ #
86
86
  # @since 0.8.0
87
87
  #
88
88
  def self.exists?(path=DEFAULT_PATH)
@@ -119,7 +119,7 @@ module Bundler
119
119
 
120
120
  path = options.fetch(:path,DEFAULT_PATH)
121
121
 
122
- command = %w(git clone)
122
+ command = %w[git clone]
123
123
  command << '--quiet' if options[:quiet]
124
124
  command << URL << path
125
125
 
@@ -199,7 +199,7 @@ module Bundler
199
199
  def update!(options={})
200
200
  if git?
201
201
  Dir.chdir(@path) do
202
- command = %w(git pull)
202
+ command = %w[git pull]
203
203
  command << '--quiet' if options[:quiet]
204
204
  command << 'origin' << 'master'
205
205
 
@@ -212,6 +212,22 @@ module Bundler
212
212
  end
213
213
  end
214
214
 
215
+ #
216
+ # The last commit ID of the repository.
217
+ #
218
+ # @return [String, nil]
219
+ # The commit hash or `nil` if the database is not a git repository.
220
+ #
221
+ # @since 0.9.0
222
+ #
223
+ def commit_id
224
+ if git?
225
+ Dir.chdir(@path) do
226
+ `git rev-parse HEAD`.chomp
227
+ end
228
+ end
229
+ end
230
+
215
231
  #
216
232
  # Determines the time when the database was last updated.
217
233
  #
@@ -12,7 +12,7 @@
12
12
  # GNU General Public License for more details.
13
13
  #
14
14
  # You should have received a copy of the GNU General Public License
15
- # along with bundler-audit. If not, see <http://www.gnu.org/licenses/>.
15
+ # along with bundler-audit. If not, see <https://www.gnu.org/licenses/>.
16
16
  #
17
17
 
18
18
  require 'bundler/audit/results/result'
@@ -20,6 +20,9 @@ require 'bundler/audit/results/result'
20
20
  module Bundler
21
21
  module Audit
22
22
  module Results
23
+ #
24
+ # Represents an insecure gem source (ex: `git://...` or `http://...`).
25
+ #
23
26
  class InsecureSource < Result
24
27
 
25
28
  # The insecure `git://` or `http://` URI.
@@ -12,7 +12,7 @@
12
12
  # GNU General Public License for more details.
13
13
  #
14
14
  # You should have received a copy of the GNU General Public License
15
- # along with bundler-audit. If not, see <http://www.gnu.org/licenses/>.
15
+ # along with bundler-audit. If not, see <https://www.gnu.org/licenses/>.
16
16
  #
17
17
 
18
18
  require 'bundler/audit/results/result'
@@ -22,6 +22,10 @@ require 'uri'
22
22
  module Bundler
23
23
  module Audit
24
24
  module Results
25
+ #
26
+ # Represents a gem version that has known vulnerabilities and needs to be
27
+ # upgraded.
28
+ #
25
29
  class UnpatchedGem < Result
26
30
 
27
31
  # The specification of the vulnerable gem.
@@ -73,7 +77,7 @@ module Bundler
73
77
  end
74
78
 
75
79
  #
76
- # Converts the unpached gem to a Hash.
80
+ # Converts the unpatched gem to a Hash.
77
81
  #
78
82
  # @return [Hash{Symbol => Object}]
79
83
  #
@@ -12,7 +12,7 @@
12
12
  # GNU General Public License for more details.
13
13
  #
14
14
  # You should have received a copy of the GNU General Public License
15
- # along with bundler-audit. If not, see <http://www.gnu.org/licenses/>.
15
+ # along with bundler-audit. If not, see <https://www.gnu.org/licenses/>.
16
16
  #
17
17
 
18
18
  require 'bundler/audit/results/insecure_source'
@@ -12,7 +12,7 @@
12
12
  # GNU General Public License for more details.
13
13
  #
14
14
  # You should have received a copy of the GNU General Public License
15
- # along with bundler-audit. If not, see <http://www.gnu.org/licenses/>.
15
+ # along with bundler-audit. If not, see <https://www.gnu.org/licenses/>.
16
16
  #
17
17
 
18
18
  require 'bundler'
@@ -31,6 +31,9 @@ require 'yaml'
31
31
 
32
32
  module Bundler
33
33
  module Audit
34
+ #
35
+ # Scans a `Gemfile.lock` for security issues.
36
+ #
34
37
  class Scanner
35
38
 
36
39
  # The advisory database
@@ -63,6 +66,9 @@ module Bundler
63
66
  # @param [Database] database
64
67
  # The database to scan against.
65
68
  #
69
+ # @param [String] config_dot_file
70
+ # The file name of the bundler-audit config file.
71
+ #
66
72
  # @raise [Bundler::GemfileLockNotFound]
67
73
  # The `gemfile_lock` file could not be found within the `root`
68
74
  # directory.
@@ -79,7 +85,7 @@ module Bundler
79
85
 
80
86
  @lockfile = LockfileParser.new(File.read(gemfile_lock_path))
81
87
 
82
- config_dot_file_full_path = File.join(@root,config_dot_file)
88
+ config_dot_file_full_path = File.absolute_path(config_dot_file, @root)
83
89
 
84
90
  @config = if File.exist?(config_dot_file_full_path)
85
91
  Configuration.load(config_dot_file_full_path)
@@ -2,6 +2,9 @@ require 'rake/tasklib'
2
2
 
3
3
  module Bundler
4
4
  module Audit
5
+ #
6
+ # Defines the `bundle:audit` rake tasks.
7
+ #
5
8
  class Task < Rake::TaskLib
6
9
  #
7
10
  # Initializes the task.
@@ -13,16 +16,28 @@ module Bundler
13
16
  protected
14
17
 
15
18
  #
16
- # Defines the `bundle:audit` task.
19
+ # Defines the `bundle:audit` and `bundle:audit:update` task.
17
20
  #
18
21
  def define
19
22
  namespace :bundle do
20
- desc 'Checks the Gemfile.lock for insecure dependencies'
21
- task :audit do
22
- require 'bundler/audit/cli'
23
- Bundler::Audit::CLI.start %w[check]
23
+ namespace :audit do
24
+ desc 'Checks the Gemfile.lock for insecure dependencies'
25
+ task :check do
26
+ system 'bundler-audit', 'check'
27
+ end
28
+
29
+ desc 'Updates the bundler-audit vulnerability database'
30
+ task :update do
31
+ system 'bundler-audit', 'update'
32
+ end
24
33
  end
34
+
35
+ task :audit => 'audit:check'
25
36
  end
37
+
38
+ task 'bundler:audit' => 'bundle:audit'
39
+ task 'bundler:audit:check' => 'bundle:audit:check'
40
+ task 'bundler:audit:update' => 'bundle:audit:update'
26
41
  end
27
42
  end
28
43
  end
@@ -12,12 +12,12 @@
12
12
  # GNU General Public License for more details.
13
13
  #
14
14
  # You should have received a copy of the GNU General Public License
15
- # along with bundler-audit. If not, see <http://www.gnu.org/licenses/>.
15
+ # along with bundler-audit. If not, see <https://www.gnu.org/licenses/>.
16
16
  #
17
17
 
18
18
  module Bundler
19
19
  module Audit
20
20
  # bundler-audit version
21
- VERSION = '0.8.0'
21
+ VERSION = '0.9.0'
22
22
  end
23
23
  end
data/lib/bundler/audit.rb CHANGED
@@ -12,7 +12,7 @@
12
12
  # GNU General Public License for more details.
13
13
  #
14
14
  # You should have received a copy of the GNU General Public License
15
- # along with bundler-audit. If not, see <http://www.gnu.org/licenses/>.
15
+ # along with bundler-audit. If not, see <https://www.gnu.org/licenses/>.
16
16
  #
17
17
 
18
18
  require 'bundler/audit/database'
@@ -83,7 +83,7 @@ describe Bundler::Audit::Advisory do
83
83
  end
84
84
 
85
85
  context "YAML data not representing a hash" do
86
- let(:path ) do
86
+ let(:path) do
87
87
  File.expand_path('../fixtures/advisory/not_a_hash.yml', __FILE__)
88
88
  end
89
89
 
@@ -353,4 +353,12 @@ describe Bundler::Audit::Advisory do
353
353
  end
354
354
  end
355
355
  end
356
+
357
+ describe "#to_h" do
358
+ subject { super().to_h }
359
+
360
+ it "must include criticality: :critical" do
361
+ expect(subject[:criticality]).to be :critical
362
+ end
363
+ end
356
364
  end