bundler-leak 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.gitignore +11 -0
  4. data/.gitmodules +3 -0
  5. data/.rspec +1 -0
  6. data/.travis.yml +13 -0
  7. data/.yardopts +1 -0
  8. data/COPYING.txt +674 -0
  9. data/ChangeLog.md +125 -0
  10. data/Gemfile +15 -0
  11. data/README.md +118 -0
  12. data/Rakefile +57 -0
  13. data/bin/bundle-leak +10 -0
  14. data/bin/bundler-leak +3 -0
  15. data/bundler-leak.gemspec +67 -0
  16. data/data/ruby-mem-advisory-db.ts +1 -0
  17. data/data/ruby-mem-advisory-db/.gitignore +1 -0
  18. data/data/ruby-mem-advisory-db/.rspec +1 -0
  19. data/data/ruby-mem-advisory-db/.travis.yml +12 -0
  20. data/data/ruby-mem-advisory-db/CONTRIBUTING.md +69 -0
  21. data/data/ruby-mem-advisory-db/CONTRIBUTORS.md +40 -0
  22. data/data/ruby-mem-advisory-db/Gemfile +9 -0
  23. data/data/ruby-mem-advisory-db/LICENSE.txt +5 -0
  24. data/data/ruby-mem-advisory-db/README.md +72 -0
  25. data/data/ruby-mem-advisory-db/Rakefile +26 -0
  26. data/data/ruby-mem-advisory-db/gems/celluloid/670.yml +10 -0
  27. data/data/ruby-mem-advisory-db/gems/grape/301.yml +9 -0
  28. data/data/ruby-mem-advisory-db/gems/oj/229.yml +9 -0
  29. data/data/ruby-mem-advisory-db/gems/redcarpet/516.yml +12 -0
  30. data/data/ruby-mem-advisory-db/gems/redis/612.yml +9 -0
  31. data/data/ruby-mem-advisory-db/gems/sidekiq-statistic/73.yml +9 -0
  32. data/data/ruby-mem-advisory-db/gems/sidekiq/2598.yml +9 -0
  33. data/data/ruby-mem-advisory-db/gems/therubyracer/336.yml +13 -0
  34. data/data/ruby-mem-advisory-db/gems/zipruby/PRE-SA-2012-02.yml +9 -0
  35. data/data/ruby-mem-advisory-db/scripts/post-advisories.sh +18 -0
  36. data/data/ruby-mem-advisory-db/spec/advisories_spec.rb +23 -0
  37. data/data/ruby-mem-advisory-db/spec/advisory_example.rb +209 -0
  38. data/data/ruby-mem-advisory-db/spec/gem_example.rb +37 -0
  39. data/data/ruby-mem-advisory-db/spec/library_example.rb +21 -0
  40. data/data/ruby-mem-advisory-db/spec/ruby_example.rb +22 -0
  41. data/data/ruby-mem-advisory-db/spec/spec_helper.rb +1 -0
  42. data/gemspec.yml +14 -0
  43. data/lib/bundler/plumber.rb +20 -0
  44. data/lib/bundler/plumber/advisory.rb +119 -0
  45. data/lib/bundler/plumber/cli.rb +135 -0
  46. data/lib/bundler/plumber/database.rb +249 -0
  47. data/lib/bundler/plumber/scanner.rb +133 -0
  48. data/lib/bundler/plumber/task.rb +49 -0
  49. data/lib/bundler/plumber/version.rb +24 -0
  50. data/spec/advisory_spec.rb +155 -0
  51. data/spec/audit_spec.rb +8 -0
  52. data/spec/bundle/insecure_sources/Gemfile +39 -0
  53. data/spec/bundle/secure/Gemfile +38 -0
  54. data/spec/bundle/unpatched_gems/Gemfile +39 -0
  55. data/spec/cli_spec.rb +99 -0
  56. data/spec/database_spec.rb +138 -0
  57. data/spec/fixtures/not_a_hash.yml +2 -0
  58. data/spec/integration_spec.rb +68 -0
  59. data/spec/scanner_spec.rb +61 -0
  60. data/spec/spec_helper.rb +62 -0
  61. metadata +141 -0
