class Gem::SafeMarshal::Visitors::ToRuby
Constants
- COMPAT_CLASSES
Public Class Methods
new(permitted_classes:, permitted_symbols:, permitted_ivars:)
click to toggle 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
visit(target)
click to toggle source
Calls superclass method
# 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
Private Instance Methods
call_method(receiver, method, *args)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 350 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
formatted_stack()
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 358 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
map_ivars(klass, ivars)
click to toggle 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
push_stack(element)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 35 def push_stack(element) @stack[@stack_idx] = element @stack_idx += 1 end
register_object(o)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 345 def register_object(o) @objects << o o end
resolve_class(n)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 274 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
resolve_ivar(klass, name)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 301 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
resolve_symbol_name(element)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 322 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
visit_Gem_SafeMarshal_Elements_Array(a)
click to toggle 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 until idx == size push_stack idx array << visit(elements[idx]) idx += 1 end array end
visit_Gem_SafeMarshal_Elements_Bignum(b)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 234 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
visit_Gem_SafeMarshal_Elements_False(_)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 213 def visit_Gem_SafeMarshal_Elements_False(_) false end
visit_Gem_SafeMarshal_Elements_Float(f)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 221 def visit_Gem_SafeMarshal_Elements_Float(f) case f.string when "inf" ::Float::INFINITY when "-inf" -::Float::INFINITY when "nan" ::Float::NAN else f.string.to_f end end
visit_Gem_SafeMarshal_Elements_Hash(o)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 149 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
visit_Gem_SafeMarshal_Elements_HashWithDefaultValue(o)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 162 def visit_Gem_SafeMarshal_Elements_HashWithDefaultValue(o) hash = visit_Gem_SafeMarshal_Elements_Hash(o) push_stack :default hash.default = visit(o.default) hash end
visit_Gem_SafeMarshal_Elements_Integer(i)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 201 def visit_Gem_SafeMarshal_Elements_Integer(i) i.int end
visit_Gem_SafeMarshal_Elements_Nil(_)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 205 def visit_Gem_SafeMarshal_Elements_Nil(_) nil end
visit_Gem_SafeMarshal_Elements_Object(o)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 169 def visit_Gem_SafeMarshal_Elements_Object(o) register_object(resolve_class(o.name).allocate) end
visit_Gem_SafeMarshal_Elements_ObjectLink(o)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 173 def visit_Gem_SafeMarshal_Elements_ObjectLink(o) @objects[o.offset] end
visit_Gem_SafeMarshal_Elements_String(s)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 217 def visit_Gem_SafeMarshal_Elements_String(s) register_object(+s.str) end
visit_Gem_SafeMarshal_Elements_Symbol(s)
click to toggle 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
visit_Gem_SafeMarshal_Elements_SymbolLink(o)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 177 def visit_Gem_SafeMarshal_Elements_SymbolLink(o) @symbols[o.offset] end
visit_Gem_SafeMarshal_Elements_True(_)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 209 def visit_Gem_SafeMarshal_Elements_True(_) true end
visit_Gem_SafeMarshal_Elements_UserClass(r)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 250 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
visit_Gem_SafeMarshal_Elements_UserDefined(o)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 181 def visit_Gem_SafeMarshal_Elements_UserDefined(o) register_object(call_method(resolve_class(o.name), :_load, o.binary_string)) end
visit_Gem_SafeMarshal_Elements_UserMarshal(o)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 185 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
visit_Gem_SafeMarshal_Elements_WithIvars(e)
click to toggle 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 marshal_string = "\x04\bIu:\tTime".b marshal_string.concat(s.size + 5) marshal_string << s marshal_string.concat(internal.size + 5) internal.each do |k, v| marshal_string.concat(":") marshal_string.concat(k.size + 5) marshal_string.concat(k.to_s) 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
visit_symbol_type(element)
click to toggle source
# File lib/rubygems/safe_marshal/visitors/to_ruby.rb, line 309 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