holepicker 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OTdjY2M3YzA1YTE1MTc4MmZiYzE5YzNkNGVlMWIwYmNlYjE0YmZmMQ==
5
+ data.tar.gz: !binary |-
6
+ NmNhZTIyZTQ5YzNjNDBjOWMzMGM2OGU0ZTZjMWFlNjhkOGYxODBjNA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NjNhMzhiNGM1YmFhNTYzNzc3YzgzMjRkNWQ4N2I3OWU2ZDFiMmNhNTc1YTEw
10
+ MzJmZDI4NWQ5MWE0OGUyODkzYWVmMTVlOTI0ZGQ5NTFhOWZkMTljYTMzOGVh
11
+ Yjg3MzRhMGE5YWY2MWUyMzQ2ZTJmZGQxMGMwMzg3N2NiMjI5YWE=
12
+ data.tar.gz: !binary |-
13
+ NzQzMmNiOTJiZTdiZTYyODA1N2U3MzgyNjViNGRiY2RhYWMxMWE5NmI2OGNk
14
+ MmZkYWU2Y2MyM2ZlMjMyNzZhNTY2YjgzOTNkZTVmNDE1YTk3ODc3NDU4MDVl
15
+ MzIyNzBmNmQ1ODEzOTI0ZmNhNWFhNDExNzQwMzhiYTMzZDhmZWI=
data/Changelog.markdown CHANGED
@@ -1,7 +1,14 @@
1
+ #### Version 0.2.0 (7.03.2013)
2
+
3
+ * Capistrano recipe (@manuelvanrijn)
4
+ * updated JSON vulnerability from February to also include json_pure
5
+ * print warning if no gemfiles are found
6
+ * print green checkmarks instead of OK for safe gemfiles
7
+
1
8
  #### Version 0.1.2 (23.02.2013)
2
9
 
3
- * fixed issue on Ruby 1.8
4
- * fixed issue with relative paths like '.' in parameters
10
+ * fixed some issues on Ruby 1.8 (@timpeters)
11
+ * fixed issue with relative paths like '.' in parameters (@bct)
5
12
  * fixed parsing gemfiles with platform-specific gems (e.g. mingw32)
6
13
 
7
14
  #### Version 0.1.1 (18.82.2013)
data/Gemfile CHANGED
@@ -1,5 +1,7 @@
1
1
  source "http://rubygems.org"
2
2
  gemspec
3
3
 
4
+ gem 'fakefs'
4
5
  gem 'mocha'
5
6
  gem 'rspec'
7
+ gem 'webmock'
data/Gemfile.lock CHANGED
@@ -1,14 +1,17 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- holepicker (0.1.2)
4
+ holepicker (0.2.0)
5
5
  json (>= 1.7.7)
6
6
  rainbow (>= 1.1.4)
7
7
 
8
8
  GEM
9
9
  remote: http://rubygems.org/
10
10
  specs:
11
+ addressable (2.2.8)
12
+ crack (0.3.2)
11
13
  diff-lcs (1.1.3)
14
+ fakefs (0.4.2)
12
15
  json (1.7.7)
13
16
  metaclass (0.0.1)
14
17
  mocha (0.13.2)
@@ -22,11 +25,16 @@ GEM
22
25
  rspec-expectations (2.12.1)
23
26
  diff-lcs (~> 1.1.3)
24
27
  rspec-mocks (2.12.2)
28
+ webmock (1.8.7)
29
+ addressable (>= 2.2.7)
30
+ crack (>= 0.1.7)
25
31
 
26
32
  PLATFORMS
27
33
  ruby
28
34
 
29
35
  DEPENDENCIES
36
+ fakefs
30
37
  holepicker!
31
38
  mocha
32
39
  rspec
40
+ webmock
data/README.markdown CHANGED
@@ -40,6 +40,8 @@ You can also scan all apps deployed to a production or demo server; in this case
40
40
 
41
41
  holepicker -c /var/www
42
42
 
43
+ HolePicker will return a non-zero status code if vulnerabilities are found, so you could wrap it in some kind of script that's run periodically from cron that notifies you when something is wrong.
44
+
43
45
  ### Scanning Nginx/Apache config directory
44
46
 
45
47
  You might have a lot of random apps deployed in the `/var/www` directory, but only some of them currently enabled in the Nginx config files. In this case, you might want to only check the apps that are actually running. To do that, use the `-f` (`--follow-roots`) option and point HolePicker to your HTTP server's config directory. It will find all the `root` or `DocumentRoot` directives and follow the paths to find the gemfiles of enabled apps.