@@ -0,0 +1,37 @@
1
+ load File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
+ require 'advisory_example'
3
+
4
+ shared_examples_for "Gem Advisory" do |path|
5
+ include_examples 'Advisory', path
6
+
7
+ advisory = YAML.load_file(path)
8
+
9
+ describe path do
10
+ let(:gem) { File.basename(File.dirname(path)) }
11
+
12
+ describe "gem" do
13
+ subject { advisory['gem'] }
14
+
15
+ it { is_expected.to be_kind_of(String) }
16
+ it "should be equal to filename (case-insensitive)" do
17
+ expect(subject.downcase).to eq(gem.downcase)
18
+ end
19
+ end
20
+
21
+ describe "versions" do
22
+ it "assumes that future versions will be patched" do
23
+ unaffected_versions = advisory['unaffected_versions'] || []
24
+ patched_versions = advisory['patched_versions'] || []
25
+
26
+ versions = (unaffected_versions + patched_versions).sort_by do |v|
27
+ Gem::Version.new(v.match(/[0-9.]+\.\d+/)[0])
28
+ end
29
+
30
+ # If a gem is unpatched this test makes no sense
31
+ unless patched_versions.none?
32
+ expect(versions.last.match(/^>=|^>/)).to be_truthy
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ load File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
+ require 'advisory_example'
3
+
4
+ shared_examples_for "Libraries Advisory" do |path|
5
+ include_examples 'Advisory', path
6
+
7
+ advisory = YAML.load_file(path)
8
+
9
+ describe path do
10
+ let(:library) { File.basename(File.dirname(path)) }
11
+
12
+ describe "library" do
13
+ subject { advisory['library'] }
14
+
15
+ it { is_expected.to be_kind_of(String) }
16
+ it "should be equal to filename (case-insensitive)" do
17
+ expect(subject.downcase).to eq(library.downcase)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ load File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
+ require 'advisory_example'
3
+
4
+ shared_examples_for "Rubies Advisory" do |path|
5
+ include_examples 'Advisory', path
6
+
7
+ advisory = YAML.load_file(path)
8
+
9
+ describe path do
10
+ let(:engine) { File.basename(File.dirname(path)) }
11
+
12
+ describe "engine" do
13
+ subject { advisory['engine'] }
14
+
15
+ it { is_expected.to be_kind_of(String) }
16
+ it "should be equal to filename (case-insensitive)" do
17
+ expect(subject.downcase).to eq(engine.downcase)
18
+ end
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1 @@
1
+ require 'rspec'
data/gemspec.yml ADDED
@@ -0,0 +1,14 @@
1
+ name: bundler-leak
2
+ summary: Memory leaks verification for Bundler
3
+ description: bundler-leak provides memory leak verification for Bundled apps.
4
+ license: GPL-3.0+
5
+ authors: Ombulabs
6
+ email: hello@ombulabs.com
7
+ homepage: https://github.com/rubymem/bundler-leak#readme
8
+
9
+ required_ruby_version: ">= 1.9.3"
10
+ required_rubygems_version: ">= 1.8.0"
11
+
12
+ dependencies:
13
+ thor: ~> 0.18
14
+ bundler: ">= 1.2.0, < 3"
@@ -0,0 +1,20 @@
1
+ #
2
+ # Copyright (c) 2019 Ombulabs (hello at ombulabs.com)
3
+ # Copyright (c) 2013-2016 Hal Brodigan (postmodern.mod3 at gmail.com)
4
+ #
5
+ # bundler-leak is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # bundler-leak is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with bundler-leak. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ require 'bundler/plumber/database'
20
+ require 'bundler/plumber/version'
@@ -0,0 +1,119 @@
1
+ #
2
+ # Copyright (c) 2019 Ombulabs (hello at ombulabs.com)
3
+ # Copyright (c) 2013-2016 Hal Brodigan (postmodern.mod3 at gmail.com)
4
+ #
5
+ # bundler-leak is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # bundler-leak is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with bundler-leak. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ require 'yaml'
20
+
21
+ module Bundler
22
+ module Plumber
23
+ class Advisory < Struct.new(:path,
24
+ :id,
25
+ :url,
26
+ :title,
27
+ :date,
28
+ :description,
29
+ :unaffected_versions,
30
+ :patched_versions)
31
+
32
+ #
33
+ # Loads the advisory from a YAML file.
34
+ #
35
+ # @param [String] path
36
+ # The path to the advisory YAML file.
37
+ #
38
+ # @return [Advisory]
39
+ #
40
+ # @api semipublic
41
+ #
42
+ def self.load(path)
43
+ id = File.basename(path).chomp('.yml')
44
+ data = YAML.load_file(path)
45
+
46
+ unless data.kind_of?(Hash)
47
+ raise("advisory data in #{path.dump} was not a Hash")
48
+ end
49
+
50
+ parse_versions = lambda { |versions|
51
+ Array(versions).map do |version|
52
+ Gem::Requirement.new(*version.split(', '))
53
+ end
54
+ }
55
+
56
+ return new(
57
+ path,
58
+ id,
59
+ data['url'],
60
+ data['title'],
61
+ data['date'],
62
+ data['description'],
63
+ parse_versions[data['unaffected_versions']],
64
+ parse_versions[data['patched_versions']]
65
+ )
66
+ end
67
+
68
+ #
69
+ # Checks whether the version is not affected by the advisory.
70
+ #
71
+ # @param [Gem::Version] version
72
+ # The version to compare against {#unaffected_versions}.
73
+ #
74
+ # @return [Boolean]
75
+ # Specifies whether the version is not affected by the advisory.
76
+ #
77
+ # @since 0.2.0
78
+ #
79
+ def unaffected?(version)
80
+ unaffected_versions.any? do |unaffected_version|
81
+ unaffected_version === version
82
+ end
83
+ end
84
+
85
+ #
86
+ # Checks whether the version is patched against the advisory.
87
+ #
88
+ # @param [Gem::Version] version
89
+ # The version to compare against {#patched_versions}.
90
+ #
91
+ # @return [Boolean]
92
+ # Specifies whether the version is patched against the advisory.
93
+ #
94
+ # @since 0.2.0
95
+ #
96
+ def patched?(version)
97
+ patched_versions.any? do |patched_version|
98
+ patched_version === version
99
+ end
100
+ end
101
+
102
+ #
103
+ # Checks whether the version is vulnerable to the advisory.
104
+ #
105
+ # @param [Gem::Version] version
106
+ # The version to compare against {#patched_versions}.
107
+ #
108
+ # @return [Boolean]
109
+ # Specifies whether the version is vulnerable to the advisory or not.
110
+ #
111
+ def vulnerable?(version)
112
+ !patched?(version) && !unaffected?(version)
113
+ end
114
+
115
+ alias to_s id
116
+
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,135 @@
1
+ #
2
+ # Copyright (c) 2019 Ombulabs (hello at ombulabs.com)
3
+ # Copyright (c) 2013-2016 Hal Brodigan (postmodern.mod3 at gmail.com)
4
+ #
5
+ # bundler-leak is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # bundler-leak is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with bundler-leak. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ require 'bundler/plumber/scanner'
20
+ require 'bundler/plumber/version'
21
+
22
+ require 'thor'
23
+ require 'bundler'
24
+ require 'bundler/vendored_thor'
25
+
26
+ module Bundler
27
+ module Plumber
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 :quiet, :type => :boolean, :aliases => '-q'
35
+ method_option :verbose, :type => :boolean, :aliases => '-v'
36
+ method_option :update, :type => :boolean, :aliases => '-u'
37
+
38
+ def check
39
+ update if options[:update]
40
+
41
+ scanner = Scanner.new
42
+ vulnerable = false
43
+
44
+ scanner.scan do |result|
45
+ vulnerable = true
46
+
47
+ case result
48
+ when Scanner::UnpatchedGem
49
+ print_advisory result.gem, result.advisory
50
+ end
51
+ end
52
+
53
+ if vulnerable
54
+ say "Vulnerabilities found!", :red
55
+ exit 1
56
+ else
57
+ say("No vulnerabilities found", :green) unless options.quiet?
58
+ end
59
+ end
60
+
61
+ desc 'update', 'Updates the ruby-mem-advisory-db'
62
+ method_option :quiet, :type => :boolean, :aliases => '-q'
63
+
64
+ def update
65
+ say("Updating ruby-mem-advisory-db ...") unless options.quiet?
66
+
67
+ case Database.update!(quiet: options.quiet?)
68
+ when true
69
+ say("Updated ruby-mem-advisory-db", :green) unless options.quiet?
70
+ when false
71
+ say "Failed updating ruby-mem-advisory-db!", :red
72
+ exit 1
73
+ when nil
74
+ say "Skipping update", :yellow
75
+ end
76
+
77
+ unless options.quiet?
78
+ puts("ruby-mem-advisory-db: #{Database.new.size} advisories")
79
+ end
80
+ end
81
+
82
+ desc 'version', 'Prints the bundler-leak version'
83
+ def version
84
+ database = Database.new
85
+
86
+ puts "#{File.basename($0)} #{VERSION} (advisories: #{database.size})"
87
+ end
88
+
89
+ protected
90
+
91
+ def say(message="", color=nil)
92
+ color = nil unless $stdout.tty?
93
+ super(message.to_s, color)
94
+ end
95
+
96
+ def print_warning(message)
97
+ say message, :yellow
98
+ end
99
+
100
+ def print_advisory(gem, advisory)
101
+ say "Name: ", :red
102
+ say gem.name
103
+
104
+ say "Version: ", :red
105
+ say gem.version
106
+
107
+ say "URL: ", :red
108
+ say advisory.url
109
+
110
+ if options.verbose?
111
+ say "Description:", :red
112
+ say
113
+
114
+ print_wrapped advisory.description, :indent => 2
115
+ say
116
+ else
117
+
118
+ say "Title: ", :red
119
+ say advisory.title
120
+ end
121
+
122
+ unless advisory.patched_versions.empty?
123
+ say "Solution: upgrade to ", :red
124
+ say advisory.patched_versions.join(', ')
125
+ else
126
+ say "Solution: ", :red
127
+ say "remove or disable this gem until a patch is available!", [:red, :bold]
128
+ end
129
+
130
+ say
131
+ end
132
+
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,249 @@
1
+ #
2
+ # Copyright (c) 2019 Ombulabs (hello at ombulabs.com)
3
+ # Copyright (c) 2013-2016 Hal Brodigan (postmodern.mod3 at gmail.com)
4
+ #
5
+ # bundler-leak is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # bundler-leak is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with bundler-leak. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ require 'bundler/plumber/advisory'
20
+
21
+ require 'time'
22
+ require 'yaml'
23
+
24
+ module Bundler
25
+ module Plumber
26
+ #
27
+ # Represents the directory of advisories, grouped by gem name
28
+ # and CVE number.
29
+ #
30
+ class Database
31
+
32
+ # Git URL of the ruby-mem-advisory-db
33
+ URL = 'https://github.com/rubymem/ruby-mem-advisory-db.git'
34
+
35
+ # Default path to the ruby-mem-advisory-db
36
+ VENDORED_PATH = File.expand_path(File.join(File.dirname(__FILE__),'..','..','..','data','ruby-mem-advisory-db'))
37
+
38
+ # Timestamp for when the database was last updated
39
+ VENDORED_TIMESTAMP = Time.parse(File.read("#{VENDORED_PATH}.ts")).utc
40
+
41
+ # Path to the user's copy of the ruby-mem-advisory-db
42
+ USER_PATH = File.expand_path(File.join(ENV['HOME'],'.local','share','ruby-mem-advisory-db'))
43
+
44
+ # The path to the advisory database
45
+ attr_reader :path
46
+
47
+ #
48
+ # Initializes the Advisory Database.
49
+ #
50
+ # @param [String] path
51
+ # The path to the advisory database.
52
+ #
53
+ # @raise [ArgumentError]
54
+ # The path was not a directory.
55
+ #
56
+ def initialize(path=self.class.path)
57
+ unless File.directory?(path)
58
+ raise(ArgumentError,"#{path.dump} is not a directory")
59
+ end
60
+
61
+ @path = path
62
+ end
63
+
64
+ #
65
+ # The default path for the database.
66
+ #
67
+ # @return [String]
68
+ # The path to the database directory.
69
+ #
70
+ def self.path
71
+ if File.directory?(USER_PATH)
72
+ t1 = Dir.chdir(USER_PATH) { Time.parse(`git log --date=iso8601 --pretty="%cd" -1`) }
73
+ t2 = VENDORED_TIMESTAMP
74
+
75
+ if t1 >= t2 then USER_PATH
76
+ else VENDORED_PATH
77
+ end
78
+ else
79
+ VENDORED_PATH
80
+ end
81
+ end
82
+
83
+ #
84
+ # Updates the ruby-mem-advisory-db.
85
+ #
86
+ # @param [Boolean, quiet]
87
+ # Specify whether `git` should be `--quiet`.
88
+ #
89
+ # @return [Boolean, nil]
90
+ # Specifies whether the update was successful.
91
+ # A `nil` indicates no update was performed.
92
+ #
93
+ # @note
94
+ # Requires network access.
95
+ #
96
+ # @since 0.3.0
97
+ #
98
+ def self.update!(options={})
99
+ raise "Invalid option(s)" unless (options.keys - [:quiet]).empty?
100
+ if File.directory?(USER_PATH)
101
+ if File.directory?(File.join(USER_PATH, ".git"))
102
+ Dir.chdir(USER_PATH) do
103
+ command = %w(git pull)
104
+ command << '--quiet' if options[:quiet]
105
+ command << 'origin' << 'master'
106
+ system *command
107
+ end
108
+ end
109
+ else
110
+ command = %w(git clone)
111
+ command << '--quiet' if options[:quiet]
112
+ command << URL << USER_PATH
113
+ system *command
114
+ end
115
+ end
116
+
117
+ #
118
+ # Enumerates over every advisory in the database.
119
+ #
120
+ # @yield [advisory]
121
+ # If a block is given, it will be passed each advisory.
122
+ #
123
+ # @yieldparam [Advisory] advisory
124
+ # An advisory from the database.
125
+ #
126
+ # @return [Enumerator]
127
+ # If no block is given, an Enumerator will be returned.
128
+ #
129
+ def advisories(&block)
130
+ return enum_for(__method__) unless block_given?
131
+
132
+ each_advisory_path do |path|
133
+ yield Advisory.load(path)
134
+ end
135
+ end
136
+
137
+ #
138
+ # Enumerates over advisories for the given gem.
139
+ #
140
+ # @param [String] name
141
+ # The gem name to lookup.
142
+ #
143
+ # @yield [advisory]
144
+ # If a block is given, each advisory for the given gem will be yielded.
145
+ #
146
+ # @yieldparam [Advisory] advisory
147
+ # An advisory for the given gem.
148
+ #
149
+ # @return [Enumerator]
150
+ # If no block is given, an Enumerator will be returned.
151
+ #
152
+ def advisories_for(name)
153
+ return enum_for(__method__,name) unless block_given?
154
+
155
+ each_advisory_path_for(name) do |path|
156
+ yield Advisory.load(path)
157
+ end
158
+ end
159
+
160
+ #
161
+ # Verifies whether the gem is effected by any advisories.
162
+ #
163
+ # @param [Gem::Specification] gem
164
+ # The gem to verify.
165
+ #
166
+ # @yield [advisory]
167
+ # If a block is given, it will be passed advisories that effect
168
+ # the gem.
169
+ #
170
+ # @yieldparam [Advisory] advisory
171
+ # An advisory that effects the specific version of the gem.
172
+ #
173
+ # @return [Enumerator]
174
+ # If no block is given, an Enumerator will be returned.
175
+ #
176
+ def check_gem(gem)
177
+ return enum_for(__method__,gem) unless block_given?
178
+
179
+ advisories_for(gem.name) do |advisory|
180
+ if advisory.vulnerable?(gem.version)
181
+ yield advisory
182
+ end
183
+ end
184
+ end
185
+
186
+ #
187
+ # The number of advisories within the database.
188
+ #
189
+ # @return [Integer]
190
+ # The number of advisories.
191
+ #
192
+ def size
193
+ each_advisory_path.count
194
+ end
195
+
196
+ #
197
+ # Converts the database to a String.
198
+ #
199
+ # @return [String]
200
+ # The path to the database.
201
+ #
202
+ def to_s
203
+ @path
204
+ end
205
+
206
+ #
207
+ # Inspects the database.
208
+ #
209
+ # @return [String]
210
+ # The inspected database.
211
+ #
212
+ def inspect
213
+ "#<#{self.class}:#{self}>"
214
+ end
215
+
216
+ protected
217
+
218
+ #
219
+ # Enumerates over every advisory path in the database.
220
+ #
221
+ # @yield [path]
222
+ # The given block will be passed each advisory path.
223
+ #
224
+ # @yieldparam [String] path
225
+ # A path to an advisory `.yml` file.
226
+ #
227
+ def each_advisory_path(&block)
228
+ Dir.glob(File.join(@path,'gems','*','*.yml'),&block)
229
+ end
230
+
231
+ #
232
+ # Enumerates over the advisories for the given gem.
233
+ #
234
+ # @param [String] name
235
+ # The gem of the gem.
236
+ #
237
+ # @yield [path]
238
+ # The given block will be passed each advisory path.
239
+ #
240
+ # @yieldparam [String] path
241
+ # A path to an advisory `.yml` file.
242
+ #
243
+ def each_advisory_path_for(name,&block)
244
+ Dir.glob(File.join(@path,'gems',name,'*.yml'),&block)
245
+ end
246
+
247
+ end
248
+ end
249
+ end