bundler-leak 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.gitignore +11 -0
  4. data/.gitmodules +3 -0
  5. data/.rspec +1 -0
  6. data/.travis.yml +13 -0
  7. data/.yardopts +1 -0
  8. data/COPYING.txt +674 -0
  9. data/ChangeLog.md +125 -0
  10. data/Gemfile +15 -0
  11. data/README.md +118 -0
  12. data/Rakefile +57 -0
  13. data/bin/bundle-leak +10 -0
  14. data/bin/bundler-leak +3 -0
  15. data/bundler-leak.gemspec +67 -0
  16. data/data/ruby-mem-advisory-db.ts +1 -0
  17. data/data/ruby-mem-advisory-db/.gitignore +1 -0
  18. data/data/ruby-mem-advisory-db/.rspec +1 -0
  19. data/data/ruby-mem-advisory-db/.travis.yml +12 -0
  20. data/data/ruby-mem-advisory-db/CONTRIBUTING.md +69 -0
  21. data/data/ruby-mem-advisory-db/CONTRIBUTORS.md +40 -0
  22. data/data/ruby-mem-advisory-db/Gemfile +9 -0
  23. data/data/ruby-mem-advisory-db/LICENSE.txt +5 -0
  24. data/data/ruby-mem-advisory-db/README.md +72 -0
  25. data/data/ruby-mem-advisory-db/Rakefile +26 -0
  26. data/data/ruby-mem-advisory-db/gems/celluloid/670.yml +10 -0
  27. data/data/ruby-mem-advisory-db/gems/grape/301.yml +9 -0
  28. data/data/ruby-mem-advisory-db/gems/oj/229.yml +9 -0
  29. data/data/ruby-mem-advisory-db/gems/redcarpet/516.yml +12 -0
  30. data/data/ruby-mem-advisory-db/gems/redis/612.yml +9 -0
  31. data/data/ruby-mem-advisory-db/gems/sidekiq-statistic/73.yml +9 -0
  32. data/data/ruby-mem-advisory-db/gems/sidekiq/2598.yml +9 -0
  33. data/data/ruby-mem-advisory-db/gems/therubyracer/336.yml +13 -0
  34. data/data/ruby-mem-advisory-db/gems/zipruby/PRE-SA-2012-02.yml +9 -0
  35. data/data/ruby-mem-advisory-db/scripts/post-advisories.sh +18 -0
  36. data/data/ruby-mem-advisory-db/spec/advisories_spec.rb +23 -0
  37. data/data/ruby-mem-advisory-db/spec/advisory_example.rb +209 -0
  38. data/data/ruby-mem-advisory-db/spec/gem_example.rb +37 -0
  39. data/data/ruby-mem-advisory-db/spec/library_example.rb +21 -0
  40. data/data/ruby-mem-advisory-db/spec/ruby_example.rb +22 -0
  41. data/data/ruby-mem-advisory-db/spec/spec_helper.rb +1 -0
  42. data/gemspec.yml +14 -0
  43. data/lib/bundler/plumber.rb +20 -0
  44. data/lib/bundler/plumber/advisory.rb +119 -0
  45. data/lib/bundler/plumber/cli.rb +135 -0
  46. data/lib/bundler/plumber/database.rb +249 -0
  47. data/lib/bundler/plumber/scanner.rb +133 -0
  48. data/lib/bundler/plumber/task.rb +49 -0
  49. data/lib/bundler/plumber/version.rb +24 -0
  50. data/spec/advisory_spec.rb +155 -0
  51. data/spec/audit_spec.rb +8 -0
  52. data/spec/bundle/insecure_sources/Gemfile +39 -0
  53. data/spec/bundle/secure/Gemfile +38 -0
  54. data/spec/bundle/unpatched_gems/Gemfile +39 -0
  55. data/spec/cli_spec.rb +99 -0
  56. data/spec/database_spec.rb +138 -0
  57. data/spec/fixtures/not_a_hash.yml +2 -0
  58. data/spec/integration_spec.rb +68 -0
  59. data/spec/scanner_spec.rb +61 -0
  60. data/spec/spec_helper.rb +62 -0
  61. metadata +141 -0
