holepicker 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.markdown +6 -0
- data/Gemfile.lock +1 -1
- data/README.markdown +27 -7
- data/bin/holepicker +12 -3
- data/lib/holepicker.rb +1 -0
- data/lib/holepicker/data/data.json +22 -1
- data/lib/holepicker/logger.rb +44 -0
- data/lib/holepicker/offline_database.rb +4 -1
- data/lib/holepicker/online_database.rb +21 -11
- data/lib/holepicker/scanner.rb +16 -15
- data/lib/holepicker/version.rb +1 -1
- metadata +3 -2
data/Changelog.markdown
CHANGED
data/Gemfile.lock
CHANGED
data/README.markdown
CHANGED
@@ -53,6 +53,24 @@ You might have a lot of random apps deployed in the `/var/www` directory, but on
|
|
53
53
|
holepicker -f /etc/nginx/sites-enabled
|
54
54
|
|
55
55
|
|
56
|
+
## Results
|
57
|
+
|
58
|
+
This is more or less what you will get if you run HolePicker in a directory with some old Rails projects:
|
59
|
+
|
60
|
+
![screenshot](http://f.cl.ly/items/1l3C2c2s0r1k3v033B34/Screen%20Shot%202013-02-16%20at%2001.43.39.png)
|
61
|
+
|
62
|
+
|
63
|
+
## Running on app startup
|
64
|
+
|
65
|
+
If you want to check your gems when your app is started, add HolePicker to your Gemfile and then call `HolePicker::Scanner#scan` in a file that's loaded at app startup (e.g. in Rails projects you can add an initializer in `config/initializers`):
|
66
|
+
|
67
|
+
```rb
|
68
|
+
HolePicker::Scanner.new('Gemfile.lock').scan or abort
|
69
|
+
```
|
70
|
+
|
71
|
+
You may want to pass `:offline` or `:ignored_gems` options or change logger settings too - see [`bin/holepicker` source](https://github.com/jsuder/holepicker/blob/master/bin/holepicker) for more info.
|
72
|
+
|
73
|
+
|
56
74
|
## Integration with capistrano
|
57
75
|
|
58
76
|
To automatically check for vulnerabilities before deployment, you can add the HolePicker Capistrano recipe:
|
@@ -62,12 +80,6 @@ To automatically check for vulnerabilities before deployment, you can add the Ho
|
|
62
80
|
|
63
81
|
This will introduce a `cap holepicker` task which will be executed before the deploy.
|
64
82
|
|
65
|
-
## Results
|
66
|
-
|
67
|
-
This is more or less what you will get if you run HolePicker in a directory with some old Rails projects:
|
68
|
-
|
69
|
-
![screenshot](http://f.cl.ly/items/1l3C2c2s0r1k3v033B34/Screen%20Shot%202013-02-16%20at%2001.43.39.png)
|
70
|
-
|
71
83
|
|
72
84
|
## Full option list
|
73
85
|
|
@@ -87,16 +99,24 @@ Look for `root`/`DocumentRoot` directives in config files at given locations ins
|
|
87
99
|
|
88
100
|
Ignore the gems passed in the parameter.
|
89
101
|
|
102
|
+
`--no-color`
|
103
|
+
|
104
|
+
Disable output coloring (by default green is used for good gemfiles and red is used for bad gemfiles and errors).
|
105
|
+
|
90
106
|
`-o`, `--offline`
|
91
107
|
|
92
108
|
Use an offline copy of the data file - useful if you really need to run the tool, but the network or GitHub is down.
|
93
109
|
|
110
|
+
`-s`, `--silent`
|
111
|
+
|
112
|
+
Silent mode - disable info-level messages ("Looking for gemfiles...") and only print errors and found vulnerabilities.
|
113
|
+
|
94
114
|
|
95
115
|
## Similar projects
|
96
116
|
|
97
117
|
There are a few other projects with a similar purpose, take a look if HolePicker isn't exactly what you need:
|
98
118
|
|
99
|
-
* [bundler-audit](https://github.com/postmodern/bundler-audit) -
|
119
|
+
* [bundler-audit](https://github.com/postmodern/bundler-audit) - lets you scan the project in current directory
|
100
120
|
* [bundler-organization_audit](https://github.com/grosser/bundler-organization_audit) - scans all your projects on GitHub
|
101
121
|
* [ruby-advisory-db](https://github.com/rubysec/ruby-advisory-db) - a shared database of vulnerabilities - I'll try to integrate holepicker with it later
|
102
122
|
* [gemcanary](https://gemcanary.com/) - some kind of web service, not released yet (as of 23.02)
|
data/bin/holepicker
CHANGED
@@ -27,17 +27,25 @@ OptionParser.new do |opts|
|
|
27
27
|
options[:ignored_gems] = names
|
28
28
|
end
|
29
29
|
|
30
|
+
opts.on("--no-color", "Disable output coloring") do
|
31
|
+
HolePicker.logger.color = false
|
32
|
+
end
|
33
|
+
|
30
34
|
opts.on("-o", "--offline", "Use an offline copy of the data.json file") do
|
31
35
|
options[:offline] = true
|
32
36
|
end
|
33
37
|
|
38
|
+
opts.on("-s", "--silent", "Silent mode - disable info-level logging, only print errors") do
|
39
|
+
HolePicker.logger.level = Logger::ERROR
|
40
|
+
end
|
41
|
+
|
34
42
|
opts.on("-h", "--help", "Display this help") do
|
35
|
-
|
43
|
+
HolePicker.logger.info(opts)
|
36
44
|
exit
|
37
45
|
end
|
38
46
|
|
39
47
|
opts.on("-v", "--version", "Print gem version") do
|
40
|
-
|
48
|
+
HolePicker.logger.info(HolePicker::VERSION)
|
41
49
|
exit
|
42
50
|
end
|
43
51
|
|
@@ -45,7 +53,8 @@ OptionParser.new do |opts|
|
|
45
53
|
end
|
46
54
|
|
47
55
|
if ARGV.empty?
|
48
|
-
|
56
|
+
HolePicker.logger.error "Please choose at least one directory to scan for gemfiles."
|
57
|
+
exit 1
|
49
58
|
end
|
50
59
|
|
51
60
|
success = HolePicker::Scanner.new(ARGV, options).scan
|
data/lib/holepicker.rb
CHANGED
@@ -9,6 +9,20 @@
|
|
9
9
|
"date": "2013-03-18T17:21Z",
|
10
10
|
"note": "Warning: there are several issues with Rails 3.2.13, affecting view performance and other things; see http://blog.bugsnag.com/2013/03/20/rails-3-2-13-performance-regressions-major-bugs/ for more info."
|
11
11
|
},
|
12
|
+
{
|
13
|
+
"gems": {
|
14
|
+
"ftpd": ["0.2.2"]
|
15
|
+
},
|
16
|
+
"url": "http://seclists.org/bugtraq/2013/Mar/10",
|
17
|
+
"date": "2013-03-03T05:45Z"
|
18
|
+
},
|
19
|
+
{
|
20
|
+
"gems": {
|
21
|
+
"dragonfly": ["0.9.14"]
|
22
|
+
},
|
23
|
+
"url": "https://groups.google.com/d/msg/dragonfly-users/3c3WIU3VQTo/ccasejdDjcAJ",
|
24
|
+
"date": "2013-02-19T08:39Z"
|
25
|
+
},
|
12
26
|
{
|
13
27
|
"gems": {
|
14
28
|
"rails": ["3.2.12", "3.1.11", "2.3.17"]
|
@@ -31,6 +45,13 @@
|
|
31
45
|
"url": "http://rack.github.com/",
|
32
46
|
"date": "2013-02-08T03:14Z"
|
33
47
|
},
|
48
|
+
{
|
49
|
+
"gems": {
|
50
|
+
"rdoc": ["4.0.0.rc.2", "3.12.1", "3.9.5"]
|
51
|
+
},
|
52
|
+
"url": "http://rdoc.rubyforge.org/CVE-2013-0256_rdoc.html",
|
53
|
+
"date": "2013-02-07T05:49Z"
|
54
|
+
},
|
34
55
|
{
|
35
56
|
"gems": {
|
36
57
|
"rails": ["3.0.20", "2.3.16"]
|
@@ -50,7 +71,7 @@
|
|
50
71
|
"httparty": ["0.10.0"],
|
51
72
|
"extlib": ["0.9.16"],
|
52
73
|
"crack": ["0.3.2"],
|
53
|
-
"nori": ["2.0.
|
74
|
+
"nori": ["2.0.3", "1.1.4", "1.0.3"]
|
54
75
|
},
|
55
76
|
"url": "https://support.cloud.engineyard.com/entries/22915701-January-14-2013-Security-vulnerabilities-httparty-extlib-crack-nori-Update-these-gems-immediately",
|
56
77
|
"date": "2013-01-15T13:10Z"
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'rainbow'
|
3
|
+
|
4
|
+
module HolePicker
|
5
|
+
class Logger < ::Logger
|
6
|
+
attr_accessor :color
|
7
|
+
|
8
|
+
def initialize(io)
|
9
|
+
super
|
10
|
+
self.level = INFO
|
11
|
+
self.color = true
|
12
|
+
end
|
13
|
+
|
14
|
+
def format_message(level, datetime, progname, message)
|
15
|
+
progname == 'print' ? message.to_s : "#{message}\n"
|
16
|
+
end
|
17
|
+
|
18
|
+
def print(message, level = INFO)
|
19
|
+
add(level, message, 'print')
|
20
|
+
end
|
21
|
+
|
22
|
+
def fail(message)
|
23
|
+
error(color ? message.color(:red) : message)
|
24
|
+
end
|
25
|
+
|
26
|
+
def success(message)
|
27
|
+
info(color ? message.color(:green) : message)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module HasLogger
|
32
|
+
def logger
|
33
|
+
HolePicker.logger
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.included(c)
|
37
|
+
c.extend(self)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.logger
|
42
|
+
@logger ||= Logger.new(STDOUT)
|
43
|
+
end
|
44
|
+
end
|
@@ -1,13 +1,16 @@
|
|
1
1
|
require 'holepicker/database'
|
2
|
+
require 'holepicker/logger'
|
2
3
|
|
3
4
|
module HolePicker
|
4
5
|
class OfflineDatabase < Database
|
6
|
+
include HasLogger
|
7
|
+
|
5
8
|
OFFLINE_JSON_FILE = File.expand_path('../data/data.json', __FILE__)
|
6
9
|
|
7
10
|
def self.load
|
8
11
|
load_from_json_file(File.read(OFFLINE_JSON_FILE))
|
9
12
|
rescue Exception => e
|
10
|
-
|
13
|
+
logger.fail "Can't load local data file: #{e}"
|
11
14
|
exit 1
|
12
15
|
end
|
13
16
|
end
|
@@ -1,26 +1,36 @@
|
|
1
1
|
require 'holepicker/database'
|
2
|
+
require 'holepicker/logger'
|
2
3
|
require 'holepicker/utils'
|
3
4
|
require 'net/http'
|
4
5
|
require 'net/https'
|
5
6
|
|
6
7
|
module HolePicker
|
7
8
|
class OnlineDatabase < Database
|
8
|
-
|
9
|
+
include HasLogger
|
10
|
+
|
11
|
+
URL = 'https://raw.github.com/jsuder/holepicker/master/lib/holepicker/data/data.json'
|
9
12
|
|
10
13
|
def self.load
|
11
|
-
|
14
|
+
logger.info "Fetching list of vulnerabilities..."
|
12
15
|
|
13
|
-
load_from_json_file(http_get(URL))
|
14
|
-
db.check_compatibility
|
15
|
-
db.report_new_vulnerabilities
|
16
|
-
end
|
16
|
+
load_from_json_file(http_get(URL))
|
17
17
|
rescue SystemExit
|
18
18
|
raise
|
19
19
|
rescue Exception => e
|
20
|
-
|
20
|
+
logger.fail "Can't download latest data file: #{e}"
|
21
21
|
exit 1
|
22
22
|
end
|
23
23
|
|
24
|
+
def initialize(json)
|
25
|
+
super
|
26
|
+
|
27
|
+
check_compatibility
|
28
|
+
report_new_vulnerabilities
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
private
|
33
|
+
|
24
34
|
def self.http_get(url)
|
25
35
|
uri = URI(url)
|
26
36
|
http = Net::HTTP.new(uri.host, uri.port)
|
@@ -32,7 +42,7 @@ module HolePicker
|
|
32
42
|
|
33
43
|
def check_compatibility
|
34
44
|
unless compatible?
|
35
|
-
|
45
|
+
logger.fail "You need to upgrade holepicker to version #{@min_version} or later."
|
36
46
|
exit 1
|
37
47
|
end
|
38
48
|
end
|
@@ -42,14 +52,14 @@ module HolePicker
|
|
42
52
|
count = new_vulnerabilities.length
|
43
53
|
|
44
54
|
if count > 0
|
45
|
-
|
55
|
+
logger.info "#{count} new #{Utils.pluralize(count, 'vulnerability')} found in the last " +
|
46
56
|
"#{Vulnerability::NEW_VULNERABILITY_DAYS} days:"
|
47
57
|
|
48
58
|
new_vulnerabilities.each do |v|
|
49
|
-
|
59
|
+
logger.info "#{v.day} (#{v.gem_names.join(', ')}): #{v.url}"
|
50
60
|
end
|
51
61
|
|
52
|
-
|
62
|
+
logger.info
|
53
63
|
end
|
54
64
|
end
|
55
65
|
end
|
data/lib/holepicker/scanner.rb
CHANGED
@@ -3,14 +3,16 @@
|
|
3
3
|
require 'holepicker/gem'
|
4
4
|
require 'holepicker/config_gemfile_finder'
|
5
5
|
require 'holepicker/direct_gemfile_finder'
|
6
|
+
require 'holepicker/logger'
|
6
7
|
require 'holepicker/offline_database'
|
7
8
|
require 'holepicker/online_database'
|
8
9
|
require 'holepicker/utils'
|
9
|
-
require 'rainbow'
|
10
10
|
require 'set'
|
11
11
|
|
12
12
|
module HolePicker
|
13
13
|
class Scanner
|
14
|
+
include HasLogger
|
15
|
+
|
14
16
|
GEMFILE_GEM_PATTERN = %r(^ {4}[^ ])
|
15
17
|
|
16
18
|
def initialize(paths, options = {})
|
@@ -31,7 +33,7 @@ module HolePicker
|
|
31
33
|
end
|
32
34
|
|
33
35
|
def scan
|
34
|
-
|
36
|
+
logger.info "Looking for gemfiles..."
|
35
37
|
|
36
38
|
@found_vulnerabilities = Set.new
|
37
39
|
@scanned_gemfiles = 0
|
@@ -61,8 +63,6 @@ module HolePicker
|
|
61
63
|
end
|
62
64
|
|
63
65
|
def scan_gemfile(path)
|
64
|
-
print "#{path}: "
|
65
|
-
|
66
66
|
gems = read_gemfile(path)
|
67
67
|
gems.delete_if { |g| @ignored.include?(g.name) }
|
68
68
|
|
@@ -72,12 +72,14 @@ module HolePicker
|
|
72
72
|
count = vulnerable_gems.length
|
73
73
|
|
74
74
|
if count == 0
|
75
|
-
|
75
|
+
logger.print "#{path}: "
|
76
|
+
logger.success "✔"
|
76
77
|
else
|
77
|
-
|
78
|
+
logger.print "#{path}: ", Logger::ERROR
|
79
|
+
logger.fail "#{count} vulnerable #{Utils.pluralize(count, 'gem')} found!"
|
78
80
|
|
79
81
|
vulnerable_gems.each do |gem, vulnerabilities|
|
80
|
-
|
82
|
+
logger.error "- #{gem} [#{vulnerabilities.map(&:tag).join(',')}]"
|
81
83
|
|
82
84
|
@found_vulnerabilities.merge(vulnerabilities)
|
83
85
|
@matched_gems += 1
|
@@ -85,7 +87,7 @@ module HolePicker
|
|
85
87
|
|
86
88
|
@matched_gemfiles += 1
|
87
89
|
|
88
|
-
|
90
|
+
logger.error
|
89
91
|
end
|
90
92
|
|
91
93
|
@scanned_gemfiles += 1
|
@@ -93,25 +95,24 @@ module HolePicker
|
|
93
95
|
|
94
96
|
def print_report
|
95
97
|
if @scanned_gemfiles == 0
|
96
|
-
|
98
|
+
logger.warn "No gemfiles found - are you sure the paths are correct?"
|
97
99
|
elsif @matched_gemfiles == 0
|
98
|
-
|
100
|
+
logger.info "No vulnerabilities found."
|
99
101
|
else
|
100
102
|
gems = Utils.pluralize(@matched_gems, 'gem')
|
101
103
|
gemfiles = Utils.pluralize(@matched_gemfiles, 'gemfile')
|
102
104
|
|
103
|
-
|
104
|
-
puts warning.color(:red)
|
105
|
+
logger.fail "#{@matched_gems} vulnerable #{gems} found in #{@matched_gemfiles} #{gemfiles}!\n"
|
105
106
|
|
106
107
|
@found_vulnerabilities.sort_by(&:id).each do |v|
|
107
|
-
|
108
|
+
logger.error "[#{v.tag}] #{v.day}: #{v.url}"
|
108
109
|
end
|
109
110
|
|
110
111
|
if @found_vulnerabilities.any?(&:note)
|
111
|
-
|
112
|
+
logger.error
|
112
113
|
|
113
114
|
@found_vulnerabilities.select(&:note).each do |v|
|
114
|
-
|
115
|
+
logger.error "[#{v.tag}] #{v.note}"
|
115
116
|
end
|
116
117
|
end
|
117
118
|
end
|
data/lib/holepicker/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: holepicker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-04-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
@@ -63,6 +63,7 @@ files:
|
|
63
63
|
- lib/holepicker/direct_gemfile_finder.rb
|
64
64
|
- lib/holepicker/file_finder.rb
|
65
65
|
- lib/holepicker/gem.rb
|
66
|
+
- lib/holepicker/logger.rb
|
66
67
|
- lib/holepicker/offline_database.rb
|
67
68
|
- lib/holepicker/online_database.rb
|
68
69
|
- lib/holepicker/scanner.rb
|