@@ -47,6 +49,15 @@ You might have a lot of random apps deployed in the `/var/www` directory, but on
47
49
  holepicker -f /etc/nginx/sites-enabled
48
50
 
49
51
 
52
+ ## Integration with capistrano
53
+
54
+ To automatically check for vulnerabilities before deployment, you can add the HolePicker Capistrano recipe:
55
+
56
+ 1. Add `gem 'holepicker'` to your `Gemfile` (preferably with `:require => false`)
57
+ 2. Add `require 'holepicker/capistrano'` to your `config/deploy.rb`
58
+
59
+ This will introduce a `cap holepicker` task which will be executed before the deploy.
60
+
50
61
  ## Results
51
62
 
52
63
  This is more or less what you will get if you run HolePicker in a directory with some old Rails projects:
data/bin/holepicker CHANGED
@@ -31,11 +31,6 @@ OptionParser.new do |opts|
31
31
  options[:offline] = true
32
32
  end
33
33
 
34
- opts.on("-r", "--skip-releases",
35
- "Skip gemfiles in 'releases' directory (like -c but will include non-Capistrano deploys)") do
36
- options[:skip_releases] = true
37
- end
38
-
39
34
  opts.on("-h", "--help", "Display this help") do
40
35
  puts opts
41
36
  exit
@@ -0,0 +1,24 @@
1
+ require 'holepicker/scanner'
2
+
3
+ Capistrano::Configuration.instance.load do
4
+ set(:holepicker_offline, false) unless exists?(:holepicker_offline)
5
+ set(:holepicker_ignored_gems, []) unless exists?(:holepicker_ignored_gems)
6
+
7
+ before "deploy:update_code", "holepicker"
8
+
9
+ namespace :holepicker do
10
+ desc "Look for vulnerabilities in your Gemfile"
11
+ task :default, :roles => :app do
12
+ options = {
13
+ :ignored_gems => holepicker_ignored_gems,
14
+ :offline => holepicker_offline
15
+ }
16
+
17
+ gemfile_lock = "#{ENV['BUNDLE_GEMFILE']}.lock"
18
+ success = HolePicker::Scanner.new(gemfile_lock, options).scan
19
+ unless success
20
+ raise Capistrano::CommandError.new("HolePicker found vulnerabilities!")
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ require 'holepicker/config_reader'
2
+ require 'holepicker/file_finder'
3
+
4
+ module HolePicker
5
+ class ConfigGemfileFinder
6
+ def find_gemfiles(path)
7
+ configs = find_configs(path)
8
+ configs.map { |f| ConfigReader.new(f).find_gemfiles }.flatten
9
+ end
10
+
11
+ private
12
+
13
+ def find_configs(path)
14
+ full_path = File.expand_path(path)
15
+
16
+ FileFinder.find_files(full_path, '-type f -or -type l').select { |f| File.exist?(f) }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module HolePicker
2
+ class ConfigReader
3
+ ROOT_LINE_PATTERN = %r{\b(?:root|DocumentRoot)\s+(.*)/public\b}
4
+
5
+ def initialize(path)
6
+ @contents = File.read(path)
7
+ end
8
+
9
+ def find_gemfiles
10
+ @contents.scan(ROOT_LINE_PATTERN).map { |result| "#{result.first}/Gemfile.lock" }.select { |f| File.exist?(f) }
11
+ end
12
+ end
13
+ end
@@ -10,7 +10,8 @@
10
10
  },
