lib/safe_yaml/load.rb in safe_yaml-1.0.1 vs lib/safe_yaml/load.rb in safe_yaml-1.0.2

- old
+ new

@@ -2,12 +2,38 @@ # This needs to be defined up front in case any internal classes need to base # their behavior off of this. module SafeYAML YAML_ENGINE = defined?(YAML::ENGINE) ? YAML::ENGINE.yamler : "syck" + LIBYAML_VERSION = YAML_ENGINE == "psych" && Psych.const_defined?("LIBYAML_VERSION", false) ? Psych::LIBYAML_VERSION : nil + + def self.check_libyaml_version + if YAML_ENGINE == "psych" && (LIBYAML_VERSION.nil? || LIBYAML_VERSION < "0.1.6") + Kernel.warn <<-EOWARNING.gsub(/^ +/, ' ') + + \e[33mSafeYAML Warning\e[39m + \e[33m----------------\e[39m + + \e[31mYou appear to have an outdated version of libyaml (#{LIBYAML_VERSION}) installed on your system.\e[39m + + Prior to 0.1.6, libyaml is vulnerable to a heap overflow exploit from malicious YAML payloads. + + For more info, see: + https://www.ruby-lang.org/en/news/2014/03/29/heap-overflow-in-yaml-uri-escape-parsing-cve-2014-2525/ + + The easiest thing to do right now is probably to update Psych to the latest version and enable + the 'bundled-libyaml' option, which will install a vendored libyaml with the vulnerability patched: + + \e[32mgem install psych -- --enable-bundled-libyaml\e[39m + + EOWARNING + end + end end +SafeYAML.check_libyaml_version + require "set" require "safe_yaml/deep" require "safe_yaml/parse/hexadecimal" require "safe_yaml/parse/sexagesimal" require "safe_yaml/parse/date" @@ -34,11 +60,32 @@ :raise_on_unknown_tag => false }) OPTIONS = Deep.copy(DEFAULT_OPTIONS) + PREDEFINED_TAGS = {} + + if YAML_ENGINE == "syck" + YAML.tagged_classes.each do |tag, klass| + PREDEFINED_TAGS[klass] = tag + end + + else + # Special tags appear to be hard-coded in Psych: + # https://github.com/tenderlove/psych/blob/v1.3.4/lib/psych/visitors/to_ruby.rb + # Fortunately, there aren't many that SafeYAML doesn't already support. + PREDEFINED_TAGS.merge!({ + Exception => "!ruby/exception", + Range => "!ruby/range", + Regexp => "!ruby/regexp", + }) + end + + Deep.freeze(PREDEFINED_TAGS) + module_function + def restore_defaults! OPTIONS.clear.merge!(Deep.copy(DEFAULT_OPTIONS)) end def tag_safety_check!(tag, options) @@ -59,11 +106,11 @@ klass_name = klass.name raise "#{klass} cannot be anonymous" if klass_name.nil? || klass_name.empty? # Whitelist any built-in YAML tags supplied by Syck or Psych. - predefined_tag = predefined_tags[klass] + predefined_tag = PREDEFINED_TAGS[klass] if predefined_tag OPTIONS[:whitelisted_tags] << predefined_tag return end @@ -74,33 +121,9 @@ when "psych" then "!ruby/#{tag_class}" when "syck" then "tag:ruby.yaml.org,2002:#{tag_class}" else raise "unknown YAML_ENGINE #{YAML_ENGINE}" end OPTIONS[:whitelisted_tags] << "#{tag_prefix}:#{klass_name}" - end - - def predefined_tags - if @predefined_tags.nil? - @predefined_tags = {} - - if YAML_ENGINE == "syck" - YAML.tagged_classes.each do |tag, klass| - @predefined_tags[klass] = tag - end - - else - # Special tags appear to be hard-coded in Psych: - # https://github.com/tenderlove/psych/blob/v1.3.4/lib/psych/visitors/to_ruby.rb - # Fortunately, there aren't many that SafeYAML doesn't already support. - @predefined_tags.merge!({ - Exception => "!ruby/exception", - Range => "!ruby/range", - Regexp => "!ruby/regexp", - }) - end - end - - @predefined_tags end if YAML_ENGINE == "psych" def tag_is_explicitly_trusted?(tag) false