holepicker 0.3.0 → 0.3.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.
- data/Changelog.markdown +6 -0
- data/Gemfile.lock +1 -1
- data/README.markdown +2 -2
- data/bin/holepicker +5 -1
- data/lib/holepicker/gemfile_parser.rb +17 -0
- data/lib/holepicker/logger.rb +5 -2
- data/lib/holepicker/scan_reporter.rb +73 -0
- data/lib/holepicker/scanner.rb +23 -53
- data/lib/holepicker/version.rb +1 -1
- metadata +4 -2
data/Changelog.markdown
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
#### Version 0.3.1 (20.05.2013)
|
2
|
+
|
3
|
+
* stdin option
|
4
|
+
|
1
5
|
#### Version 0.3.0 (13.04.2013)
|
2
6
|
|
3
7
|
* silent option
|
@@ -7,10 +11,12 @@
|
|
7
11
|
#### Version 0.2.2 (29.03.2013)
|
8
12
|
|
9
13
|
* fixed Capistrano recipe
|
14
|
+
* updated Devise vulnerability info
|
10
15
|
|
11
16
|
#### Version 0.2.1 (21.03.2013)
|
12
17
|
|
13
18
|
* added possibility to display additional notes about specific vulnerabilities (see data.json)
|
19
|
+
* added multi_xml, httparty, extlib, crack and nori vulnerabilities
|
14
20
|
|
15
21
|
#### Version 0.2.0 (7.03.2013)
|
16
22
|
|
data/Gemfile.lock
CHANGED
data/README.markdown
CHANGED
@@ -6,7 +6,7 @@ HolePicker is a Ruby gem for quickly checking all your `Gemfile.lock` files for
|
|
6
6
|
|
7
7
|
[](https://travis-ci.org/jsuder/holepicker)
|
8
8
|
|
9
|
-
[](https://codeclimate.com/github/jsuder/holepicker)
|
10
10
|
|
11
11
|
## The story
|
12
12
|
|
@@ -119,7 +119,7 @@ There are a few other projects with a similar purpose, take a look if HolePicker
|
|
119
119
|
* [bundler-audit](https://github.com/postmodern/bundler-audit) - lets you scan the project in current directory
|
120
120
|
* [bundler-organization_audit](https://github.com/grosser/bundler-organization_audit) - scans all your projects on GitHub
|
121
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
|
122
|
-
* [gemcanary](https://gemcanary.com/) -
|
122
|
+
* [gemcanary](https://gemcanary.com/) - a web service that notifies you by email when a new vulnerability is found in a gem used by one of your apps
|
123
123
|
* [gems-status](https://github.com/jordimassaguerpla/gems-status) - a more general tool for checking everything that might be wrong with your gems (work in progress)
|
124
124
|
|
125
125
|
## Credits & contributing
|
data/bin/holepicker
CHANGED
@@ -39,6 +39,10 @@ OptionParser.new do |opts|
|
|
39
39
|
HolePicker.logger.level = Logger::ERROR
|
40
40
|
end
|
41
41
|
|
42
|
+
opts.on("--stdin", "Read a gemfile directly from STDIN instead of paths given in arguments") do
|
43
|
+
options[:stdin] = true
|
44
|
+
end
|
45
|
+
|
42
46
|
opts.on("-h", "--help", "Display this help") do
|
43
47
|
HolePicker.logger.info(opts)
|
44
48
|
exit
|
@@ -52,7 +56,7 @@ OptionParser.new do |opts|
|
|
52
56
|
opts.parse!
|
53
57
|
end
|
54
58
|
|
55
|
-
if ARGV.empty?
|
59
|
+
if ARGV.empty? && !options[:stdin]
|
56
60
|
HolePicker.logger.error "Please choose at least one directory to scan for gemfiles."
|
57
61
|
exit 1
|
58
62
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'holepicker/gem'
|
2
|
+
|
3
|
+
module HolePicker
|
4
|
+
class GemfileParser
|
5
|
+
GEM_PATTERN = %r(^ {4}[^ ])
|
6
|
+
|
7
|
+
def initialize(ignored_gems = nil)
|
8
|
+
@ignored_gems = ignored_gems || []
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse_gemfile(data)
|
12
|
+
gem_lines = data.lines.select { |l| l =~ GEM_PATTERN }
|
13
|
+
gems = gem_lines.map { |l| Gem.new(l) }
|
14
|
+
gems.reject { |g| @ignored_gems.include?(g.name) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/holepicker/logger.rb
CHANGED
@@ -30,11 +30,14 @@ module HolePicker
|
|
30
30
|
|
31
31
|
module HasLogger
|
32
32
|
def logger
|
33
|
-
HolePicker.logger
|
33
|
+
@logger ||= HolePicker.logger
|
34
34
|
end
|
35
35
|
|
36
36
|
def self.included(c)
|
37
|
-
c.
|
37
|
+
c.class_eval do
|
38
|
+
attr_writer :logger
|
39
|
+
extend HasLogger
|
40
|
+
end
|
38
41
|
end
|
39
42
|
end
|
40
43
|
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'holepicker/logger'
|
2
|
+
require 'holepicker/utils'
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module HolePicker
|
6
|
+
class ScanReporter
|
7
|
+
include HasLogger
|
8
|
+
|
9
|
+
attr_reader :safe_gemfiles, :vulnerable_gems, :vulnerable_gemfiles, :vulnerabilities
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@safe_gemfiles = []
|
13
|
+
@vulnerable_gemfiles = []
|
14
|
+
@vulnerable_gems = []
|
15
|
+
@vulnerabilities = Set.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_vulnerable_gem(gem, vulnerabilities)
|
19
|
+
@vulnerabilities.merge(vulnerabilities)
|
20
|
+
@vulnerable_gems << gem
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_vulnerable_gemfile(path)
|
24
|
+
@vulnerable_gemfiles << path
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_safe_gemfile(path)
|
28
|
+
@safe_gemfiles << path
|
29
|
+
end
|
30
|
+
|
31
|
+
def success?
|
32
|
+
@vulnerable_gems.empty?
|
33
|
+
end
|
34
|
+
|
35
|
+
def print_report
|
36
|
+
if success?
|
37
|
+
if @safe_gemfiles.empty?
|
38
|
+
logger.warn "No gemfiles found - are you sure the paths are correct?"
|
39
|
+
else
|
40
|
+
logger.info "No vulnerabilities found."
|
41
|
+
end
|
42
|
+
else
|
43
|
+
gem_count = @vulnerable_gems.length
|
44
|
+
gemfile_count = @vulnerable_gemfiles.length
|
45
|
+
|
46
|
+
gems = Utils.pluralize(gem_count, 'gem')
|
47
|
+
gemfiles = Utils.pluralize(gemfile_count, 'gemfile')
|
48
|
+
|
49
|
+
logger.fail "#{gem_count} vulnerable #{gems} found in #{gemfile_count} #{gemfiles}!\n"
|
50
|
+
|
51
|
+
report_vulnerabilities
|
52
|
+
print_notes if @vulnerabilities.any?(&:note)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def report_vulnerabilities
|
60
|
+
@vulnerabilities.sort_by(&:id).each do |v|
|
61
|
+
logger.error "[#{v.tag}] #{v.day}: #{v.url}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def print_notes
|
66
|
+
logger.error
|
67
|
+
|
68
|
+
@vulnerabilities.select(&:note).each do |v|
|
69
|
+
logger.error "[#{v.tag}] #{v.note}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/holepicker/scanner.rb
CHANGED
@@ -1,22 +1,21 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require 'holepicker/gem'
|
4
3
|
require 'holepicker/config_gemfile_finder'
|
5
4
|
require 'holepicker/direct_gemfile_finder'
|
5
|
+
require 'holepicker/gemfile_parser'
|
6
6
|
require 'holepicker/logger'
|
7
7
|
require 'holepicker/offline_database'
|
8
8
|
require 'holepicker/online_database'
|
9
|
+
require 'holepicker/scan_reporter'
|
9
10
|
require 'holepicker/utils'
|
10
|
-
require 'set'
|
11
11
|
|
12
12
|
module HolePicker
|
13
13
|
class Scanner
|
14
14
|
include HasLogger
|
15
15
|
|
16
|
-
GEMFILE_GEM_PATTERN = %r(^ {4}[^ ])
|
17
|
-
|
18
16
|
def initialize(paths, options = {})
|
19
17
|
@paths = paths.is_a?(Array) ? paths : [paths]
|
18
|
+
@stdin = options[:stdin]
|
20
19
|
|
21
20
|
@database = options[:offline] ? OfflineDatabase.load : OnlineDatabase.load
|
22
21
|
|
@@ -29,22 +28,23 @@ module HolePicker
|
|
29
28
|
)
|
30
29
|
end
|
31
30
|
|
32
|
-
@
|
31
|
+
@parser = GemfileParser.new(options[:ignored_gems])
|
33
32
|
end
|
34
33
|
|
35
34
|
def scan
|
36
|
-
|
35
|
+
@reporter = ScanReporter.new
|
37
36
|
|
38
|
-
@
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
if @stdin
|
38
|
+
scan_gemfile(STDIN.read, nil)
|
39
|
+
else
|
40
|
+
logger.info "Looking for gemfiles..."
|
42
41
|
|
43
|
-
|
42
|
+
@paths.each { |p| scan_path(p) }
|
43
|
+
end
|
44
44
|
|
45
|
-
print_report
|
45
|
+
@reporter.print_report
|
46
46
|
|
47
|
-
@
|
47
|
+
@reporter.success?
|
48
48
|
end
|
49
49
|
|
50
50
|
|
@@ -54,68 +54,38 @@ module HolePicker
|
|
54
54
|
@database.vulnerabilities.select { |v| v.gem_vulnerable?(gem) }
|
55
55
|
end
|
56
56
|
|
57
|
-
def read_gemfile(path)
|
58
|
-
File.readlines(path).select { |l| l =~ GEMFILE_GEM_PATTERN }.map { |l| Gem.new(l) }
|
59
|
-
end
|
60
|
-
|
61
57
|
def scan_path(path)
|
62
|
-
@finder.find_gemfiles(path).each { |f| scan_gemfile(f) }
|
58
|
+
@finder.find_gemfiles(path).each { |f| scan_gemfile(File.read(f), f) }
|
63
59
|
end
|
64
60
|
|
65
|
-
def scan_gemfile(path)
|
66
|
-
gems =
|
67
|
-
gems.delete_if { |g| @ignored.include?(g.name) }
|
61
|
+
def scan_gemfile(data, path)
|
62
|
+
gems = @parser.parse_gemfile(data)
|
68
63
|
|
69
64
|
vulnerable_gems = gems.map { |g| [g, vulnerabilities_for_gem(g)] }
|
70
65
|
vulnerable_gems.delete_if { |g, v| v.empty? }
|
71
66
|
|
72
67
|
count = vulnerable_gems.length
|
68
|
+
label = path || "Scanning gemfile"
|
73
69
|
|
74
70
|
if count == 0
|
75
|
-
logger.print "#{
|
71
|
+
logger.print "#{label}: "
|
76
72
|
logger.success "✔"
|
73
|
+
|
74
|
+
@reporter.add_safe_gemfile(path)
|
77
75
|
else
|
78
|
-
logger.print "#{
|
76
|
+
logger.print "#{label}: ", Logger::ERROR
|
79
77
|
logger.fail "#{count} vulnerable #{Utils.pluralize(count, 'gem')} found!"
|
80
78
|
|
81
79
|
vulnerable_gems.each do |gem, vulnerabilities|
|
82
80
|
logger.error "- #{gem} [#{vulnerabilities.map(&:tag).join(',')}]"
|
83
81
|
|
84
|
-
@
|
85
|
-
@matched_gems += 1
|
82
|
+
@reporter.add_vulnerable_gem(gem, vulnerabilities)
|
86
83
|
end
|
87
84
|
|
88
|
-
@
|
85
|
+
@reporter.add_vulnerable_gemfile(path)
|
89
86
|
|
90
87
|
logger.error
|
91
88
|
end
|
92
|
-
|
93
|
-
@scanned_gemfiles += 1
|
94
|
-
end
|
95
|
-
|
96
|
-
def print_report
|
97
|
-
if @scanned_gemfiles == 0
|
98
|
-
logger.warn "No gemfiles found - are you sure the paths are correct?"
|
99
|
-
elsif @matched_gemfiles == 0
|
100
|
-
logger.info "No vulnerabilities found."
|
101
|
-
else
|
102
|
-
gems = Utils.pluralize(@matched_gems, 'gem')
|
103
|
-
gemfiles = Utils.pluralize(@matched_gemfiles, 'gemfile')
|
104
|
-
|
105
|
-
logger.fail "#{@matched_gems} vulnerable #{gems} found in #{@matched_gemfiles} #{gemfiles}!\n"
|
106
|
-
|
107
|
-
@found_vulnerabilities.sort_by(&:id).each do |v|
|
108
|
-
logger.error "[#{v.tag}] #{v.day}: #{v.url}"
|
109
|
-
end
|
110
|
-
|
111
|
-
if @found_vulnerabilities.any?(&:note)
|
112
|
-
logger.error
|
113
|
-
|
114
|
-
@found_vulnerabilities.select(&:note).each do |v|
|
115
|
-
logger.error "[#{v.tag}] #{v.note}"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
89
|
end
|
120
90
|
end
|
121
91
|
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.3.
|
4
|
+
version: 0.3.1
|
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-05-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
@@ -63,9 +63,11 @@ 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/gemfile_parser.rb
|
66
67
|
- lib/holepicker/logger.rb
|
67
68
|
- lib/holepicker/offline_database.rb
|
68
69
|
- lib/holepicker/online_database.rb
|
70
|
+
- lib/holepicker/scan_reporter.rb
|
69
71
|
- lib/holepicker/scanner.rb
|
70
72
|
- lib/holepicker/utils.rb
|
71
73
|
- lib/holepicker/version.rb
|