bundler-audit 0.8.0.rc1 → 0.9.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/ISSUE_TEMPLATE/bug-report.md +44 -0
  4. data/.github/workflows/ruby.yml +16 -2
  5. data/.rubocop.yml +83 -0
  6. data/COPYING.txt +4 -4
  7. data/ChangeLog.md +45 -11
  8. data/Gemfile +7 -3
  9. data/README.md +20 -15
  10. data/Rakefile +7 -3
  11. data/bundler-audit.gemspec +3 -4
  12. data/gemspec.yml +2 -2
  13. data/lib/bundler/audit/advisory.rb +24 -3
  14. data/lib/bundler/audit/cli/formats/json.rb +17 -3
  15. data/lib/bundler/audit/cli/formats/junit.rb +127 -0
  16. data/lib/bundler/audit/cli/formats/text.rb +19 -13
  17. data/lib/bundler/audit/cli/formats.rb +8 -4
  18. data/lib/bundler/audit/cli/thor_ext/shell/basic/say_error.rb +33 -0
  19. data/lib/bundler/audit/cli.rb +41 -29
  20. data/lib/bundler/audit/configuration.rb +12 -5
  21. data/lib/bundler/audit/database.rb +21 -5
  22. data/lib/bundler/audit/results/insecure_source.rb +5 -2
  23. data/lib/bundler/audit/results/unpatched_gem.rb +7 -3
  24. data/lib/bundler/audit/results.rb +2 -2
  25. data/lib/bundler/audit/scanner.rb +9 -3
  26. data/lib/bundler/audit/task.rb +20 -5
  27. data/lib/bundler/audit/version.rb +3 -3
  28. data/lib/bundler/audit.rb +2 -2
  29. data/spec/advisory_spec.rb +9 -1
  30. data/spec/bundle/insecure_sources/Gemfile.lock +73 -71
  31. data/spec/bundle/secure/Gemfile.lock +55 -53
  32. data/spec/cli/formats/json_spec.rb +1 -0
  33. data/spec/cli/formats/junit_spec.rb +284 -0
  34. data/spec/cli/formats/text_spec.rb +113 -19
  35. data/spec/cli_spec.rb +61 -21
  36. data/spec/configuration_spec.rb +8 -0
  37. data/spec/database_spec.rb +25 -1
  38. data/spec/fixtures/advisory/CVE-2020-1234.yml +2 -0
  39. data/spec/fixtures/config/bad/empty.yml +0 -0
  40. data/spec/fixtures/lib/bundler/audit/cli/formats/bad.rb +0 -2
  41. data/spec/fixtures/lib/bundler/audit/cli/formats/good.rb +0 -2
  42. data/spec/integration_spec.rb +17 -103
  43. data/spec/results/unpatched_gem_spec.rb +2 -2
  44. data/spec/scanner_spec.rb +25 -1
  45. data/spec/spec_helper.rb +5 -1
  46. metadata +18 -17
@@ -1,99 +1,101 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
- actioncable (5.2.4.4)
5
- actionpack (= 5.2.4.4)
4
+ actioncable (5.2.6)
5
+ actionpack (= 5.2.6)
6
6
  nio4r (~> 2.0)
7
7
  websocket-driver (>= 0.6.1)
8
- actionmailer (5.2.4.4)
9
- actionpack (= 5.2.4.4)
10
- actionview (= 5.2.4.4)
11
- activejob (= 5.2.4.4)
8
+ actionmailer (5.2.6)
9
+ actionpack (= 5.2.6)
10
+ actionview (= 5.2.6)
11
+ activejob (= 5.2.6)
12
12
  mail (~> 2.5, >= 2.5.4)
13
13
  rails-dom-testing (~> 2.0)
14
- actionpack (5.2.4.4)
15
- actionview (= 5.2.4.4)
16
- activesupport (= 5.2.4.4)
14
+ actionpack (5.2.6)
15
+ actionview (= 5.2.6)
16
+ activesupport (= 5.2.6)
17
17
  rack (~> 2.0, >= 2.0.8)
18
18
  rack-test (>= 0.6.3)
19
19
  rails-dom-testing (~> 2.0)
