holepicker 0.2.2 → 0.3.0
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.
- 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
|
+

|
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
|
-

|
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
|