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
@@ -10,122 +10,120 @@ GIT
10
10
  GEM
11
11
  remote: http://rubygems.org/
12
12
  specs:
13
- actioncable (6.1.0)
14
- actionpack (= 6.1.0)
15
- activesupport (= 6.1.0)
13
+ actioncable (6.1.3.2)
14
+ actionpack (= 6.1.3.2)
15
+ activesupport (= 6.1.3.2)
16
16
  nio4r (~> 2.0)
17
17
  websocket-driver (>= 0.6.1)
18
- actionmailbox (6.1.0)
19
- actionpack (= 6.1.0)
20
- activejob (= 6.1.0)
21
- activerecord (= 6.1.0)
22
- activestorage (= 6.1.0)
23
- activesupport (= 6.1.0)
18
+ actionmailbox (6.1.3.2)
19
+ actionpack (= 6.1.3.2)
20
+ activejob (= 6.1.3.2)
21
+ activerecord (= 6.1.3.2)
22
+ activestorage (= 6.1.3.2)
23
+ activesupport (= 6.1.3.2)
24
24
  mail (>= 2.7.1)
25
- actionmailer (6.1.0)
26
- actionpack (= 6.1.0)
27
- actionview (= 6.1.0)
28
- activejob (= 6.1.0)
29
- activesupport (= 6.1.0)
25
+ actionmailer (6.1.3.2)
26
+ actionpack (= 6.1.3.2)
27
+ actionview (= 6.1.3.2)
28
+ activejob (= 6.1.3.2)
29
+ activesupport (= 6.1.3.2)
30
30
  mail (~> 2.5, >= 2.5.4)
31
31
  rails-dom-testing (~> 2.0)
32
- actionpack (6.1.0)
33
- actionview (= 6.1.0)
34
- activesupport (= 6.1.0)
32
+ actionpack (6.1.3.2)
33
+ actionview (= 6.1.3.2)
34
+ activesupport (= 6.1.3.2)
35
35
  rack (~> 2.0, >= 2.0.9)
36
36
  rack-test (>= 0.6.3)
37
37
  rails-dom-testing (~> 2.0)
38
38
  rails-html-sanitizer (~> 1.0, >= 1.2.0)
39
- actiontext (6.1.0)
40
- actionpack (= 6.1.0)
41
- activerecord (= 6.1.0)
42
- activestorage (= 6.1.0)
43
- activesupport (= 6.1.0)
39
+ actiontext (6.1.3.2)
40
+ actionpack (= 6.1.3.2)
41
+ activerecord (= 6.1.3.2)
42
+ activestorage (= 6.1.3.2)
43
+ activesupport (= 6.1.3.2)
44
44
  nokogiri (>= 1.8.5)
45
- actionview (6.1.0)
46
- activesupport (= 6.1.0)
45
+ actionview (6.1.3.2)
46
+ activesupport (= 6.1.3.2)
47
47
  builder (~> 3.1)
48
48
  erubi (~> 1.4)
49
49
  rails-dom-testing (~> 2.0)
50
50
  rails-html-sanitizer (~> 1.1, >= 1.2.0)
51
- activejob (6.1.0)
52
- activesupport (= 6.1.0)
51
+ activejob (6.1.3.2)
52
+ activesupport (= 6.1.3.2)
53
53
  globalid (>= 0.3.6)
54
- activemodel (6.1.0)
55
- activesupport (= 6.1.0)
56
- activerecord (6.1.0)
57
- activemodel (= 6.1.0)
58
- activesupport (= 6.1.0)
59
- activestorage (6.1.0)
60
- actionpack (= 6.1.0)
61
- activejob (= 6.1.0)
62
- activerecord (= 6.1.0)
63
- activesupport (= 6.1.0)
64
- marcel (~> 0.3.1)
65
- mimemagic (~> 0.3.2)
66
- activesupport (6.1.0)
54
+ activemodel (6.1.3.2)
55
+ activesupport (= 6.1.3.2)
56
+ activerecord (6.1.3.2)
57
+ activemodel (= 6.1.3.2)
58
+ activesupport (= 6.1.3.2)
59
+ activestorage (6.1.3.2)
60
+ actionpack (= 6.1.3.2)
61
+ activejob (= 6.1.3.2)
62
+ activerecord (= 6.1.3.2)
63
+ activesupport (= 6.1.3.2)
64
+ marcel (~> 1.0.0)
65
+ mini_mime (~> 1.0.2)
66
+ activesupport (6.1.3.2)
67
67
  concurrent-ruby (~> 1.0, >= 1.0.2)