20
20
  rails-html-sanitizer (~> 1.0, >= 1.0.2)
21
- actionview (5.2.4.4)
22
- activesupport (= 5.2.4.4)
21
+ actionview (5.2.6)
22
+ activesupport (= 5.2.6)
23
23
  builder (~> 3.1)
24
24
  erubi (~> 1.4)
25
25
  rails-dom-testing (~> 2.0)
26
26
  rails-html-sanitizer (~> 1.0, >= 1.0.3)
27
- activejob (5.2.4.4)
28
- activesupport (= 5.2.4.4)
27
+ activejob (5.2.6)
28
+ activesupport (= 5.2.6)
29
29
  globalid (>= 0.3.6)
30
- activemodel (5.2.4.4)
31
- activesupport (= 5.2.4.4)
32
- activerecord (5.2.4.4)
33
- activemodel (= 5.2.4.4)
34
- activesupport (= 5.2.4.4)
30
+ activemodel (5.2.6)
31
+ activesupport (= 5.2.6)
32
+ activerecord (5.2.6)
33
+ activemodel (= 5.2.6)
34
+ activesupport (= 5.2.6)
35
35
  arel (>= 9.0)
36
- activestorage (5.2.4.4)
37
- actionpack (= 5.2.4.4)
38
- activerecord (= 5.2.4.4)
39
- marcel (~> 0.3.1)
40
- activesupport (5.2.4.4)
36
+ activestorage (5.2.6)
37
+ actionpack (= 5.2.6)
38
+ activerecord (= 5.2.6)
39
+ marcel (~> 1.0.0)
40
+ activesupport (5.2.6)
41
41
  concurrent-ruby (~> 1.0, >= 1.0.2)
42
42
  i18n (>= 0.7, < 2)
43
43
  minitest (~> 5.1)
44
44
  tzinfo (~> 1.1)
45
45
  arel (9.0.0)
46
46
  builder (3.2.4)
47
- concurrent-ruby (1.1.7)
47
+ concurrent-ruby (1.1.8)
48
48
  crass (1.0.6)
49
49
  erubi (1.10.0)
50
50
  globalid (0.4.2)
51
51
  activesupport (>= 4.2.0)
52
- i18n (1.8.5)
52
+ i18n (1.8.10)
53
53
  concurrent-ruby (~> 1.0)
54
- loofah (2.8.0)
54
+ loofah (2.9.1)
55
55
  crass (~> 1.0.2)
56
56
  nokogiri (>= 1.5.9)
57
57
  mail (2.7.1)
58
58
  mini_mime (>= 0.1.1)
59
- marcel (0.3.3)
60
- mimemagic (~> 0.3.2)
59
+ marcel (1.0.1)
61
60
  method_source (1.0.0)
62
- mimemagic (0.3.5)
63
- mini_mime (1.0.2)
64
- mini_portile2 (2.4.0)
65
- minitest (5.14.2)
66
- nio4r (2.5.4)
67
- nokogiri (1.10.10)
68
- mini_portile2 (~> 2.4.0)
61
+ mini_mime (1.1.0)
62
+ mini_portile2 (2.5.1)
63
+ minitest (5.14.4)
64
+ nio4r (2.5.7)
65
+ nokogiri (1.11.6)
66
+ mini_portile2 (~> 2.5.0)
67
+ racc (~> 1.4)
68
+ nokogiri (1.11.6-x86_64-linux)
69
+ racc (~> 1.4)
70
+ racc (1.5.2)
69
71
  rack (2.2.3)
70
72
  rack-test (1.1.0)
71
73
  rack (>= 1.0, < 3)
72
- rails (5.2.4.4)
73
- actioncable (= 5.2.4.4)
74
- actionmailer (= 5.2.4.4)
75
- actionpack (= 5.2.4.4)
76
- actionview (= 5.2.4.4)
77
- activejob (= 5.2.4.4)
78
- activemodel (= 5.2.4.4)
79
- activerecord (= 5.2.4.4)
80
- activestorage (= 5.2.4.4)
81
- activesupport (= 5.2.4.4)
74
+ rails (5.2.6)
75
+ actioncable (= 5.2.6)
76
+ actionmailer (= 5.2.6)
77
+ actionpack (= 5.2.6)
78
+ actionview (= 5.2.6)
79
+ activejob (= 5.2.6)
80
+ activemodel (= 5.2.6)
81
+ activerecord (= 5.2.6)
82
+ activestorage (= 5.2.6)
83
+ activesupport (= 5.2.6)
82
84
  bundler (>= 1.3.0)
