bundler-organization_audit 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/.gitignore +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +33 -0
- data/Rakefile +6 -0
- data/Readme.md +76 -0
- data/bin/bundle-organization-audit +35 -0
- data/bundler-organization_audit.gemspec +16 -0
- data/gem-public_cert.pem +20 -0
- data/lib/bundler/organization_audit.rb +68 -0
- data/lib/bundler/organization_audit/repo.rb +101 -0
- data/lib/bundler/organization_audit/version.rb +5 -0
- data/spec/bundler/organization_audit/repo_spec.rb +58 -0
- data/spec/bundler/organization_audit_spec.rb +112 -0
- data/spec/private.example.yml +7 -0
- data/spec/spec_helper.rb +1 -0
- metadata +111 -0
- metadata.gz.sig +0 -0
data.tar.gz.sig
ADDED
Binary file
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
spec/private.yml
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
bundler-organization_audit (0.1.0)
|
5
|
+
json
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
bump (0.3.9)
|
11
|
+
bundler-audit (0.1.2)
|
12
|
+
bundler (~> 1.2)
|
13
|
+
diff-lcs (1.1.3)
|
14
|
+
json (1.7.7)
|
15
|
+
rake (10.0.3)
|
16
|
+
rspec (2.12.0)
|
17
|
+
rspec-core (~> 2.12.0)
|
18
|
+
rspec-expectations (~> 2.12.0)
|
19
|
+
rspec-mocks (~> 2.12.0)
|
20
|
+
rspec-core (2.12.2)
|
21
|
+
rspec-expectations (2.12.1)
|
22
|
+
diff-lcs (~> 1.1.3)
|
23
|
+
rspec-mocks (2.12.2)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
ruby
|
27
|
+
|
28
|
+
DEPENDENCIES
|
29
|
+
bump
|
30
|
+
bundler-audit
|
31
|
+
bundler-organization_audit!
|
32
|
+
rake
|
33
|
+
rspec (~> 2)
|
data/Rakefile
ADDED
data/Readme.md
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
Audit all Gemfiles of a user/organization on Github for unpatched versions
|
2
|
+
|
3
|
+
gem install bundler-organization_audit
|
4
|
+
|
5
|
+
Usage
|
6
|
+
=====
|
7
|
+
|
8
|
+
### Public repos
|
9
|
+
For yourself (git config github.user)
|
10
|
+
```Bash
|
11
|
+
bundle-organization-audit
|
12
|
+
parallel
|
13
|
+
No Gemfile.lock found
|
14
|
+
|
15
|
+
parllel_tests
|
16
|
+
bundle-audit
|
17
|
+
No unpatched versions found
|
18
|
+
|
19
|
+
rails_example_app
|
20
|
+
bundle-audit
|
21
|
+
Name: rack
|
22
|
+
Version: 1.4.4
|
23
|
+
CVE: 2013-0263
|
24
|
+
Criticality: High
|
25
|
+
URL: http://osvdb.org/show/osvdb/89939
|
26
|
+
Title: Rack Rack::Session::Cookie Function Timing Attack Remote Code Execution
|
27
|
+
Patched Versions: ~> 1.1.6, ~> 1.2.8, ~> 1.3.10, ~> 1.4.5, >= 1.5.2
|
28
|
+
|
29
|
+
Vulnerable:
|
30
|
+
https://github.com/grosser/rails_example_app
|
31
|
+
```
|
32
|
+
|
33
|
+
For someone elese
|
34
|
+
```Bash
|
35
|
+
bundle-organization-audit --user grosser
|
36
|
+
```
|
37
|
+
|
38
|
+
Ignore gems (ignores repos that have a %{repo}.gemspec)
|
39
|
+
```Bash
|
40
|
+
bundle-organization-audit --ignore-gems
|
41
|
+
```
|
42
|
+
|
43
|
+
For pipe -> only show vulnerable repos
|
44
|
+
```
|
45
|
+
bundle-organization-audit 2>/dev/null
|
46
|
+
```
|
47
|
+
|
48
|
+
Use for CI -> ignore old/unmaintained proejcts
|
49
|
+
```
|
50
|
+
bundle-organization-audit \
|
51
|
+
--ignore https://github.com/xxx/a \
|
52
|
+
--ignore https://github.com/xxx/b \
|
53
|
+
--organization xxx \
|
54
|
+
--token yyy
|
55
|
+
```
|
56
|
+
|
57
|
+
### Private repos
|
58
|
+
|
59
|
+
```Bash
|
60
|
+
# create a token that has access to your repositories
|
61
|
+
curl -v -u your-user-name -X POST https://api.github.com/authorizations --data '{"scopes":["repo"]}'
|
62
|
+
enter your password -> TOKEN
|
63
|
+
|
64
|
+
bundle-organization-audit --user your-user --token TOKEN --organization your-organization
|
65
|
+
```
|
66
|
+
|
67
|
+
Dev
|
68
|
+
===
|
69
|
+
- test private repo fetching via `cp spec/private{.example,}.yml` and filling it out
|
70
|
+
|
71
|
+
Author
|
72
|
+
======
|
73
|
+
[Michael Grosser](http://grosser.it)<br/>
|
74
|
+
michael@grosser.it<br/>
|
75
|
+
License: MIT<br/>
|
76
|
+
[![Build Status](https://travis-ci.org/grosser/bundler-organization_audit.png)](https://travis-ci.org/grosser/bundler-organization_audit)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "rubygems"
|
3
|
+
require "optparse"
|
4
|
+
|
5
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
6
|
+
require "bundler/organization_audit"
|
7
|
+
|
8
|
+
def git_config(thing)
|
9
|
+
result = `git config #{thing}`.strip
|
10
|
+
result.empty? ? nil : result
|
11
|
+
end
|
12
|
+
|
13
|
+
options = {
|
14
|
+
:ignore => [],
|
15
|
+
:user => git_config("github.user")
|
16
|
+
}
|
17
|
+
OptionParser.new do |opts|
|
18
|
+
opts.banner = <<BANNER
|
19
|
+
Audit all Gemfiles of a user/organization on github for unpatched versions
|
20
|
+
|
21
|
+
Usage:
|
22
|
+
bundle-organization-audit your-user-name
|
23
|
+
|
24
|
+
Options:
|
25
|
+
BANNER
|
26
|
+
opts.on("--token TOKEN", "Use token") { |token| options[:token] = token }
|
27
|
+
opts.on("--user USER", "Use user") { |user| options[:user] = user }
|
28
|
+
opts.on("--ignore REPO_URL", "Ignore given repo urls (use multiple times)") { |repo_url| options[:ignore] << repo_url }
|
29
|
+
opts.on("--ignore-gems", "Ignore repos that have a %{repo}.gemspec") { options[:ignore_gems] = true }
|
30
|
+
opts.on("--organization ORGANIZATION", "Use user") { |organization| options[:organization] = organization }
|
31
|
+
opts.on("-h", "--help", "Show this.") { puts opts; exit }
|
32
|
+
opts.on("-v", "--version", "Show Version"){ puts Bundler::OrganizationAudit::VERSION; exit}
|
33
|
+
end.parse!
|
34
|
+
|
35
|
+
exit Bundler::OrganizationAudit.run(options)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
2
|
+
name = "bundler-organization_audit"
|
3
|
+
require "#{name.gsub("-","/")}/version"
|
4
|
+
|
5
|
+
Gem::Specification.new name, Bundler::OrganizationAudit::VERSION do |s|
|
6
|
+
s.summary = s.description = "Audit all Gemfiles of a user/organization on github for unpatched versions"
|
7
|
+
s.authors = ["Michael Grosser"]
|
8
|
+
s.email = "michael@grosser.it"
|
9
|
+
s.homepage = "http://github.com/grosser/#{name}"
|
10
|
+
s.files = `git ls-files`.split("\n")
|
11
|
+
s.license = "MIT"
|
12
|
+
s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem")
|
13
|
+
s.executables = ["bundle-organization-audit"]
|
14
|
+
s.cert_chain = ["gem-public_cert.pem"]
|
15
|
+
s.add_runtime_dependency "json"
|
16
|
+
end
|
data/gem-public_cert.pem
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDMjCCAhqgAwIBAgIBADANBgkqhkiG9w0BAQUFADA/MRAwDgYDVQQDDAdtaWNo
|
3
|
+
YWVsMRcwFQYKCZImiZPyLGQBGRYHZ3Jvc3NlcjESMBAGCgmSJomT8ixkARkWAml0
|
4
|
+
MB4XDTEzMDIwMzE4MTMxMVoXDTE0MDIwMzE4MTMxMVowPzEQMA4GA1UEAwwHbWlj
|
5
|
+
aGFlbDEXMBUGCgmSJomT8ixkARkWB2dyb3NzZXIxEjAQBgoJkiaJk/IsZAEZFgJp
|
6
|
+
dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMorXo/hgbUq97+kII9H
|
7
|
+
MsQcLdC/7wQ1ZP2OshVHPkeP0qH8MBHGg6eYisOX2ubNagF9YTCZWnhrdKrwpLOO
|
8
|
+
cPLaZbjUjljJ3cQR3B8Yn1veV5IhG86QseTBjymzJWsLpqJ1UZGpfB9tXcsFtuxO
|
9
|
+
6vHvcIHdzvc/OUkICttLbH+1qb6rsHUceqh+JrH4GrsJ5H4hAfIdyS2XMK7YRKbh
|
10
|
+
h+IBu6dFWJJByzFsYmV1PDXln3UBmgAt65cmCu4qPfThioCGDzbSJrGDGLmw/pFX
|
11
|
+
FPpVCm1zgYSb1v6Qnf3cgXa2f2wYGm17+zAVyIDpwryFru9yF/jJxE38z/DRsd9R
|
12
|
+
/88CAwEAAaM5MDcwCQYDVR0TBAIwADAdBgNVHQ4EFgQUsiNnXHtKeMYYcr4yJVmQ
|
13
|
+
WONL+IwwCwYDVR0PBAQDAgSwMA0GCSqGSIb3DQEBBQUAA4IBAQAlyN7kKo/NQCQ0
|
14
|
+
AOzZLZ3WAePvStkCFIJ53tsv5Kyo4pMAllv+BgPzzBt7qi605mFSL6zBd9uLou+W
|
15
|
+
Co3s48p1dy7CjjAfVQdmVNHF3MwXtfC2OEyvSQPi4xKR8iba8wa3xp9LVo1PuLpw
|
16
|
+
/6DsrChWw74HfsJN6qJOK684hJeT8lBYAUfiC3wD0owoPSg+XtyAAddisR+KV5Y1
|
17
|
+
NmVHuLtQcNTZy+gRht3ahJRMuC6QyLmkTsf+6MaenwAMkAgHdswGsJztOnNnBa3F
|
18
|
+
y0kCSWmK6D+x/SbfS6r7Ke07MRqziJdB9GuE1+0cIRuFh8EQ+LN6HXCKM5pon/GU
|
19
|
+
ycwMXfl0
|
20
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "bundler/organization_audit/version"
|
2
|
+
require "tmpdir"
|
3
|
+
require "bundler/organization_audit/repo"
|
4
|
+
|
5
|
+
module Bundler
|
6
|
+
module OrganizationAudit
|
7
|
+
class << self
|
8
|
+
def run(options)
|
9
|
+
vulnerable = find_vulnerable(options).map(&:url)
|
10
|
+
vulnerable -= (options[:ignore] || [])
|
11
|
+
if vulnerable.size == 0
|
12
|
+
0
|
13
|
+
else
|
14
|
+
$stderr.puts "Vulnerable:"
|
15
|
+
puts vulnerable
|
16
|
+
1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def download_file(repo, file, options)
|
23
|
+
return unless content = repo.content(file, options)
|
24
|
+
File.open(file, "w") { |f| f.write content }
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_vulnerable(options)
|
28
|
+
Repo.all(options).select do |repo|
|
29
|
+
audit_repo(repo, options)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def audit_repo(repo, options)
|
34
|
+
success = false
|
35
|
+
$stderr.puts repo.project
|
36
|
+
in_temp_dir do
|
37
|
+
if download_file(repo, "Gemfile.lock", options)
|
38
|
+
if options[:ignore_gems] && repo.gem?(options)
|
39
|
+
$stderr.puts "Ignored because it's a gem"
|
40
|
+
else
|
41
|
+
success = !sh("bundle-audit")
|
42
|
+
end
|
43
|
+
else
|
44
|
+
$stderr.puts "No Gemfile.lock found"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
$stderr.puts ""
|
48
|
+
success
|
49
|
+
rescue Exception => e
|
50
|
+
$stderr.puts "Error auditing #{repo.project} (#{e})"
|
51
|
+
end
|
52
|
+
|
53
|
+
def in_temp_dir(&block)
|
54
|
+
Dir.mktmpdir { |dir| Dir.chdir(dir, &block) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def sh(cmd)
|
58
|
+
$stderr.puts cmd
|
59
|
+
IO.popen(cmd) do |pipe|
|
60
|
+
while str = pipe.gets
|
61
|
+
$stderr.puts str
|
62
|
+
end
|
63
|
+
end
|
64
|
+
$?.success?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "open-uri"
|
2
|
+
require "json"
|
3
|
+
require "base64"
|
4
|
+
|
5
|
+
module Bundler
|
6
|
+
module OrganizationAudit
|
7
|
+
class Repo
|
8
|
+
HOST = "https://api.github.com"
|
9
|
+
|
10
|
+
def initialize(data)
|
11
|
+
@data = data
|
12
|
+
end
|
13
|
+
|
14
|
+
def gem?(options={})
|
15
|
+
!!content("#{project}.gemspec", options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def url
|
19
|
+
api_url.sub("api.", "").sub("/repos", "")
|
20
|
+
end
|
21
|
+
alias_method :to_s, :url
|
22
|
+
|
23
|
+
def project
|
24
|
+
api_url.split("/").last
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.all(options)
|
28
|
+
user = if options[:organization]
|
29
|
+
"orgs/#{options[:organization]}"
|
30
|
+
elsif options[:user]
|
31
|
+
"users/#{options[:user]}"
|
32
|
+
else
|
33
|
+
"user"
|
34
|
+
end
|
35
|
+
url = File.join(HOST, user, "repos")
|
36
|
+
|
37
|
+
download_all_pages(url, headers(options[:token])).map { |data| Repo.new(data) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def content(file, options={})
|
41
|
+
if private?
|
42
|
+
download_content_via_api(file, options)
|
43
|
+
else
|
44
|
+
download_content_via_raw(file)
|
45
|
+
end
|
46
|
+
rescue OpenURI::HTTPError => e
|
47
|
+
raise "Error downloading #{file} from #{url} (#{e})" unless e.message.start_with?("404")
|
48
|
+
end
|
49
|
+
|
50
|
+
def private?
|
51
|
+
@data["private"]
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def self.download_all_pages(url, headers)
|
57
|
+
results = []
|
58
|
+
page = 1
|
59
|
+
loop do
|
60
|
+
result = JSON.parse(open("#{url}?page=#{page}", headers).read)
|
61
|
+
if result.size == 0
|
62
|
+
break
|
63
|
+
else
|
64
|
+
results.concat(result)
|
65
|
+
page += 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
results
|
69
|
+
end
|
70
|
+
|
71
|
+
def branch
|
72
|
+
@data["default_branch"] || @data["master_branch"] || "master"
|
73
|
+
end
|
74
|
+
|
75
|
+
def api_url
|
76
|
+
@data["url"]
|
77
|
+
end
|
78
|
+
|
79
|
+
def raw_url
|
80
|
+
url.sub("://", "://raw.")
|
81
|
+
end
|
82
|
+
|
83
|
+
# increases api rate limit
|
84
|
+
def download_content_via_api(file, options)
|
85
|
+
url = File.join(api_url, "contents", file, "?ref=#{branch}")
|
86
|
+
content = open(url, self.class.headers(options.fetch(:token))).read
|
87
|
+
content = JSON.load(content)["content"]
|
88
|
+
Base64.decode64(content)
|
89
|
+
end
|
90
|
+
|
91
|
+
# unlimited
|
92
|
+
def download_content_via_raw(file)
|
93
|
+
open(File.join(raw_url, branch, file)).read
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.headers(token)
|
97
|
+
token ? {"Authorization" => "token #{token}"} : {}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Bundler::OrganizationAudit::Repo do
|
4
|
+
let(:config){ YAML.load_file("spec/private.yml") }
|
5
|
+
let(:repo) do
|
6
|
+
Bundler::OrganizationAudit::Repo.new(
|
7
|
+
"url" => "https://api.github.com/repos/grosser/parallel"
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe ".all" do
|
12
|
+
it "returns the list of public repositories" do
|
13
|
+
# use a big account -> make sure pagination works
|
14
|
+
list = Bundler::OrganizationAudit::Repo.all(:user => "grosser")
|
15
|
+
list.map(&:url).should include("https://github.com/grosser/parallel")
|
16
|
+
end
|
17
|
+
|
18
|
+
if File.exist?("spec/private.yml")
|
19
|
+
it "returns the list of private repositories from a user" do
|
20
|
+
list = Bundler::OrganizationAudit::Repo.all(:token => config["token"])
|
21
|
+
list.map(&:url).should include("https://github.com/#{config["user"]}/#{config["expected_user"]}")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns the list of private repositories from a organization" do
|
25
|
+
list = Bundler::OrganizationAudit::Repo.all(:token => config["token"], :organization => config["organization"])
|
26
|
+
list.map(&:url).should include("https://github.com/#{config["organization"]}/#{config["expected_organization"]}")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#content" do
|
32
|
+
it "can download a public file" do
|
33
|
+
repo.content("Gemfile.lock").should include('rspec (2')
|
34
|
+
end
|
35
|
+
|
36
|
+
if File.exist?("spec/private.yml")
|
37
|
+
it "can download a private file" do
|
38
|
+
url = "https://api.github.com/repos/#{config["organization"]}/#{config["expected_organization"]}"
|
39
|
+
repo = Bundler::OrganizationAudit::Repo.new(
|
40
|
+
"url" => url, "private" => true
|
41
|
+
)
|
42
|
+
content = repo.content("Gemfile.lock", :token => config["token"], :user => config["user"])
|
43
|
+
content.should include('i18n (0.')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#gem?" do
|
49
|
+
it "is a gem if it has a gemspec" do
|
50
|
+
repo.should be_gem
|
51
|
+
end
|
52
|
+
|
53
|
+
it "is not a gem if it has no gemspec" do
|
54
|
+
Bundler::OrganizationAudit::Repo.new("url" => "https://api.github.com/repos/grosser/dotfiles").should_not be_gem
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Bundler::OrganizationAudit do
|
4
|
+
it "has a VERSION" do
|
5
|
+
Bundler::OrganizationAudit::VERSION.should =~ /^[\.\da-z]+$/
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Bundler::OrganizationAudit do
|
9
|
+
let(:repo) do
|
10
|
+
Bundler::OrganizationAudit::Repo.new(
|
11
|
+
"url" => "https://api.github.com/repos/grosser/parallel"
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe ".audit_repo" do
|
16
|
+
it "audits public repos" do
|
17
|
+
out = record_out do
|
18
|
+
Bundler::OrganizationAudit.send(:audit_repo, repo, {})
|
19
|
+
end
|
20
|
+
out.strip.should == "parallel\nbundle-audit\nNo unpatched versions found"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "does not audit ignored repos" do
|
24
|
+
out = record_out do
|
25
|
+
Bundler::OrganizationAudit.send(:audit_repo, repo, :ignore_gems => true)
|
26
|
+
end
|
27
|
+
out.strip.should == "parallel\nIgnored because it's a gem"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe ".run" do
|
32
|
+
before do
|
33
|
+
Bundler::OrganizationAudit.stub(:puts)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "is successful when failed are empty" do
|
37
|
+
Bundler::OrganizationAudit.should_receive(:find_vulnerable).and_return([])
|
38
|
+
record_out do
|
39
|
+
Bundler::OrganizationAudit.run({}).should == 0
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "fails with failed" do
|
44
|
+
Bundler::OrganizationAudit.should_receive(:find_vulnerable).and_return([repo])
|
45
|
+
record_out do
|
46
|
+
Bundler::OrganizationAudit.run({}).should == 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "CLI" do
|
53
|
+
it "can audit a user" do
|
54
|
+
result = audit("--user anamartinez")
|
55
|
+
result.should include "I18N-tools\nNo Gemfile.lock found" # did not use audit when not necessary
|
56
|
+
result.should include "js-cldr-timezones\nbundle-audit\nNo unpatched versions found" # used audit where necessary
|
57
|
+
end
|
58
|
+
|
59
|
+
it "can audit a unpatched user" do
|
60
|
+
result = audit("--user user-with-unpatched-apps", :fail => true)
|
61
|
+
result.should include "unpatched\nbundle-audit\nName: json\nVersion: 1.5.3" # Individual vulnerabilities
|
62
|
+
result.should include "Vulnerable:\nhttps://github.com/user-with-unpatched-apps/unpatched" # Summary
|
63
|
+
end
|
64
|
+
|
65
|
+
it "only shows failed projects on stdout" do
|
66
|
+
result = audit("--user user-with-unpatched-apps 2>/dev/null", :fail => true, :keep_output => true)
|
67
|
+
result.should == "https://github.com/user-with-unpatched-apps/unpatched\n"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "ignores projects in --ignore" do
|
71
|
+
result = audit("--user user-with-unpatched-apps --ignore https://github.com/user-with-unpatched-apps/unpatched 2>/dev/null", :keep_output => true)
|
72
|
+
result.should == ""
|
73
|
+
end
|
74
|
+
|
75
|
+
it "shows --version" do
|
76
|
+
audit("--version").should include(Bundler::OrganizationAudit::VERSION)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "shows --help" do
|
80
|
+
audit("--help").should include("Audit all Gemfiles")
|
81
|
+
end
|
82
|
+
|
83
|
+
def audit(command, options={})
|
84
|
+
sh("bin/bundle-organization-audit #{command}", options)
|
85
|
+
end
|
86
|
+
|
87
|
+
def sh(command, options={})
|
88
|
+
result = `#{command} #{"2>&1" unless options[:keep_output]}`
|
89
|
+
raise "FAILED #{command}\n#{result}" if $?.success? == !!options[:fail]
|
90
|
+
decolorize(result)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def decolorize(string)
|
95
|
+
string.gsub(/\e\[\d+m/, "")
|
96
|
+
end
|
97
|
+
|
98
|
+
def record_out
|
99
|
+
recorder = StringIO.new
|
100
|
+
$stdout, out = recorder, $stdout
|
101
|
+
$stderr, err = recorder, $stderr
|
102
|
+
yield
|
103
|
+
decolorize(recorder.string)
|
104
|
+
ensure
|
105
|
+
$stdout = out
|
106
|
+
$stderr = err
|
107
|
+
end
|
108
|
+
|
109
|
+
def in_temp_dir(&block)
|
110
|
+
Dir.mktmpdir { |dir| Dir.chdir(dir, &block) }
|
111
|
+
end
|
112
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/organization_audit"
|
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bundler-organization_audit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Michael Grosser
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain:
|
12
|
+
- !binary |-
|
13
|
+
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNakNDQWhxZ0F3SUJB
|
14
|
+
Z0lCQURBTkJna3Foa2lHOXcwQkFRVUZBREEvTVJBd0RnWURWUVFEREFkdGFX
|
15
|
+
Tm8KWVdWc01SY3dGUVlLQ1pJbWlaUHlMR1FCR1JZSFozSnZjM05sY2pFU01C
|
16
|
+
QUdDZ21TSm9tVDhpeGtBUmtXQW1sMApNQjRYRFRFek1ESXdNekU0TVRNeE1W
|
17
|
+
b1hEVEUwTURJd016RTRNVE14TVZvd1B6RVFNQTRHQTFVRUF3d0hiV2xqCmFH
|
18
|
+
RmxiREVYTUJVR0NnbVNKb21UOGl4a0FSa1dCMmR5YjNOelpYSXhFakFRQmdv
|
19
|
+
SmtpYUprL0lzWkFFWkZnSnAKZERDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFB
|
20
|
+
RGdnRVBBRENDQVFvQ2dnRUJBTW9yWG8vaGdiVXE5NytrSUk5SApNc1FjTGRD
|
21
|
+
Lzd3UTFaUDJPc2hWSFBrZVAwcUg4TUJIR2c2ZVlpc09YMnViTmFnRjlZVENa
|
22
|
+
V25ocmRLcndwTE9PCmNQTGFaYmpVamxqSjNjUVIzQjhZbjF2ZVY1SWhHODZR
|
23
|
+
c2VUQmp5bXpKV3NMcHFKMVVaR3BmQjl0WGNzRnR1eE8KNnZIdmNJSGR6dmMv
|
24
|
+
T1VrSUN0dExiSCsxcWI2cnNIVWNlcWgrSnJINEdyc0o1SDRoQWZJZHlTMlhN
|
25
|
+
SzdZUktiaApoK0lCdTZkRldKSkJ5ekZzWW1WMVBEWGxuM1VCbWdBdDY1Y21D
|
26
|
+
dTRxUGZUaGlvQ0dEemJTSnJHREdMbXcvcEZYCkZQcFZDbTF6Z1lTYjF2NlFu
|
27
|
+
ZjNjZ1hhMmYyd1lHbTE3K3pBVnlJRHB3cnlGcnU5eUYvakp4RTM4ei9EUnNk
|
28
|
+
OVIKLzg4Q0F3RUFBYU01TURjd0NRWURWUjBUQkFJd0FEQWRCZ05WSFE0RUZn
|
29
|
+
UVVzaU5uWEh0S2VNWVljcjR5SlZtUQpXT05MK0l3d0N3WURWUjBQQkFRREFn
|
30
|
+
U3dNQTBHQ1NxR1NJYjNEUUVCQlFVQUE0SUJBUUFseU43a0tvL05RQ1EwCkFP
|
31
|
+
elpMWjNXQWVQdlN0a0NGSUo1M3RzdjVLeW80cE1BbGx2K0JnUHp6QnQ3cWk2
|
32
|
+
MDVtRlNMNnpCZDl1TG91K1cKQ28zczQ4cDFkeTdDampBZlZRZG1WTkhGM013
|
33
|
+
WHRmQzJPRXl2U1FQaTR4S1I4aWJhOHdhM3hwOUxWbzFQdUxwdwovNkRzckNo
|
34
|
+
V3c3NEhmc0pONnFKT0s2ODRoSmVUOGxCWUFVZmlDM3dEMG93b1BTZytYdHlB
|
35
|
+
QWRkaXNSK0tWNVkxCk5tVkh1THRRY05UWnkrZ1JodDNhaEpSTXVDNlF5TG1r
|
36
|
+
VHNmKzZNYWVud0FNa0FnSGRzd0dzSnp0T25ObkJhM0YKeTBrQ1NXbUs2RCt4
|
37
|
+
L1NiZlM2cjdLZTA3TVJxemlKZEI5R3VFMSswY0lSdUZoOEVRK0xONkhYQ0tN
|
38
|
+
NXBvbi9HVQp5Y3dNWGZsMAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
|
39
|
+
date: 2013-02-18 00:00:00.000000000 Z
|
40
|
+
dependencies:
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: json
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
description: Audit all Gemfiles of a user/organization on github for unpatched versions
|
58
|
+
email: michael@grosser.it
|
59
|
+
executables:
|
60
|
+
- bundle-organization-audit
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- .gitignore
|
65
|
+
- .travis.yml
|
66
|
+
- Gemfile
|
67
|
+
- Gemfile.lock
|
68
|
+
- Rakefile
|
69
|
+
- Readme.md
|
70
|
+
- bin/bundle-organization-audit
|
71
|
+
- bundler-organization_audit.gemspec
|
72
|
+
- gem-public_cert.pem
|
73
|
+
- lib/bundler/organization_audit.rb
|
74
|
+
- lib/bundler/organization_audit/repo.rb
|
75
|
+
- lib/bundler/organization_audit/version.rb
|
76
|
+
- spec/bundler/organization_audit/repo_spec.rb
|
77
|
+
- spec/bundler/organization_audit_spec.rb
|
78
|
+
- spec/private.example.yml
|
79
|
+
- spec/spec_helper.rb
|
80
|
+
homepage: http://github.com/grosser/bundler-organization_audit
|
81
|
+
licenses:
|
82
|
+
- MIT
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ! '>='
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
hash: 75415256473976345
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
segments:
|
103
|
+
- 0
|
104
|
+
hash: 75415256473976345
|
105
|
+
requirements: []
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 1.8.24
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: Audit all Gemfiles of a user/organization on github for unpatched versions
|
111
|
+
test_files: []
|
metadata.gz.sig
ADDED
Binary file
|