68
68
  i18n (>= 1.6, < 2)
69
69
  minitest (>= 5.1)
70
70
  tzinfo (~> 2.0)
71
71
  zeitwerk (~> 2.3)
72
72
  builder (3.2.4)
73
- concurrent-ruby (1.1.7)
73
+ concurrent-ruby (1.1.8)
74
74
  crass (1.0.6)
75
75
  erubi (1.10.0)
76
76
  globalid (0.4.2)
77
77
  activesupport (>= 4.2.0)
78
- i18n (1.8.5)
78
+ i18n (1.8.10)
79
79
  concurrent-ruby (~> 1.0)
80
- loofah (2.8.0)
80
+ loofah (2.9.1)
81
81
  crass (~> 1.0.2)
82
82
  nokogiri (>= 1.5.9)
83
83
  mail (2.7.1)
84
84
  mini_mime (>= 0.1.1)
85
- marcel (0.3.3)
86
- mimemagic (~> 0.3.2)
85
+ marcel (1.0.1)
87
86
  method_source (1.0.0)
88
- mimemagic (0.3.5)
89
- mini_mime (1.0.2)
90
- mini_portile2 (2.5.0)
91
- minitest (5.14.2)
92
- nio4r (2.5.4)
93
- nokogiri (1.11.1)
87
+ mini_mime (1.0.3)
88
+ mini_portile2 (2.5.1)
89
+ minitest (5.14.4)
90
+ nio4r (2.5.7)
91
+ nokogiri (1.11.6)
94
92
  mini_portile2 (~> 2.5.0)
95
93
  racc (~> 1.4)
96
- nokogiri (1.11.1-x86_64-linux)
94
+ nokogiri (1.11.6-x86_64-linux)
97
95
  racc (~> 1.4)
98
96
  racc (1.5.2)
99
97
  rack (2.2.3)
100
98
  rack-test (1.1.0)
101
99
  rack (>= 1.0, < 3)
102
- rails (6.1.0)
103
- actioncable (= 6.1.0)
104
- actionmailbox (= 6.1.0)
105
- actionmailer (= 6.1.0)
106
- actionpack (= 6.1.0)
107
- actiontext (= 6.1.0)
108
- actionview (= 6.1.0)
109
- activejob (= 6.1.0)
110
- activemodel (= 6.1.0)
111
- activerecord (= 6.1.0)
112
- activestorage (= 6.1.0)
113
- activesupport (= 6.1.0)
100
+ rails (6.1.3.2)
101
+ actioncable (= 6.1.3.2)
102
+ actionmailbox (= 6.1.3.2)
103
+ actionmailer (= 6.1.3.2)
104
+ actionpack (= 6.1.3.2)
105
+ actiontext (= 6.1.3.2)
106
+ actionview (= 6.1.3.2)
107
+ activejob (= 6.1.3.2)
108
+ activemodel (= 6.1.3.2)
109
+ activerecord (= 6.1.3.2)
110
+ activestorage (= 6.1.3.2)
111
+ activesupport (= 6.1.3.2)
114
112
  bundler (>= 1.15.0)
115
- railties (= 6.1.0)
113
+ railties (= 6.1.3.2)
116
114
  sprockets-rails (>= 2.0.0)
117
115
  rails-dom-testing (2.0.3)
118
116
  activesupport (>= 4.2.0)
119
117
  nokogiri (>= 1.6)
120
118
  rails-html-sanitizer (1.3.0)