83
- railties (= 5.2.4.4)
85
+ railties (= 5.2.6)
84
86
  sprockets-rails (>= 2.0.0)
85
87
  rails-dom-testing (2.0.3)
86
88
  activesupport (>= 4.2.0)
87
89
  nokogiri (>= 1.6)
88
90
  rails-html-sanitizer (1.0.4)
89
91
  loofah (~> 2.2, >= 2.2.2)
90
- railties (5.2.4.4)
91
- actionpack (= 5.2.4.4)
92
- activesupport (= 5.2.4.4)
92
+ railties (5.2.6)
93
+ actionpack (= 5.2.6)
94
+ activesupport (= 5.2.6)
93
95
  method_source
94
96
  rake (>= 0.8.7)
95
97
  thor (>= 0.19.0, < 2.0)
96
- rake (13.0.1)
98
+ rake (13.0.3)
97
99
  sprockets (4.0.2)
98
100
  concurrent-ruby (~> 1.0)
99
101
  rack (> 1, < 3)
@@ -101,11 +103,11 @@ GEM
101
103
  actionpack (>= 4.0)
102
104
  activesupport (>= 4.0)
103
105
  sprockets (>= 3.0.0)
104
- thor (1.0.1)
106
+ thor (1.1.0)
105
107
  thread_safe (0.3.6)
106
- tzinfo (1.2.8)
108
+ tzinfo (1.2.9)
107
109
  thread_safe (~> 0.1)
108
- websocket-driver (0.7.3)
110
+ websocket-driver (0.7.4)
109
111
  websocket-extensions (>= 0.1.0)
110
112
  websocket-extensions (0.1.5)
111
113
 
@@ -97,6 +97,7 @@ describe Bundler::Audit::CLI::Formats::JSON do
97
97
  expect(output_json[:results][0][:advisory][:cve]).to be == advisory.cve
98
98
  expect(output_json[:results][0][:advisory][:osvdb]).to be == advisory.osvdb
99
99
  expect(output_json[:results][0][:advisory][:ghsa]).to be == advisory.ghsa
100
+ expect(output_json[:results][0][:advisory][:criticality]).to be == advisory.criticality.to_s.downcase
100
101
  expect(output_json[:results][0][:advisory][:unaffected_versions]).to be == advisory.unaffected_versions.map(&:to_s)
101
102
  expect(output_json[:results][0][:advisory][:patched_versions]).to be == advisory.patched_versions.map(&:to_s)
102
103
  end
