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 CHANGED
@@ -1,3 +1,9 @@
1
+ #### Version 0.3.0 (13.04.2013)
2
+
3
+ * silent option
4
+ * no-color option
5
+ * added vulnerabilities for ftpd, dragonfly, rdoc
6
+
1
7
  #### Version 0.2.2 (29.03.2013)
2
8
 
3
9
  * fixed Capistrano recipe
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- holepicker (0.2.2)
4
+ holepicker (0.3.0)
5
5
  json (>= 1.7.7)
6
6
  rainbow (>= 1.1.4)
7
7
 
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) - scans the current project when the app is loaded
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
- puts opts
43
+ HolePicker.logger.info(opts)
36
44
  exit
37
45
  end
38
46
 
39
47
  opts.on("-v", "--version", "Print gem version") do
40
- puts HolePicker::VERSION
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
- abort "Please choose at least one directory to scan for gemfiles."
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
@@ -1,2 +1,3 @@
1
+ require 'holepicker/logger'
1
2
  require 'holepicker/scanner'
2
3
  require 'holepicker/version'
@@ -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.2", "1.1.4", "1.0.3"]
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
- puts "Can't load local data file: #{e}"
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
- URL='https://raw.github.com/jsuder/holepicker/master/lib/holepicker/data/data.json'
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
- puts "Fetching list of vulnerabilities..."
14
+ logger.info "Fetching list of vulnerabilities..."
12
15
 
13
- load_from_json_file(http_get(URL)).tap do |db|
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
- puts "Can't download latest data file: #{e}"
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
- puts "You need to upgrade holepicker to version #{@min_version} or later."
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
- puts "#{count} new #{Utils.pluralize(count, 'vulnerability')} found in the last " +
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
- puts "#{v.day} (#{v.gem_names.join(', ')}): #{v.url}"
59
+ logger.info "#{v.day} (#{v.gem_names.join(', ')}): #{v.url}"
50
60
  end
51
61
 
52
- puts
62
+ logger.info
53
63
  end
54
64
  end
55
65
  end
@@ -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
- puts "Looking for gemfiles..."
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
- puts "".color(:green)
75
+ logger.print "#{path}: "
76
+ logger.success "✔"
76
77
  else
77
- puts "#{count} vulnerable #{Utils.pluralize(count, 'gem')} found!".color(:red)
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
- puts "- #{gem} [#{vulnerabilities.map(&:tag).join(',')}]"
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
- puts
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
- puts "No gemfiles found - are you sure the paths are correct?".color(:red)
98
+ logger.warn "No gemfiles found - are you sure the paths are correct?"
97
99
  elsif @matched_gemfiles == 0
98
- puts "No vulnerabilities found."
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
- warning = "#{@matched_gems} vulnerable #{gems} found in #{@matched_gemfiles} #{gemfiles}!\n"
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
- puts "[#{v.tag}] #{v.day}: #{v.url}"
108
+ logger.error "[#{v.tag}] #{v.day}: #{v.url}"
108
109
  end
109
110
 
110
111
  if @found_vulnerabilities.any?(&:note)
111
- puts
112
+ logger.error
112
113
 
113
114
  @found_vulnerabilities.select(&:note).each do |v|
114
- puts "[#{v.tag}] #{v.note}"
115
+ logger.error "[#{v.tag}] #{v.note}"
115
116
  end
116
117
  end
117
118
  end
@@ -1,7 +1,7 @@
1
1
  require 'rubygems'
2
2
 
3
3
  module HolePicker
4
- VERSION = "0.2.2"
4
+ VERSION = "0.3.0"
5
5
 
6
6
  def self.version
7
7
  ::Gem::Version.new(VERSION)
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.2.2
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-03-29 00:00:00.000000000 Z
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