Class Gem::Validator
In: lib/rubygems/validator.rb
Parent: Object

Validator performs various gem file and gem database validation

Methods

Included Modules

Gem::UserInteraction

Constants

ErrorData = Struct.new :path, :problem

Public Class methods

[Source]

    # File lib/rubygems/validator.rb, line 17
17:   def initialize
18:     require 'find'
19:     require 'digest'
20:   end

Public Instance methods

Checks the gem directory for the following potential inconsistencies/problems:

  • Checksum gem itself
  • For each file in each gem, check consistency of installed versions
  • Check for files that aren‘t part of the gem but are in the gems directory
  • 1 cache - 1 spec - 1 directory.

returns a hash of ErrorData objects, keyed on the problem gem‘s name.

[Source]

     # File lib/rubygems/validator.rb, line 84
 84:   def alien(gems=[])
 85:     errors = Hash.new { |h,k| h[k] = {} }
 86: 
 87:     Gem::SourceIndex.from_installed_gems.each do |gem_name, gem_spec|
 88:       next unless gems.include? gem_spec.name unless gems.empty?
 89: 
 90:       install_dir = gem_spec.installation_path
 91:       gem_path = Gem.cache_gem(gem_spec.file_name, install_dir)
 92:       spec_path = File.join install_dir, "specifications", gem_spec.spec_name
 93:       gem_directory = gem_spec.full_gem_path
 94: 
 95:       unless File.directory? gem_directory then
 96:         errors[gem_name][gem_spec.full_name] =
 97:           "Gem registered but doesn't exist at #{gem_directory}"
 98:         next
 99:       end
100: 
101:       unless File.exist? spec_path then
102:         errors[gem_name][spec_path] = "Spec file missing for installed gem"
103:       end
104: 
105:       begin
106:         verify_gem_file(gem_path)
107: 
108:         good, gone, unreadable = nil, nil, nil, nil
109: 
110:         open gem_path, Gem.binary_mode do |file|
111:           format = Gem::Format.from_file_by_path(gem_path)
112: 
113:           good, gone = format.file_entries.partition { |entry, _|
114:             File.exist? File.join(gem_directory, entry['path'])
115:           }
116: 
117:           gone.map! { |entry, _| entry['path'] }
118:           gone.sort.each do |path|
119:             errors[gem_name][path] = "Missing file"
120:           end
121: 
122:           good, unreadable = good.partition { |entry, _|
123:             File.readable? File.join(gem_directory, entry['path'])
124:           }
125: 
126:           unreadable.map! { |entry, _| entry['path'] }
127:           unreadable.sort.each do |path|
128:             errors[gem_name][path] = "Unreadable file"
129:           end
130: 
131:           good.each do |entry, data|
132:             begin
133:               next unless data # HACK `gem check -a mkrf`
134: 
135:               open File.join(gem_directory, entry['path']), Gem.binary_mode do |f|
136:                 unless Digest::MD5.hexdigest(f.read).to_s ==
137:                     Digest::MD5.hexdigest(data).to_s then
138:                   errors[gem_name][entry['path']] = "Modified from original"
139:                 end
140:               end
141:             end
142:           end
143:         end
144: 
145:         installed_files = find_files_for_gem(gem_directory)
146:         good.map! { |entry, _| entry['path'] }
147:         extras = installed_files - good - unreadable
148: 
149:         extras.each do |extra|
150:           errors[gem_name][extra] = "Extra file"
151:         end
152:       rescue Gem::VerificationError => e
153:         errors[gem_name][gem_path] = e.message
154:       end
155:     end
156: 
157:     errors.each do |name, subhash|
158:       errors[name] = subhash.map { |path, msg| ErrorData.new(path, msg) }
159:     end
160: 
161:     errors
162:   end

[Source]

     # File lib/rubygems/validator.rb, line 164
164:   def remove_leading_dot_dir(path)
165:     path.sub(/^\.\//, "")
166:   end

Given a gem file‘s contents, validates against its own MD5 checksum

gem_data:[String] Contents of the gem file

[Source]

    # File lib/rubygems/validator.rb, line 26
26:   def verify_gem(gem_data)
27:     raise Gem::VerificationError, 'empty gem file' if gem_data.size == 0
28: 
29:     unless gem_data =~ /MD5SUM/ then
30:       return # Don't worry about it...this sucks.  Need to fix MD5 stuff for
31:       # new format
32:       # FIXME
33:     end
34: 
35:     sum_data = gem_data.gsub(/MD5SUM = "([a-z0-9]+)"/,
36:                              "MD5SUM = \"#{"F" * 32}\"")
37: 
38:     unless Digest::MD5.hexdigest(sum_data) == $1.to_s then
39:       raise Gem::VerificationError, 'invalid checksum for gem file'
40:     end
41:   end

Given the path to a gem file, validates against its own MD5 checksum

gem_path:[String] Path to gem file

[Source]

    # File lib/rubygems/validator.rb, line 48
48:   def verify_gem_file(gem_path)
49:     open gem_path, Gem.binary_mode do |file|
50:       gem_data = file.read
51:       verify_gem gem_data
52:     end
53:   rescue Errno::ENOENT, Errno::EINVAL
54:     raise Gem::VerificationError, "missing gem file #{gem_path}"
55:   end

[Validate]