mrjoy-bundler-audit 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.gitignore +6 -0
  4. data/.gitmodules +3 -0
  5. data/.rspec +1 -0
  6. data/.travis.yml +5 -0
  7. data/.yardopts +1 -0
  8. data/COPYING.txt +674 -0
  9. data/ChangeLog.md +79 -0
  10. data/Gemfile +14 -0
  11. data/README.md +105 -0
  12. data/Rakefile +47 -0
  13. data/bin/bundle-audit +10 -0
  14. data/data/ruby-advisory-db/.rspec +1 -0
  15. data/data/ruby-advisory-db/CONTRIBUTING.md +6 -0
  16. data/data/ruby-advisory-db/CONTRIBUTORS.md +13 -0
  17. data/data/ruby-advisory-db/Gemfile +3 -0
  18. data/data/ruby-advisory-db/LICENSE.txt +5 -0
  19. data/data/ruby-advisory-db/README.md +86 -0
  20. data/data/ruby-advisory-db/Rakefile +27 -0
  21. data/data/ruby-advisory-db/gems/actionpack/OSVDB-79727.yml +26 -0
  22. data/data/ruby-advisory-db/gems/actionpack/OSVDB-84243.yml +28 -0
  23. data/data/ruby-advisory-db/gems/actionpack/OSVDB-84513.yml +23 -0
  24. data/data/ruby-advisory-db/gems/actionpack/OSVDB-84515.yml +26 -0
  25. data/data/ruby-advisory-db/gems/actionpack/OSVDB-89026.yml +24 -0
  26. data/data/ruby-advisory-db/gems/actionpack/OSVDB-91452.yml +20 -0
  27. data/data/ruby-advisory-db/gems/actionpack/OSVDB-91454.yml +23 -0
  28. data/data/ruby-advisory-db/gems/activerecord/OSVDB-82403.yml +25 -0
  29. data/data/ruby-advisory-db/gems/activerecord/OSVDB-82610.yml +24 -0
  30. data/data/ruby-advisory-db/gems/activerecord/OSVDB-89025.yml +24 -0
  31. data/data/ruby-advisory-db/gems/activerecord/OSVDB-90072.yml +21 -0
  32. data/data/ruby-advisory-db/gems/activerecord/OSVDB-90073.yml +23 -0
  33. data/data/ruby-advisory-db/gems/activerecord/OSVDB-91453.yml +26 -0
  34. data/data/ruby-advisory-db/gems/activesupport/OSVDB-79726.yml +26 -0
  35. data/data/ruby-advisory-db/gems/activesupport/OSVDB-84516.yml +23 -0
  36. data/data/ruby-advisory-db/gems/activesupport/OSVDB-89594.yml +25 -0
  37. data/data/ruby-advisory-db/gems/activesupport/OSVDB-91451.yml +28 -0
  38. data/data/ruby-advisory-db/gems/command_wrap/OSVDB-91450.yml +10 -0
  39. data/data/ruby-advisory-db/gems/crack/OSVDB-90742.yml +17 -0
  40. data/data/ruby-advisory-db/gems/cremefraiche/OSVDB-93395.yml +11 -0
  41. data/data/ruby-advisory-db/gems/curl/OSVDB-91230.yml +12 -0
  42. data/data/ruby-advisory-db/gems/devise/OSVDB-89642.yml +20 -0
  43. data/data/ruby-advisory-db/gems/dragonfly/OSVDB-90647.yml +19 -0
  44. data/data/ruby-advisory-db/gems/enum_column3/OSVDB-94679.yml +9 -0
  45. data/data/ruby-advisory-db/gems/extlib/OSVDB-90740.yml +18 -0
  46. data/data/ruby-advisory-db/gems/fastreader/OSVDB-91232.yml +12 -0
  47. data/data/ruby-advisory-db/gems/fileutils/OSVDB-90715.yml +10 -0
  48. data/data/ruby-advisory-db/gems/fileutils/OSVDB-90716.yml +10 -0
  49. data/data/ruby-advisory-db/gems/fileutils/OSVDB-90717.yml +10 -0
  50. data/data/ruby-advisory-db/gems/flash_tool/OSVDB-90829.yml +9 -0
  51. data/data/ruby-advisory-db/gems/ftpd/OSVDB-90784.yml +18 -0
  52. data/data/ruby-advisory-db/gems/gtk2/OSVDB-40774.yml +20 -0
  53. data/data/ruby-advisory-db/gems/httparty/OSVDB-90741.yml +19 -0
  54. data/data/ruby-advisory-db/gems/json/OSVDB-90074.yml +23 -0
  55. data/data/ruby-advisory-db/gems/karteek-docsplit/OSVDB-92117.yml +10 -0
  56. data/data/ruby-advisory-db/gems/kelredd-pruview/OSVDB-92228.yml +10 -0
  57. data/data/ruby-advisory-db/gems/ldoce/OSVDB-91870.yml +10 -0
  58. data/data/ruby-advisory-db/gems/loofah/OSVDB-90945.yml +21 -0
  59. data/data/ruby-advisory-db/gems/mail/OSVDB-70667.yml +21 -0
  60. data/data/ruby-advisory-db/gems/mail/OSVDB-81631.yml +14 -0
  61. data/data/ruby-advisory-db/gems/mail/OSVDB-81632.yml +16 -0
  62. data/data/ruby-advisory-db/gems/md2pdf/OSVDB-92290.yml +10 -0
  63. data/data/ruby-advisory-db/gems/mini_magick/OSVDB-91231.yml +15 -0
  64. data/data/ruby-advisory-db/gems/multi_xml/OSVDB-89148.yml +16 -0
  65. data/data/ruby-advisory-db/gems/newrelic_rpm/OSVDB-90189.yml +17 -0
  66. data/data/ruby-advisory-db/gems/nori/OSVDB-90196.yml +19 -0
  67. data/data/ruby-advisory-db/gems/omniauth-oauth2/OSVDB-90264.yml +16 -0
  68. data/data/ruby-advisory-db/gems/pdfkit/OSVDB-90867.yml +11 -0
  69. data/data/ruby-advisory-db/gems/rack-cache/OSVDB-83077.yml +18 -0
  70. data/data/ruby-advisory-db/gems/rack/OSVDB-89939.yml +23 -0
  71. data/data/ruby-advisory-db/gems/rdoc/OSVDB-90004.yml +27 -0
  72. data/data/ruby-advisory-db/gems/rgpg/OSVDB-95948.yml +13 -0
  73. data/data/ruby-advisory-db/gems/ruby_parser/OSVDB-90561.yml +11 -0
  74. data/data/ruby-advisory-db/gems/spree/OSVDB-91216.yml +10 -0
  75. data/data/ruby-advisory-db/gems/spree/OSVDB-91217.yml +10 -0
  76. data/data/ruby-advisory-db/gems/spree/OSVDB-91218.yml +10 -0
  77. data/data/ruby-advisory-db/gems/spree/OSVDB-91219.yml +10 -0
  78. data/data/ruby-advisory-db/gems/thumbshooter/OSVDB-91839.yml +10 -0
  79. data/data/ruby-advisory-db/lib/scrape.rb +87 -0
  80. data/data/ruby-advisory-db/spec/advisory_example.rb +165 -0
  81. data/data/ruby-advisory-db/spec/gems_spec.rb +8 -0
  82. data/data/ruby-advisory-db/spec/spec_helper.rb +1 -0
  83. data/gemspec.yml +16 -0
  84. data/lib/bundler/audit.rb +21 -0
  85. data/lib/bundler/audit/advisory.rb +142 -0
  86. data/lib/bundler/audit/cli.rb +124 -0
  87. data/lib/bundler/audit/database.rb +187 -0
  88. data/lib/bundler/audit/scanner.rb +97 -0
  89. data/lib/bundler/audit/version.rb +25 -0
  90. data/mrjoy-bundler-audit.gemspec +66 -0
  91. data/spec/advisory_spec.rb +145 -0
  92. data/spec/audit_spec.rb +8 -0
  93. data/spec/bundle/insecure_sources/Gemfile +39 -0
  94. data/spec/bundle/secure/Gemfile +38 -0
  95. data/spec/bundle/unpatched_gems/Gemfile +38 -0
  96. data/spec/database_spec.rb +81 -0
  97. data/spec/integration_spec.rb +81 -0
  98. data/spec/scanner_spec.rb +74 -0
  99. data/spec/spec_helper.rb +21 -0
  100. metadata +162 -0