121
119
  loofah (~> 2.3)
122
- railties (6.1.0)
123
- actionpack (= 6.1.0)
124
- activesupport (= 6.1.0)
120
+ railties (6.1.3.2)
121
+ actionpack (= 6.1.3.2)
122
+ activesupport (= 6.1.3.2)
125
123
  method_source
126
124
  rake (>= 0.8.7)
127
125
  thor (~> 1.0)
128
- rake (13.0.1)
126
+ rake (13.0.3)
129
127
  sprockets (4.0.2)
130
128
  concurrent-ruby (~> 1.0)
131
129
  rack (> 1, < 3)
@@ -133,10 +131,10 @@ GEM
133
131
  actionpack (>= 4.0)
134
132
  activesupport (>= 4.0)
135
133
  sprockets (>= 3.0.0)
136
- thor (1.0.1)
137
- tzinfo (2.0.3)
134
+ thor (1.1.0)
135
+ tzinfo (2.0.4)
138
136
  concurrent-ruby (~> 1.0)
139
- websocket-driver (0.7.3)
137
+ websocket-driver (0.7.4)
140
138
  websocket-extensions (>= 0.1.0)
141
139
  websocket-extensions (0.1.5)
142
140
  zeitwerk (2.4.2)
@@ -1,103 +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.5.0)
65
- minitest (5.14.2)
66
- nio4r (2.5.4)
67
- nokogiri (1.11.1)
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)
68
66
  mini_portile2 (~> 2.5.0)
69
67
  racc (~> 1.4)
70
- nokogiri (1.11.1-x86_64-linux)
68
+ nokogiri (1.11.6-x86_64-linux)
71
69
  racc (~> 1.4)
72
70
  racc (1.5.2)
73
71
  rack (2.2.3)
74
72
  rack-test (1.1.0)
75
73
  rack (>= 1.0, < 3)
76
- rails (5.2.4.4)
77
- actioncable (= 5.2.4.4)
78
- actionmailer (= 5.2.4.4)
79
- actionpack (= 5.2.4.4)
80
- actionview (= 5.2.4.4)
81
- activejob (= 5.2.4.4)
82
- activemodel (= 5.2.4.4)
83
- activerecord (= 5.2.4.4)
84
- activestorage (= 5.2.4.4)
85
- 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)
86
84
  bundler (>= 1.3.0)
87
- railties (= 5.2.4.4)
85
+ railties (= 5.2.6)
88
86
  sprockets-rails (>= 2.0.0)
89
87
  rails-dom-testing (2.0.3)
90
88
  activesupport (>= 4.2.0)
91
89
  nokogiri (>= 1.6)
92
90
  rails-html-sanitizer (1.0.4)
93
91
  loofah (~> 2.2, >= 2.2.2)
94
- railties (5.2.4.4)
95
- actionpack (= 5.2.4.4)
96
- activesupport (= 5.2.4.4)
92
+ railties (5.2.6)
93
+ actionpack (= 5.2.6)
94
+ activesupport (= 5.2.6)
97
95
  method_source
98
96
  rake (>= 0.8.7)
99
97
  thor (>= 0.19.0, < 2.0)
100
- rake (13.0.1)
98
+ rake (13.0.3)
101
99
  sprockets (4.0.2)
102
100
  concurrent-ruby (~> 1.0)
103
101
  rack (> 1, < 3)
@@ -105,11 +103,11 @@ GEM
105
103
  actionpack (>= 4.0)
106
104
  activesupport (>= 4.0)
107
105
  sprockets (>= 3.0.0)
108
- thor (1.0.1)
106
+ thor (1.1.0)
109
107
  thread_safe (0.3.6)
110
- tzinfo (1.2.8)
108
+ tzinfo (1.2.9)
111
109
  thread_safe (~> 0.1)
112
- websocket-driver (0.7.3)
110
+ websocket-driver (0.7.4)
113
111
  websocket-extensions (>= 0.1.0)
114
112
  websocket-extensions (0.1.5)
115
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