@@ -0,0 +1,284 @@
1
+ require 'spec_helper'
2
+ require 'bundler/audit/cli/formats'
3
+ require 'bundler/audit/cli/formats/junit'
4
+ require 'bundler/audit/report'
5
+
6
+ describe Bundler::Audit::CLI::Formats::Junit do
7
+ it "must register the 'junit' format" do
8
+ expect(Bundler::Audit::CLI::Formats[:junit]).to be described_class
9
+ end
10
+
11
+ let(:options) { {} }
12
+
13
+ subject do
14
+ Bundler::Audit::CLI.new([],options).tap do |obj|
15
+ obj.extend described_class
16
+ end
17
+ end
18
+
19
+ describe "#print_report" do
20
+ let(:report) { Bundler::Audit::Report.new }
21
+ let(:stdout) { StringIO.new }
22
+
23
+ before { subject.print_report(report,stdout) }
24
+
25
+ let(:output) { stdout.string }
26
+
27
+ context "when vulnerabilities were found" do
28
+ context "when the report contains InsecureSources" do
29
+ let(:uri) { URI('git://github.com/foo/bar.git') }
30
+ let(:insecure_source) do
31
+ Bundler::Audit::Results::InsecureSource.new(uri)
32
+ end
33
+
34
+ let(:report) do
35
+ super().tap do |report|
36
+ report << insecure_source
37
+ end
38
+ end
39
+
40
+ it 'must print "Insecure Source URI found: ..."' do
41
+ expect(output).to include("Insecure Source URI found: #{uri}")
42
+ end
43
+
44
+ it 'must print have a positive number of failures' do
45
+ expect(output).to match(/failures="[1-9][0-9]*"/)
46
+ end
47
+ end
48
+
49
+ context "when the report contains UnpatchedGems" do
50
+ let(:gem) do
51
+ Gem::Specification.new do |spec|
52
+ spec.name = 'test'
53
+ spec.version = '0.1.0'
54
+ end
55
+ end
56
+
57
+ let(:advisory) do
58
+ Bundler::Audit::Advisory.load(Fixtures.join('advisory','CVE-2020-1234.yml'))
59
+ end
60
+ let(:unpatched_gem) do
61
+ Bundler::Audit::Results::UnpatchedGem.new(gem,advisory)
62
+ end
63
+
64
+ let(:report) do
65
+ super().tap do |report|
66
+ report << unpatched_gem
67
+ end
68
+ end
69
+
70
+ it "must print 'Name: ...'" do
71
+ expect(output).to include("Name: #{gem.name}")
72
+ end
73
+
74
+ it "must print 'Version: ...'" do
75
+ expect(output).to include("Version: #{gem.version}")
76
+ end
77
+
78
+ context "when the advisory has a CVE ID" do
79
+ it "must print 'CVE: CVE-YYYY-NNNN'" do
80
+ expect(output).to include("Advisory: CVE-#{advisory.cve} GHSA-aaaa-bbbb-cccc")
81
+ end
82
+ end
83
+
84
+ context "when the advisory does not have a CVE ID" do
85
+ let(:advisory) do
86
+ super().tap do |advisory|
87
+ advisory.cve = nil
88
+ end
89
+ end
90
+
91
+ it "must not print 'CVE: CVE-YYYY-NNNN'" do
92
+ expect(output).to_not include("CVE-")
93
+ end
94
+ end
95
+
96
+ context "when the advisory has a GHSA ID" do
97
+ it "must print 'GHSA-xxxx-xxxx-xxxx'" do
98
+ expect(output).to include("GHSA-#{advisory.ghsa}")
99
+ end
100
+ end
101
+
102
+ context "when the advisory does not have a GHSA ID" do
103
+ let(:advisory) do
104
+ super().tap do |advisory|
105
+ advisory.ghsa = nil
106
+ end
107
+ end
108
+
109
+ it "must not print 'GHSA-xxxx-xxxx-xxxx'" do
110
+ expect(output).to_not include("GHSA-#{advisory.ghsa}")
111
+ end
112
+ end
113
+
114
+ context "when CVSS v3 is present" do
115
+ context "when Advisory#criticality is :none (cvss_v3 only)" do
116
+ let(:advisory) do
117
+ super().tap do |advisory|
118
+ advisory.cvss_v3 = 0.0
119
+ end
120
+ end
121
+
122
+ it "must print 'Criticality: None'" do
123
+ expect(output).to include("Criticality: None")
124
+ end
125
+ end
126
+
127
+ context "when Advisory#criticality is :low" do
128
+ let(:advisory) do
129
+ super().tap do |advisory|
130
+ advisory.cvss_v3 = 0.1
131
+ end
132
+ end
133
+
134
+ it "must print 'Criticality: Low'" do
135
+ expect(output).to include("Criticality: Low")
136
+ end
137
+ end
138
+
139
+ context "when Advisory#criticality is :medium" do
140
+ let(:advisory) do
141
+ super().tap do |advisory|
142
+ advisory.cvss_v3 = 4.0
143
+ end
144
+ end
145
+
146
+ it "must print 'Criticality: Medium'" do
147
+ expect(output).to include("Criticality: Medium")
148
+ end
149
+ end
150
+
151
+ context "when Advisory#criticality is :high" do
152
+ let(:advisory) do
153
+ super().tap do |advisory|
154
+ advisory.cvss_v3 = 7.0
155
+ end
156
+ end
157
+
158
+ it "must print 'Criticality: High'" do
159
+ expect(output).to include("Criticality: High")
160
+ end
161
+ end
162
+
163
+ context "when Advisory#criticality is :critical (cvss_v3 only)" do
164
+ let(:advisory) do
165
+ super().tap do |advisory|
166
+ advisory.cvss_v3 = 9.0
167
+ end
168
+ end
169
+
170
+ it "must print 'Criticality: High'" do
171
+ expect(output).to include("Criticality: Critical")
172
+ end
173
+ end
174
+ end
175
+
176
+ context "when CVSS v2 is present" do
177
+ let(:advisory) do
178
+ super().tap do |advisory|
179
+ advisory.cvss_v3 = nil
180
+ end
181
+ end
182
+
183
+ context "when Advisory#criticality is :low" do
184
+ let(:advisory) do
185
+ super().tap do |advisory|
186
+ advisory.cvss_v2 = 0.0
187
+ end
188
+ end
189
+
190
+ it "must print 'Criticality: Low'" do
191
+ expect(output).to include("Criticality: Low")
192
+ end
193
+ end
194
+
195
+ context "when Advisory#criticality is :medium" do
196
+ let(:advisory) do
197
+ super().tap do |advisory|
198
+ advisory.cvss_v2 = 4.0
199
+ end
200
+ end
201
+
202
+ it "must print 'Criticality: Medium'" do
203
+ expect(output).to include("Criticality: Medium")
204
+ end
205
+ end
206
+
207
+ context "when Advisory#criticality is :high" do
208
+ let(:advisory) do
209
+ super().tap do |advisory|
210
+ advisory.cvss_v2 = 7.0
211
+ end
212
+ end
213
+
214
+ it "must print 'Criticality: High'" do
215
+ expect(output).to include("Criticality: High")
216
+ end
217
+ end
218
+ end
219
+
220
+ it "must print 'URL: ...'" do
221
+ expect(output).to include("URL: #{advisory.url}")
222
+ end
223
+
224
+ context "when :verbose is not enabled" do
225
+ it 'must print "Title:" and the advisory description' do
226
+ expect(output).to include("Title: #{advisory.title}")
227
+ end
228
+ end
229
+
230
+ context "when Advisory#title contains XML special chars" do
231
+ let(:advisory) do
232
+ super().tap do |advisory|
233
+ advisory.title = '<entity id="one">One</entity>'
234
+ end
235
+ end
236
+
237
+ it 'must print "Title:" with escaped characters' do
238
+ expect(output).to include("Title: #{CGI.escapeHTML(advisory.title)}")
239
+ end
240
+ end
241
+
242
+ context "when Advisory#patched_versions is not empty" do
243
+ it 'must print "Solution: upgrade to ..."' do
244
+ expect(output).to include("Solution: upgrade to #{CGI.escapeHTML(advisory.patched_versions.join(', '))}")
245
+ end
246
+ end
247
+
248
+ context "when Advisory#patched_versions is empty" do
249
+ let(:advisory) do
250
+ super().tap do |advisory|
251
+ advisory.patched_versions = []
252
+ end
253
+ end
254
+
255
+ it 'must print "Solution: remove or disable this gem until a patch is available!"' do
256
+ expect(output).to include("Solution: remove or disable this gem until a patch is available!")
257
+ end
258
+ end
259
+
260
+ it 'must print have a positive number of failures' do
261
+ expect(output).to match(/failures="[1-9][0-9]*"/)
262
+ end
263
+ end
264
+ end
265
+
266
+ context "when no vulnerabilities were found" do
267
+ it 'must print an empty testsuite' do
268
+ expect(output).to include('failures="0"')
269
+ end
270
+
271
+ context "when :quiet is enabled" do
272
+ let(:options) { {quiet: true} }
273
+
274
+ it "should print nothing" do
275
+ expect(output).to be_empty
276
+ end
277
+ end
278
+ end
279
+
280
+ it "must restore $stdout" do
281
+ expect($stdout).to_not be(stdout)
282
+ end
283
+ end
284
+ end