@@ -0,0 +1,21 @@
1
+ #
2
+ # Copyright (c) 2013 Hal Brodigan (postmodern.mod3 at gmail.com)
3
+ # Modifications Copyright (c) 2013 Jon Frisby (jfrisby@mrjoy.com), or their
4
+ # respective authors.
5
+ #
6
+ # mrjoy-bundler-audit is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # mrjoy-bundler-audit is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with mrjoy-bundler-audit. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'bundler/audit/database'
21
+ require 'bundler/audit/version'
@@ -0,0 +1,142 @@
1
+ #
2
+ # Copyright (c) 2013 Hal Brodigan (postmodern.mod3 at gmail.com)
3
+ # Modifications Copyright (c) 2013 Jon Frisby (jfrisby@mrjoy.com), or their
4
+ # respective authors.
5
+ #
6
+ # mrjoy-bundler-audit is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # mrjoy-bundler-audit is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with mrjoy-bundler-audit. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'yaml'
21
+
22
+ module Bundler
23
+ module Audit
24
+ class Advisory < Struct.new(:path,
25
+ :cve,
26
+ :url,
27
+ :title,
28
+ :description,
29
+ :cvss_v2,
30
+ :unaffected_versions,
31
+ :patched_versions)
32
+
33
+ #
34
+ # Loads the advisory from a YAML file.
35
+ #
36
+ # @param [String] path
37
+ # The path to the advisory YAML file.
38
+ #
39
+ # @return [Advisory]
40
+ #
41
+ # @api semipublic
42
+ #
43
+ def self.load(path)
44
+ data = YAML.load_file(path)
45
+ cve = data['cve']
46
+
47
+ unless data.kind_of?(Hash)
48
+ raise("advisory data in #{path.dump} was not a Hash")
49
+ end
50
+
51
+ parse_versions = lambda { |versions|
52
+ Array(versions).map do |version|
53
+ Gem::Requirement.new(*version.split(', '))
54
+ end
55
+ }
56
+
57
+ return new(
58
+ path,
59
+ cve,
60
+ data['url'],
61
+ data['title'],
62
+ data['description'],
63
+ data['cvss_v2'],
64
+ parse_versions[data['unaffected_versions']],
65
+ parse_versions[data['patched_versions']]
66
+ )
67
+ end
68
+
69
+ #
70
+ # Determines how critical the vulnerability is.
71
+ #
72
+ # @return [:low, :medium, :high]
73
+ # The criticality of the vulnerability based on the CVSSv2 score.
74
+ #
75
+ def criticality
76
+ case cvss_v2
77
+ when 0.0..3.3 then :low
78
+ when 3.3..6.6 then :medium
79
+ when 6.6..10.0 then :high
80
+ end
81
+ end
82
+
83
+ #
84
+ # Checks whether the version is not affected by the advisory.
85
+ #
86
+ # @param [Gem::Version] version
87
+ # The version to compare against {#unaffected_version}.
88
+ #
89
+ # @return [Boolean]
90
+ # Specifies whether the version is not affected by the advisory.
91
+ #
92
+ # @since 0.2.0
93
+ #
94
+ def unaffected?(version)
95
+ unaffected_versions.any? do |unaffected_version|
96
+ unaffected_version === version
97
+ end
98
+ end
99
+
100
+ #
101
+ # Checks whether the version is patched against the advisory.
102
+ #
103
+ # @param [Gem::Version] version
104
+ # The version to compare against {#patched_version}.
105
+ #
106
+ # @return [Boolean]
107
+ # Specifies whether the version is patched against the advisory.
108
+ #
109
+ # @since 0.2.0
110
+ #
111
+ def patched?(version)
112
+ patched_versions.any? do |patched_version|
113
+ patched_version === version
114
+ end
115
+ end
116
+
117
+ #
118
+ # Checks whether the version is vulnerable to the advisory.
119
+ #
120
+ # @param [Gem::Version] version
121
+ # The version to compare against {#patched_versions}.
122
+ #
123
+ # @return [Boolean]
124
+ # Specifies whether the version is vulnerable to the advisory or not.
125
+ #
126
+ def vulnerable?(version)
127
+ !patched?(version) && !unaffected?(version)
128
+ end
129
+
130
+ #
131
+ # Converts the advisory to a String.
132
+ #
133
+ # @return [String]
134
+ # The CVE identifier.
135
+ #
136
+ def to_s
137
+ "CVE-#{cve}"
138
+ end
139
+
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,124 @@
1
+ #
2
+ # Copyright (c) 2013 Hal Brodigan (postmodern.mod3 at gmail.com)
3
+ # Modifications Copyright (c) 2013 Jon Frisby (jfrisby@mrjoy.com), or their
4
+ # respective authors.
5
+ #
6
+ # mrjoy-bundler-audit is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # mrjoy-bundler-audit is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with mrjoy-bundler-audit. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'bundler/audit/scanner'
21
+ require 'bundler/audit/version'
22
+
23
+ require 'bundler'
24
+ require 'bundler/vendored_thor'
25
+
26
+ module Bundler
27
+ module Audit
28
+ class CLI < Thor
29
+
30
+ default_task :check
31
+ map '--version' => :version
32
+
33
+ desc 'check', 'Checks the Gemfile.lock for insecure dependencies'
34
+ method_option :verbose, :type => :boolean, :aliases => '-v'
35
+ method_option :ignore, :type => :array, :aliases => '-i'
36
+
37
+ def check
38
+ scanner = Scanner.new
39
+ vulnerable = false
40
+
41
+ scanner.scan(:ignore => options.ignore) do |result|
42
+ vulnerable = true
43
+
44
+ case result
45
+ when Scanner::InsecureSource
46
+ print_warning "Insecure Source URI found: #{result.source}"
47
+ when Scanner::UnpatchedGem
48
+ print_advisory result.gem, result.advisory
49
+ end
50
+ end
51
+
52
+ if vulnerable
53
+ say "Unpatched versions found!", :red
54
+ exit 1
55
+ else
56
+ say "No unpatched versions found", :green
57
+ end
58
+ end
59
+
60
+ desc 'version', 'Prints the bundler-audit version'
61
+ def version
62
+ database = Database.new
63
+
64
+ puts "#{File.basename($0)} #{VERSION} (advisories: #{database.size})"
65
+ end
66
+
67
+ protected
68
+
69
+ def say(string="", color=nil)
70
+ color = nil unless $stdout.tty?
71
+ super(string, color)
72
+ end
73
+
74
+ def print_warning(message)
75
+ say message, :yellow
76
+ end
77
+
78
+ def print_advisory(gem, advisory)
79
+ say "Name: ", :red
80
+ say gem.name
81
+
82
+ say "Version: ", :red
83
+ say gem.version
84
+
85
+ say "Advisory: ", :red
86
+ say "CVE-#{advisory.cve}"
87
+
88
+ say "Criticality: ", :red
89
+ case advisory.criticality
90
+ when :low then say "Low"
91
+ when :medium then say "Medium", :yellow
92
+ when :high then say "High", [:red, :bold]
93
+ else say "Unknown"
94
+ end
95
+
96
+ say "URL: ", :red
97
+ say advisory.url
98
+
99
+ if options.verbose?
100
+ say "Description:", :red
101
+ say
102
+
103
+ print_wrapped advisory.description, :indent => 2
104
+ say
105
+ else
106
+
107
+ say "Title: ", :red
108
+ say advisory.title
109
+ end
110
+
111
+ unless advisory.patched_versions.empty?
112
+ say "Solution: upgrade to ", :red
113
+ say advisory.patched_versions.join(', ')
114
+ else
115
+ say "Solution: ", :red
116
+ say "remove or disable this gem until a patch is available!", [:red, :bold]
117
+ end
118
+
119
+ say
120
+ end
121
+
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,187 @@
1
+ #
2
+ # Copyright (c) 2013 Hal Brodigan (postmodern.mod3 at gmail.com)
3
+ # Modifications Copyright (c) 2013 Jon Frisby (jfrisby@mrjoy.com), or their
4
+ # respective authors.
5
+ #
6
+ # mrjoy-bundler-audit is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # mrjoy-bundler-audit is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with mrjoy-bundler-audit. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'bundler/audit/advisory'
21
+
22
+ require 'yaml'
23
+
24
+ module Bundler
25
+ module Audit
26
+ #
27
+ # Represents the directory of advisories, grouped by gem name
28
+ # and CVE number.
29
+ #
30
+ class Database
31
+
32
+ # directory containing advisories
33
+ PATH = File.expand_path(File.join(File.dirname(__FILE__),'..','..','..','data','ruby-advisory-db','gems'))
34
+
35
+ # The path to the advisory database
36
+ attr_reader :path
37
+
38
+ #
39
+ # Initializes the Advisory Database.
40
+ #
41
+ # @param [String] path
42
+ # The path to the advisory database.
43
+ #
44
+ # @raise [ArgumentError]
45
+ # The path was not a directory.
46
+ #
47
+ def initialize(path=PATH)
48
+ unless File.directory?(path)
49
+ raise(ArgumentError,"#{path.dump} is not a directory")
50
+ end
51
+
52
+ @path = path
53
+ end
54
+
55
+ #
56
+ # Enumerates over every advisory in the database.
57
+ #
58
+ # @yield [advisory]
59
+ # If a block is given, it will be passed each advisory.
60
+ #
61
+ # @yieldparam [Advisory] advisory
62
+ # An advisory from the database.
63
+ #
64
+ # @return [Enumerator]
65
+ # If no block is given, an Enumerator will be returned.
66
+ #
67
+ def advisories(&block)
68
+ return enum_for(__method__) unless block_given?
69
+
70
+ each_advisory_path do |path|
71
+ yield Advisory.load(path)
72
+ end
73
+ end
74
+
75
+ #
76
+ # Enumerates over advisories for the given gem.
77
+ #
78
+ # @param [String] name
79
+ # The gem name to lookup.
80
+ #
81
+ # @yield [advisory]
82
+ # If a block is given, each advisory for the given gem will be yielded.
83
+ #
84
+ # @yieldparam [Advisory] advisory
85
+ # An advisory for the given gem.
86
+ #
87
+ # @return [Enumerator]
88
+ # If no block is given, an Enumerator will be returned.
89
+ #
90
+ def advisories_for(name)
91
+ return enum_for(__method__,name) unless block_given?
92
+
93
+ each_advisory_path_for(name) do |path|
94
+ yield Advisory.load(path)
95
+ end
96
+ end
97
+
98
+ #
99
+ # Verifies whether the gem is effected by any advisories.
100
+ #
101
+ # @param [Gem::Specification] gem
102
+ # The gem to verify.
103
+ #
104
+ # @yield [advisory]
105
+ # If a block is given, it will be passed advisories that effect
106
+ # the gem.
107
+ #
108
+ # @yieldparam [Advisory] advisory
109
+ # An advisory that effects the specific version of the gem.
110
+ #
111
+ # @return [Enumerator]
112
+ # If no block is given, an Enumerator will be returned.
113
+ #
114
+ def check_gem(gem)
115
+ return enum_for(__method__,gem) unless block_given?
116
+
117
+ advisories_for(gem.name) do |advisory|
118
+ if advisory.vulnerable?(gem.version)
119
+ yield advisory
120
+ end
121
+ end
122
+ end
123
+
124
+ #
125
+ # The number of advisories within the database.
126
+ #
127
+ # @return [Integer]
128
+ # The number of advisories.
129
+ #
130
+ def size
131
+ each_advisory_path.count
132
+ end
133
+
134
+ #
135
+ # Converts the database to a String.
136
+ #
137
+ # @return [String]
138
+ # The path to the database.
139
+ #
140
+ def to_s
141
+ @path
142
+ end
143
+
144
+ #
145
+ # Inspects the database.
146
+ #
147
+ # @return [String]
148
+ # The inspected database.
149
+ #
150
+ def inspect
151
+ "#<#{self.class}:#{self}>"
152
+ end
153
+
154
+ protected
155
+
156
+ #
157
+ # Enumerates over every advisory path in the database.
158
+ #
159
+ # @yield [path]
160
+ # The given block will be passed each advisory path.
161
+ #
162
+ # @yieldparam [String] path
163
+ # A path to an advisory `.yml` file.
164
+ #
165
+ def each_advisory_path(&block)
166
+ Dir.glob(File.join(@path,'*','*.yml'),&block)
167
+ end
168
+
169
+ #
170
+ # Enumerates over the advisories for the given gem.
171
+ #
172
+ # @param [String] name
173
+ # The gem of the gem.
174
+ #
175
+ # @yield [path]
176
+ # The given block will be passed each advisory path.
177
+ #
178
+ # @yieldparam [String] path
179
+ # A path to an advisory `.yml` file.
180
+ #
181
+ def each_advisory_path_for(name,&block)
182
+ Dir.glob(File.join(@path,name,'*.yml'),&block)
183
+ end
184
+
185
+ end
186
+ end
187
+ end