bundler-audit-ng 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+ require 'bundler/audit'
3
+
4
+ describe Bundler::Audit do
5
+ it "should have a VERSION constant" do
6
+ expect(subject.const_get('VERSION')).not_to be_empty
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'rails'
4
+ gem 'jquery-rails', git: 'git://github.com/rails/jquery-rails.git'
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 5.2'
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '3.2.10'
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+ require 'bundler/audit/cli'
3
+
4
+ describe Bundler::Audit::CLI do
5
+ describe "#update" do
6
+ context "not --quiet (the default)" do
7
+ context "when update succeeds" do
8
+
9
+ before { expect(Bundler::Audit::Database).to receive(:update!).and_return(true) }
10
+
11
+ it "prints updated message" do
12
+ expect { subject.update }.to output(/Updated ruby-advisory-db/).to_stdout
13
+ end
14
+
15
+ it "prints total advisory count" do
16
+ database = double
17
+ expect(database).to receive(:size).and_return(1234)
18
+ expect(Bundler::Audit::Database).to receive(:new).and_return(database)
19
+
20
+ expect { subject.update }.to output(/ruby-advisory-db: 1234 advisories/).to_stdout
21
+ end
22
+ end
23
+
24
+ context "when update fails" do
25
+
26
+ before { expect(Bundler::Audit::Database).to receive(:update!).and_return(false) }
27
+
28
+ it "prints failure message" do
29
+ expect do
30
+ begin
31
+ subject.update
32
+ rescue SystemExit
33
+ end
34
+ end.to output(/Failed updating ruby-advisory-db!/).to_stdout
35
+ end
36
+
37
+ it "exits with error status code" do
38
+ expect {
39
+ # Capture output of `update` only to keep spec output clean.
40
+ # The test regarding specific output is above.
41
+ expect { subject.update }.to output.to_stdout
42
+ }.to raise_error(SystemExit) do |error|
43
+ expect(error.success?).to eq(false)
44
+ expect(error.status).to eq(1)
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+
51
+ context "--quiet" do
52
+ before do
53
+ allow(subject).to receive(:options).and_return(double("Options", quiet?: true))
54
+ end
55
+
56
+ context "when update succeeds" do
57
+
58
+ before do
59
+ expect(Bundler::Audit::Database).to(
60
+ receive(:update!).with(quiet: true).and_return(true)
61
+ )
62
+ end
63
+
64
+ it "does not print any output" do
65
+ expect { subject.update }.to_not output.to_stdout
66
+ end
67
+ end
68
+
69
+ context "when update fails" do
70
+
71
+ before do
72
+ expect(Bundler::Audit::Database).to(
73
+ receive(:update!).with(quiet: true).and_return(false)
74
+ )
75
+ end
76
+
77
+ it "prints failure message" do
78
+ expect do
79
+ begin
80
+ subject.update
81
+ rescue SystemExit
82
+ end
83
+ end.to output(/Failed updating ruby-advisory-db!/).to_stdout
84
+ end
85
+
86
+ it "exits with error status code" do
87
+ expect {
88
+ # Capture output of `update` only to keep spec output clean.
89
+ # The test regarding specific output is above.
90
+ expect { subject.update }.to output.to_stdout
91
+ }.to raise_error(SystemExit) do |error|
92
+ expect(error.success?).to eq(false)
93
+ expect(error.status).to eq(1)
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,138 @@
1
+ require 'spec_helper'
2
+ require 'bundler/audit/database'
3
+ require 'tmpdir'
4
+
5
+ describe Bundler::Audit::Database do
6
+ let(:vendored_advisories) do
7
+ Dir[File.join(Bundler::Audit::Database::VENDORED_PATH, 'gems/*/*.yml')].sort
8
+ end
9
+
10
+ describe "path" do
11
+ subject { described_class.path }
12
+
13
+ it "it should be a directory" do
14
+ expect(File.directory?(subject)).to be_truthy
15
+ end
16
+
17
+ it "should prefer the user repo, iff it's as up to date, or more up to date than the vendored one" do
18
+ Bundler::Audit::Database.update!(quiet: false)
19
+
20
+ Dir.chdir(Bundler::Audit::Database::USER_PATH) do
21
+ puts "Timestamp:"
22
+ system 'git log --pretty="%cd" -1'
23
+ end
24
+
25
+ # As up to date...
26
+ expect(Bundler::Audit::Database.path).to eq mocked_user_path
27
+
28
+ # More up to date...
29
+ fake_a_commit_in_the_user_repo
30
+ expect(Bundler::Audit::Database.path).to eq mocked_user_path
31
+
32
+ roll_user_repo_back(20)
33
+ expect(Bundler::Audit::Database.path).to eq Bundler::Audit::Database::VENDORED_PATH
34
+ end
35
+ end
36
+
37
+ describe "update!" do
38
+ it "should create the USER_PATH path as needed" do
39
+ Bundler::Audit::Database.update!(quiet: false)
40
+ expect(File.directory?(mocked_user_path)).to be true
41
+ end
42
+
43
+ it "should create the repo, then update it given multple successive calls." do
44
+ expect_update_to_clone_repo!
45
+ Bundler::Audit::Database.update!(quiet: false)
46
+ expect(File.directory?(mocked_user_path)).to be true
47
+
48
+ expect_update_to_update_repo!
49
+ Bundler::Audit::Database.update!(quiet: false)
50
+ expect(File.directory?(mocked_user_path)).to be true
51
+ end
52
+ end
53
+
54
+ describe "#initialize" do
55
+ context "when given no arguments" do
56
+ subject { described_class.new }
57
+
58
+ it "should default path to path" do
59
+ expect(subject.path).to eq(described_class.path)
60
+ end
61
+ end
62
+
63
+ context "when given a directory" do
64
+ let(:path ) { Dir.tmpdir }
65
+
66
+ subject { described_class.new(path) }
67
+
68
+ it "should set #path" do
69
+ expect(subject.path).to eq(path)
70
+ end
71
+ end
72
+
73
+ context "when given an invalid directory" do
74
+ it "should raise an ArgumentError" do
75
+ expect {
76
+ described_class.new('/foo/bar/baz')
77
+ }.to raise_error(ArgumentError)
78
+ end
79
+ end
80
+ end
81
+
82
+ describe "#check_gem" do
83
+ let(:gem) do
84
+ Gem::Specification.new do |s|
85
+ s.name = 'actionpack'
86
+ s.version = '3.1.9'
87
+ end
88
+ end
89
+
90
+ context "when given a block" do
91
+ it "should yield every advisory effecting the gem" do
92
+ advisories = []
93
+
94
+ subject.check_gem(gem) do |advisory|
95
+ advisories << advisory
96
+ end
97
+
98
+ expect(advisories).not_to be_empty
99
+ expect(advisories.all? { |advisory|
100
+ advisory.kind_of?(Bundler::Audit::Advisory)
101
+ }).to be_truthy
102
+ end
103
+ end
104
+
105
+ context "when given no block" do
106
+ it "should return an Enumerator" do
107
+ expect(subject.check_gem(gem)).to be_kind_of(Enumerable)
108
+ end
109
+ end
110
+ end
111
+
112
+ describe "#size" do
113
+ it { expect(subject.size).to eq vendored_advisories.count }
114
+ end
115
+
116
+ describe "#advisories" do
117
+ it "should return a list of all advisories." do
118
+ actual_advisories = Bundler::Audit::Database.new.
119
+ advisories.
120
+ map(&:path).
121
+ sort
122
+
123
+ expect(actual_advisories).to eq vendored_advisories
124
+ end
125
+ end
126
+
127
+ describe "#to_s" do
128
+ it "should return the Database path" do
129
+ expect(subject.to_s).to eq(subject.path)
130
+ end
131
+ end
132
+
133
+ describe "#inspect" do
134
+ it "should produce a Ruby-ish instance descriptor" do
135
+ expect(Bundler::Audit::Database.new.inspect).to eq("#<Bundler::Audit::Database:#{Bundler::Audit::Database::VENDORED_PATH}>")
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,2 @@
1
+ ---
2
+ "Just a string."
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ describe "CLI" do
4
+ include Helpers
5
+
6
+ let(:command) do
7
+ File.expand_path(File.join(File.dirname(__FILE__),'..','bin','bundler-audit'))
8
+ end
9
+
10
+ context "when auditing a bundle with unpatched gems" do
11
+ let(:bundle) { 'unpatched_gems' }
12
+ let(:directory) { File.join('spec','bundle',bundle) }
13
+
14
+ subject do
15
+ Dir.chdir(directory) { sh(command, :fail => true) }
16
+ end
17
+
18
+ it "should print a warning" do
19
+ expect(subject).to include("Vulnerabilities found!")
20
+ end
21
+
22
+ it "should print advisory information for the vulnerable gems" do
23
+ advisory_pattern = %r{(Name: [^\n]+
24
+ Version: \d+\.\d+\.\d+(\.\d+)?
25
+ Advisory: CVE-[0-9]{4}-[0-9]{4}
26
+ Criticality: (High|Medium|Low|Unknown)
27
+ URL: https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#!?&//=]*)
28
+ Title: [^\n]*?
29
+ Solution: upgrade to (~>|>=) \d+\.\d+\.\d+(\.\d+)?(, (~>|>=) \d+\.\d+\.\d+(\.\d+)?)*[\s\n]*?)}
30
+
31
+ expect(subject).to match(advisory_pattern)
32
+ expect(subject).to include("Vulnerabilities found!")
33
+ end
34
+ end
35
+
36
+ context "when auditing a bundle with ignored gems" do
37
+ let(:bundle) { 'unpatched_gems' }
38
+ let(:directory) { File.join('spec','bundle',bundle) }
39
+
40
+ let(:command) do
41
+ File.expand_path(File.join(File.dirname(__FILE__),'..','bin','bundler-audit -i OSVDB-89026'))
42
+ end
43
+
44
+ subject do
45
+ Dir.chdir(directory) { sh(command, :fail => true) }
46
+ end
47
+
48
+ it "should not print advisory information for ignored gem" do
49
+ expect(subject).not_to include("OSVDB-89026")
50
+ end
51
+ end
52
+
53
+ context "when auditing a bundle with insecure sources" do
54
+ let(:bundle) { 'insecure_sources' }
55
+ let(:directory) { File.join('spec','bundle',bundle) }
56
+
57
+ subject do
58
+ Dir.chdir(directory) { sh(command, :fail => true) }
59
+ end
60
+
61
+ it "should print warnings about insecure sources" do
62
+ expect(subject).to include(%{
63
+ Insecure Source URI found: git://github.com/rails/jquery-rails.git
64
+ Insecure Source URI found: http://rubygems.org/
65
+ }.strip)
66
+ end
67
+ end
68
+
69
+ context "when auditing a secure bundle" do
70
+ let(:bundle) { 'secure' }
71
+ let(:directory) { File.join('spec','bundle',bundle) }
72
+
73
+ subject do
74
+ Dir.chdir(directory) { sh(command) }
75
+ end
76
+
77
+ it "should print nothing when everything is fine" do
78
+ expect(subject.strip).to eq("No vulnerabilities found")
79
+ end
80
+ end
81
+
82
+ describe "update" do
83
+
84
+ let(:update_command) { "#{command} update" }
85
+ let(:bundle) { 'secure' }
86
+ let(:directory) { File.join('spec','bundle',bundle) }
87
+
88
+ subject do
89
+ Dir.chdir(directory) { sh(update_command) }
90
+ end
91
+
92
+ context "when advisories update successfully" do
93
+ it "should print status" do
94
+ expect(subject).not_to include("Fail")
95
+ expect(subject).to include("Updating ruby-advisory-db ...\n")
96
+ expect(subject).to include("Updated ruby-advisory-db\n")
97
+ expect(subject.lines.to_a.last).to match(/ruby-advisory-db: [1-9]\d+ advisories/)
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+ require 'bundler/audit/scanner'
3
+
4
+ describe Scanner do
5
+ describe "#scan" do
6
+ let(:bundle) { 'unpatched_gems' }
7
+ let(:directory) { File.join('spec','bundle',bundle) }
8
+
9
+ subject { described_class.new(directory) }
10
+
11
+ it "should yield results" do
12
+ results = []
13
+
14
+ subject.scan { |result| results << result }
15
+
16
+ expect(results).not_to be_empty
17
+ end
18
+
19
+ context "when not called with a block" do
20
+ it "should return an Enumerator" do
21
+ expect(subject.scan).to be_kind_of(Enumerable)
22
+ end
23
+ end
24
+ end
25
+
26
+ context "when auditing a bundle with unpatched gems" do
27
+ let(:bundle) { 'unpatched_gems' }
28
+ let(:directory) { File.join('spec','bundle',bundle) }
29
+ let(:scanner) { described_class.new(directory) }
30
+
31
+ subject { scanner.scan.to_a }
32
+
33
+ it "should match unpatched gems to their advisories" do
34
+ ids = subject.map { |result| result.advisory.id }
35
+ expect(ids).to include('OSVDB-89025')
36
+ expect(subject.all? { |result|
37
+ result.advisory.vulnerable?(result.gem.version)
38
+ }).to be_truthy
39
+ end
40
+
41
+ context "when the :ignore option is given" do
42
+ subject { scanner.scan(:ignore => ['OSVDB-89025']) }
43
+
44
+ it "should ignore the specified advisories" do
45
+ ids = subject.map { |result| result.advisory.id }
46
+ expect(ids).not_to include('OSVDB-89025')
47
+ end
48
+ end
49
+ end
50
+
51
+ context "when auditing a bundle with insecure sources" do
52
+ let(:bundle) { 'insecure_sources' }
53
+ let(:directory) { File.join('spec','bundle',bundle) }
54
+ let(:scanner) { described_class.new(directory) }
55
+
56
+ subject { scanner.scan.to_a }
57
+
58
+ it "should match unpatched gems to their advisories" do
59
+ expect(subject[0].source).to eq('git://github.com/rails/jquery-rails.git')
60
+ expect(subject[1].source).to eq('http://rubygems.org/')
61
+ end
62
+ end
63
+
64
+ context "when auditing a secure bundle" do
65
+ let(:bundle) { 'secure' }
66
+ let(:directory) { File.join('spec','bundle',bundle) }
67
+ let(:scanner) { described_class.new(directory) }
68
+
69
+ subject { scanner.scan.to_a }
70
+
71
+ it "should print nothing when everything is fine" do
72
+ expect(subject).to be_empty
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,62 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'rspec'
5
+ require 'bundler/audit/version'
6
+ require 'bundler/audit/database'
7
+
8
+ module Helpers
9
+ def sh(command, options={})
10
+ Bundler.with_clean_env do
11
+ result = `#{command} 2>&1`
12
+ raise "FAILED #{command}\n#{result}" if $?.success? == !!options[:fail]
13
+ result
14
+ end
15
+ end
16
+
17
+ def decolorize(string)
18
+ string.gsub(/\e\[\d+m/, "")
19
+ end
20
+
21
+ def mocked_user_path
22
+ File.expand_path('../../tmp/ruby-advisory-db', __FILE__)
23
+ end
24
+
25
+ def expect_update_to_clone_repo!
26
+ expect(Bundler::Audit::Database).
27
+ to receive(:system).
28
+ with('git', 'clone', Bundler::Audit::Database::VENDORED_PATH, mocked_user_path).
29
+ and_call_original
30
+ end
31
+
32
+ def expect_update_to_update_repo!
33
+ expect(Bundler::Audit::Database).
34
+ to receive(:system).
35
+ with('git', 'pull', '--no-rebase', 'origin', 'master').
36
+ and_call_original
37
+ end
38
+
39
+ def fake_a_commit_in_the_user_repo
40
+ Dir.chdir(mocked_user_path) do
41
+ system 'git', 'commit', '--allow-empty', '-m', 'Dummy commit.'
42
+ end
43
+ end
44
+
45
+ def roll_user_repo_back(num_commits)
46
+ Dir.chdir(mocked_user_path) do
47
+ system 'git', 'reset', '--hard', "HEAD~#{num_commits}"
48
+ end
49
+ end
50
+ end
51
+
52
+ include Bundler::Audit
53
+
54
+ RSpec.configure do |config|
55
+ include Helpers
56
+
57
+ config.before(:each) do
58
+ stub_const("Bundler::Audit::Database::URL", Bundler::Audit::Database::VENDORED_PATH)
59
+ stub_const("Bundler::Audit::Database::USER_PATH", mocked_user_path)
60
+ FileUtils.rm_rf(mocked_user_path) if File.exist?(mocked_user_path)
61
+ end
62
+ end