11
11
  {
12
12
  "gems": {
13
- "json": ["1.7.7", "1.6.8", "1.5.5"]
13
+ "json": ["1.7.7", "1.6.8", "1.5.5"],
14
+ "json_pure": ["1.7.7", "1.6.8", "1.5.5"]
14
15
  },
15
16
  "url": "https://groups.google.com/forum/?fromgroups=#!topic/rubyonrails-security/4_YvCpLzL58",
16
17
  "date": "2013-02-11T18:26Z"
@@ -0,0 +1,31 @@
1
+ require 'holepicker/file_finder'
2
+
3
+ module HolePicker
4
+ class DirectGemfileFinder
5
+ SKIPPED_DIRECTORIES = [
6
+ "-name cached-copy",
7
+ "-path '*/bundle/ruby'",
8
+ "-name tmp",
9
+ "-name '.*'"
10
+ ]
11
+
12
+ def initialize(options = {})
13
+ @skip_ignored = options.fetch(:skip_ignored, true)
14
+ @only_current = options.fetch(:only_current, false)
15
+ end
16
+
17
+ def find_gemfiles(path)
18
+ full_path = File.expand_path(path)
19
+ gemfiles = @only_current ? "-path '*/current/Gemfile.lock'" : "-name 'Gemfile.lock'"
20
+ options = @skip_ignored ? "\\( #{skipped_directories} \\) -prune -or #{gemfiles} -print" : gemfiles
21
+
22
+ FileFinder.find_files(full_path, options)
23
+ end
24
+
25
+ private
26
+
27
+ def skipped_directories
28
+ SKIPPED_DIRECTORIES.join(" -or ")
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,7 @@
1
+ module HolePicker
2
+ module FileFinder
3
+ def self.find_files(path, options = "")
4
+ %x(find -L #{path} #{options}).lines.map(&:strip)
5
+ end
6
+ end
7
+ end
@@ -1,4 +1,8 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'holepicker/gem'
4
+ require 'holepicker/config_gemfile_finder'
5
+ require 'holepicker/direct_gemfile_finder'
2
6
  require 'holepicker/offline_database'
3
7
  require 'holepicker/online_database'
4
8
  require 'holepicker/utils'
@@ -7,8 +11,6 @@ require 'set'
7
11
 
8
12
  module HolePicker
9
13
  class Scanner
10
- SKIPPED_DIRECTORIES = ["-name cached-copy", "-path '*/bundle/ruby'", "-name tmp", "-name '.*'"]
11
- ROOT_LINE_PATTERN = %r{\b(?:root|DocumentRoot)\s+(.*)/public\b}
12
14
  GEMFILE_GEM_PATTERN = %r(^ {4}[^ ])
13
15
 
14
16
  def initialize(paths, options = {})
@@ -16,16 +18,23 @@ module HolePicker
16
18
 
17
19
  @database = options[:offline] ? OfflineDatabase.load : OnlineDatabase.load
18
20
 
21
+ @finder = if options[:follow_roots]
22
+ ConfigGemfileFinder.new
23
+ else
24
+ DirectGemfileFinder.new(
25
+ :skip_ignored => !options[:dont_skip],
26
+ :only_current => options[:current]
27
+ )
28
+ end
29
+
19
30
  @ignored = options[:ignored_gems] || []
20
- @skip = !options[:dont_skip]
21
- @current = options[:current]
22
- @roots = options[:follow_roots]
23
31
  end
24
32
 
25
33
  def scan
26
34
  puts "Looking for gemfiles..."
27
35
 
28
36
  @found_vulnerabilities = Set.new
37
+ @scanned_gemfiles = 0
29
38
  @matched_gemfiles = 0
30
39
  @matched_gems = 0
31
40
 
@@ -43,37 +52,12 @@ module HolePicker
43
52
  @database.vulnerabilities.select { |v| v.gem_vulnerable?(gem) }
44
53
  end
45
54
 
46
- def find_gemfiles_in_path(path)
47
- skips = SKIPPED_DIRECTORIES.join(" -or ")
48
- gemfiles = @current ? "-path '*/current/Gemfile.lock'" : "-name 'Gemfile.lock'"
49
-
50
- command = if @skip
51
- "find -L #{path} \\( #{skips} \\) -prune -or #{gemfiles} -print"
52
- else
53
- "find -L #{path} #{gemfiles}"
54
- end
55
-
56
- run_and_read_lines(command)
57
- end
58
-
59
- def find_gemfiles_in_configs(path)
60
- configs = run_and_read_lines("find -L #{path} -type f -or -type l")
61
- configs = select_existing(configs)
62
-
63
- directories = configs.map { |f| File.read(f).scan(ROOT_LINE_PATTERN) }
64
- gemfiles = directories.flatten.map { |dir| "#{dir}/Gemfile.lock" }
65
-
66
- select_existing(gemfiles)
67
- end
68
-
69
55
  def read_gemfile(path)
70
56
  File.readlines(path).select { |l| l =~ GEMFILE_GEM_PATTERN }.map { |l| Gem.new(l) }
71
57
  end
72
58
 
73
59
  def scan_path(path)
74
- path = File.expand_path(path)
75
- gemfiles = @roots ? find_gemfiles_in_configs(path) : find_gemfiles_in_path(path)
76
- gemfiles.each { |f| scan_gemfile(f) }
60
+ @finder.find_gemfiles(path).each { |f| scan_gemfile(f) }
77
61
  end
78
62
 
79
63
  def scan_gemfile(path)
@@ -88,7 +72,7 @@ module HolePicker
88
72
  count = vulnerable_gems.length
89
73
 
90
74
  if count == 0
91
- puts "OK"
75
+ puts "".color(:green)
92
76
  else
93
77
  puts "#{count} vulnerable #{Utils.pluralize(count, 'gem')} found!".color(:red)
94
78
 
@@ -103,27 +87,26 @@ module HolePicker
103
87
 
104
88
  puts
105
89
  end
90
+
91
+ @scanned_gemfiles += 1
106
92
  end
107
93
 
108
94
  def print_report
109
- if @matched_gemfiles == 0
95
+ if @scanned_gemfiles == 0
96
+ puts "No gemfiles found - are you sure the paths are correct?".color(:red)
97
+ elsif @matched_gemfiles == 0
110
98
  puts "No vulnerabilities found."
111
99
  else
112
- puts(("#{@matched_gems} vulnerable #{Utils.pluralize(@matched_gems, 'gem')} found in " +
113
- "#{@matched_gemfiles} #{Utils.pluralize(@matched_gemfiles, 'gemfile')}!").color(:red) + "\n\n")
100
+ gems = Utils.pluralize(@matched_gems, 'gem')
101
+ gemfiles = Utils.pluralize(@matched_gemfiles, 'gemfile')
102
+
103
+ warning = "#{@matched_gems} vulnerable #{gems} found in #{@matched_gemfiles} #{gemfiles}!\n"
104
+ puts warning.color(:red)
114
105
 
115
106
  @found_vulnerabilities.sort_by(&:id).each do |v|
116
107
  puts "[#{v.tag}] #{v.day}: #{v.url}"
117
108
  end
118
109
  end
119
110
  end
120
-
121
- def select_existing(files)
122
- files.select { |f| File.exist?(f) }
123
- end
124
-
125
- def run_and_read_lines(command)
126
- %x(#{command}).lines.map(&:strip)
127
- end
128
111
  end
129
112
  end
@@ -1,7 +1,7 @@
1
1
  require 'rubygems'
2
2
 
3
3
  module HolePicker
4
- VERSION = "0.1.2"
4
+ VERSION = "0.2.0"
5
5
 
6
6
  def self.version
7
7
  ::Gem::Version.new(VERSION)
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: holepicker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
5
- prerelease:
4
+ version: 0.2.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Jakub Suder
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-02-23 00:00:00.000000000 Z
11
+ date: 2013-03-07 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: json
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ! '>='
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ! '>='
28
25
  - !ruby/object:Gem::Version
@@ -30,7 +27,6 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rainbow
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ! '>='
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ! '>='
44
39
  - !ruby/object:Gem::Version
@@ -55,40 +50,43 @@ files:
55
50
  - Changelog.markdown
56
51
  - Gemfile
57
52
  - Gemfile.lock
53
+ - lib/holepicker/capistrano.rb
54
+ - lib/holepicker/config_gemfile_finder.rb
55
+ - lib/holepicker/config_reader.rb
58
56
  - lib/holepicker/data/data.json
59
57
  - lib/holepicker/database.rb
58
+ - lib/holepicker/direct_gemfile_finder.rb
59
+ - lib/holepicker/file_finder.rb
60
60
  - lib/holepicker/gem.rb
61
61
  - lib/holepicker/offline_database.rb
62
62
  - lib/holepicker/online_database.rb
63
63
  - lib/holepicker/scanner.rb
64
64
  - lib/holepicker/utils.rb
65
- - lib/holepicker/validator.rb
66
65
  - lib/holepicker/version.rb
67
66
  - lib/holepicker/vulnerability.rb
68
67
  - lib/holepicker.rb
69
68
  - bin/holepicker
70
69
  homepage: http://github.com/jsuder/holepicker
71
70
  licenses: []
71
+ metadata: {}
72
72
  post_install_message:
73
73
  rdoc_options: []
74
74
  require_paths:
75
75
  - lib
76
76
  required_ruby_version: !ruby/object:Gem::Requirement
77
- none: false
78
77
  requirements:
79
78
  - - ! '>='
80
79
  - !ruby/object:Gem::Version
81
80
  version: '0'
82
81
  required_rubygems_version: !ruby/object:Gem::Requirement
83
- none: false
84
82
  requirements:
85
83
  - - ! '>='
86
84
  - !ruby/object:Gem::Version
87
85
  version: '0'
88
86
  requirements: []
89
87
  rubyforge_project:
90
- rubygems_version: 1.8.24
88
+ rubygems_version: 2.0.0
91
89
  signing_key:
92
- specification_version: 3
90
+ specification_version: 4
93
91
  summary: A tool for checking gem versions in Gemfile.lock files for known vulnerabilities
94
92
  test_files: []
File without changes