@@ -0,0 +1,39 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "celluloid", "0.17.0"
4
+ gem "therubyracer", "0.12.1"
5
+
6
+ # Bundle edge Rails instead:
7
+ # gem 'rails', :git => 'git://github.com/rails/rails.git'
8
+
9
+ gem 'sqlite3', platform: [:mri, :rbx]
10
+
11
+
12
+ # Gems used only for assets and not required
13
+ # in production environments by default.
14
+ group :assets do
15
+ # gem 'sass-rails', '~> 3.2.3'
16
+ # gem 'coffee-rails', '~> 3.2.1'
17
+
18
+ # See https://github.com/sstephenson/execjs#readme for more supported runtimes
19
+ # gem 'therubyracer', :platforms => :ruby
20
+
21
+ # gem 'uglifier', '>= 1.0.3'
22
+ end
23
+
24
+ gem 'jquery-rails'
25
+
26
+ # To use ActiveModel has_secure_password
27
+ # gem 'bcrypt-ruby', '~> 3.0.0'
28
+
29
+ # To use Jbuilder templates for JSON
30
+ # gem 'jbuilder'
31
+
32
+ # Use unicorn as the app server
33
+ # gem 'unicorn'
34
+
35
+ # Deploy with Capistrano
36
+ # gem 'capistrano'
37
+
38
+ # To use debugger
39
+ # gem 'debugger'
data/spec/cli_spec.rb ADDED
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+ require 'bundler/plumber/cli'
3
+
4
+ describe Bundler::Plumber::CLI do
5
+ describe "#update" do
6
+ context "not --quiet (the default)" do
7
+ context "when update succeeds" do
8
+
9
+ before { expect(Bundler::Plumber::Database).to receive(:update!).and_return(true) }
10
+
11
+ it "prints updated message" do
12
+ expect { subject.update }.to output(/Updated ruby-mem-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::Plumber::Database).to receive(:new).and_return(database)
19
+
20
+ expect { subject.update }.to output(/ruby-mem-advisory-db: 1234 advisories/).to_stdout
21
+ end
22
+ end
23
+
24
+ context "when update fails" do
25
+
26
+ before { expect(Bundler::Plumber::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-mem-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::Plumber::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::Plumber::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-mem-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/plumber/database'
3
+ require 'tmpdir'
4
+
5
+ describe Bundler::Plumber::Database do
6
+ let(:vendored_advisories) do
7
+ Dir[File.join(Bundler::Plumber::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::Plumber::Database.update!(quiet: false)
19
+
20
+ Dir.chdir(Bundler::Plumber::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::Plumber::Database.path).to eq mocked_user_path
27
+
28
+ # More up to date...
29
+ fake_a_commit_in_the_user_repo
30
+ expect(Bundler::Plumber::Database.path).to eq mocked_user_path
31
+
32
+ roll_user_repo_back(20)
33
+ expect(Bundler::Plumber::Database.path).to eq Bundler::Plumber::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::Plumber::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::Plumber::Database.update!(quiet: false)
46
+ expect(File.directory?(mocked_user_path)).to be true
47
+
48
+ expect_update_to_update_repo!
49
+ Bundler::Plumber::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 = 'celluloid'
86
+ s.version = '0.16.1'
87
+ end
88
+ end
89
+
90
+ context "when given a block" do
91
+ it "should yield every advisory affecting 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::Plumber::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::Plumber::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::Plumber::Database.new.inspect).to eq("#<Bundler::Plumber::Database:#{Bundler::Plumber::Database::VENDORED_PATH}>")
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,2 @@
1
+ ---
2
+ "Just a string."
@@ -0,0 +1,68 @@
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-leak'))
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 = /(Name: [^\n]+
24
+ Version: \d+.\d+.\d+
25
+ URL: https?:\/\/(www\.)?.+
26
+ Title: [^\n]*?
27
+ Solution: remove or disable this gem until a patch is available!)+/
28
+
29
+ expect(subject).to match(advisory_pattern)
30
+ expect(subject).to include("Vulnerabilities found!")
31
+ end
32
+ end
33
+
34
+ context "when auditing a secure bundle" do
35
+ let(:bundle) { 'secure' }
36
+ let(:directory) { File.join('spec','bundle',bundle) }
37
+
38
+ subject do
39
+ Dir.chdir(directory) { sh(command) }
40
+ end
41
+
42
+ it "should print nothing when everything is fine" do
43
+ expect(subject.strip).to eq("No vulnerabilities found")
44
+ end
45
+ end
46
+
47
+ describe "update" do
48
+
49
+ let(:update_command) { "#{command} update" }
50
+ let(:bundle) { 'secure' }
51
+ let(:directory) { File.join('spec','bundle',bundle) }
52
+
53
+ subject do
54
+ Dir.chdir(directory) { sh(update_command) }
55
+ end
56
+
57
+ context "when advisories update successfully" do
58
+ it "should print status" do
59
+ expect(subject).not_to include("Fail")
60
+ expect(subject).to include("Updating ruby-mem-advisory-db ...\n")
61
+ expect(subject).to include("Updated ruby-mem-advisory-db\n")
62
+ expect(subject.lines.to_a.last).to match(/ruby-mem-advisory-db: \d+ advisories/)
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+ require 'bundler/plumber/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
+ expect(subject.all? { |result|
35
+ result.advisory.vulnerable?(result.gem.version)
36
+ }).to be_truthy
37
+ end
38
+
39
+ context "when the :ignore option is given" do
40
+ subject { scanner.scan(:ignore => ['OSVDB-89026']) }
41
+
42
+ it "should ignore the specified advisories" do
43
+ ids = subject.map { |result| result.advisory.id }
44
+
45
+ expect(ids).not_to include('OSVDB-89026')
46
+ end
47
+ end
48
+ end
49
+
50
+ context "when auditing a secure bundle" do
51
+ let(:bundle) { 'secure' }
52
+ let(:directory) { File.join('spec','bundle',bundle) }
53
+ let(:scanner) { described_class.new(directory) }
54
+
55
+ subject { scanner.scan.to_a }
56
+
57
+ it "should print nothing when everything is fine" do
58
+ expect(subject).to be_empty
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,62 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'rspec'
5
+ require 'bundler/plumber/version'
6
+ require 'bundler/plumber/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-mem-advisory-db', __FILE__)
23
+ end
24
+
25
+ def expect_update_to_clone_repo!
26
+ expect(Bundler::Plumber::Database).
27
+ to receive(:system).
28
+ with('git', 'clone', Bundler::Plumber::Database::VENDORED_PATH, mocked_user_path).
29
+ and_call_original
30
+ end
31
+
32
+ def expect_update_to_update_repo!
33
+ expect(Bundler::Plumber::Database).
34
+ to receive(:system).
35
+ with('git', 'pull', '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::Plumber
53
+
54
+ RSpec.configure do |config|
55
+ include Helpers
56
+
57
+ config.before(:each) do
58
+ stub_const("Bundler::Plumber::Database::URL", Bundler::Plumber::Database::VENDORED_PATH)
59
+ stub_const("Bundler::Plumber::Database::USER_PATH", mocked_user_path)
60
+ FileUtils.rm_rf(mocked_user_path) if File.exist?(mocked_user_path)
61
+ end
62
+ end