holepicker 0.3.0 → 0.3.1
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 +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
|
[![Build Status](https://travis-ci.org/jsuder/holepicker.png?branch=master)](https://travis-ci.org/jsuder/holepicker)
|
8
8
|
|
9
|
-
[![Code Climate](https://codeclimate.com/github/jsuder/
|
9
|
+
[![Code Climate](https://codeclimate.com/github/jsuder/holepicker.png)](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
|