class Gem::SafeMarshal::Visitors::ToRuby
Constants
- COMPAT_CLASSES
Public Class Methods
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 8 def initialize(permitted_classes:, permitted_symbols:, permitted_ivars:) @permitted_classes = permitted_classes @permitted_symbols = ["E"].concat(permitted_symbols).concat(permitted_classes) @permitted_ivars = permitted_ivars @objects = [] @symbols = [] @class_cache = {} @stack = ["root"] @stack_idx = 1 end
Public Instance Methods
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 26 def visit(target) stack_idx = @stack_idx super ensure @stack_idx = stack_idx - 1 end
Calls superclass method
Private Instance Methods
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 357 def call_method(receiver, method, *args) receiver.__send__(method, *args) rescue NoMethodError => e raise unless e.receiver == receiver raise MethodCallError, "Unable to call #{method.inspect} on #{receiver.inspect}, perhaps it is a class using marshal compat, which is not visible in ruby? #{e}" end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 365 def formatted_stack formatted = [] @stack[0, @stack_idx].each do |e| if e.is_a?(Integer) if formatted.last == "ivar_" formatted[-1] = "ivar_#{e}" else formatted << "[#{e}]" end else formatted << e end end formatted end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 63 def map_ivars(klass, ivars) stack_idx = @stack_idx ivars.map.with_index do |(k, v), i| @stack_idx = stack_idx push_stack "ivar_" push_stack i k = resolve_ivar(klass, k) @stack_idx = stack_idx push_stack k next k, visit(v) end end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 35 def push_stack(element) @stack[@stack_idx] = element @stack_idx += 1 end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 352 def register_object(o) @objects << o o end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 281 def resolve_class(n) @class_cache[n] ||= begin to_s = resolve_symbol_name(n) raise UnpermittedClassError.new(name: to_s, stack: formatted_stack) unless @permitted_classes.include?(to_s) visit_symbol_type(n) begin ::Object.const_get(to_s) rescue NameError raise ArgumentError, "Undefined class #{to_s.inspect}" end end end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 308 def resolve_ivar(klass, name) to_s = resolve_symbol_name(name) raise UnpermittedIvarError.new(symbol: to_s, klass: klass, stack: formatted_stack) unless @permitted_ivars.fetch(klass.name, [].freeze).include?(to_s) visit_symbol_type(name) end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 329 def resolve_symbol_name(element) case element when Elements::Symbol element.name when Elements::SymbolLink visit_Gem_SafeMarshal_Elements_SymbolLink(element).name else raise FormatError, "Expected symbol or symbol link, got #{element.inspect} @ #{formatted_stack.join(".")}" end end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 40 def visit_Gem_SafeMarshal_Elements_Array(a) array = register_object([]) elements = a.elements size = elements.size idx = 0 # not idiomatic, but there's a huge number of IMEMOs allocated here, so we avoid the block # because this is such a hot path when doing a bundle install with the full index while idx < size push_stack idx array << visit(elements[idx]) idx += 1 end array end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 241 def visit_Gem_SafeMarshal_Elements_Bignum(b) result = 0 b.data.each_byte.with_index do |byte, exp| result += (byte * 2**(exp * 8)) end case b.sign when 43 # ?+ result when 45 # ?- -result else raise FormatError, "Unexpected sign for Bignum #{b.sign.chr.inspect} (#{b.sign})" end end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 218 def visit_Gem_SafeMarshal_Elements_False(_) false end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 226 def visit_Gem_SafeMarshal_Elements_Float(f) register_object( case f.string when "inf" ::Float::INFINITY when "-inf" -::Float::INFINITY when "nan" ::Float::NAN else f.string.to_f end ) end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 154 def visit_Gem_SafeMarshal_Elements_Hash(o) hash = register_object({}) o.pairs.each_with_index do |(k, v), i| push_stack i k = visit(k) push_stack k hash[k] = visit(v) end hash end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 167 def visit_Gem_SafeMarshal_Elements_HashWithDefaultValue(o) hash = visit_Gem_SafeMarshal_Elements_Hash(o) push_stack :default hash.default = visit(o.default) hash end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 206 def visit_Gem_SafeMarshal_Elements_Integer(i) i.int end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 210 def visit_Gem_SafeMarshal_Elements_Nil(_) nil end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 174 def visit_Gem_SafeMarshal_Elements_Object(o) register_object(resolve_class(o.name).allocate) end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 178 def visit_Gem_SafeMarshal_Elements_ObjectLink(o) @objects.fetch(o.offset) end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 222 def visit_Gem_SafeMarshal_Elements_String(s) register_object(+s.str) end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 57 def visit_Gem_SafeMarshal_Elements_Symbol(s) name = s.name raise UnpermittedSymbolError.new(symbol: name, stack: formatted_stack) unless @permitted_symbols.include?(name) visit_symbol_type(s) end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 182 def visit_Gem_SafeMarshal_Elements_SymbolLink(o) @symbols.fetch(o.offset) end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 214 def visit_Gem_SafeMarshal_Elements_True(_) true end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 257 def visit_Gem_SafeMarshal_Elements_UserClass(r) if resolve_class(r.name) == ::Hash && r.wrapped_object.is_a?(Elements::Hash) hash = register_object({}.compare_by_identity) o = r.wrapped_object o.pairs.each_with_index do |(k, v), i| push_stack i k = visit(k) push_stack k hash[k] = visit(v) end if o.is_a?(Elements::HashWithDefaultValue) push_stack :default hash.default = visit(o.default) end hash else raise UnsupportedError.new("Unsupported user class #{resolve_class(r.name)} in marshal stream", stack: formatted_stack) end end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 186 def visit_Gem_SafeMarshal_Elements_UserDefined(o) register_object(call_method(resolve_class(o.name), :_load, o.binary_string)) end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 190 def visit_Gem_SafeMarshal_Elements_UserMarshal(o) klass = resolve_class(o.name) compat = COMPAT_CLASSES.fetch(klass, nil) idx = @objects.size object = register_object(call_method(compat || klass, :allocate)) push_stack :data ret = call_method(object, :marshal_load, visit(o.data)) if compat object = @objects[idx] = ret end object end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 79 def visit_Gem_SafeMarshal_Elements_WithIvars(e) object_offset = @objects.size push_stack "object" object = visit(e.object) ivars = map_ivars(object.class, e.ivars) case e.object when Elements::UserDefined if object.class == ::Time internal = [] ivars.reject! do |k, v| case k when :offset, :zone, :nano_num, :nano_den, :submicro internal << [k, v] true else false end end s = e.object.binary_string # 122 is the largest integer that can be represented in marshal in a single byte raise TimeTooLargeError.new("binary string too large", stack: formatted_stack) if s.bytesize > 122 marshal_string = "\x04\bIu:\tTime".b marshal_string.concat(s.bytesize + 5) marshal_string << s # internal is limited to 5, so no overflow is possible marshal_string.concat(internal.size + 5) internal.each do |k, v| k = k.name # ivar name can't be too large because only known ivars are in the internal ivars list marshal_string.concat(":") marshal_string.concat(k.bytesize + 5) marshal_string.concat(k) dumped = Marshal.dump(v) dumped[0, 2] = "" marshal_string.concat(dumped) end object = @objects[object_offset] = Marshal.load(marshal_string) end when Elements::String enc = nil ivars.reject! do |k, v| case k when :E case v when TrueClass enc = "UTF-8" when FalseClass enc = "US-ASCII" else raise FormatError, "Unexpected value for String :E #{v.inspect}" end when :encoding enc = v else next false end true end object.force_encoding(enc) if enc end ivars.each do |k, v| object.instance_variable_set k, v end object end
Source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 316 def visit_symbol_type(element) case element when Elements::Symbol sym = element.name.to_sym @symbols << sym sym when Elements::SymbolLink visit_Gem_SafeMarshal_Elements_SymbolLink(element) end end